From 998a74906d1e97a35caeb1dd6ab04bc0a057d3a7 Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Tue, 26 May 2026 14:28:37 +0530 Subject: [PATCH] FIX_CLAIM_STATUS_MISS_MATCH --- app/Controllers/TicketController.php | 124 ++++++++++++++++++++++++++- app/Views/ticket_form_gmc.php | 69 ++++++++++++++- 2 files changed, 190 insertions(+), 3 deletions(-) diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index ed1fdc15..4421fb71 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -968,6 +968,58 @@ class TicketController extends BaseController return $extra_fields; } + /** + * Overlay conditional `required` rules onto a base validation rule-set based on the + * submitted `claim_status_id`. For example, when claim_status_id is a "Settled" status + * (11/24/34/44), fields like `utr_details` and `settled_date` are promoted from + * `permit_empty` to `required` so the request is rejected when they are blank. + * + * Letter URL fields (`approved_letter`, `settle_letter`) accept an alternative PDF + * upload, so they are intentionally not promoted here; that pairing is enforced by + * the upload validation flow. `approved_description` stays optional to match the + * existing UI contract. + */ + private function applyClaimStatusConditionalRules(array $rules): array + { + $claimStatusId = (int) $this->request->getPost('claim_status_id'); + if ($claimStatusId <= 0) { + return $rules; + } + + if (!isset($this->extraFields[$claimStatusId]) || !is_array($this->extraFields[$claimStatusId])) { + return $rules; + } + + $requiredFields = $this->extraFields[$claimStatusId]; + $skipFields = ['approved_letter', 'settle_letter', 'approved_description']; + + foreach ($requiredFields as $field) { + if (in_array($field, $skipFields, true) || !isset($rules[$field])) { + continue; + } + + $existingRules = $rules[$field]['rules'] ?? ''; + $parts = $existingRules === '' + ? [] + : array_values(array_filter(array_map('trim', explode('|', $existingRules)), function ($part) { + return $part !== '' && $part !== 'permit_empty'; + })); + + if (!in_array('required', $parts, true)) { + array_unshift($parts, 'required'); + } + $rules[$field]['rules'] = implode('|', array_unique($parts)); + + if (!isset($rules[$field]['errors']) || !is_array($rules[$field]['errors'])) { + $rules[$field]['errors'] = []; + } + $label = $rules[$field]['label'] ?? ucwords(str_replace('_', ' ', $field)); + $rules[$field]['errors']['required'] = $label . ' is required for the selected claim status.'; + } + + return $rules; + } + public function view_ticket($ticket_id) { // $ticket_data = $this->ticketMasterModel->where('id', $ticket_id)->where('is_active', 1)->first(); @@ -1456,6 +1508,8 @@ class TicketController extends BaseController ]; } + $rules = $this->applyClaimStatusConditionalRules($rules); + if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, @@ -1944,6 +1998,9 @@ class TicketController extends BaseController 'return_remark' => ['label' => 'Return Remark','rules' => 'permit_empty','errors' => []], ]; } + + $rules = $this->applyClaimStatusConditionalRules($rules); + if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, @@ -3053,7 +3110,7 @@ class TicketController extends BaseController } } - public function getLastMatchedStatus($incoming_form_values, $old_ticket_data) + public function getLastMatchedStatusOld($incoming_form_values, $old_ticket_data) { if (empty($incoming_form_values) || !isset($incoming_form_values['claim_status_id']) || !isset($incoming_form_values['extra_fields_array_for_validate'])) { return null; @@ -3122,6 +3179,71 @@ class TicketController extends BaseController return $lastMatchedStatus; } + public function getLastMatchedStatus($incoming_form_values, $old_ticket_data) + { + // Preserve the incoming claim_status_id (if any) as the safe fallback so callers + // never accidentally nullify the status when validation inputs are missing. + $incomingStatus = $incoming_form_values['claim_status_id'] ?? null; + + if (empty($incoming_form_values) || !isset($incoming_form_values['extra_fields_array_for_validate'])) { + return $incomingStatus; + } + + $lastMatchedStatus = $incomingStatus; + + $statusMapping = json_decode($incoming_form_values['extra_fields_array_for_validate'], true); + if (!is_array($statusMapping) || empty($statusMapping)) { + return $incomingStatus; + } + + // Drop transitional / aggregate statuses that should never be auto-selected. + $removeKeys = is_array($this->removeArrayKey) ? $this->removeArrayKey : []; + $statusMapping = array_diff_key($statusMapping, $removeKeys); + + $oldStatusId = is_array($old_ticket_data) ? ($old_ticket_data['claim_status_id'] ?? null) : null; + + $skipFields = ['approved_description']; + + foreach ($statusMapping as $status => $requiredFields) { + if (empty($requiredFields)) { + continue; + } + + // Skip the current/old status — we only advance forward to a newer status. + if ($oldStatusId !== null && (int) $status === (int) $oldStatusId) { + continue; + } + + if (!is_array($requiredFields)) { + $requiredFields = [$requiredFields]; + } + + $allFieldsMatched = true; + foreach ($requiredFields as $field) { + if (in_array($field, $skipFields, true)) { + continue; + } + + if (!isset($incoming_form_values[$field]) || $incoming_form_values[$field] === '' || $incoming_form_values[$field] === null) { + $allFieldsMatched = false; + break; + } + } + + if ($allFieldsMatched) { + $lastMatchedStatus = $status; + } + } + + // When a TPA number is present, promote the base "registered" statuses + // (1 -> 2 for standard, 67 -> 68 for the alternate flow). + if (!empty($incoming_form_values['tpa_no']) && in_array((int) $lastMatchedStatus, [1, 67], true)) { + $lastMatchedStatus = ((int) $lastMatchedStatus === 1) ? 2 : 68; + } + + return $lastMatchedStatus; + } + public function getPolicyStartDate() { diff --git a/app/Views/ticket_form_gmc.php b/app/Views/ticket_form_gmc.php index e473d36b..03034d81 100644 --- a/app/Views/ticket_form_gmc.php +++ b/app/Views/ticket_form_gmc.php @@ -579,6 +579,42 @@ var doa; var STATUS_SECTION_CLASSES = ['cda_ir', 'settled', 'approved', 'rejected', 'up_qdr', 'up_cnu', 'canceled', 'returned', 'payment', 'non_id']; var statusSectionInitialValues = {}; + // Cached full mapping of claim_status_id -> required fields. + // The hidden input gets destructively reduced when the status dropdown changes, + // so we capture the full map once at load time for use by frontend required-logic. + var STATUS_REQUIRED_FIELDS_MAP = {}; + + function initStatusRequiredFieldsMap() { + var raw = $('#extra_fields_array_for_validate').val(); + if (!raw) { + STATUS_REQUIRED_FIELDS_MAP = {}; + return; + } + try { + var parsed = JSON.parse(raw); + STATUS_REQUIRED_FIELDS_MAP = (parsed && typeof parsed === 'object') ? parsed : {}; + } catch (e) { + STATUS_REQUIRED_FIELDS_MAP = {}; + } + } + + function getRequiredFieldsForSelectedStatus() { + if (!STATUS_REQUIRED_FIELDS_MAP) { + return []; + } + var selected = $('#claim_status_id').val(); + if (selected === undefined || selected === null || selected === '') { + return []; + } + var fields = STATUS_REQUIRED_FIELDS_MAP[selected]; + if (Array.isArray(fields)) { + return fields; + } + if (fields) { + return [fields]; + } + return []; + } function captureSectionInitialValues(sectionClass) { if (!sectionClass) { @@ -656,12 +692,40 @@ return; } if (sectionClass === 'approved' || sectionClass === 'settled') { + // The URL-or-File letter pair has its own validity rules (see enforceLetterPairRules). + // We still need to enforce required on the other status-mandated fields + // (e.g. utr_details, settled_date, approved_amount, approved_date), which the + // previous early-return was skipping. enforceLetterPairRules(); - updateSectionRequiredLabels(sectionClass, hasSectionChanged(sectionClass)); + + var letterPairIds = sectionClass === 'approved' + ? ['approved_letter', 'approved_letter_file', 'approved_description'] + : ['settle_letter', 'settle_letter_file']; + + var statusRequiredFields = getRequiredFieldsForSelectedStatus(); + var sectionChanged = hasSectionChanged(sectionClass); + + $('.' + sectionClass).find('input, textarea, select').each(function () { + var id = this.id; + if (!id || letterPairIds.indexOf(id) !== -1) { + return; + } + var statusRequires = statusRequiredFields.indexOf(id) !== -1; + $(this).prop('required', statusRequires || sectionChanged); + }); + + var statusMandatesSection = false; + for (var i = 0; i < statusRequiredFields.length; i++) { + if ($('.' + sectionClass + ' #' + statusRequiredFields[i]).length > 0) { + statusMandatesSection = true; + break; + } + } + updateSectionRequiredLabels(sectionClass, sectionChanged || statusMandatesSection); return; } var changed = hasSectionChanged(sectionClass); - $('.' + sectionClass).find('input, textarea, select').each(function() { + $('.' + sectionClass).find('input, textarea, select').each(function () { if (this.id === 'approved_description') { return; } @@ -738,6 +802,7 @@ allowInput: false }); + initStatusRequiredFieldsMap(); updateClaimStatusDisplay("") var extraFields = ; GlobelExtraFields = extraFields;