diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 2e68cac..e69e7d7 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -206,11 +206,14 @@ $routes->group('api', ['filter' => ["jwtAuth:1,2,3,4,agent","appSignature"] ], f $routes->get('invoice/details', 'InvoiceController::findInvoiceWithItems'); $routes->post('invoice/create-or-update', 'InvoiceController::createOrUpdateInvoice'); $routes->post('invoice/add-payment', 'InvoiceController::addInvoicePayment'); + $routes->get('invoice/add-payment-history', 'InvoiceController::addPaymentHistory'); $routes->get('invoice/delete', 'InvoiceController::deleteInvoice'); $routes->post('invoice/commission-rate-list', 'InvoiceController::getCommissionRateList'); $routes->get('invoice/getAgentUnusedCommissionList', 'InvoiceController::getAgentUnusedCommissionList'); $routes->post('invoice/bulk-upload-commission', 'InvoiceController::bulkUploadCommission'); $routes->post('invoice/bulk-upload-commission/proceed', 'InvoiceController::bulkUploadCommissionProceed'); + $routes->post('invoice/bulk-upload-commission', 'InvoiceController::bulkUploadCommission'); + $routes->post('invoice/bulk-upload-commission/proceed', 'InvoiceController::bulkUploadCommissionProceed'); // SALES EXECUTIVE diff --git a/app/Controllers/InvoiceController.php b/app/Controllers/InvoiceController.php index beb89d6..2ec020c 100644 --- a/app/Controllers/InvoiceController.php +++ b/app/Controllers/InvoiceController.php @@ -9,9 +9,9 @@ use App\Models\InvoiceModel; use App\Models\InvoiceItemModel; use App\Models\InvoiceUtrModel; use App\Models\PartnerAccountHistoryModel; -// use App\Models\AgentIncentiveFileModel; +use App\Models\AgentIncentiveFileModel; use CodeIgniter\Database\Exceptions\DataException; -// use PhpOffice\PhpSpreadsheet\IOFactory; +use PhpOffice\PhpSpreadsheet\IOFactory; class InvoiceController extends ResourceController { @@ -22,7 +22,7 @@ class InvoiceController extends ResourceController protected $InvoiceItemModel; protected $InvoiceUtrModel; protected $PartnerAccountHistoryModel; - // protected $AgentIncentiveFileModel; + protected $AgentIncentiveFileModel; protected $db; public function __construct() @@ -34,7 +34,7 @@ class InvoiceController extends ResourceController $this->InvoiceItemModel = new InvoiceItemModel(); $this->InvoiceUtrModel = new InvoiceUtrModel(); $this->PartnerAccountHistoryModel = new PartnerAccountHistoryModel(); - // $this->AgentIncentiveFileModel = new AgentIncentiveFileModel(); + $this->AgentIncentiveFileModel = new AgentIncentiveFileModel(); $this->db = \Config\Database::connect(); } @@ -197,7 +197,7 @@ class InvoiceController extends ResourceController if (!$invoiceId) { // Generate invoice number only for CREATE - $invoiceData['invoice_no'] = $this->generateInvoiceNo($input['pos_id'] ?? 0); + $invoiceData['invoice_no'] = $this->generateInvoiceNo($input['pos_id']); $invoiceData['created_at'] = date('Y-m-d H:i:s'); $invoiceData['created_by'] = $input['created_by'] ?? 0; @@ -270,8 +270,9 @@ class InvoiceController extends ResourceController private function generateInvoiceNo($pos_id) { + $pos_id = !empty($pos_id) ? $pos_id : 0; $year = date('Y'); - $prefix = !empty($pos_id) & $pos_id != 0 ? "NIIB/$pos_id/$year/" : "MIG/$year/"; + $prefix = "NIIB/$pos_id/$year/"; // Get last invoice of current year $lastInvoice = $this->InvoiceModel @@ -595,116 +596,181 @@ class InvoiceController extends ResourceController } } - // public function bulkUploadCommission() - // { - // $this->db->transBegin(); - // try { - // $input = $this->request->getJSON(true); - // if (!is_array($input)) { - // $input = []; - // } + public function addPaymentHistory() + { + try { + $invoiceId = (int)($this->request->getGet('invoice_id') ?? 0); + if ($invoiceId <= 0) { + return $this->respond([ + 'status' => 'failed', + 'code' => 400, + 'message'=> 'invoice_id is required' + ], 400); + } - // $post = $this->request->getPost(); - // if (!is_array($post)) { - // $post = []; - // } + $invoice = $this->InvoiceModel + ->select('id, invoice_no, invoice_amount') + ->where('id', $invoiceId) + ->where('is_active', 1) + ->first(); - // $rows = $input['rows'] ?? []; - // $updatedBy = (int)($input['updated_by'] ?? ($post['created_by'] ?? 0)); + if (empty($invoice)) { + return $this->respond([ + 'status' => 'failed', + 'code' => 404, + 'message'=> 'Invoice not found' + ], 404); + } - // // Multipart flow: read excel on backend and store upload metadata. - // $file = $this->request->getFile('file_name'); - // if ($file && $file->isValid()) { - // $month = date('Y-m-d'); - // $uploadPath = WRITEPATH . 'uploads/agent/incentive_file/'; - // if (!is_dir($uploadPath)) { - // mkdir($uploadPath, 0777, true); - // } + $history = $this->db->table('partner_account_history pah') + ->select(' + pah.id, + pah.invoice_id, + pi.invoice_no, + pi.invoice_amount AS total_amount, + pah.paid_amount, + pah.paid_date, + pah.created_at, + COALESCE(ps.name, "-") AS createdby_name, + ( + pi.invoice_amount - COALESCE(( + SELECT SUM(pah2.paid_amount) + FROM partner_account_history pah2 + WHERE pah2.invoice_id = pah.invoice_id + AND pah2.is_active = 1 + AND pah2.id <= pah.id + ), 0) + ) AS balance_amount + ', false) + ->join('partner_invoice pi', 'pi.id = pah.invoice_id', 'inner') + ->join('partner_staff ps', 'ps.id = pah.created_by', 'left') + ->where('pah.invoice_id', $invoiceId) + ->where('pah.is_active', 1) + ->orderBy('pah.id', 'DESC') + ->get() + ->getResultArray(); - // $storedFileName = time() . '_' . $file->getRandomName(); - // $file->move($uploadPath, $storedFileName); - - // $this->AgentIncentiveFileModel->insert([ - // 'incentive_month' => $month, - // 'incentive_file_name' => $storedFileName, - // 'file_type' => 'invoice', - // 'is_active' => 1, - // 'created_by' => $updatedBy > 0 ? $updatedBy : null, - // ], true); - - // $spreadsheet = IOFactory::load($uploadPath . $storedFileName); - // $excelRows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false); - - // if (empty($excelRows)) { - // return $this->respond([ - // 'status' => 'failed', - // 'code' => 400, - // 'message' => 'Uploaded file is empty', - // ], 400); - // } - - // $headerRow = $excelRows[0] ?? []; - // $headerIndex = []; - // foreach ($headerRow as $idx => $headerValue) { - // $normalized = preg_replace('/[^a-z0-9]/', '', strtolower(trim((string)$headerValue))); - // if (!empty($normalized)) { - // $headerIndex[$normalized] = (int)$idx; - // } - // } - - // $policyIdx = $headerIndex['policynumber'] ?? null; - // $invoiceIdx = $headerIndex['invoicenumber'] ?? null; - // $commissionIdx = $headerIndex['commission'] ?? ($headerIndex['commissionamount'] ?? null); - - // if ($policyIdx === null || $commissionIdx === null) { - // return $this->respond([ - // 'status' => 'failed', - // 'code' => 400, - // 'message' => 'Expected columns: Policy Number, Invoice Number, Commission or Commission Amount', - // ], 400); - // } - - // $fallbackInvoiceNo = trim((string)($post['invoice_number'] ?? '')); - // $rows = []; - // foreach ($excelRows as $index => $row) { - // if ($index === 0) { - // continue; - // } - - // $policyNo = trim((string)($row[$policyIdx] ?? '')); - // $invoiceNoFromFile = $invoiceIdx !== null ? trim((string)($row[$invoiceIdx] ?? '')) : ''; - // $invoiceNo = $invoiceNoFromFile !== '' ? $invoiceNoFromFile : $fallbackInvoiceNo; - // $commissionRaw = trim((string)($row[$commissionIdx] ?? '')); - - // if ($policyNo === '' && $invoiceNo === '' && $commissionRaw === '') { - // continue; - // } - - // $commission = (float)str_replace(',', '', $commissionRaw); - // if ($policyNo === '' || $invoiceNo === '' || !is_numeric(str_replace(',', '', $commissionRaw))) { - // return $this->respond([ - // 'status' => 'failed', - // 'code' => 400, - // 'message' => 'Invalid data at line ' . ($index + 1), - // ], 400); - // } - - // $rows[] = [ - // 'line_no' => $index + 1, - // 'policy_number' => $policyNo, - // 'invoice_no' => $invoiceNo, - // 'commission_amount' => $commission, - // ]; - // } - // } + return $this->respond([ + 'status' => 'success', + 'code' => 200, + 'data' => $history, + 'invoice'=> [ + 'invoice_id' => (int)$invoice['id'], + 'invoice_no' => $invoice['invoice_no'], + 'total_amount' => (float)$invoice['invoice_amount'], + ], + ], 200); + } catch (\Exception $e) { + return $this->respond([ + 'status' => 'failed', + 'code' => 500, + 'message'=> $e->getMessage() + ], 500); + } + } public function bulkUploadCommission() { $this->db->transBegin(); try { $input = $this->request->getJSON(true); + if (!is_array($input)) { + $input = []; + } + + $post = $this->request->getPost(); + if (!is_array($post)) { + $post = []; + } + $rows = $input['rows'] ?? []; - $updatedBy = (int)($input['updated_by'] ?? 0); + $updatedBy = (int)($input['updated_by'] ?? ($post['created_by'] ?? 0)); + + // Multipart flow: read excel on backend and store upload metadata. + $file = $this->request->getFile('file_name'); + if ($file && $file->isValid()) { + $month = date('Y-m-d'); + $uploadPath = WRITEPATH . 'uploads/agent/incentive_file/'; + if (!is_dir($uploadPath)) { + mkdir($uploadPath, 0777, true); + } + + $storedFileName = time() . '_' . $file->getRandomName(); + $file->move($uploadPath, $storedFileName); + + $this->AgentIncentiveFileModel->insert([ + 'incentive_month' => $month, + 'incentive_file_name' => $storedFileName, + 'file_type' => 'invoice', + 'is_active' => 1, + 'created_by' => $updatedBy > 0 ? $updatedBy : null, + ], true); + + $spreadsheet = IOFactory::load($uploadPath . $storedFileName); + $excelRows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false); + + if (empty($excelRows)) { + return $this->respond([ + 'status' => 'failed', + 'code' => 400, + 'message' => 'Uploaded file is empty', + ], 400); + } + + $headerRow = $excelRows[0] ?? []; + $headerIndex = []; + foreach ($headerRow as $idx => $headerValue) { + $normalized = preg_replace('/[^a-z0-9]/', '', strtolower(trim((string)$headerValue))); + if (!empty($normalized)) { + $headerIndex[$normalized] = (int)$idx; + } + } + + $policyIdx = $headerIndex['policynumber'] ?? null; + $invoiceIdx = $headerIndex['invoicenumber'] ?? null; + $commissionIdx = $headerIndex['commission'] ?? ($headerIndex['commissionamount'] ?? null); + + if ($policyIdx === null || $commissionIdx === null) { + return $this->respond([ + 'status' => 'failed', + 'code' => 400, + 'message' => 'Expected columns: Policy Number, Invoice Number, Commission or Commission Amount', + ], 400); + } + + $fallbackInvoiceNo = trim((string)($post['invoice_number'] ?? '')); + $rows = []; + foreach ($excelRows as $index => $row) { + if ($index === 0) { + continue; + } + + $policyNo = trim((string)($row[$policyIdx] ?? '')); + $invoiceNoFromFile = $invoiceIdx !== null ? trim((string)($row[$invoiceIdx] ?? '')) : ''; + $invoiceNo = $invoiceNoFromFile !== '' ? $invoiceNoFromFile : $fallbackInvoiceNo; + $commissionRaw = trim((string)($row[$commissionIdx] ?? '')); + + if ($policyNo === '' && $invoiceNo === '' && $commissionRaw === '') { + continue; + } + + $commission = (float)str_replace(',', '', $commissionRaw); + if ($policyNo === '' || $invoiceNo === '' || !is_numeric(str_replace(',', '', $commissionRaw))) { + return $this->respond([ + 'status' => 'failed', + 'code' => 400, + 'message' => 'Invalid data at line ' . ($index + 1), + ], 400); + } + + $rows[] = [ + 'line_no' => $index + 1, + 'policy_number' => $policyNo, + 'invoice_no' => $invoiceNo, + 'commission_amount' => $commission, + ]; + } + } if (empty($rows) || !is_array($rows)) { return $this->respond([ @@ -798,7 +864,8 @@ class InvoiceController extends ResourceController 'is_active' => 1, ]); - $this->db->transRollback(); + // Keep staged token/payload so proceed API can continue later. + $this->db->transCommit(); return $this->respond([ 'status' => 'partner_mismatch', 'code' => 200,