Medical GST: persist item tax breakup and add backfill

This commit is contained in:
DevSoftTech Server 2026-07-02 22:35:34 +05:30
parent ce15a42239
commit 068cbe8ccd
2 changed files with 126 additions and 6 deletions

View File

@ -1656,8 +1656,11 @@ class Medical extends BaseController
return redirect()->to(base_url('Medical/invoice_edit/' . $invoiceId . '?msg=' . urlencode('Required stock tables are missing')));
}
$purchaseItemFields = $this->db->getFieldNames('purchase_invoice_item') ?? [];
$igstPerStockExpr = in_array('IGST_per', $purchaseItemFields, true) ? 't.IGST_per' : '0 AS IGST_per';
$stockSql = "SELECT p.id AS item_code,p.item_name,p.formulation,t.id AS stock_id,t.batch_no,t.expiry_date,t.mrp,t.selling_unit_rate,
t.HSNCODE,t.CGST_per,t.SGST_per,t.packing,
t.HSNCODE,t.CGST_per,t.SGST_per," . $igstPerStockExpr . ",t.packing,
(ifnull(t.total_unit,0)-ifnull(t.total_sale_unit,0)-ifnull(t.total_return_unit,0)-ifnull(t.total_lost_unit,0)) AS stock_qty
FROM med_product_master p
JOIN purchase_invoice_item t ON p.id=t.item_code
@ -1682,6 +1685,10 @@ class Medical extends BaseController
$amount = $qty * $price;
$discAmount = ($discPer > 0) ? ($amount * $discPer / 100) : 0;
$netAmount = $amount - $discAmount;
$cgstPer = (float) ($stock->CGST_per ?? 0);
$sgstPer = (float) ($stock->SGST_per ?? 0);
$igstPer = (float) ($stock->IGST_per ?? 0);
$taxBreakup = $this->calculateInvoiceItemTaxBreakup($netAmount, $cgstPer, $sgstPer, $igstPer);
$itemFields = $this->db->getFieldNames('inv_med_item') ?? [];
$insert = [];
@ -1712,8 +1719,13 @@ class Medical extends BaseController
$setIfExists($insert, $itemFields, 'amount', $amount);
$setIfExists($insert, $itemFields, 'tamount', $netAmount);
$setIfExists($insert, $itemFields, 'twdisc_amount', $netAmount);
$setIfExists($insert, $itemFields, 'CGST_per', (float) ($stock->CGST_per ?? 0));
$setIfExists($insert, $itemFields, 'SGST_per', (float) ($stock->SGST_per ?? 0));
$setIfExists($insert, $itemFields, 'TaxableAmount', $taxBreakup['taxable']);
$setIfExists($insert, $itemFields, 'CGST_per', $cgstPer);
$setIfExists($insert, $itemFields, 'CGST', $taxBreakup['cgst']);
$setIfExists($insert, $itemFields, 'SGST_per', $sgstPer);
$setIfExists($insert, $itemFields, 'SGST', $taxBreakup['sgst']);
$setIfExists($insert, $itemFields, 'IGST_per', $igstPer);
$setIfExists($insert, $itemFields, 'IGST', $taxBreakup['igst']);
$setIfExists($insert, $itemFields, 'HSNCODE', (string) ($stock->HSNCODE ?? ''));
$setIfExists($insert, $itemFields, 'store_stock_id', (int) ($stock->stock_id ?? 0));
$setIfExists($insert, $itemFields, 'packing', (int) ($stock->packing ?? 1));
@ -1875,6 +1887,10 @@ class Medical extends BaseController
$amountValue = $updateQty * $itemRate;
$discAmount = $amountValue * $discPer / 100;
$tamountValue = $amountValue - $discAmount;
$cgstPer = (float) ($item->CGST_per ?? 0);
$sgstPer = (float) ($item->SGST_per ?? 0);
$igstPer = (float) ($item->IGST_per ?? 0);
$taxBreakup = $this->calculateInvoiceItemTaxBreakup($tamountValue, $cgstPer, $sgstPer, $igstPer);
$update = [
'qty' => $updateQty,
@ -1885,6 +1901,18 @@ class Medical extends BaseController
if (in_array('twdisc_amount', $itemFields, true)) {
$update['twdisc_amount'] = $tamountValue;
}
if (in_array('TaxableAmount', $itemFields, true)) {
$update['TaxableAmount'] = $taxBreakup['taxable'];
}
if (in_array('CGST', $itemFields, true)) {
$update['CGST'] = $taxBreakup['cgst'];
}
if (in_array('SGST', $itemFields, true)) {
$update['SGST'] = $taxBreakup['sgst'];
}
if (in_array('IGST', $itemFields, true)) {
$update['IGST'] = $taxBreakup['igst'];
}
$this->db->table('inv_med_item')->where('id', $itemId)->update($update);
@ -1988,6 +2016,10 @@ class Medical extends BaseController
$discAmount = $amountValue * $discPer / 100;
$tamountValue = $amountValue - $discAmount;
$cgstPer = (float) ($source['CGST_per'] ?? 0);
$sgstPer = (float) ($source['SGST_per'] ?? 0);
$igstPer = (float) ($source['IGST_per'] ?? 0);
$taxBreakup = $this->calculateInvoiceItemTaxBreakup($tamountValue, $cgstPer, $sgstPer, $igstPer);
$payload = [];
foreach ($itemFields as $field) {
@ -2017,6 +2049,10 @@ class Medical extends BaseController
$setIfExists($payload, $itemFields, 'disc_per', $discPer);
$setIfExists($payload, $itemFields, 'disc_whole', 0);
$setIfExists($payload, $itemFields, 'twdisc_amount', $tamountValue);
$setIfExists($payload, $itemFields, 'TaxableAmount', $taxBreakup['taxable']);
$setIfExists($payload, $itemFields, 'CGST', $taxBreakup['cgst']);
$setIfExists($payload, $itemFields, 'SGST', $taxBreakup['sgst']);
$setIfExists($payload, $itemFields, 'IGST', $taxBreakup['igst']);
$setIfExists($payload, $itemFields, 'update_by_id', $userId);
$setIfExists($payload, $itemFields, 'update_by_remark', $userInfo . '[' . $userId . '][' . date('d-m-Y H:i:s') . ']');
@ -3423,6 +3459,23 @@ class Medical extends BaseController
}
}
private function calculateInvoiceItemTaxBreakup(float $netAmount, float $cgstPer, float $sgstPer, float $igstPer): array
{
$rateTotal = $cgstPer + $sgstPer + $igstPer;
$taxable = $netAmount;
if ($rateTotal > 0) {
$taxable = round(($netAmount * 100) / (100 + $rateTotal), 2);
}
return [
'taxable' => $taxable,
'cgst' => round($taxable * $cgstPer / 100, 2),
'sgst' => round($taxable * $sgstPer / 100, 2),
'igst' => round($taxable * $igstPer / 100, 2),
];
}
private function refreshInvoicePaymentFields(int $invoiceId, bool $rebuildFromItems = false): void
{
if (! $this->db->tableExists('invoice_med_master')) {
@ -10780,6 +10833,7 @@ class Medical extends BaseController
$lineAmount = $resolveField($iFields, ['twdisc_amount', 'tamount', 'amount']);
$taxable = $resolveField($iFields, ['TaxableAmount', 'taxableamount']);
$cgstPer = $resolveField($iFields, ['CGST_per', 'cgst_per']);
$sgstPer = $resolveField($iFields, ['SGST_per', 'sgst_per']);
$cgst = $resolveField($iFields, ['CGST', 'c_gst_amt', 'cgst', 'cgst_amt']);
$sgst = $resolveField($iFields, ['SGST', 's_gst_amt', 'sgst', 'sgst_amt']);
@ -10789,10 +10843,19 @@ class Medical extends BaseController
$qtyExpr = $qty !== null ? ('IFNULL(i.' . $qty . ',0)') : '0';
$lineAmountExpr = $lineAmount !== null ? ('IFNULL(i.' . $lineAmount . ',0)') : '0';
$taxableExpr = $taxable !== null ? ('IFNULL(i.' . $taxable . ',0)') : $lineAmountExpr;
$baseTaxableExpr = $taxable !== null ? ('IFNULL(i.' . $taxable . ',0)') : '0';
$cgstPerExpr = $cgstPer !== null ? ('IFNULL(i.' . $cgstPer . ',0)') : '0';
$cgstExpr = $cgst !== null ? ('IFNULL(i.' . $cgst . ',0)') : '((' . $taxableExpr . ' * ' . $cgstPerExpr . ')/100)';
$sgstExpr = $sgst !== null ? ('IFNULL(i.' . $sgst . ',0)') : '((' . $taxableExpr . ' * ' . $cgstPerExpr . ')/100)';
$sgstPerExpr = $sgstPer !== null ? ('IFNULL(i.' . $sgstPer . ',0)') : $cgstPerExpr;
$taxableExpr = '(CASE '
. 'WHEN ' . $baseTaxableExpr . ' > 0 THEN ' . $baseTaxableExpr . ' '
. 'WHEN ((' . $cgstPerExpr . ' + ' . $sgstPerExpr . ') > 0 AND ' . $lineAmountExpr . ' > 0) THEN ROUND((' . $lineAmountExpr . ' * 100) / (100 + (' . $cgstPerExpr . ' + ' . $sgstPerExpr . ')), 2) '
. 'ELSE ' . $lineAmountExpr . ' END)';
$cgstExpr = $cgst !== null
? '(CASE WHEN IFNULL(i.' . $cgst . ',0) > 0 THEN IFNULL(i.' . $cgst . ',0) WHEN ((' . $cgstPerExpr . ' + ' . $sgstPerExpr . ') > 0 AND ' . $taxableExpr . ' > 0) THEN ROUND((' . $taxableExpr . ' * ' . $cgstPerExpr . ') / 100, 2) ELSE 0 END)'
: '((' . $taxableExpr . ' * ' . $cgstPerExpr . ')/100)';
$sgstExpr = $sgst !== null
? '(CASE WHEN IFNULL(i.' . $sgst . ',0) > 0 THEN IFNULL(i.' . $sgst . ',0) WHEN ((' . $cgstPerExpr . ' + ' . $sgstPerExpr . ') > 0 AND ' . $taxableExpr . ' > 0) THEN ROUND((' . $taxableExpr . ' * ' . $sgstPerExpr . ') / 100, 2) ELSE 0 END)'
: '((' . $taxableExpr . ' * ' . $sgstPerExpr . ')/100)';
$invTypeExpr = "'CASH'";
if ($ipdId !== null && $ipdCredit !== null) {

View File

@ -0,0 +1,57 @@
-- Backfill GST breakup columns in inv_med_item from existing rate + net amount.
-- Safe to run multiple times; it only targets rows where tax values are currently zero/empty.
UPDATE inv_med_item
SET
TaxableAmount = ROUND(
CASE
WHEN (IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0)) > 0
THEN (IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0))) * 100)
/ (100 + IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0))
ELSE IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0)))
END
, 2),
CGST = ROUND(
(
CASE
WHEN (IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0)) > 0
THEN (IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0))) * 100)
/ (100 + IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0))
ELSE IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0)))
END
) * IFNULL(CGST_per, 0) / 100
, 2),
SGST = ROUND(
(
CASE
WHEN (IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0)) > 0
THEN (IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0))) * 100)
/ (100 + IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0))
ELSE IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0)))
END
) * IFNULL(SGST_per, 0) / 100
, 2),
IGST = ROUND(
(
CASE
WHEN (IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0)) > 0
THEN (IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0))) * 100)
/ (100 + IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0))
ELSE IFNULL(twdisc_amount, IFNULL(tamount, IFNULL(amount, 0)))
END
) * IFNULL(IGST_per, 0) / 100
, 2)
WHERE
(IFNULL(CGST_per, 0) + IFNULL(SGST_per, 0) + IFNULL(IGST_per, 0)) >= 0
AND (
IFNULL(TaxableAmount, 0) = 0
OR IFNULL(CGST, 0) = 0
OR IFNULL(SGST, 0) = 0
OR IFNULL(IGST, 0) = 0
);
-- Quick verification sample:
-- SELECT id, inv_med_id, qty, amount, tamount, twdisc_amount, TaxableAmount, CGST_per, SGST_per, IGST_per, CGST, SGST, IGST
-- FROM inv_med_item
-- WHERE inv_med_id IN (1002584, 1002585, 1002586)
-- ORDER BY id;