[ function ($attribute, $value, $fail) { $stmtSentType = $this->input('stmt_sent_type'); // Jika stmt_sent_type kosong atau tidak ada, maka account_number wajib diisi if (empty($stmtSentType) || (is_array($stmtSentType) && count(array_filter($stmtSentType)) === 0)) { if (empty($value)) { $fail('Account number is required when statement type is not specified.'); } } }, 'string' ], 'stmt_sent_type' => ['nullable', 'array'], 'stmt_sent_type.*' => ['string', 'in:ALL,BY.EMAIL,BY.MAIL.TO.DOM.ADDR,BY.MAIL.TO.KTP.ADDR,NO.PRINT,PRINT'], 'is_period_range' => ['sometimes', 'boolean'], 'email' => ['nullable', 'email'], 'email_sent_at' => ['nullable', 'timestamp'], 'request_type' => ['sometimes', 'string', 'in:single_account,branch,all_branches'], 'batch_id' => ['nullable', 'string'], 'period_from' => [ 'required', 'string', 'regex:/^\d{6}$/', // YYYYMM format // Prevent duplicate requests with same account number and period function ($attribute, $value, $fail) { // Hanya cek duplikasi jika account_number ada if (!empty($this->input('account_number'))) { $query = Statement::where('account_number', $this->input('account_number')) ->where('authorization_status', '!=', 'rejected') ->where(function($query) { $query->where('is_available', true) ->orWhere('is_generated', true); }) ->where('user_id', $this->user()->id) ->where('period_from', $value); // If this is an update request, exclude the current record if ($this->route('statement')) { $query->where('id', '!=', $this->route('statement')); } // If period_to is provided, check for overlapping periods if ($this->input('period_to')) { $query->where(function ($q) use ($value) { $q->where('period_from', '<=', $this->input('period_to')) ->where('period_to', '>=', $value); }); } if ($query->exists()) { $fail('A statement request with this account number and period already exists.'); } } } ], ]; // If it's a period range, require period_to if ($this->input('period_to')) { $rules['period_to'] = [ 'required', 'string', 'regex:/^\d{6}$/', // YYYYMM format 'gte:period_from' // period_to must be greater than or equal to period_from ]; } return $rules; } /** * Get custom messages for validator errors. */ public function messages(): array { return [ 'account_number.required' => 'Account number is required when statement type is not specified', 'stmt_sent_type.*.in' => 'Invalid statement type selected', 'period_from.required' => 'Period is required', 'period_from.regex' => 'Period must be in YYYYMM format', 'period_to.required' => 'End period is required for period range', 'period_to.regex' => 'End period must be in YYYYMM format', 'period_to.gte' => 'End period must be after or equal to start period', 'request_type.in' => 'Request type must be single_account, branch, or all_branches', ]; } /** * Prepare the data for validation. */ protected function prepareForValidation(): void { if ($this->has('period_from')) { // Convert to YYYYMM format $this->merge([ 'period_from' => substr($this->period_from, 0, 4) . substr($this->period_from, 5, 2), ]); } if ($this->has('period_to')) { // Convert to YYYYMM format $this->merge([ 'period_to' => substr($this->period_to, 0, 4) . substr($this->period_to, 5, 2), ]); // Only set is_period_range to true if period_to is different from period_from if ($this->period_to !== $this->period_from) { $this->merge([ 'is_period_range' => true, ]); } } // Set default request_type if not provided if (!$this->has('request_type')) { $this->merge([ 'request_type' => 'single_account', ]); } } }