diff --git a/app/Controllers/MedicalProcurement.php b/app/Controllers/MedicalProcurement.php
index a0e39d3..49e6052 100644
--- a/app/Controllers/MedicalProcurement.php
+++ b/app/Controllers/MedicalProcurement.php
@@ -25,6 +25,46 @@ class MedicalProcurement extends BaseController
$this->poItemModel = new PharmacyPurchaseOrderItemModel();
$this->grnModel = new PharmacyGrnModel();
$this->grnItemModel = new PharmacyGrnItemModel();
+ $this->ensureGrnSchema();
+ }
+
+ private function ensureGrnSchema(): void
+ {
+ try {
+ if (!$this->db->tableExists('mst_grns')) {
+ return;
+ }
+
+ if (!$this->db->fieldExists('cgst_amount', 'mst_grns')) {
+ $this->db->query("ALTER TABLE `mst_grns` ADD COLUMN `cgst_amount` DECIMAL(10,2) DEFAULT 0.00 AFTER `total_tax_amount`");
+ }
+ if (!$this->db->fieldExists('sgst_amount', 'mst_grns')) {
+ $this->db->query("ALTER TABLE `mst_grns` ADD COLUMN `sgst_amount` DECIMAL(10,2) DEFAULT 0.00 AFTER `cgst_amount`");
+ }
+ if (!$this->db->fieldExists('igst_amount', 'mst_grns')) {
+ $this->db->query("ALTER TABLE `mst_grns` ADD COLUMN `igst_amount` DECIMAL(10,2) DEFAULT 0.00 AFTER `sgst_amount`");
+ }
+ if (!$this->db->fieldExists('round_off', 'mst_grns')) {
+ $this->db->query("ALTER TABLE `mst_grns` ADD COLUMN `round_off` DECIMAL(6,2) DEFAULT 0.00 AFTER `igst_amount`");
+ }
+ if (!$this->db->fieldExists('gst_type', 'mst_grns')) {
+ $this->db->query("ALTER TABLE `mst_grns` ADD COLUMN `gst_type` VARCHAR(20) DEFAULT 'intra_state' AFTER `round_off`");
+ }
+
+ if ($this->db->tableExists('mst_grn_items')) {
+ if (!$this->db->fieldExists('gross_amount', 'mst_grn_items')) {
+ $this->db->query("ALTER TABLE `mst_grn_items` ADD COLUMN `gross_amount` DECIMAL(12,2) DEFAULT 0.00 AFTER `unit_purchase_rate`");
+ }
+ if (!$this->db->fieldExists('sch_disc_per', 'mst_grn_items')) {
+ $this->db->query("ALTER TABLE `mst_grn_items` ADD COLUMN `sch_disc_per` DECIMAL(5,2) DEFAULT 0.00 AFTER `discount_percent`");
+ }
+ if (!$this->db->fieldExists('sch_disc_amt', 'mst_grn_items')) {
+ $this->db->query("ALTER TABLE `mst_grn_items` ADD COLUMN `sch_disc_amt` DECIMAL(10,2) DEFAULT 0.00 AFTER `sch_disc_per`");
+ }
+ }
+ } catch (\Throwable $e) {
+ log_message('error', 'ensureGrnSchema: ' . $e->getMessage());
+ }
}
private function ensurePharmacyAccess()
diff --git a/app/Database/Migrations/2026-10-22-000001_AddGrnEnhancementsColumns.php b/app/Database/Migrations/2026-10-22-000001_AddGrnEnhancementsColumns.php
new file mode 100644
index 0000000..801aa97
--- /dev/null
+++ b/app/Database/Migrations/2026-10-22-000001_AddGrnEnhancementsColumns.php
@@ -0,0 +1,120 @@
+tableExists('mst_grns')) {
+ $colsToAdd = [];
+ if (!$this->fieldExists('mst_grns', 'cgst_amount')) {
+ $colsToAdd['cgst_amount'] = [
+ 'type' => 'DECIMAL',
+ 'constraint' => '10,2',
+ 'default' => 0.00,
+ 'after' => 'total_tax_amount',
+ ];
+ }
+ if (!$this->fieldExists('mst_grns', 'sgst_amount')) {
+ $colsToAdd['sgst_amount'] = [
+ 'type' => 'DECIMAL',
+ 'constraint' => '10,2',
+ 'default' => 0.00,
+ 'after' => 'cgst_amount',
+ ];
+ }
+ if (!$this->fieldExists('mst_grns', 'igst_amount')) {
+ $colsToAdd['igst_amount'] = [
+ 'type' => 'DECIMAL',
+ 'constraint' => '10,2',
+ 'default' => 0.00,
+ 'after' => 'sgst_amount',
+ ];
+ }
+ if (!$this->fieldExists('mst_grns', 'round_off')) {
+ $colsToAdd['round_off'] = [
+ 'type' => 'DECIMAL',
+ 'constraint' => '6,2',
+ 'default' => 0.00,
+ 'after' => 'igst_amount',
+ ];
+ }
+ if (!$this->fieldExists('mst_grns', 'gst_type')) {
+ $colsToAdd['gst_type'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 20,
+ 'default' => 'intra_state',
+ 'after' => 'round_off',
+ ];
+ }
+ if (!empty($colsToAdd)) {
+ $this->forge->addColumn('mst_grns', $colsToAdd);
+ }
+ }
+
+ // 2. Enhancements to mst_grn_items
+ if ($this->tableExists('mst_grn_items')) {
+ $itemColsToAdd = [];
+ if (!$this->fieldExists('mst_grn_items', 'gross_amount')) {
+ $itemColsToAdd['gross_amount'] = [
+ 'type' => 'DECIMAL',
+ 'constraint' => '12,2',
+ 'default' => 0.00,
+ 'after' => 'unit_purchase_rate',
+ ];
+ }
+ if (!$this->fieldExists('mst_grn_items', 'sch_disc_per')) {
+ $itemColsToAdd['sch_disc_per'] = [
+ 'type' => 'DECIMAL',
+ 'constraint' => '5,2',
+ 'default' => 0.00,
+ 'after' => 'discount_percent',
+ ];
+ }
+ if (!$this->fieldExists('mst_grn_items', 'sch_disc_amt')) {
+ $itemColsToAdd['sch_disc_amt'] = [
+ 'type' => 'DECIMAL',
+ 'constraint' => '10,2',
+ 'default' => 0.00,
+ 'after' => 'sch_disc_per',
+ ];
+ }
+ if (!empty($itemColsToAdd)) {
+ $this->forge->addColumn('mst_grn_items', $itemColsToAdd);
+ }
+ }
+ }
+
+ public function down(): void
+ {
+ if ($this->tableExists('mst_grn_items')) {
+ foreach (['sch_disc_amt', 'sch_disc_per', 'gross_amount'] as $col) {
+ if ($this->fieldExists('mst_grn_items', $col)) {
+ $this->forge->dropColumn('mst_grn_items', $col);
+ }
+ }
+ }
+
+ if ($this->tableExists('mst_grns')) {
+ foreach (['gst_type', 'round_off', 'igst_amount', 'sgst_amount', 'cgst_amount'] as $col) {
+ if ($this->fieldExists('mst_grns', $col)) {
+ $this->forge->dropColumn('mst_grns', $col);
+ }
+ }
+ }
+ }
+
+ private function tableExists(string $table): bool
+ {
+ return $this->db->tableExists($table);
+ }
+
+ private function fieldExists(string $table, string $field): bool
+ {
+ return $this->db->fieldExists($field, $table);
+ }
+}
diff --git a/app/Views/medical/grn_detail.php b/app/Views/medical/grn_detail.php
index cea61b5..e725c50 100644
--- a/app/Views/medical/grn_detail.php
+++ b/app/Views/medical/grn_detail.php
@@ -2,7 +2,7 @@
GRN Inspection Details
- = esc($grn['grn_number']) ?>
+ = esc($grn['grn_number'] ?? 'GRN') ?>
Inward Goods Inspection & Live Stock Posting
@@ -10,9 +10,11 @@
Back to GRN List
-
- Print Slip
-
+
+
+ Print Slip
+
+
@@ -21,19 +23,19 @@
Supplier / Vendor
- = esc($grn['name_supplier']) ?>
+ = esc($grn['name_supplier'] ?? 'Supplier') ?>
Vendor Invoice #
- = esc($grn['supplier_invoice_no']) ?>
+ = esc($grn['supplier_invoice_no'] ?? '-') ?>
Invoice Date
- = date('d-M-Y', strtotime($grn['invoice_date'])) ?>
+ = !empty($grn['invoice_date']) ? date('d-M-Y', strtotime($grn['invoice_date'])) : '-' ?>
GRN Date
- = date('d-M-Y', strtotime($grn['grn_date'])) ?>
+ = !empty($grn['grn_date']) ? date('d-M-Y', strtotime($grn['grn_date'])) : '-' ?>
Linked PO
@@ -56,8 +58,8 @@
@@ -91,44 +93,56 @@
$it): ?>
0
+ ? (float)$it['gross_amount']
+ : round($recPacks * $rate, 2);
+
$schPer = (float)($it['sch_disc_per'] ?? 0);
$schAmt = (float)($it['sch_disc_amt'] ?? 0);
- $schTotal = ($schPer > 0 ? ($gross * $schPer / 100) : 0) + $schAmt;
- $tradeDisc = (float)$it['discount_percent'];
+ $schTotal = ($schPer > 0 ? round($gross * $schPer / 100, 2) : 0) + $schAmt;
+ $tradeDisc = (float)($it['discount_percent'] ?? 0);
$afterSch = max(0, $gross - $schTotal);
- $tradeDiscAmt = ($tradeDisc > 0) ? ($afterSch * $tradeDisc / 100) : 0;
+ $tradeDiscAmt = ($tradeDisc > 0) ? round($afterSch * $tradeDisc / 100, 2) : 0;
$rowDisc = $schTotal + $tradeDiscAmt;
$totalGross += $gross;
$totalDiscount += $rowDisc;
- $isInter = (!empty($grn['gst_type']) && $grn['gst_type'] === 'inter_state') || ((float)$it['igst_percent'] > 0);
+ $cgstPer = (float)($it['cgst_percent'] ?? 0);
+ $sgstPer = (float)($it['sgst_percent'] ?? 0);
+ $igstPer = (float)($it['igst_percent'] ?? 0);
+
+ $isInter = (!empty($grn['gst_type']) && $grn['gst_type'] === 'inter_state') || ($igstPer > 0);
$gstLabel = $isInter
- ? number_format((float)$it['igst_percent'], 1) . '% IGST'
- : number_format((float)$it['cgst_percent'] + (float)$it['sgst_percent'], 0) . '% (' . number_format((float)$it['cgst_percent'], 1) . '+' . number_format((float)$it['sgst_percent'], 1) . '%)';
+ ? number_format($igstPer, 1) . '% IGST'
+ : number_format($cgstPer + $sgstPer, 0) . '% (' . number_format($cgstPer, 1) . '+' . number_format($sgstPer, 1) . '%)';
+
+ $taxableAmt = isset($it['taxable_amount']) ? (float)$it['taxable_amount'] : ($gross - $rowDisc);
+ $totalAmt = isset($it['total_amount']) ? (float)$it['total_amount'] : ($taxableAmt + ($taxableAmt * ($cgstPer + $sgstPer + $igstPer) / 100));
?>
= $idx + 1 ?>
- = esc($it['item_name']) ?>
+ = esc($it['item_name'] ?? 'Item') ?>
0): ?>
- Damaged: = (float)$it['rejected_packs'] ?> (= esc($it['rejection_reason'] ?: 'Damaged') ?>)
+ Damaged: = (float)$it['rejected_packs'] ?> (= esc($it['rejection_reason'] ?? 'Damaged') ?>)
- = esc($it['batch_no']) ?>
- = date('d-M-Y', strtotime($it['expiry_date'])) ?>
- = (int)$it['packing'] ?>s
- = (float)$it['received_packs'] ?>
- = (float)$it['free_packs'] ?>
- ₹= number_format((float)$it['unit_purchase_rate'], 2) ?>
- ₹= number_format((float)$it['mrp'], 2) ?>
+ = esc($it['batch_no'] ?? '-') ?>
+ = !empty($it['expiry_date']) ? date('d-M-Y', strtotime($it['expiry_date'])) : '-' ?>
+ = (int)($it['packing'] ?? 1) ?>s
+ = $recPacks ?>
+ = (float)($it['free_packs'] ?? 0) ?>
+ ₹= number_format($rate, 2) ?>
+ ₹= number_format((float)($it['mrp'] ?? 0), 2) ?>
₹= number_format($gross, 2) ?>
= number_format($tradeDisc, 1) ?>%
₹= number_format($schTotal, 2) ?>
- ₹= number_format((float)$it['taxable_amount'], 2) ?>
+ ₹= number_format($taxableAmt, 2) ?>
= esc($gstLabel) ?>
- ₹= number_format((float)$it['total_amount'], 2) ?>
+ ₹= number_format($totalAmt, 2) ?>
Item #= (int)$it['purchase_item_id'] ?>
@@ -153,30 +167,30 @@
Taxable Amount:
- ₹= number_format((float)$grn['total_received_amount'], 2) ?>
+ ₹= number_format((float)($grn['total_received_amount'] ?? $totalGross), 2) ?>
IGST Amount:
- ₹= number_format((float)($grn['igst_amount'] ?? $grn['total_tax_amount']), 2) ?>
+ ₹= number_format((float)($grn['igst_amount'] ?? ($grn['total_tax_amount'] ?? 0)), 2) ?>
CGST Amount:
- ₹= number_format((float)($grn['cgst_amount'] ?? ((float)$grn['total_tax_amount'] / 2)), 2) ?>
+ ₹= number_format((float)($grn['cgst_amount'] ?? (((float)($grn['total_tax_amount'] ?? 0)) / 2)), 2) ?>
SGST Amount:
- ₹= number_format((float)($grn['sgst_amount'] ?? ((float)$grn['total_tax_amount'] / 2)), 2) ?>
+ ₹= number_format((float)($grn['sgst_amount'] ?? (((float)($grn['total_tax_amount'] ?? 0)) / 2)), 2) ?>
Total GST:
- ₹= number_format((float)$grn['total_tax_amount'], 2) ?>
+ ₹= number_format((float)($grn['total_tax_amount'] ?? 0), 2) ?>
@@ -188,7 +202,7 @@
Net Inward Bill Total (Grand Total):
- ₹= number_format((float)$grn['net_amount'], 2) ?>
+ ₹= number_format((float)($grn['net_amount'] ?? 0), 2) ?>
@@ -200,9 +214,9 @@
-
+
- Posted to Live Store Inventory (Purchase Bill Ref #= (int)$grn['purchase_id'] ?>)
+ Posted to Live Store Inventory (Purchase Bill Ref #= (int)($grn['purchase_id'] ?? 0) ?>)
@@ -211,11 +225,11 @@
-
+
Post to Live Inventory
-
+
View Purchase Invoice
diff --git a/app/Views/medical/grn_print_pdf.php b/app/Views/medical/grn_print_pdf.php
index a9261db..3fad94b 100644
--- a/app/Views/medical/grn_print_pdf.php
+++ b/app/Views/medical/grn_print_pdf.php
@@ -86,38 +86,50 @@
$it): ?>
0
+ ? (float)$it['gross_amount']
+ : round($recPacks * $rate, 2);
+
$schPer = (float)($it['sch_disc_per'] ?? 0);
$schAmt = (float)($it['sch_disc_amt'] ?? 0);
- $schTotal = ($schPer > 0 ? ($gross * $schPer / 100) : 0) + $schAmt;
- $tradeDisc = (float)$it['discount_percent'];
+ $schTotal = ($schPer > 0 ? round($gross * $schPer / 100, 2) : 0) + $schAmt;
+ $tradeDisc = (float)($it['discount_percent'] ?? 0);
$afterSch = max(0, $gross - $schTotal);
- $tradeDiscAmt = ($tradeDisc > 0) ? ($afterSch * $tradeDisc / 100) : 0;
+ $tradeDiscAmt = ($tradeDisc > 0) ? round($afterSch * $tradeDisc / 100, 2) : 0;
$rowDisc = $schTotal + $tradeDiscAmt;
$pdfGross += $gross;
$pdfDisc += $rowDisc;
- $isInter = (!empty($grn['gst_type']) && $grn['gst_type'] === 'inter_state') || ((float)($it['igst_percent'] ?? 0) > 0);
+ $cgstPer = (float)($it['cgst_percent'] ?? 0);
+ $sgstPer = (float)($it['sgst_percent'] ?? 0);
+ $igstPer = (float)($it['igst_percent'] ?? 0);
+
+ $isInter = (!empty($grn['gst_type']) && $grn['gst_type'] === 'inter_state') || ($igstPer > 0);
$gstStr = $isInter
- ? number_format((float)($it['igst_percent'] ?? 0), 0) . '% IGST'
- : number_format((float)$it['cgst_percent'] + (float)$it['sgst_percent'], 0) . '%';
+ ? number_format($igstPer, 0) . '% IGST'
+ : number_format($cgstPer + $sgstPer, 0) . '%';
+
+ $taxableAmt = isset($it['taxable_amount']) ? (float)$it['taxable_amount'] : ($gross - $rowDisc);
+ $totalAmt = isset($it['total_amount']) ? (float)$it['total_amount'] : ($taxableAmt + ($taxableAmt * ($cgstPer + $sgstPer + $igstPer) / 100));
?>
= $idx + 1 ?>
- = esc($it['item_name']) ?>
- = esc($it['batch_no']) ?>
- = date('d-M-Y', strtotime($it['expiry_date'])) ?>
- = (int)$it['packing'] ?>s
- = (float)$it['received_packs'] ?>
- = (float)$it['free_packs'] ?>
- ₹= number_format((float)$it['unit_purchase_rate'], 2) ?>
- ₹= number_format((float)$it['mrp'], 2) ?>
+ = esc($it['item_name'] ?? 'Item') ?>
+ = esc($it['batch_no'] ?? '-') ?>
+ = !empty($it['expiry_date']) ? date('d-M-Y', strtotime($it['expiry_date'])) : '-' ?>
+ = (int)($it['packing'] ?? 1) ?>s
+ = $recPacks ?>
+ = (float)($it['free_packs'] ?? 0) ?>
+ ₹= number_format($rate, 2) ?>
+ ₹= number_format((float)($it['mrp'] ?? 0), 2) ?>
₹= number_format($gross, 2) ?>
= number_format($tradeDisc, 0) ?>%
- ₹= number_format((float)$it['taxable_amount'], 2) ?>
+ ₹= number_format($taxableAmt, 2) ?>
= esc($gstStr) ?>
- ₹= number_format((float)$it['total_amount'], 2) ?>
+ ₹= number_format($totalAmt, 2) ?>
@@ -135,7 +147,7 @@
Taxable Amount:
- ₹= number_format((float)$grn['total_received_amount'], 2) ?>
+ ₹= number_format((float)($grn['total_received_amount'] ?? $pdfGross), 2) ?>