1427 lines
57 KiB
PHP
1427 lines
57 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
use CodeIgniter\RESTful\ResourceController;
|
|
use App\Models\PolicyModel;
|
|
use App\Models\EnquiryModel;
|
|
use App\Models\QuotationModel;
|
|
use App\Models\InvoiceModel;
|
|
use App\Models\InvoiceItemModel;
|
|
use App\Models\InvoiceUtrModel;
|
|
use App\Models\AgentIncentiveFileModel;
|
|
use CodeIgniter\Database\Exceptions\DataException;
|
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
|
|
|
class InvoiceController extends ResourceController
|
|
{
|
|
protected $PolicyModel;
|
|
protected $QuotationModel;
|
|
protected $EnquiryModel;
|
|
protected $InvoiceModel;
|
|
protected $InvoiceItemModel;
|
|
protected $InvoiceUtrModel;
|
|
protected $AgentIncentiveFileModel;
|
|
protected $db;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->PolicyModel = new PolicyModel();
|
|
$this->QuotationModel = new QuotationModel();
|
|
$this->EnquiryModel = new EnquiryModel();
|
|
$this->InvoiceModel = new InvoiceModel();
|
|
$this->InvoiceItemModel = new InvoiceItemModel();
|
|
$this->InvoiceUtrModel = new InvoiceUtrModel();
|
|
$this->AgentIncentiveFileModel = new AgentIncentiveFileModel();
|
|
$this->db = \Config\Database::connect();
|
|
}
|
|
|
|
public function invoiceList()
|
|
{
|
|
try {
|
|
|
|
$data = $this->InvoiceModel
|
|
->select('partner_invoice.*,
|
|
PB.name as broker_name,
|
|
pos.name as pos_name,
|
|
FORMAT(partner_invoice.invoice_amount, 2, "en_IN") AS invoice_amount_indian_format,
|
|
DATE_FORMAT(partner_invoice.invoice_date, "%d-%m-%Y") AS invoice_date_ui_format,
|
|
(
|
|
SELECT GROUP_CONCAT(pa.name ORDER BY pa.id SEPARATOR ", ")
|
|
FROM partner_agent pa
|
|
WHERE JSON_SEARCH(partner_invoice.agent_id, "one", CAST(pa.id AS CHAR)) IS NOT NULL
|
|
) AS partner_names,
|
|
(
|
|
SELECT GROUP_CONCAT(piu.utr_no ORDER BY piu.id SEPARATOR ", ")
|
|
FROM partner_invoice_utr piu
|
|
WHERE piu.invoice_id = partner_invoice.id
|
|
AND piu.is_active = 1
|
|
) AS utr_numbers,
|
|
partner_invoice.invoice_amount AS invoiced_amount,
|
|
(
|
|
COALESCE((
|
|
SELECT SUM(piu.amount)
|
|
FROM partner_invoice_utr piu
|
|
WHERE piu.invoice_id = partner_invoice.id
|
|
AND piu.is_active = 1
|
|
), 0.00)
|
|
) AS payout_amount,
|
|
(
|
|
partner_invoice.invoice_amount - (
|
|
COALESCE((
|
|
SELECT SUM(piu.amount)
|
|
FROM partner_invoice_utr piu
|
|
WHERE piu.invoice_id = partner_invoice.id
|
|
AND piu.is_active = 1
|
|
), 0.00)
|
|
)
|
|
) AS balance_amount',
|
|
false)
|
|
->join('partner_brokers PB', 'PB.id = partner_invoice.broker_id', 'left')
|
|
->join('partner_pos pos', 'pos.id = partner_invoice.pos_id', 'left')
|
|
->where('partner_invoice.is_active', 1)
|
|
->orderBy('partner_invoice.id', 'DESC')
|
|
->findAll();
|
|
|
|
return $this->respond(['status' => 'success', 'code' => 200,'data' => $data ], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
public function findInvoiceWithItems()
|
|
{
|
|
try {
|
|
$id = $this->request->getGet('id');
|
|
|
|
if (!$id) {
|
|
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'ID is required'], 400);
|
|
}
|
|
|
|
$invoice = $this->InvoiceModel->where('id', $id)->first();
|
|
|
|
if (!$invoice) {
|
|
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Invoice not found'], 404);
|
|
}
|
|
|
|
$items = $this->InvoiceItemModel->select('partner_invoice_items.* , pp.issued_date , pe.name as customer_name , pq.premium_amount')
|
|
->join('partner_policy pp','pp.id = partner_invoice_items.policy_id ', 'left')
|
|
->join('partner_enquiry pe','pe.id = pp.enquiry_id ', 'left')
|
|
->join('partner_quotation pq','pq.id = pp.quotation_id ', 'left')
|
|
->where('partner_invoice_items.invoice_id', $id)
|
|
->where('partner_invoice_items.is_active', 1)
|
|
->findAll();
|
|
|
|
$paymentHistory = $this->InvoiceUtrModel
|
|
->select('id, invoice_id, amount as paid_amount, utr_no, utr_date as paid_date, created_at, created_by')
|
|
->where('invoice_id', $id)
|
|
->where('is_active', 1)
|
|
->where('amount >', 0)
|
|
->orderBy('id', 'DESC')
|
|
->findAll();
|
|
|
|
$utrPaidAmount = (float) (
|
|
$this->InvoiceUtrModel
|
|
->selectSum('amount', 'total')
|
|
->where('invoice_id', $id)
|
|
->where('is_active', 1)
|
|
->first()['total'] ?? 0
|
|
);
|
|
|
|
$totalPaidAmount = $utrPaidAmount;
|
|
$invoiceAmount = (float) ($invoice['invoice_amount'] ?? 0);
|
|
$balanceAmount = max($invoiceAmount - $totalPaidAmount, 0);
|
|
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'data' => [
|
|
'invoice' => $invoice,
|
|
'items' => $items,
|
|
'payment_history' => $paymentHistory,
|
|
'paid_amount' => $totalPaidAmount,
|
|
'balance_amount' => $balanceAmount
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
public function createOrUpdateInvoice()
|
|
{
|
|
$this->db->transBegin();
|
|
|
|
try {
|
|
$input = $this->request->getJSON(true);
|
|
|
|
// Normalize pos_id
|
|
$posIdRaw = $input['pos_id'] ?? 0;
|
|
$posId = is_numeric($posIdRaw) ? (int)$posIdRaw : 0;
|
|
|
|
$payoutStatus = isset($input['payout_status']) ? (int) $input['payout_status'] : 1;
|
|
|
|
$invoiceId = $input['id'] ?? null;
|
|
$isCreate = empty($invoiceId);
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
// PRE-SAVE VALIDATION: UTR checks (only on CREATE)
|
|
// Run BEFORE any insert/update so nothing is written on failure
|
|
// ─────────────────────────────────────────────────────────────
|
|
if ($isCreate && !empty($input['utrs']) && is_array($input['utrs'])) {
|
|
|
|
$seenUtrs = [];
|
|
|
|
foreach ($input['utrs'] as $utrRow) {
|
|
|
|
$utrNo = trim((string)($utrRow['utr_no'] ?? ''));
|
|
|
|
if ($utrNo === '') {
|
|
continue;
|
|
}
|
|
|
|
// 1. Duplicate within the same request
|
|
if (in_array($utrNo, $seenUtrs, true)) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message' => "Duplicate UTR found: \"{$utrNo}\" appears more than once in your submission. Please remove the duplicate and try again.",
|
|
], 400);
|
|
}
|
|
|
|
$seenUtrs[] = $utrNo;
|
|
|
|
// 2. Already exists in DB (used in another payout)
|
|
$exists = $this->InvoiceUtrModel
|
|
->where('utr_no', $utrNo)
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if ($exists) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message' => "UTR \"{$utrNo}\" is already linked to another payout. Please verify the UTR number or contact support if you believe this is an error.",
|
|
], 400);
|
|
}
|
|
}
|
|
}
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
// Basic invoice data
|
|
$invoiceData = [
|
|
'invoice_amount' => $input['invoice_amount'] ?? 0,
|
|
'broker_id' => $input['broker_id'] ?? 0,
|
|
'agent_id' => json_encode($input['agent_id'] ?? []),
|
|
'pos_id' => $posId,
|
|
'till_date' => $input['till_date'] ? date('Y-m-d', strtotime($input['till_date'])) : null,
|
|
'invoice_date' => $input['invoice_date'] ? date('Y-m-d', strtotime($input['invoice_date'])) : null,
|
|
'payout_status' => $payoutStatus,
|
|
'is_active' => 1,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => $input['updated_by'] ?? 0
|
|
];
|
|
|
|
if (!$invoiceId) {
|
|
|
|
// Generate invoice number only for CREATE
|
|
$invoiceData['invoice_no'] = $this->generateInvoiceNo($posId);
|
|
|
|
$invoiceData['created_at'] = date('Y-m-d H:i:s');
|
|
$invoiceData['created_by'] = $input['created_by'] ?? 0;
|
|
|
|
$invoiceId = $this->InvoiceModel->insert($invoiceData);
|
|
|
|
if ($invoiceId === false) {
|
|
$this->db->transRollback();
|
|
log_message('error', json_encode($this->InvoiceModel->errors()));
|
|
return $this->respond(['status' => 'failed', 'message' => 'Failed to create invoice', 'error' => json_encode($this->InvoiceModel->errors())], 500);
|
|
}
|
|
} else {
|
|
|
|
// Invoice number should NOT change on update
|
|
unset($invoiceData['invoice_no']);
|
|
|
|
$this->InvoiceModel->update($invoiceId, $invoiceData);
|
|
}
|
|
|
|
// Insert or update items
|
|
if (!empty($input['items']) && is_array($input['items'])) {
|
|
|
|
foreach ($input['items'] as $item) {
|
|
|
|
$itemData = [
|
|
'invoice_id' => $invoiceId,
|
|
'policy_id' => $item['policy_id'],
|
|
'policy_no' => $item['policy_no'],
|
|
'commission_amount' => $item['commission_amount'] ?? 0,
|
|
'is_active' => $item['is_active'] ?? 1,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => $input['updated_by'] ?? 0
|
|
];
|
|
|
|
// UPDATE (if id exists)
|
|
if (!empty($item['id'])) {
|
|
|
|
$this->InvoiceItemModel->update($item['id'], $itemData);
|
|
|
|
}else{ // INSERT (if id missing)
|
|
|
|
$itemData['created_at'] = date('Y-m-d H:i:s');
|
|
$itemData['created_by'] = $input['created_by'] ?? 0;
|
|
$this->InvoiceItemModel->insert($itemData);
|
|
|
|
//update pos_id to policy table
|
|
$this->PolicyModel->update($item['policy_id'], ['pos_id' => $posId ]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// UTR save — validations already passed above, just insert
|
|
if ($isCreate && !empty($input['utrs']) && is_array($input['utrs'])) {
|
|
|
|
foreach ($input['utrs'] as $utrRow) {
|
|
|
|
$utrNo = trim((string)($utrRow['utr_no'] ?? ''));
|
|
if ($utrNo === '') continue;
|
|
|
|
$utrDate = !empty($utrRow['utr_date'])
|
|
? date('Y-m-d', strtotime($utrRow['utr_date']))
|
|
: null;
|
|
|
|
$amount = isset($utrRow['amount']) ? (float) $utrRow['amount'] : 0;
|
|
|
|
$result_utr = $this->InvoiceUtrModel->insert([
|
|
'invoice_id' => $invoiceId,
|
|
'utr_no' => $utrNo,
|
|
'utr_date' => $utrDate,
|
|
'amount' => $amount,
|
|
'is_active' => 1,
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'created_by' => $input['created_by'] ?? 0,
|
|
]);
|
|
|
|
if ($result_utr === false) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message' => 'Could not save UTR details. Please try again.',
|
|
], 500);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($this->db->transStatus() === false) {
|
|
$this->db->transRollback();
|
|
throw new DataException("Transaction failed");
|
|
}
|
|
|
|
$this->db->transCommit();
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'data' => ['invoice_id' => $invoiceId]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
$this->db->transRollback();
|
|
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
private function generateInvoiceNo($pos_id)
|
|
{
|
|
$pos_id = !empty($pos_id) ? $pos_id : 0;
|
|
$year = date('Y');
|
|
|
|
// If pos_id is missing/invalid (0/null/"w"), generate invoice as MIG/Year/NNNNN
|
|
// Otherwise generate as NIIB/{pos_id}/Year/NNNNN
|
|
$prefix = ($pos_id > 0)
|
|
? "NIIB/{$pos_id}/{$year}/"
|
|
: "MIG/{$year}/";
|
|
|
|
// Get last invoice of current year
|
|
$lastInvoice = $this->InvoiceModel
|
|
->select('invoice_no')
|
|
->like('invoice_no', $prefix, 'after')
|
|
->where('pos_id',$pos_id)
|
|
->orderBy('id', 'DESC')
|
|
->first();
|
|
|
|
if ($lastInvoice && isset($lastInvoice['invoice_no'])) {
|
|
// Extract numeric part
|
|
$lastNumber = intval(substr($lastInvoice['invoice_no'], -5));
|
|
$nextNumber = $lastNumber + 1;
|
|
} else {
|
|
$nextNumber = 1;
|
|
}
|
|
|
|
return $prefix . str_pad($nextNumber, 5, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
|
|
public function deleteInvoice()
|
|
{
|
|
try {
|
|
$id = $this->request->getGet('id');
|
|
|
|
if (!$id) {
|
|
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'ID is required'], 400);
|
|
}
|
|
|
|
$this->InvoiceModel->update($id, ['is_active' => 0]);
|
|
$this->InvoiceItemModel->where('invoice_id', $id)->set(['is_active' => 0])->update();
|
|
|
|
return $this->respond(['status' => 'success', 'code' => 200, 'data' => 'Invoice deleted'], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
public function getCommissionRateList()
|
|
{
|
|
try {
|
|
|
|
$input = $this->request->getJSON(true);
|
|
|
|
// if (empty($input['broker_id'])) {
|
|
// return $this->respond([
|
|
// 'status' => 'failed',
|
|
// 'code' => 400,
|
|
// 'message'=> 'broker_id is required'
|
|
// ], 400);
|
|
// }
|
|
|
|
$invoice_id = isset($input['invoice_id']) ? $input['invoice_id'] : '';
|
|
|
|
// Build the main query
|
|
$query = $this->PolicyModel
|
|
->select('partner_policy.policy_number as policy_no, partner_policy.id as policy_id, partner_policy.issued_date, partner_policy.commission_amount, partner_policy.insured_name as customer_name, pe.agent_id ,pa.name as agent_name,pa.agent_code, partner_policy.premium_amount, pi.id as invoice_id, pi.invoice_no,
|
|
COALESCE((
|
|
SELECT GROUP_CONCAT(piu.utr_no ORDER BY piu.id SEPARATOR ", ")
|
|
FROM partner_invoice_utr piu
|
|
WHERE piu.invoice_id = pi.id
|
|
AND piu.is_active = 1
|
|
), "") as utr_no', false)
|
|
// ->join(
|
|
// 'partner_enquiry pe',
|
|
// 'pe.id = partner_policy.enquiry_id AND pe.broker_id = ' . (int)$input['broker_id'],
|
|
// 'left'
|
|
// )
|
|
->join('partner_enquiry pe','pe.id = partner_policy.enquiry_id','left')
|
|
->join('partner_quotation pq', 'pq.id = partner_policy.quotation_id', 'left')
|
|
->join('partner_invoice_items pii', 'pii.policy_id = partner_policy.id', 'left')
|
|
->join('partner_invoice pi', 'pi.id = pii.invoice_id AND pi.is_active = 1', 'left')
|
|
->join('partner_agent pa', 'pa.id = pe.agent_id', 'left')
|
|
->where('partner_policy.is_active', 1)
|
|
->where('partner_policy.commission_amount > 0')
|
|
->where('partner_policy.is_data_accuracy_checked', 1)
|
|
->where('partner_policy.manager_id', $input['manager_id']);
|
|
|
|
// Apply agent_id filter (supports multiple agents)
|
|
if (!empty($input['agent_id'])) {
|
|
if (is_array($input['agent_id'])) {
|
|
$query->whereIn('pe.agent_id', $input['agent_id']);
|
|
} else {
|
|
$query->where('pe.agent_id', $input['agent_id']);
|
|
}
|
|
}
|
|
|
|
// Apply date range filters
|
|
if (!empty($input['from_date'])) {
|
|
$query->where('partner_policy.issued_date >=', date('Y-m-d', strtotime($input['from_date'])));
|
|
}
|
|
|
|
if (!empty($input['to_date'])) {
|
|
$query->where('partner_policy.issued_date <=', date('Y-m-d', strtotime($input['to_date'])));
|
|
}
|
|
|
|
// Apply POS filter if provided
|
|
if (!empty($input['pos_id'])) {
|
|
$query->where('partner_policy.pos_id', $input['pos_id']);
|
|
}
|
|
|
|
// Filter based on invoice_id or policies not yet invoiced
|
|
if (empty($invoice_id)) {
|
|
$query->where('pii.policy_id IS NULL');
|
|
} else {
|
|
$query->where('pi.id', $invoice_id);
|
|
}
|
|
|
|
$data = $query->findAll();
|
|
|
|
// Calculate total commission
|
|
$total_commission = 0;
|
|
foreach ($data as $policy) {
|
|
$total_commission += $policy['commission_amount'];
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'data' => $data,
|
|
'total_commission' => $total_commission,
|
|
'total_policies' => count($data)
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message'=> $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function getAgentUnusedCommissionList()
|
|
{
|
|
try {
|
|
|
|
$manager_id = $this->request->getGet('manager_id');
|
|
// $broker_id = $this->request->getGet('broker_id');
|
|
|
|
// if (empty($broker_id)) {
|
|
// return $this->respond([
|
|
// 'status' => 'failed',
|
|
// 'code' => 400,
|
|
// 'message'=> 'broker_id is required'
|
|
// ], 400);
|
|
// }
|
|
|
|
$query = $this->PolicyModel
|
|
->select([
|
|
'pe.agent_id',
|
|
'pa.name as agent_name',
|
|
'SUM(partner_policy.commission_amount) as unused_commission_amount',
|
|
'COUNT(partner_policy.id) as total_policies'
|
|
])
|
|
// ->join(
|
|
// 'partner_enquiry pe',
|
|
// 'pe.id = partner_policy.enquiry_id AND pe.broker_id = ' . (int)$broker_id,
|
|
// 'left'
|
|
// )
|
|
->join('partner_enquiry pe','pe.id = partner_policy.enquiry_id','left')
|
|
->join('partner_agent pa', 'pa.id = pe.agent_id', 'left')
|
|
->join('partner_invoice_items pii', 'pii.policy_id = partner_policy.id', 'left')
|
|
->join('partner_invoice pi', 'pi.id = pii.invoice_id AND pi.is_active = 1', 'left')
|
|
->where('partner_policy.is_active', 1)
|
|
->where('partner_policy.commission_amount >=', 0)
|
|
// ->where('partner_policy.is_data_accuracy_checked', 1)
|
|
->where('partner_policy.manager_id', $manager_id)
|
|
->where('pii.policy_id IS NULL') // ❗ unused commission
|
|
->groupBy('pe.agent_id');
|
|
|
|
|
|
|
|
$data = $query->findAll();
|
|
|
|
// Grand total (optional but useful for FE)
|
|
$grand_total = 0;
|
|
foreach ($data as $row) {
|
|
$grand_total += (float)$row['unused_commission_amount'];
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'data' => $data,
|
|
'grand_total_unused_commission' => $grand_total,
|
|
'total_agents' => count($data)
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message'=> $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function updateUtrDetails()
|
|
{
|
|
$this->db->transBegin();
|
|
|
|
try {
|
|
$input = $this->request->getJSON(true);
|
|
|
|
$invoiceId = (int)($input['invoice_id'] ?? 0);
|
|
$paidAmount = (float)($input['paid_amount'] ?? 0);
|
|
$paidDateRaw = $input['paid_date'] ?? null;
|
|
$utrId = (int)($input['utr_id'] ?? 0);
|
|
$utrNoInput = trim((string)($input['utr_no'] ?? ''));
|
|
|
|
if ($invoiceId <= 0 || $paidAmount < 0 || empty($paidDateRaw)) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message'=> 'invoice_id, paid_amount and paid_date are required'
|
|
], 400);
|
|
}
|
|
|
|
$invoice = $this->InvoiceModel
|
|
->where('id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if (empty($invoice)) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 404,
|
|
'message'=> 'Invoice not found'
|
|
], 404);
|
|
}
|
|
|
|
$paidDateTs = strtotime($paidDateRaw);
|
|
if ($paidDateTs === false) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message'=> 'Invalid paid_date format'
|
|
], 400);
|
|
}
|
|
$paidDate = date('Y-m-d', $paidDateTs);
|
|
|
|
$utrPaidAmount = (float) (
|
|
$this->InvoiceUtrModel
|
|
->selectSum('amount', 'total')
|
|
->where('invoice_id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->first()['total'] ?? 0
|
|
);
|
|
|
|
$currentTotalPaid = $utrPaidAmount;
|
|
$invoiceAmount = (float) ($invoice['invoice_amount'] ?? 0);
|
|
$remainingBalance = max($invoiceAmount - $currentTotalPaid, 0);
|
|
|
|
/*
|
|
* If utr_id is empty/0 from request:
|
|
* 1) create a fresh UTR row with given utr_no (or generated fallback),
|
|
* 2) keep amount = 0 first,
|
|
* 3) then continue existing update flow using the new utr_id.
|
|
*/
|
|
if ($utrId <= 0) {
|
|
$utrNo = $utrNoInput !== '' ? $utrNoInput : ('UTR' . date('YmdHis'));
|
|
while (
|
|
!empty(
|
|
$this->InvoiceUtrModel
|
|
->where('invoice_id', $invoiceId)
|
|
->where('utr_no', $utrNo)
|
|
->where('is_active', 1)
|
|
->first()
|
|
)
|
|
) {
|
|
usleep(200000);
|
|
$utrNo = 'UTR' . date('YmdHis');
|
|
}
|
|
|
|
$insertedUtrId = $this->InvoiceUtrModel->insert([
|
|
'invoice_id' => $invoiceId,
|
|
'utr_no' => $utrNo,
|
|
'utr_date' => null,
|
|
'amount' => 0,
|
|
'is_active' => 1,
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'created_by' => (int)($input['created_by'] ?? 0),
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => (int)($input['updated_by'] ?? 0),
|
|
]);
|
|
|
|
if ($insertedUtrId === false) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message'=> 'Failed to create UTR',
|
|
'error' => $this->InvoiceUtrModel->errors()
|
|
], 500);
|
|
}
|
|
|
|
$utrId = (int)$insertedUtrId;
|
|
}
|
|
|
|
$utrQuery = $this->InvoiceUtrModel
|
|
->where('invoice_id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->where('id', $utrId);
|
|
$utrRow = $utrQuery->first();
|
|
|
|
if (empty($utrRow)) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 404,
|
|
'message'=> 'Selected UTR not found for this invoice'
|
|
], 404);
|
|
}
|
|
|
|
$existingUtrAmount = (float)($utrRow['amount'] ?? 0);
|
|
if ($paidAmount > ($remainingBalance + $existingUtrAmount)) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message'=> 'Paid amount exceeds invoice balance'
|
|
], 400);
|
|
}
|
|
if ($paidAmount < $existingUtrAmount) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message'=> 'Paid amount cannot be less than existing paid amount'
|
|
], 400);
|
|
}
|
|
|
|
$otherUtrPaidAmount = (float) (
|
|
$this->InvoiceUtrModel
|
|
->selectSum('amount', 'total')
|
|
->where('invoice_id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->where('id !=', (int)$utrRow['id'])
|
|
->first()['total'] ?? 0
|
|
);
|
|
$maxAllowedForSelectedUtr = max($invoiceAmount - $otherUtrPaidAmount, 0);
|
|
if ($paidAmount > $maxAllowedForSelectedUtr) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message'=> 'Paid amount exceeds invoice balance'
|
|
], 400);
|
|
}
|
|
|
|
$updated = $this->InvoiceUtrModel->update((int)$utrRow['id'], [
|
|
'amount' => $paidAmount,
|
|
'utr_date' => $paidDate,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => (int)($input['updated_by'] ?? 0),
|
|
]);
|
|
|
|
if ($updated === false) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message'=> 'Failed to update UTR payment',
|
|
'error' => $this->InvoiceUtrModel->errors()
|
|
], 500);
|
|
}
|
|
|
|
$latestTotalPaid = ($currentTotalPaid - $existingUtrAmount) + $paidAmount;
|
|
$newBalance = max($invoiceAmount - $latestTotalPaid, 0);
|
|
$payoutStatus = $newBalance == 0.0 ? 2 : 1;
|
|
|
|
$this->InvoiceModel->update($invoiceId, [
|
|
'payout_status' => $payoutStatus,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => (int)($input['updated_by'] ?? 0),
|
|
]);
|
|
|
|
if ($this->db->transStatus() === false) {
|
|
$this->db->transRollback();
|
|
throw new DataException('Transaction failed');
|
|
}
|
|
|
|
$this->db->transCommit();
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'data' => [
|
|
'utr_id' => (int)$utrRow['id'],
|
|
'utr_no' => (string)$utrRow['utr_no'],
|
|
'invoice_id' => $invoiceId,
|
|
'paid_amount' => $latestTotalPaid,
|
|
'balance_amount' => $newBalance
|
|
]
|
|
], 200);
|
|
} catch (\Exception $e) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message'=> $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function addInvoicePayment()
|
|
{
|
|
return $this->updateUtrDetails();
|
|
}
|
|
|
|
public function listUtrDetails()
|
|
{
|
|
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);
|
|
}
|
|
|
|
$invoice = $this->InvoiceModel
|
|
->select('id, invoice_no, invoice_amount')
|
|
->where('id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if (empty($invoice)) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 404,
|
|
'message'=> 'Invoice not found'
|
|
], 404);
|
|
}
|
|
|
|
$history = $this->db->table('partner_invoice_utr piu')
|
|
->select('
|
|
piu.id,
|
|
piu.invoice_id,
|
|
pi.invoice_no,
|
|
pi.invoice_amount AS total_amount,
|
|
piu.amount AS paid_amount,
|
|
piu.utr_no,
|
|
piu.utr_date AS paid_date,
|
|
piu.created_at,
|
|
COALESCE(psu.name, psc.name, "-") AS createdby_name,
|
|
(
|
|
pi.invoice_amount - COALESCE((
|
|
SELECT SUM(piu2.amount)
|
|
FROM partner_invoice_utr piu2
|
|
WHERE piu2.invoice_id = piu.invoice_id
|
|
AND piu2.is_active = 1
|
|
AND piu2.amount > 0
|
|
AND piu2.id <= piu.id
|
|
), 0)
|
|
) AS balance_amount
|
|
', false)
|
|
->join('partner_invoice pi', 'pi.id = piu.invoice_id', 'inner')
|
|
->join('partner_staff psc', 'psc.id = piu.created_by', 'left')
|
|
->join('partner_staff psu', 'psu.id = piu.updated_by', 'left')
|
|
->where('piu.invoice_id', $invoiceId)
|
|
->where('piu.is_active', 1)
|
|
->where('piu.amount >', 0)
|
|
->orderBy('piu.id', 'DESC')
|
|
->get()
|
|
->getResultArray();
|
|
|
|
// Return unique UTR list for this invoice (remove duplicates by utr_no).
|
|
// Keep only unpaid UTR rows (amount 0 or null).
|
|
$availableUtrs = $this->db->table('partner_invoice_utr')
|
|
->select('MIN(id) as id, utr_no')
|
|
->where('invoice_id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->where('(amount IS NULL OR amount <= 0)', null, false)
|
|
->where('TRIM(COALESCE(utr_no, "")) !=', '')
|
|
->groupBy('utr_no')
|
|
->orderBy('MIN(id)', 'ASC', false)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
$paidToDate = (float) (
|
|
$this->InvoiceUtrModel
|
|
->selectSum('amount', 'total')
|
|
->where('invoice_id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->first()['total'] ?? 0
|
|
);
|
|
$balanceAmount = max(((float)$invoice['invoice_amount']) - $paidToDate, 0);
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'data' => $history,
|
|
'available_utrs' => $availableUtrs,
|
|
'invoice'=> [
|
|
'invoice_id' => (int)$invoice['id'],
|
|
'invoice_no' => $invoice['invoice_no'],
|
|
'total_amount' => (float)$invoice['invoice_amount'],
|
|
'paid_to_date' => $paidToDate,
|
|
'balance_amount' => $balanceAmount,
|
|
],
|
|
], 200);
|
|
} catch (\Exception $e) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message'=> $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function addPaymentHistory()
|
|
{
|
|
return $this->listUtrDetails();
|
|
}
|
|
|
|
public function utrDetails()
|
|
{
|
|
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);
|
|
}
|
|
|
|
$invoice = $this->InvoiceModel
|
|
->select('id, invoice_no, invoice_amount')
|
|
->where('id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if (empty($invoice)) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 404,
|
|
'message'=> 'Invoice not found'
|
|
], 404);
|
|
}
|
|
|
|
$utrDetails = $this->InvoiceUtrModel
|
|
->select('id, invoice_id, amount as paid_amount, utr_no, utr_date')
|
|
->where('invoice_id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->orderBy('id', 'ASC')
|
|
->findAll();
|
|
|
|
$paidToDate = (float) (
|
|
$this->InvoiceUtrModel
|
|
->selectSum('amount', 'total')
|
|
->where('invoice_id', $invoiceId)
|
|
->where('is_active', 1)
|
|
->first()['total'] ?? 0
|
|
);
|
|
|
|
$balanceAmount = max(((float)$invoice['invoice_amount']) - $paidToDate, 0);
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'data' => [
|
|
'utrs_details' => $utrDetails,
|
|
'invoice' => [
|
|
'invoice_id' => (int)$invoice['id'],
|
|
'invoice_no' => $invoice['invoice_no'],
|
|
'total_amount' => (float)$invoice['invoice_amount'],
|
|
'paid_to_date' => $paidToDate,
|
|
'balance_amount' => $balanceAmount,
|
|
],
|
|
],
|
|
], 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'] ?? ($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([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message' => 'rows is required',
|
|
], 400);
|
|
}
|
|
|
|
$mismatchRows = [];
|
|
$validRows = [];
|
|
$skippedRows = 0;
|
|
|
|
foreach ($rows as $row) {
|
|
$policyNo = trim((string)($row['policy_number'] ?? ''));
|
|
$invoiceNo = trim((string)($row['invoice_no'] ?? ''));
|
|
$commissionAmount = (float)($row['commission_amount'] ?? 0);
|
|
$lineNo = (int)($row['line_no'] ?? 0);
|
|
|
|
if ($policyNo === '' || $invoiceNo === '') {
|
|
$skippedRows++;
|
|
continue;
|
|
}
|
|
|
|
$record = $this->db->table('partner_invoice_items pii')
|
|
->select('
|
|
pii.id as item_id,
|
|
pii.invoice_id,
|
|
pii.policy_id,
|
|
pii.policy_no,
|
|
pi.invoice_no,
|
|
pi.agent_id as invoice_agent_json,
|
|
pe.agent_id as policy_agent_id
|
|
')
|
|
->join('partner_invoice pi', 'pi.id = pii.invoice_id', 'inner')
|
|
->join('partner_policy pp', 'pp.id = pii.policy_id', 'left')
|
|
->join('partner_enquiry pe', 'pe.id = pp.enquiry_id', 'left')
|
|
->where('pii.is_active', 1)
|
|
->where('pi.is_active', 1)
|
|
->where('pi.invoice_no', $invoiceNo)
|
|
->where('pii.policy_no', $policyNo)
|
|
->get()
|
|
->getRowArray();
|
|
|
|
if (empty($record)) {
|
|
$skippedRows++;
|
|
continue;
|
|
}
|
|
|
|
$invoiceAgentIds = $this->extractAgentIdsFromInvoice($record['invoice_agent_json'] ?? '');
|
|
$policyAgentId = isset($record['policy_agent_id']) ? (int)$record['policy_agent_id'] : 0;
|
|
$targetAgentId = !empty($invoiceAgentIds) ? (int)$invoiceAgentIds[0] : 0;
|
|
$isMismatch = $policyAgentId > 0 && !in_array($policyAgentId, $invoiceAgentIds, true);
|
|
|
|
if ($isMismatch) {
|
|
$mismatchRows[] = [
|
|
'line_no' => $lineNo,
|
|
'item_id' => (int)$record['item_id'],
|
|
'invoice_id' => (int)$record['invoice_id'],
|
|
'policy_id' => (int)$record['policy_id'],
|
|
'policy_number' => $policyNo,
|
|
'invoice_no' => $invoiceNo,
|
|
'commission_amount' => $commissionAmount,
|
|
'policy_agent_id' => $policyAgentId,
|
|
'target_agent_id' => $targetAgentId,
|
|
];
|
|
continue;
|
|
}
|
|
|
|
$validRows[] = [
|
|
'item_id' => (int)$record['item_id'],
|
|
'invoice_id' => (int)$record['invoice_id'],
|
|
'policy_id' => (int)$record['policy_id'],
|
|
'commission_amount' => $commissionAmount,
|
|
];
|
|
}
|
|
|
|
if (!empty($mismatchRows)) {
|
|
$this->ensureBulkUploadStagingTable();
|
|
$token = $this->generateProceedToken();
|
|
|
|
$this->db->table('invoice_bulk_upload_staging')->insert([
|
|
'proceed_token' => $token,
|
|
'payload_json' => json_encode([
|
|
'rows' => $rows,
|
|
'updated_by' => $updatedBy,
|
|
]),
|
|
'mismatch_json' => json_encode($mismatchRows),
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'created_by' => $updatedBy,
|
|
'is_active' => 1,
|
|
]);
|
|
|
|
// Keep staged token/payload so proceed API can continue later.
|
|
$this->db->transCommit();
|
|
return $this->respond([
|
|
'status' => 'partner_mismatch',
|
|
'code' => 200,
|
|
'message' => 'Partner mismatch detected',
|
|
'mismatch_count' => count($mismatchRows),
|
|
'mismatches' => $mismatchRows,
|
|
'proceed_token' => $token,
|
|
], 200);
|
|
}
|
|
|
|
$updatedCount = $this->applyBulkCommissionRows($validRows, $updatedBy);
|
|
|
|
if ($this->db->transStatus() === false) {
|
|
$this->db->transRollback();
|
|
throw new DataException('Transaction failed');
|
|
}
|
|
|
|
$this->db->transCommit();
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'message' => 'Bulk upload completed',
|
|
'updated_count' => $updatedCount,
|
|
'skipped_count' => $skippedRows,
|
|
], 200);
|
|
} catch (\Exception $e) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message' => $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function bulkUploadCommissionProceed()
|
|
{
|
|
$this->db->transBegin();
|
|
try {
|
|
$input = $this->request->getJSON(true);
|
|
$proceedToken = trim((string)($input['proceed_token'] ?? ''));
|
|
$forceReassign = (int)($input['force_reassign_partner'] ?? 0);
|
|
$updatedBy = (int)($input['updated_by'] ?? 0);
|
|
|
|
if ($proceedToken === '' || $forceReassign !== 1) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 400,
|
|
'message' => 'proceed_token and force_reassign_partner=1 are required',
|
|
], 400);
|
|
}
|
|
|
|
$this->ensureBulkUploadStagingTable();
|
|
$staging = $this->db->table('invoice_bulk_upload_staging')
|
|
->where('proceed_token', $proceedToken)
|
|
->where('is_active', 1)
|
|
->get()
|
|
->getRowArray();
|
|
|
|
if (empty($staging)) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 404,
|
|
'message' => 'Invalid or expired proceed token',
|
|
], 404);
|
|
}
|
|
|
|
$payload = json_decode($staging['payload_json'] ?? '{}', true);
|
|
$rows = $payload['rows'] ?? [];
|
|
$mismatchRows = json_decode($staging['mismatch_json'] ?? '[]', true);
|
|
|
|
foreach ($mismatchRows as $mismatch) {
|
|
$policyId = (int)($mismatch['policy_id'] ?? 0);
|
|
$targetAgentId = (int)($mismatch['target_agent_id'] ?? 0);
|
|
if ($policyId <= 0 || $targetAgentId <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$policy = $this->PolicyModel->select('enquiry_id')->where('id', $policyId)->first();
|
|
if (!empty($policy['enquiry_id'])) {
|
|
$this->EnquiryModel->update((int)$policy['enquiry_id'], [
|
|
'agent_id' => $targetAgentId,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => $updatedBy,
|
|
]);
|
|
}
|
|
}
|
|
|
|
$finalRows = [];
|
|
foreach ($rows as $row) {
|
|
$policyNo = trim((string)($row['policy_number'] ?? ''));
|
|
$invoiceNo = trim((string)($row['invoice_no'] ?? ''));
|
|
$commissionAmount = (float)($row['commission_amount'] ?? 0);
|
|
if ($policyNo === '' || $invoiceNo === '') {
|
|
continue;
|
|
}
|
|
|
|
$record = $this->db->table('partner_invoice_items pii')
|
|
->select('pii.id as item_id, pii.invoice_id, pii.policy_id')
|
|
->join('partner_invoice pi', 'pi.id = pii.invoice_id', 'inner')
|
|
->where('pii.is_active', 1)
|
|
->where('pi.is_active', 1)
|
|
->where('pi.invoice_no', $invoiceNo)
|
|
->where('pii.policy_no', $policyNo)
|
|
->get()
|
|
->getRowArray();
|
|
|
|
if (empty($record)) {
|
|
continue;
|
|
}
|
|
|
|
$finalRows[] = [
|
|
'item_id' => (int)$record['item_id'],
|
|
'invoice_id' => (int)$record['invoice_id'],
|
|
'policy_id' => (int)$record['policy_id'],
|
|
'commission_amount' => $commissionAmount,
|
|
];
|
|
}
|
|
|
|
$updatedCount = $this->applyBulkCommissionRows($finalRows, $updatedBy);
|
|
|
|
$this->db->table('invoice_bulk_upload_staging')
|
|
->where('id', (int)$staging['id'])
|
|
->update([
|
|
'is_active' => 0,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => $updatedBy,
|
|
]);
|
|
|
|
if ($this->db->transStatus() === false) {
|
|
$this->db->transRollback();
|
|
throw new DataException('Transaction failed');
|
|
}
|
|
|
|
$this->db->transCommit();
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'code' => 200,
|
|
'message' => 'Bulk upload completed with partner reassignment',
|
|
'updated_count' => $updatedCount,
|
|
], 200);
|
|
} catch (\Exception $e) {
|
|
$this->db->transRollback();
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'code' => 500,
|
|
'message' => $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
private function applyBulkCommissionRows(array $rows, int $updatedBy): int
|
|
{
|
|
$updatedCount = 0;
|
|
foreach ($rows as $row) {
|
|
$itemId = (int)($row['item_id'] ?? 0);
|
|
$invoiceId = (int)($row['invoice_id'] ?? 0);
|
|
$policyId = (int)($row['policy_id'] ?? 0);
|
|
$commissionAmount = (float)($row['commission_amount'] ?? 0);
|
|
if ($itemId <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$affected = $this->db->table('partner_invoice_items')
|
|
->where('id', $itemId)
|
|
->where('invoice_id', $invoiceId)
|
|
->where('policy_id', $policyId)
|
|
->where('is_active', 1)
|
|
->update([
|
|
'commission_amount' => $commissionAmount,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
'updated_by' => $updatedBy,
|
|
]);
|
|
|
|
if ($affected) {
|
|
$updatedCount++;
|
|
}
|
|
}
|
|
|
|
return $updatedCount;
|
|
}
|
|
|
|
private function extractAgentIdsFromInvoice($agentJson): array
|
|
{
|
|
if (is_array($agentJson)) {
|
|
return array_values(array_map('intval', $agentJson));
|
|
}
|
|
if ($agentJson === null || $agentJson === '') {
|
|
return [];
|
|
}
|
|
|
|
$decoded = json_decode((string)$agentJson, true);
|
|
if (is_array($decoded)) {
|
|
return array_values(array_map('intval', $decoded));
|
|
}
|
|
|
|
if (is_numeric($agentJson)) {
|
|
return [(int)$agentJson];
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
private function generateProceedToken(): string
|
|
{
|
|
return bin2hex(random_bytes(16));
|
|
}
|
|
|
|
private function ensureBulkUploadStagingTable(): void
|
|
{
|
|
$sql = "CREATE TABLE IF NOT EXISTS invoice_bulk_upload_staging (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
proceed_token VARCHAR(64) NOT NULL UNIQUE,
|
|
payload_json LONGTEXT NULL,
|
|
mismatch_json LONGTEXT NULL,
|
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
|
created_at DATETIME NULL,
|
|
created_by INT NULL,
|
|
updated_at DATETIME NULL,
|
|
updated_by INT NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
|
|
$this->db->query($sql);
|
|
}
|
|
|
|
|
|
// public function getCommissionRateList()
|
|
// {
|
|
// try {
|
|
|
|
// $input = $this->request->getJSON(true);
|
|
|
|
// if (empty($input['broker_id']) || empty($input['issued_date'])) {
|
|
// return $this->respond([ 'status' => 'failed', 'code' => 400, 'message'=> 'issued_date are required' ], 400);
|
|
// }
|
|
|
|
// $invoice_id = isset($input['invoice_id']) ? $input['invoice_id'] : '';
|
|
|
|
|
|
// $data_previous = $this->PolicyModel
|
|
// ->select('partner_policy.policy_number as policy_no,partner_policy.id as policy_id , partner_policy.issued_date, partner_policy.commission_amount, pe.name as customer_name , pq.premium_amount')
|
|
// ->join(
|
|
// 'partner_enquiry pe',
|
|
// 'pe.id = partner_policy.enquiry_id AND pe.broker_id = ' . (int)$input['broker_id'],
|
|
// 'left'
|
|
// )
|
|
// ->join('partner_quotation pq','pq.id = partner_policy.quotation_id ', 'left')
|
|
// // ->where('partner_policy.pos_id', $input['pos_id'])
|
|
// ->where('partner_policy.issued_date <=', date('Y-m-d', strtotime($input['issued_date'])))
|
|
// ->where('partner_policy.is_active', 1)
|
|
// ->where('partner_policy.is_data_accuracy_checked', 1)
|
|
// ->findAll();
|
|
|
|
|
|
// $query = $this->PolicyModel
|
|
// ->select('partner_policy.policy_number as policy_no, partner_policy.id as policy_id, partner_policy.issued_date, partner_policy.commission_amount, pe.name as customer_name, pq.premium_amount, pi.id as invoice_id, pi.invoice_no')
|
|
// ->join(
|
|
// 'partner_enquiry pe',
|
|
// 'pe.id = partner_policy.enquiry_id AND pe.broker_id = ' . (int)$input['broker_id'],
|
|
// 'left'
|
|
// )
|
|
// ->join('partner_quotation pq','pq.id = partner_policy.quotation_id ', 'left')
|
|
// ->join('partner_invoice_items pii', 'pii.policy_id = partner_policy.id', 'left' )
|
|
// ->join('partner_invoice pi', 'pi.id = pii.invoice_id AND pi.is_active = 1', 'left')
|
|
// // ->where('partner_policy.pos_id', $input['pos_id'])
|
|
// ->where('partner_policy.issued_date <=', date('Y-m-d', strtotime($input['issued_date'])))
|
|
// ->where('partner_policy.is_active', 1)
|
|
// ->where('partner_policy.commission_amount > 0')
|
|
// ->where('partner_policy.is_data_accuracy_checked', 1);
|
|
// if (empty($invoice_id)) {
|
|
// $query->where('pii.policy_id IS NULL');
|
|
// } else {
|
|
// $query->where('pi.id', $invoice_id);
|
|
// }
|
|
|
|
// $data = $query->findAll();
|
|
|
|
// return $this->respond([
|
|
// 'status' => 'success',
|
|
// 'code' => 200,
|
|
// 'previous' => $data_previous,
|
|
// 'data' => $data
|
|
// ], 200);
|
|
|
|
// } catch (\Exception $e) {
|
|
// return $this->respond([
|
|
// 'status' => 'failed',
|
|
// 'code' => 500,
|
|
// 'message'=> $e->getMessage()
|
|
// ], 500);
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|