Allow editing GRN Inspection Details with full item reconciliation and live inventory synchronization
This commit is contained in:
parent
9236ff1da9
commit
e946000fb9
|
|
@ -138,7 +138,10 @@ $routes->get('Medical/grn_list', 'MedicalProcurement::grn_list');
|
||||||
$routes->get('Medical/grn_new', 'MedicalProcurement::grn_new');
|
$routes->get('Medical/grn_new', 'MedicalProcurement::grn_new');
|
||||||
$routes->get('Medical/grn_new/(:num)', 'MedicalProcurement::grn_new/$1');
|
$routes->get('Medical/grn_new/(:num)', 'MedicalProcurement::grn_new/$1');
|
||||||
$routes->post('Medical/grn_create', 'MedicalProcurement::grn_create');
|
$routes->post('Medical/grn_create', 'MedicalProcurement::grn_create');
|
||||||
|
$routes->get('Medical/grn_detail/(:num)', 'MedicalProcurement::grn_detail/$1');
|
||||||
$routes->get('Medical/grn_edit/(:num)', 'MedicalProcurement::grn_edit/$1');
|
$routes->get('Medical/grn_edit/(:num)', 'MedicalProcurement::grn_edit/$1');
|
||||||
|
$routes->get('Medical/grn_modify/(:num)', 'MedicalProcurement::grn_edit/$1');
|
||||||
|
$routes->post('Medical/grn_update/(:num)', 'MedicalProcurement::grn_update/$1');
|
||||||
$routes->post('Medical/grn_post_inventory/(:num)', 'MedicalProcurement::grn_post_inventory/$1');
|
$routes->post('Medical/grn_post_inventory/(:num)', 'MedicalProcurement::grn_post_inventory/$1');
|
||||||
$routes->get('Medical/grn_print/(:num)', 'MedicalProcurement::grn_print/$1');
|
$routes->get('Medical/grn_print/(:num)', 'MedicalProcurement::grn_print/$1');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -819,7 +819,7 @@ class MedicalProcurement extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function grn_edit(int $grnId)
|
public function grn_detail(int $grnId)
|
||||||
{
|
{
|
||||||
if ($deny = $this->ensurePharmacyAccess()) {
|
if ($deny = $this->ensurePharmacyAccess()) {
|
||||||
return $deny;
|
return $deny;
|
||||||
|
|
@ -845,6 +845,402 @@ class MedicalProcurement extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function grn_edit(int $grnId)
|
||||||
|
{
|
||||||
|
if ($deny = $this->ensurePharmacyAccess()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->getGet('mode') === 'view') {
|
||||||
|
return $this->grn_detail($grnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$grn = $this->db->table('mst_grns grn')
|
||||||
|
->select('grn.*, s.name_supplier, po.po_number')
|
||||||
|
->join('med_supplier s', 's.sid = grn.supplier_id', 'left')
|
||||||
|
->join('mst_purchase_orders po', 'po.po_id = grn.po_id', 'left')
|
||||||
|
->where('grn.grn_id', $grnId)
|
||||||
|
->get()
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
if (!$grn) {
|
||||||
|
return view('medical/placeholder', ['title' => 'Goods Received Note Not Found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $this->grnItemModel->where('grn_id', $grnId)->findAll();
|
||||||
|
$suppliers = $this->db->table('med_supplier')->orderBy('name_supplier', 'ASC')->get()->getResultArray();
|
||||||
|
$approvedPos = $this->poModel->orderBy('po_id', 'DESC')->findAll();
|
||||||
|
|
||||||
|
return view('medical/grn_form', [
|
||||||
|
'isEdit' => true,
|
||||||
|
'grn' => $grn,
|
||||||
|
'grnItems' => $items,
|
||||||
|
'suppliers' => $suppliers,
|
||||||
|
'approvedPos' => $approvedPos,
|
||||||
|
'selectedPo' => !empty($grn['po_id']) ? $this->poModel->find($grn['po_id']) : null,
|
||||||
|
'poItems' => [],
|
||||||
|
'nextGrnNumber' => $grn['grn_number'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function grn_update(int $grnId)
|
||||||
|
{
|
||||||
|
if ($deny = $this->ensurePharmacyAccess()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
|
|
||||||
|
$grn = $this->grnModel->find($grnId);
|
||||||
|
if (!$grn) {
|
||||||
|
return $this->response->setJSON(['status' => 0, 'message' => 'Goods Received Note not found.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$supplierId = (int)$this->request->getPost('supplier_id');
|
||||||
|
$poId = (int)$this->request->getPost('po_id');
|
||||||
|
$supplierInvoiceNo = trim((string)$this->request->getPost('supplier_invoice_no'));
|
||||||
|
$invoiceDate = $this->request->getPost('invoice_date');
|
||||||
|
$grnDate = $this->request->getPost('grn_date');
|
||||||
|
$gstType = $this->request->getPost('gst_type') === 'inter_state' ? 'inter_state' : 'intra_state';
|
||||||
|
$challanNo = trim((string)$this->request->getPost('challan_no'));
|
||||||
|
$remarks = trim((string)$this->request->getPost('remarks'));
|
||||||
|
$roundOff = (float)$this->request->getPost('round_off');
|
||||||
|
$syncInventory = (int)($this->request->getPost('sync_inventory') ?? $this->request->getPost('post_to_inventory') ?? 0);
|
||||||
|
$items = $this->request->getPost('items') ?? [];
|
||||||
|
|
||||||
|
if ($supplierId <= 0) {
|
||||||
|
return $this->response->setJSON(['status' => 0, 'message' => 'Please select a valid supplier/vendor.']);
|
||||||
|
}
|
||||||
|
if (empty($supplierInvoiceNo)) {
|
||||||
|
return $this->response->setJSON(['status' => 0, 'message' => 'Vendor invoice number is required.']);
|
||||||
|
}
|
||||||
|
if (empty($invoiceDate) || empty($grnDate)) {
|
||||||
|
return $this->response->setJSON(['status' => 0, 'message' => 'Please provide valid invoice and received dates.']);
|
||||||
|
}
|
||||||
|
if (empty($items) || !is_array($items)) {
|
||||||
|
return $this->response->setJSON(['status' => 0, 'message' => 'GRN must contain at least one medicine item.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalGross = 0.0;
|
||||||
|
$totalDisc = 0.0;
|
||||||
|
$totalTaxable = 0.0;
|
||||||
|
$totalCgst = 0.0;
|
||||||
|
$totalSgst = 0.0;
|
||||||
|
$totalIgst = 0.0;
|
||||||
|
$totalTax = 0.0;
|
||||||
|
|
||||||
|
$validItems = [];
|
||||||
|
foreach ($items as $idx => $it) {
|
||||||
|
$itemCode = !empty($it['item_code']) ? (int)$it['item_code'] : null;
|
||||||
|
$itemName = trim((string)($it['item_name'] ?? ''));
|
||||||
|
$batchNo = trim((string)($it['batch_no'] ?? ''));
|
||||||
|
$expiryDate = !empty($it['expiry_date']) ? date('Y-m-d', strtotime($it['expiry_date'])) : null;
|
||||||
|
$packing = max(1, (int)($it['packing'] ?? 1));
|
||||||
|
$receivedPacks = (float)($it['received_packs'] ?? 0);
|
||||||
|
$freePacks = (float)($it['free_packs'] ?? 0);
|
||||||
|
$unitPurchaseRate = (float)($it['unit_purchase_rate'] ?? 0);
|
||||||
|
$mrp = (float)($it['mrp'] ?? ($unitPurchaseRate * 1.3));
|
||||||
|
$sellingUnitRate = $packing > 0 ? round($mrp / $packing, 4) : $mrp;
|
||||||
|
|
||||||
|
if (empty($itemCode) || empty($itemName) || empty($batchNo) || empty($expiryDate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$grossAmount = round($receivedPacks * $unitPurchaseRate, 2);
|
||||||
|
$discPercent = (float)($it['discount_percent'] ?? 0);
|
||||||
|
$schDiscPer = (float)($it['sch_disc_per'] ?? 0);
|
||||||
|
$schDiscAmt = (float)($it['sch_disc_amt'] ?? 0);
|
||||||
|
|
||||||
|
// Scheme discount
|
||||||
|
$schPerAmt = ($schDiscPer > 0) ? round($grossAmount * ($schDiscPer / 100), 2) : 0.0;
|
||||||
|
$lineSchDisc = $schPerAmt + $schDiscAmt;
|
||||||
|
$afterSch = max(0.0, $grossAmount - $lineSchDisc);
|
||||||
|
|
||||||
|
// Trade discount
|
||||||
|
$tradeDiscAmt = ($discPercent > 0) ? round($afterSch * ($discPercent / 100), 2) : 0.0;
|
||||||
|
$lineTaxable = max(0.0, round($afterSch - $tradeDiscAmt, 2));
|
||||||
|
|
||||||
|
$gstRate = (float)($it['gst_rate'] ?? 5);
|
||||||
|
if ($gstType === 'inter_state') {
|
||||||
|
$igstPer = $gstRate;
|
||||||
|
$cgstAmt = 0.0;
|
||||||
|
$sgstAmt = 0.0;
|
||||||
|
$igstAmt = round($lineTaxable * ($igstPer / 100), 2);
|
||||||
|
$lineTax = $igstAmt;
|
||||||
|
$cgstPer = 0.0;
|
||||||
|
$sgstPer = 0.0;
|
||||||
|
} else {
|
||||||
|
$cgstPer = round($gstRate / 2, 2);
|
||||||
|
$sgstPer = round($gstRate / 2, 2);
|
||||||
|
$igstPer = 0.0;
|
||||||
|
$cgstAmt = round($lineTaxable * ($cgstPer / 100), 2);
|
||||||
|
$sgstAmt = round($lineTaxable * ($sgstPer / 100), 2);
|
||||||
|
$igstAmt = 0.0;
|
||||||
|
$lineTax = $cgstAmt + $sgstAmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lineTotal = round($lineTaxable + $lineTax, 2);
|
||||||
|
|
||||||
|
$totalGross += $grossAmount;
|
||||||
|
$totalDisc += ($lineSchDisc + $tradeDiscAmt);
|
||||||
|
$totalTaxable += $lineTaxable;
|
||||||
|
$totalCgst += $cgstAmt;
|
||||||
|
$totalSgst += $sgstAmt;
|
||||||
|
$totalIgst += $igstAmt;
|
||||||
|
$totalTax += $lineTax;
|
||||||
|
|
||||||
|
$validItems[] = [
|
||||||
|
'grn_item_id' => !empty($it['grn_item_id']) ? (int)$it['grn_item_id'] : 0,
|
||||||
|
'purchase_item_id' => !empty($it['purchase_item_id']) ? (int)$it['purchase_item_id'] : 0,
|
||||||
|
'po_item_id' => !empty($it['po_item_id']) ? (int)$it['po_item_id'] : null,
|
||||||
|
'item_code' => $itemCode,
|
||||||
|
'item_name' => $itemName,
|
||||||
|
'batch_no' => $batchNo,
|
||||||
|
'expiry_date' => $expiryDate,
|
||||||
|
'packing' => $packing,
|
||||||
|
'order_packs' => (float)($it['order_packs'] ?? 0),
|
||||||
|
'received_packs' => $receivedPacks,
|
||||||
|
'free_packs' => $freePacks,
|
||||||
|
'rejected_packs' => (float)($it['rejected_packs'] ?? 0),
|
||||||
|
'rejection_reason' => $it['rejection_reason'] ?? null,
|
||||||
|
'unit_purchase_rate' => $unitPurchaseRate,
|
||||||
|
'gross_amount' => $grossAmount,
|
||||||
|
'mrp' => $mrp,
|
||||||
|
'selling_unit_rate' => $sellingUnitRate,
|
||||||
|
'discount_percent' => $discPercent,
|
||||||
|
'sch_disc_per' => $schDiscPer,
|
||||||
|
'sch_disc_amt' => $schDiscAmt,
|
||||||
|
'taxable_amount' => $lineTaxable,
|
||||||
|
'cgst_percent' => $cgstPer,
|
||||||
|
'cgst_amount' => $cgstAmt,
|
||||||
|
'sgst_percent' => $sgstPer,
|
||||||
|
'sgst_amount' => $sgstAmt,
|
||||||
|
'igst_percent' => $igstPer,
|
||||||
|
'igst_amount' => $igstAmt,
|
||||||
|
'hsn_code' => !empty($it['hsn_code']) ? $it['hsn_code'] : '3004',
|
||||||
|
'total_amount' => $lineTotal,
|
||||||
|
'rack_no' => $it['rack_no'] ?? null,
|
||||||
|
'shelf_no' => $it['shelf_no'] ?? null,
|
||||||
|
'cold_storage' => $it['cold_storage'] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($validItems)) {
|
||||||
|
return $this->response->setJSON(['status' => 0, 'message' => 'No valid item rows with batch and expiry were found.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$netTotal = round($totalTaxable + $totalTax + $roundOff, 2);
|
||||||
|
|
||||||
|
$this->db->transStart();
|
||||||
|
|
||||||
|
$grnHeaderUpdate = [
|
||||||
|
'po_id' => $poId > 0 ? $poId : null,
|
||||||
|
'supplier_id' => $supplierId,
|
||||||
|
'challan_no' => $challanNo ?: null,
|
||||||
|
'supplier_invoice_no' => $supplierInvoiceNo,
|
||||||
|
'invoice_date' => $invoiceDate,
|
||||||
|
'grn_date' => $grnDate,
|
||||||
|
'total_received_amount' => $totalTaxable,
|
||||||
|
'total_tax_amount' => $totalTax,
|
||||||
|
'cgst_amount' => $totalCgst,
|
||||||
|
'sgst_amount' => $totalSgst,
|
||||||
|
'igst_amount' => $totalIgst,
|
||||||
|
'round_off' => $roundOff,
|
||||||
|
'gst_type' => $gstType,
|
||||||
|
'net_amount' => $netTotal,
|
||||||
|
'remarks' => $remarks,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->grnModel->update($grnId, $grnHeaderUpdate);
|
||||||
|
|
||||||
|
$existingGrnItems = $this->grnItemModel->where('grn_id', $grnId)->findAll();
|
||||||
|
$existingItemsById = [];
|
||||||
|
foreach ($existingGrnItems as $ei) {
|
||||||
|
$existingItemsById[(int)$ei['grn_item_id']] = $ei;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isPosted = ($grn['status'] === 'posted_to_inventory') && !empty($grn['purchase_id']);
|
||||||
|
$purchaseId = $isPosted ? (int)$grn['purchase_id'] : 0;
|
||||||
|
$medModel = new MedicalModel();
|
||||||
|
$userLabel = $this->getCurrentUserLabel();
|
||||||
|
$processedGrnItemIds = [];
|
||||||
|
|
||||||
|
foreach ($validItems as $vItem) {
|
||||||
|
$grnItemId = $vItem['grn_item_id'];
|
||||||
|
$vItem['grn_id'] = $grnId;
|
||||||
|
$purchaseItemId = $vItem['purchase_item_id'];
|
||||||
|
unset($vItem['grn_item_id']);
|
||||||
|
|
||||||
|
if ($grnItemId > 0 && isset($existingItemsById[$grnItemId])) {
|
||||||
|
// Update existing GRN item
|
||||||
|
$this->grnItemModel->update($grnItemId, $vItem);
|
||||||
|
$processedGrnItemIds[] = $grnItemId;
|
||||||
|
|
||||||
|
// If posted, update linked purchase_invoice_item
|
||||||
|
if ($isPosted && $purchaseItemId > 0) {
|
||||||
|
$packing = (int)($vItem['packing'] ?: 1);
|
||||||
|
$recPacks = (float)$vItem['received_packs'];
|
||||||
|
$freePacks = (float)$vItem['free_packs'];
|
||||||
|
$totalUnits = (int)round(($recPacks + $freePacks) * $packing);
|
||||||
|
$purchasePricePerPack = (float)$vItem['unit_purchase_rate'];
|
||||||
|
$purchaseUnitRate = $packing > 0 ? round($purchasePricePerPack / $packing, 4) : $purchasePricePerPack;
|
||||||
|
|
||||||
|
$piItemUpdate = [
|
||||||
|
'item_code' => $vItem['item_code'],
|
||||||
|
'Item_name' => $vItem['item_name'],
|
||||||
|
'packing' => $packing,
|
||||||
|
'batch_no' => $vItem['batch_no'],
|
||||||
|
'purchase_price' => $purchasePricePerPack,
|
||||||
|
'expiry_date' => $vItem['expiry_date'],
|
||||||
|
'mrp' => $vItem['mrp'],
|
||||||
|
'qty' => $recPacks,
|
||||||
|
'qty_free' => $freePacks,
|
||||||
|
'tqty' => $recPacks + $freePacks,
|
||||||
|
'amount' => $vItem['gross_amount'],
|
||||||
|
'discount' => $vItem['discount_percent'],
|
||||||
|
'sch_disc_amt' => $vItem['sch_disc_amt'],
|
||||||
|
'sch_disc_per' => $vItem['sch_disc_per'],
|
||||||
|
'taxable_amount' => $vItem['taxable_amount'],
|
||||||
|
'CGST_per' => $vItem['cgst_percent'],
|
||||||
|
'CGST' => $vItem['cgst_amount'],
|
||||||
|
'SGST_per' => $vItem['sgst_percent'],
|
||||||
|
'SGST' => $vItem['sgst_amount'],
|
||||||
|
'net_amount' => $vItem['total_amount'],
|
||||||
|
'HSNCODE' => $vItem['hsn_code'],
|
||||||
|
'selling_price' => $vItem['selling_unit_rate'] * $packing,
|
||||||
|
'total_unit' => $totalUnits,
|
||||||
|
'selling_unit_rate' => $vItem['selling_unit_rate'],
|
||||||
|
'purchase_unit_rate' => $purchaseUnitRate,
|
||||||
|
'stock_date' => $invoiceDate,
|
||||||
|
'rack_no' => $vItem['rack_no'] ?: '',
|
||||||
|
'shelf_no' => $vItem['shelf_no'] ?: '',
|
||||||
|
'cold_storage' => $vItem['cold_storage'] ?: 'No',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->db->table('purchase_invoice_item')->where('id', $purchaseItemId)->update($piItemUpdate);
|
||||||
|
$medModel->updatePurchaseStockById($purchaseItemId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Insert new GRN item
|
||||||
|
$this->grnItemModel->insert($vItem);
|
||||||
|
$newGrnItemId = (int)$this->db->insertID();
|
||||||
|
$processedGrnItemIds[] = $newGrnItemId;
|
||||||
|
|
||||||
|
// If posted, also insert new purchase_invoice_item
|
||||||
|
if ($isPosted && $purchaseId > 0) {
|
||||||
|
$packing = (int)($vItem['packing'] ?: 1);
|
||||||
|
$recPacks = (float)$vItem['received_packs'];
|
||||||
|
$freePacks = (float)$vItem['free_packs'];
|
||||||
|
$totalUnits = (int)round(($recPacks + $freePacks) * $packing);
|
||||||
|
$purchasePricePerPack = (float)$vItem['unit_purchase_rate'];
|
||||||
|
$purchaseUnitRate = $packing > 0 ? round($purchasePricePerPack / $packing, 4) : $purchasePricePerPack;
|
||||||
|
|
||||||
|
$piItemData = [
|
||||||
|
'purchase_id' => $purchaseId,
|
||||||
|
'item_code' => $vItem['item_code'],
|
||||||
|
'Item_name' => $vItem['item_name'],
|
||||||
|
'packing' => $packing,
|
||||||
|
'batch_no' => $vItem['batch_no'],
|
||||||
|
'purchase_price' => $purchasePricePerPack,
|
||||||
|
'expiry_date' => $vItem['expiry_date'],
|
||||||
|
'mrp' => $vItem['mrp'],
|
||||||
|
'qty' => $recPacks,
|
||||||
|
'qty_free' => $freePacks,
|
||||||
|
'tqty' => $recPacks + $freePacks,
|
||||||
|
'amount' => $vItem['gross_amount'],
|
||||||
|
'discount' => $vItem['discount_percent'],
|
||||||
|
'sch_disc_amt' => $vItem['sch_disc_amt'],
|
||||||
|
'sch_disc_per' => $vItem['sch_disc_per'],
|
||||||
|
'taxable_amount' => $vItem['taxable_amount'],
|
||||||
|
'CGST_per' => $vItem['cgst_percent'],
|
||||||
|
'CGST' => $vItem['cgst_amount'],
|
||||||
|
'SGST_per' => $vItem['sgst_percent'],
|
||||||
|
'SGST' => $vItem['sgst_amount'],
|
||||||
|
'net_amount' => $vItem['total_amount'],
|
||||||
|
'HSNCODE' => $vItem['hsn_code'],
|
||||||
|
'selling_price' => $vItem['selling_unit_rate'] * $packing,
|
||||||
|
'total_unit' => $totalUnits,
|
||||||
|
'total_sale_unit' => 0,
|
||||||
|
'total_return_unit' => 0,
|
||||||
|
'total_lost_unit' => 0,
|
||||||
|
'selling_unit_rate' => $vItem['selling_unit_rate'],
|
||||||
|
'purchase_unit_rate' => $purchaseUnitRate,
|
||||||
|
'item_return' => 0,
|
||||||
|
'remove_item' => 0,
|
||||||
|
'stock_date' => $invoiceDate,
|
||||||
|
'rack_no' => $vItem['rack_no'] ?: '',
|
||||||
|
'shelf_no' => $vItem['shelf_no'] ?: '',
|
||||||
|
'cold_storage' => $vItem['cold_storage'] ?: 'No',
|
||||||
|
'insert_by' => $userLabel,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->db->table('purchase_invoice_item')->insert($piItemData);
|
||||||
|
$newPiItemId = (int)$this->db->insertID();
|
||||||
|
$this->grnItemModel->update($newGrnItemId, ['purchase_item_id' => $newPiItemId]);
|
||||||
|
$medModel->updatePurchaseStockById($newPiItemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete any items that were removed
|
||||||
|
foreach ($existingItemsById as $oldId => $oldItem) {
|
||||||
|
if (!in_array($oldId, $processedGrnItemIds, true)) {
|
||||||
|
if ($isPosted && !empty($oldItem['purchase_item_id'])) {
|
||||||
|
$piId = (int)$oldItem['purchase_item_id'];
|
||||||
|
$piRow = $this->db->table('purchase_invoice_item')->where('id', $piId)->get()->getRowArray();
|
||||||
|
$soldUnits = (float)($piRow['total_sale_unit'] ?? 0);
|
||||||
|
if ($soldUnits <= 0) {
|
||||||
|
$this->db->table('purchase_invoice_item')->where('id', $piId)->delete();
|
||||||
|
$this->grnItemModel->delete($oldId);
|
||||||
|
} else {
|
||||||
|
// Mark removed instead of hard delete to preserve sales audit
|
||||||
|
$this->db->table('purchase_invoice_item')->where('id', $piId)->update(['remove_item' => 1]);
|
||||||
|
$this->grnItemModel->delete($oldId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->grnItemModel->delete($oldId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update purchase_invoice header if already posted
|
||||||
|
if ($isPosted && $purchaseId > 0) {
|
||||||
|
$supplier = $this->db->table('med_supplier')->where('sid', $supplierId)->get()->getRowArray();
|
||||||
|
$suppName = $supplier['name_supplier'] ?? 'Supplier';
|
||||||
|
|
||||||
|
$this->db->table('purchase_invoice')->where('id', $purchaseId)->update([
|
||||||
|
'sid' => $supplierId,
|
||||||
|
'ischallan' => !empty($challanNo) ? 1 : 0,
|
||||||
|
'monthyear' => date('m/Y', strtotime($invoiceDate)),
|
||||||
|
'supp_name' => $suppName,
|
||||||
|
'date_of_invoice' => $invoiceDate,
|
||||||
|
'Invoice_no' => $supplierInvoiceNo,
|
||||||
|
'Taxable_Amt' => $totalTaxable,
|
||||||
|
'CGST_Amt' => $totalCgst,
|
||||||
|
'SGST_Amt' => $totalSgst,
|
||||||
|
'T_Net_Amount' => $netTotal,
|
||||||
|
]);
|
||||||
|
} elseif (!$isPosted && $syncInventory === 1) {
|
||||||
|
// If was not posted before, but user checked "Post to live inventory"
|
||||||
|
$this->executeGrnStockPosting($grnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->transComplete();
|
||||||
|
|
||||||
|
if ($this->db->transStatus() === false) {
|
||||||
|
$err = $this->db->error();
|
||||||
|
$errMsg = !empty($err['message']) ? ': ' . $err['message'] : '.';
|
||||||
|
log_message('error', 'grn_update transaction failed' . $errMsg);
|
||||||
|
return $this->response->setJSON(['status' => 0, 'message' => 'Failed to update Goods Received Note' . $errMsg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'status' => 1,
|
||||||
|
'message' => "GRN {$grn['grn_number']} updated successfully!",
|
||||||
|
'grn_id' => $grnId,
|
||||||
|
'grn_number' => $grn['grn_number'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function grn_post_inventory(int $grnId)
|
public function grn_post_inventory(int $grnId)
|
||||||
{
|
{
|
||||||
if ($deny = $this->ensurePharmacyAccess()) {
|
if ($deny = $this->ensurePharmacyAccess()) {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@
|
||||||
<i class="bi bi-arrow-left"></i> Back to GRN List
|
<i class="bi bi-arrow-left"></i> Back to GRN List
|
||||||
</a>
|
</a>
|
||||||
<?php if (!empty($grn['grn_id'])): ?>
|
<?php if (!empty($grn['grn_id'])): ?>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" onclick="load_form_div('<?= base_url('Medical/grn_edit/' . (int)$grn['grn_id']) ?>','medical-main','Edit GRN Inspection');">
|
||||||
|
<i class="bi bi-pencil-square"></i> Edit GRN Details
|
||||||
|
</button>
|
||||||
<a href="<?= base_url('Medical/grn_print/' . (int)$grn['grn_id']) ?>" target="_blank" class="btn btn-outline-secondary btn-sm">
|
<a href="<?= base_url('Medical/grn_print/' . (int)$grn['grn_id']) ?>" target="_blank" class="btn btn-outline-secondary btn-sm">
|
||||||
<i class="bi bi-printer"></i> Print Slip
|
<i class="bi bi-printer"></i> Print Slip
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -225,6 +228,11 @@
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
|
<?php if (!empty($grn['grn_id'])): ?>
|
||||||
|
<button type="button" class="btn btn-warning" onclick="load_form_div('<?= base_url('Medical/grn_edit/' . (int)$grn['grn_id']) ?>','medical-main','Edit GRN Inspection');">
|
||||||
|
<i class="bi bi-pencil-square"></i> Edit GRN
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
<?php if (($grn['status'] ?? '') !== 'posted_to_inventory' && !empty($grn['grn_id'])): ?>
|
<?php if (($grn['status'] ?? '') !== 'posted_to_inventory' && !empty($grn['grn_id'])): ?>
|
||||||
<button type="button" class="btn btn-success px-4" onclick="postGrnToLiveInventory(<?= (int)$grn['grn_id'] ?>)">
|
<button type="button" class="btn btn-success px-4" onclick="postGrnToLiveInventory(<?= (int)$grn['grn_id'] ?>)">
|
||||||
<i class="bi bi-box-arrow-in-down"></i> Post to Live Inventory
|
<i class="bi bi-box-arrow-in-down"></i> Post to Live Inventory
|
||||||
|
|
@ -245,7 +253,7 @@ function postGrnToLiveInventory(grnId) {
|
||||||
$.post('<?= base_url('Medical/grn_post_inventory/') ?>' + grnId, {}, function(data) {
|
$.post('<?= base_url('Medical/grn_post_inventory/') ?>' + grnId, {}, function(data) {
|
||||||
alert(data.message || 'Done');
|
alert(data.message || 'Done');
|
||||||
if (data.status) {
|
if (data.status) {
|
||||||
load_form_div('<?= base_url('Medical/grn_edit/') ?>' + grnId, 'medical-main', 'GRN Inspection Detail');
|
load_form_div('<?= base_url('Medical/grn_detail/') ?>' + grnId, 'medical-main', 'GRN Inspection Detail');
|
||||||
}
|
}
|
||||||
}, 'json').fail(function() {
|
}, 'json').fail(function() {
|
||||||
alert('Error posting GRN.');
|
alert('Error posting GRN.');
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,34 @@
|
||||||
<?php
|
<?php
|
||||||
$poId = !empty($selectedPo) ? (int)$selectedPo['po_id'] : 0;
|
$isEdit = !empty($isEdit);
|
||||||
$supplierId = !empty($selectedPo) ? (int)$selectedPo['supplier_id'] : 0;
|
$poId = !empty($grn['po_id']) ? (int)$grn['po_id'] : (!empty($selectedPo) ? (int)$selectedPo['po_id'] : 0);
|
||||||
$nextGrnNumber = $nextGrnNumber ?? 'GRN-NEW';
|
$supplierId = !empty($grn['supplier_id']) ? (int)$grn['supplier_id'] : (!empty($selectedPo) ? (int)$selectedPo['supplier_id'] : 0);
|
||||||
|
$displayGrnNumber = !empty($grn['grn_number']) ? $grn['grn_number'] : ($nextGrnNumber ?? 'GRN-NEW');
|
||||||
|
$isPosted = !empty($grn['status']) && $grn['status'] === 'posted_to_inventory';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="pagetitle d-flex justify-content-between align-items-center mb-3">
|
<div class="pagetitle d-flex justify-content-between align-items-center mb-3">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="mb-0 text-success">
|
<h4 class="mb-0 text-success">
|
||||||
<i class="bi bi-box-arrow-in-down"></i> Create Goods Received Note (GRN)
|
<i class="bi <?= $isEdit ? 'bi-pencil-square' : 'bi-box-arrow-in-down' ?>"></i>
|
||||||
<span class="badge bg-light text-dark border ms-2 font-monospace"><?= esc($nextGrnNumber) ?></span>
|
<?= $isEdit ? 'Edit Goods Received Note (GRN)' : 'Create Goods Received Note (GRN)' ?>
|
||||||
|
<span class="badge bg-light text-dark border ms-2 font-monospace"><?= esc($displayGrnNumber) ?></span>
|
||||||
|
<?php if ($isEdit && $isPosted): ?>
|
||||||
|
<span class="badge bg-success ms-1">POSTED TO STOCK (REF #<?= (int)($grn['purchase_id'] ?? 0) ?>)</span>
|
||||||
|
<?php elseif ($isEdit): ?>
|
||||||
|
<span class="badge bg-info text-dark ms-1">INSPECTION DRAFT / VERIFIED</span>
|
||||||
|
<?php endif; ?>
|
||||||
</h4>
|
</h4>
|
||||||
<small class="text-muted">Inward Stock Physical Inspection & Vendor Invoice Reconciliation</small>
|
<small class="text-muted">Inward Stock Physical Inspection & Vendor Invoice Reconciliation</small>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="d-flex gap-2">
|
||||||
|
<?php if ($isEdit && !empty($grn['grn_id'])): ?>
|
||||||
|
<a href="javascript:load_form_div('<?= base_url('Medical/grn_detail/' . (int)$grn['grn_id']) ?>','medical-main','GRN Inspection Detail');" class="btn btn-outline-info btn-sm">
|
||||||
|
<i class="bi bi-eye"></i> View Inspection Detail
|
||||||
|
</a>
|
||||||
|
<a href="<?= base_url('Medical/grn_print/' . (int)$grn['grn_id']) ?>" target="_blank" class="btn btn-outline-secondary btn-sm">
|
||||||
|
<i class="bi bi-printer"></i> Print Slip
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
<a href="javascript:load_form_div('<?= base_url('Medical/grn_list') ?>','medical-main','GRN Register :Pharmacy');" class="btn btn-outline-secondary btn-sm">
|
<a href="javascript:load_form_div('<?= base_url('Medical/grn_list') ?>','medical-main','GRN Register :Pharmacy');" class="btn btn-outline-secondary btn-sm">
|
||||||
<i class="bi bi-arrow-left"></i> Back to GRN List
|
<i class="bi bi-arrow-left"></i> Back to GRN List
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -26,6 +42,9 @@
|
||||||
<div class="card-body p-3">
|
<div class="card-body p-3">
|
||||||
<form id="grn-create-form" onsubmit="event.preventDefault(); submitGrnForm();">
|
<form id="grn-create-form" onsubmit="event.preventDefault(); submitGrnForm();">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
|
<?php if ($isEdit && !empty($grn['grn_id'])): ?>
|
||||||
|
<input type="hidden" name="grn_id" id="grn_id" value="<?= (int)$grn['grn_id'] ?>">
|
||||||
|
<?php endif; ?>
|
||||||
<div class="row g-2 mb-2">
|
<div class="row g-2 mb-2">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label form-label-sm fw-semibold mb-1">Linked Purchase Order (PO)</label>
|
<label class="form-label form-label-sm fw-semibold mb-1">Linked Purchase Order (PO)</label>
|
||||||
|
|
@ -55,33 +74,34 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<label class="form-label form-label-sm fw-semibold mb-1">Vendor Invoice / Bill # <span class="text-danger">*</span></label>
|
<label class="form-label form-label-sm fw-semibold mb-1">Vendor Invoice / Bill # <span class="text-danger">*</span></label>
|
||||||
<input type="text" name="supplier_invoice_no" id="supplier_invoice_no" class="form-control form-control-sm" placeholder="e.g. INV-88421" required>
|
<input type="text" name="supplier_invoice_no" id="supplier_invoice_no" class="form-control form-control-sm" placeholder="e.g. INV-88421" value="<?= esc($grn['supplier_invoice_no'] ?? '') ?>" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<label class="form-label form-label-sm fw-semibold mb-1">Invoice Date <span class="text-danger">*</span></label>
|
<label class="form-label form-label-sm fw-semibold mb-1">Invoice Date <span class="text-danger">*</span></label>
|
||||||
<input type="date" name="invoice_date" id="invoice_date" class="form-control form-control-sm" value="<?= date('Y-m-d') ?>" required>
|
<input type="date" name="invoice_date" id="invoice_date" class="form-control form-control-sm" value="<?= !empty($grn['invoice_date']) ? esc($grn['invoice_date']) : date('Y-m-d') ?>" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<label class="form-label form-label-sm fw-semibold mb-1">GRN Received Date <span class="text-danger">*</span></label>
|
<label class="form-label form-label-sm fw-semibold mb-1">GRN Received Date <span class="text-danger">*</span></label>
|
||||||
<input type="date" name="grn_date" id="grn_date" class="form-control form-control-sm" value="<?= date('Y-m-d') ?>" required>
|
<input type="date" name="grn_date" id="grn_date" class="form-control form-control-sm" value="<?= !empty($grn['grn_date']) ? esc($grn['grn_date']) : date('Y-m-d') ?>" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-2 mb-3">
|
<div class="row g-2 mb-3">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label form-label-sm fw-semibold mb-1">GST Supply Type</label>
|
<label class="form-label form-label-sm fw-semibold mb-1">GST Supply Type</label>
|
||||||
|
<?php $gstType = $grn['gst_type'] ?? 'intra_state'; ?>
|
||||||
<select name="gst_type" id="grn_gst_type" class="form-select form-select-sm" onchange="recalculateGrnTotals();">
|
<select name="gst_type" id="grn_gst_type" class="form-select form-select-sm" onchange="recalculateGrnTotals();">
|
||||||
<option value="intra_state" selected>Intra-State (CGST + SGST)</option>
|
<option value="intra_state" <?= ($gstType === 'intra_state') ? 'selected' : '' ?>>Intra-State (CGST + SGST)</option>
|
||||||
<option value="inter_state">Inter-State (IGST)</option>
|
<option value="inter_state" <?= ($gstType === 'inter_state') ? 'selected' : '' ?>>Inter-State (IGST)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label form-label-sm fw-semibold mb-1">Delivery Challan / Gate Pass #</label>
|
<label class="form-label form-label-sm fw-semibold mb-1">Delivery Challan / Gate Pass #</label>
|
||||||
<input type="text" name="challan_no" id="challan_no" class="form-control form-control-sm" placeholder="Optional Challan No.">
|
<input type="text" name="challan_no" id="challan_no" class="form-control form-control-sm" placeholder="Optional Challan No." value="<?= esc($grn['challan_no'] ?? '') ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label form-label-sm fw-semibold mb-1">Receiving Inspection Remarks</label>
|
<label class="form-label form-label-sm fw-semibold mb-1">Receiving Inspection Remarks</label>
|
||||||
<input type="text" name="remarks" id="grn_remarks" class="form-control form-control-sm" placeholder="Package integrity verified, cold chain maintained, etc.">
|
<input type="text" name="remarks" id="grn_remarks" class="form-control form-control-sm" placeholder="Package integrity verified, cold chain maintained, etc." value="<?= esc($grn['remarks'] ?? '') ?>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -155,7 +175,93 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="grn-items-body">
|
<tbody id="grn-items-body">
|
||||||
<?php if (!empty($poItems)): ?>
|
<?php if (!empty($grnItems)): ?>
|
||||||
|
<?php foreach ($grnItems as $idx => $it): ?>
|
||||||
|
<?php
|
||||||
|
$gstRate = (float)($it['igst_percent'] > 0 ? $it['igst_percent'] : ((float)($it['cgst_percent'] ?? 0) + (float)($it['sgst_percent'] ?? 0)));
|
||||||
|
if ($gstRate <= 0 && isset($it['cgst_percent']) && (float)$it['cgst_percent'] == 0 && (float)$it['sgst_percent'] == 0 && (float)$it['igst_percent'] == 0) {
|
||||||
|
$gstRate = 0;
|
||||||
|
} elseif ($gstRate <= 0) {
|
||||||
|
$gstRate = 5;
|
||||||
|
}
|
||||||
|
$recQty = (float)($it['received_packs'] ?? 0);
|
||||||
|
$freeQty = (float)($it['free_packs'] ?? 0);
|
||||||
|
$rate = (float)($it['unit_purchase_rate'] ?? 0);
|
||||||
|
$gross = isset($it['gross_amount']) && (float)$it['gross_amount'] > 0 ? (float)$it['gross_amount'] : ($recQty * $rate);
|
||||||
|
$mrp = (float)($it['mrp'] ?? ($rate * 1.3));
|
||||||
|
$tradeDisc = (float)($it['discount_percent'] ?? 0);
|
||||||
|
$schPer = (float)($it['sch_disc_per'] ?? 0);
|
||||||
|
$schAmt = (float)($it['sch_disc_amt'] ?? 0);
|
||||||
|
$taxable = isset($it['taxable_amount']) ? (float)$it['taxable_amount'] : $gross;
|
||||||
|
$lineTotal = isset($it['total_amount']) ? (float)$it['total_amount'] : $taxable;
|
||||||
|
$expDate = !empty($it['expiry_date']) ? date('Y-m-d', strtotime($it['expiry_date'])) : '';
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td class="row-num text-center"><?= $idx + 1 ?></td>
|
||||||
|
<td>
|
||||||
|
<strong class="text-dark"><?= esc($it['item_name']) ?></strong>
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][grn_item_id]" value="<?= (int)($it['grn_item_id'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][purchase_item_id]" value="<?= (int)($it['purchase_item_id'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][item_code]" value="<?= esc($it['item_code']) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][item_name]" value="<?= esc($it['item_name']) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][po_item_id]" value="<?= (int)($it['po_item_id'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][order_packs]" value="<?= (float)($it['order_packs'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][rejected_packs]" value="<?= (float)($it['rejected_packs'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][hsn_code]" value="<?= esc($it['hsn_code'] ?? '3004') ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][cgst_percent]" class="line-cgst" value="<?= (float)($it['cgst_percent'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][sgst_percent]" class="line-sgst" value="<?= (float)($it['sgst_percent'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][igst_percent]" class="line-igst" value="<?= (float)($it['igst_percent'] ?? 0) ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][gross_amount]" class="line-gross-hid" value="<?= number_format($gross, 2, '.', '') ?>">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][taxable_amount]" class="line-taxable-hid" value="<?= number_format($taxable, 2, '.', '') ?>">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="items[<?= $idx ?>][batch_no]" class="form-control form-control-sm font-monospace line-batch" value="<?= esc($it['batch_no'] ?? '') ?>" placeholder="Batch #" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="date" name="items[<?= $idx ?>][expiry_date]" class="form-control form-control-sm line-expiry" value="<?= esc($expDate) ?>" required>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][packing]" class="form-control form-control-sm text-center line-pack px-1" value="<?= (int)($it['packing'] ?? 10) ?>" min="1">
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][received_packs]" class="form-control form-control-sm text-center fw-bold line-rec px-1" value="<?= $recQty ?>" min="0" step="1" oninput="recalculateGrnTotals();" required>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][free_packs]" class="form-control form-control-sm text-center text-success line-free px-1" value="<?= $freeQty ?>" min="0" step="1" oninput="recalculateGrnTotals();">
|
||||||
|
</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][unit_purchase_rate]" class="form-control form-control-sm text-end line-rate px-1" value="<?= number_format($rate, 2, '.', '') ?>" min="0" step="0.01" oninput="recalculateGrnTotals();">
|
||||||
|
</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][mrp]" class="form-control form-control-sm text-end line-mrp px-1" value="<?= number_format($mrp, 2, '.', '') ?>" min="0" step="0.01">
|
||||||
|
</td>
|
||||||
|
<td class="text-end font-monospace line-gross-disp bg-light">₹<?= number_format($gross, 2) ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][discount_percent]" class="form-control form-control-sm text-center line-disc px-1" value="<?= number_format($tradeDisc, 2, '.', '') ?>" min="0" max="100" step="0.01" oninput="recalculateGrnTotals();">
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][sch_disc_per]" class="form-control form-control-sm text-center line-sch-per px-1" value="<?= number_format($schPer, 2, '.', '') ?>" min="0" max="100" step="0.01" oninput="recalculateGrnTotals();">
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number" name="items[<?= $idx ?>][sch_disc_amt]" class="form-control form-control-sm text-center line-sch-amt px-1" value="<?= number_format($schAmt, 2, '.', '') ?>" min="0" step="0.01" oninput="recalculateGrnTotals();">
|
||||||
|
</td>
|
||||||
|
<td class="text-end font-monospace line-taxable-disp bg-light">₹<?= number_format($taxable, 2) ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<select name="items[<?= $idx ?>][gst_rate]" class="form-select form-select-sm text-center line-gst px-1" onchange="recalculateGrnTotals();">
|
||||||
|
<option value="5" <?= (abs($gstRate - 5) < 0.1) ? 'selected' : '' ?>>5%</option>
|
||||||
|
<option value="12" <?= (abs($gstRate - 12) < 0.1) ? 'selected' : '' ?>>12%</option>
|
||||||
|
<option value="18" <?= (abs($gstRate - 18) < 0.1) ? 'selected' : '' ?>>18%</option>
|
||||||
|
<option value="28" <?= (abs($gstRate - 28) < 0.1) ? 'selected' : '' ?>>28%</option>
|
||||||
|
<option value="0" <?= (abs($gstRate - 0) < 0.1) ? 'selected' : '' ?>>0%</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-end font-monospace fw-semibold line-total-disp bg-light">₹<?= number_format($lineTotal, 2) ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="btn btn-outline-danger btn-sm py-0 px-1" onclick="removeGrnRow(this);">✖</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php elseif (!empty($poItems)): ?>
|
||||||
<?php foreach ($poItems as $idx => $it): ?>
|
<?php foreach ($poItems as $idx => $it): ?>
|
||||||
<?php
|
<?php
|
||||||
$defGst = (float)($it['gst_rate'] ?? 5);
|
$defGst = (float)($it['gst_rate'] ?? 5);
|
||||||
|
|
@ -170,6 +276,8 @@
|
||||||
<td class="row-num text-center"><?= $idx + 1 ?></td>
|
<td class="row-num text-center"><?= $idx + 1 ?></td>
|
||||||
<td>
|
<td>
|
||||||
<strong class="text-dark"><?= esc($it['item_name']) ?></strong>
|
<strong class="text-dark"><?= esc($it['item_name']) ?></strong>
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][grn_item_id]" value="0">
|
||||||
|
<input type="hidden" name="items[<?= $idx ?>][purchase_item_id]" value="0">
|
||||||
<input type="hidden" name="items[<?= $idx ?>][item_code]" value="<?= (int)$it['item_id'] ?>">
|
<input type="hidden" name="items[<?= $idx ?>][item_code]" value="<?= (int)$it['item_id'] ?>">
|
||||||
<input type="hidden" name="items[<?= $idx ?>][item_name]" value="<?= esc($it['item_name']) ?>">
|
<input type="hidden" name="items[<?= $idx ?>][item_name]" value="<?= esc($it['item_name']) ?>">
|
||||||
<input type="hidden" name="items[<?= $idx ?>][po_item_id]" value="<?= (int)$it['po_item_id'] ?>">
|
<input type="hidden" name="items[<?= $idx ?>][po_item_id]" value="<?= (int)$it['po_item_id'] ?>">
|
||||||
|
|
@ -276,7 +384,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="13" class="text-end text-muted">Round Off (+ / -):</th>
|
<th colspan="13" class="text-end text-muted">Round Off (+ / -):</th>
|
||||||
<th colspan="3" class="text-end">
|
<th colspan="3" class="text-end">
|
||||||
<input type="number" step="0.01" name="round_off" id="grn_round_off" class="form-control form-control-sm text-end font-monospace d-inline-block" style="width: 105px;" value="0.00" oninput="recalculateGrnTotals();">
|
<input type="number" step="0.01" name="round_off" id="grn_round_off" class="form-control form-control-sm text-end font-monospace d-inline-block" style="width: 105px;" value="<?= isset($grn['round_off']) ? number_format((float)$grn['round_off'], 2, '.', '') : '0.00' ?>" oninput="recalculateGrnTotals();">
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -293,17 +401,29 @@
|
||||||
|
|
||||||
<div class="card p-3 mt-3 bg-light border-0">
|
<div class="card p-3 mt-3 bg-light border-0">
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||||
<div class="form-check">
|
<div>
|
||||||
<input class="form-check-input" type="checkbox" name="post_to_inventory" id="post_to_inventory" value="1" checked>
|
<?php if ($isEdit && $isPosted): ?>
|
||||||
<label class="form-check-label fw-bold text-dark" for="post_to_inventory">
|
<div class="form-check">
|
||||||
Immediately Post Verified Quantities to Live Pharmacy Store Stock & Purchase Ledgers
|
<input class="form-check-input" type="checkbox" name="sync_inventory" id="sync_inventory" value="1" checked>
|
||||||
</label>
|
<label class="form-check-label fw-bold text-success" for="sync_inventory">
|
||||||
<small class="d-block text-muted">Creates verified purchase invoice records and updates stock ledgers.</small>
|
<i class="bi bi-arrow-repeat"></i> Synchronize Changes to Live Store Inventory & Purchase Invoice (Ref #<?= (int)($grn['purchase_id'] ?? 0) ?>)
|
||||||
|
</label>
|
||||||
|
<small class="d-block text-muted">Automatically updates linked purchase bill line items and live stock batches.</small>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="post_to_inventory" id="post_to_inventory" value="1" checked>
|
||||||
|
<label class="form-check-label fw-bold text-dark" for="post_to_inventory">
|
||||||
|
Immediately Post Verified Quantities to Live Pharmacy Store Stock & Purchase Ledgers
|
||||||
|
</label>
|
||||||
|
<small class="d-block text-muted">Creates verified purchase invoice records and updates stock ledgers.</small>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button type="button" class="btn btn-outline-secondary" onclick="load_form_div('<?= base_url('Medical/grn_list') ?>','medical-main');">Cancel</button>
|
<button type="button" class="btn btn-outline-secondary" onclick="load_form_div('<?= $isEdit ? base_url('Medical/grn_detail/' . (int)$grn['grn_id']) : base_url('Medical/grn_list') ?>','medical-main');">Cancel</button>
|
||||||
<button type="submit" class="btn btn-success px-4" id="btn-save-grn">
|
<button type="submit" class="btn btn-success px-4" id="btn-save-grn">
|
||||||
<i class="bi bi-check-all"></i> Save & Complete Inward GRN
|
<i class="bi bi-check-all"></i> <?= $isEdit ? 'Update GRN Inspection' : 'Save & Complete Inward GRN' ?>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -350,6 +470,11 @@ function initGrnDrugAutocomplete() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePoSelection(poId) {
|
function handlePoSelection(poId) {
|
||||||
|
<?php if ($isEdit): ?>
|
||||||
|
if (!confirm('Selecting another PO will switch to creating a new GRN. Continue?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
<?php endif; ?>
|
||||||
if (poId && parseInt(poId) > 0) {
|
if (poId && parseInt(poId) > 0) {
|
||||||
load_form_div('<?= base_url('Medical/grn_new/') ?>' + poId, 'medical-main', 'Create GRN for PO');
|
load_form_div('<?= base_url('Medical/grn_new/') ?>' + poId, 'medical-main', 'Create GRN for PO');
|
||||||
}
|
}
|
||||||
|
|
@ -394,6 +519,8 @@ function addCustomItemToGrn() {
|
||||||
<td class="row-num text-center">${rowIdx + 1}</td>
|
<td class="row-num text-center">${rowIdx + 1}</td>
|
||||||
<td>
|
<td>
|
||||||
<strong class="text-dark">${escapeHtml(name)}</strong>
|
<strong class="text-dark">${escapeHtml(name)}</strong>
|
||||||
|
<input type="hidden" name="items[${rowIdx}][grn_item_id]" value="0">
|
||||||
|
<input type="hidden" name="items[${rowIdx}][purchase_item_id]" value="0">
|
||||||
<input type="hidden" name="items[${rowIdx}][item_code]" value="${code}">
|
<input type="hidden" name="items[${rowIdx}][item_code]" value="${code}">
|
||||||
<input type="hidden" name="items[${rowIdx}][item_name]" value="${escapeHtml(name)}">
|
<input type="hidden" name="items[${rowIdx}][item_name]" value="${escapeHtml(name)}">
|
||||||
<input type="hidden" name="items[${rowIdx}][po_item_id]" value="">
|
<input type="hidden" name="items[${rowIdx}][po_item_id]" value="">
|
||||||
|
|
@ -614,10 +741,13 @@ function submitGrnForm() {
|
||||||
var formData = new FormData(form);
|
var formData = new FormData(form);
|
||||||
|
|
||||||
var saveBtn = document.getElementById('btn-save-grn');
|
var saveBtn = document.getElementById('btn-save-grn');
|
||||||
|
var btnDefaultHtml = '<?= $isEdit ? '<i class="bi bi-check-all"></i> Update GRN Inspection' : '<i class="bi bi-check-all"></i> Save & Complete Inward GRN' ?>';
|
||||||
saveBtn.disabled = true;
|
saveBtn.disabled = true;
|
||||||
saveBtn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Processing Inward Stock...';
|
saveBtn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Processing Inward Stock...';
|
||||||
|
|
||||||
fetch('<?= base_url('Medical/grn_create') ?>', {
|
var submitUrl = '<?= $isEdit ? base_url('Medical/grn_update/' . (int)($grn['grn_id'] ?? 0)) : base_url('Medical/grn_create') ?>';
|
||||||
|
|
||||||
|
fetch(submitUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
body: formData
|
body: formData
|
||||||
|
|
@ -625,18 +755,19 @@ function submitGrnForm() {
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
saveBtn.disabled = false;
|
saveBtn.disabled = false;
|
||||||
saveBtn.innerHTML = '<i class="bi bi-check-all"></i> Save & Complete Inward GRN';
|
saveBtn.innerHTML = btnDefaultHtml;
|
||||||
|
|
||||||
if (data.status) {
|
if (data.status) {
|
||||||
alert(data.message);
|
alert(data.message);
|
||||||
load_form_div('<?= base_url('Medical/grn_list') ?>', 'medical-main', 'GRN Register :Pharmacy');
|
var targetGrnId = data.grn_id || '<?= (int)($grn['grn_id'] ?? 0) ?>';
|
||||||
|
load_form_div('<?= base_url('Medical/grn_detail/') ?>' + targetGrnId, 'medical-main', 'GRN Inspection Detail');
|
||||||
} else {
|
} else {
|
||||||
alert('Error: ' + (data.message || 'Failed to save GRN.'));
|
alert('Error: ' + (data.message || 'Failed to save GRN.'));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
saveBtn.disabled = false;
|
saveBtn.disabled = false;
|
||||||
saveBtn.innerHTML = '<i class="bi bi-check-all"></i> Save & Complete Inward GRN';
|
saveBtn.innerHTML = btnDefaultHtml;
|
||||||
alert('Server communication error.');
|
alert('Server communication error.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,9 +114,12 @@
|
||||||
<a href="<?= base_url('Medical/grn_print/' . $row['grn_id']) ?>" target="_blank" class="btn btn-outline-secondary" title="Print GRN Slip">
|
<a href="<?= base_url('Medical/grn_print/' . $row['grn_id']) ?>" target="_blank" class="btn btn-outline-secondary" title="Print GRN Slip">
|
||||||
<i class="bi bi-printer"></i>
|
<i class="bi bi-printer"></i>
|
||||||
</a>
|
</a>
|
||||||
<button type="button" class="btn btn-outline-primary" onclick="load_form_div('<?= base_url('Medical/grn_edit/' . $row['grn_id']) ?>','medical-main','GRN Inspection Detail');" title="View Inspection Details">
|
<button type="button" class="btn btn-outline-primary" onclick="load_form_div('<?= base_url('Medical/grn_detail/' . $row['grn_id']) ?>','medical-main','GRN Inspection Detail');" title="View Inspection Details">
|
||||||
<i class="bi bi-eye"></i> View
|
<i class="bi bi-eye"></i> View
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-warning" onclick="load_form_div('<?= base_url('Medical/grn_edit/' . $row['grn_id']) ?>','medical-main','Edit GRN Inspection');" title="Edit Inspection Details">
|
||||||
|
<i class="bi bi-pencil-square"></i> Edit
|
||||||
|
</button>
|
||||||
<?php if ($row['status'] === 'verified'): ?>
|
<?php if ($row['status'] === 'verified'): ?>
|
||||||
<button type="button" class="btn btn-success" onclick="postGrnToStock(<?= (int)$row['grn_id'] ?>)" title="Post to Live Hospital Stock">
|
<button type="button" class="btn btn-success" onclick="postGrnToStock(<?= (int)$row['grn_id'] ?>)" title="Post to Live Hospital Stock">
|
||||||
<i class="bi bi-box-arrow-in-down"></i> Post Stock
|
<i class="bi bi-box-arrow-in-down"></i> Post Stock
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<i class="bi bi-pencil-square"></i> Edit Invoice
|
<i class="bi bi-pencil-square"></i> Edit Invoice
|
||||||
</button>
|
</button>
|
||||||
<?php if (!empty($linked_grn)): ?>
|
<?php if (!empty($linked_grn)): ?>
|
||||||
<button onclick="load_form_div('<?= base_url('Medical/grn_edit/' . (int) $linked_grn['grn_id']) ?>','medical-main','GRN Detail');" type="button" class="btn btn-outline-info btn-sm ms-1">
|
<button onclick="load_form_div('<?= base_url('Medical/grn_detail/' . (int) $linked_grn['grn_id']) ?>','medical-main','GRN Detail');" type="button" class="btn btn-outline-info btn-sm ms-1">
|
||||||
<i class="bi bi-box-arrow-left"></i> Back to GRN #<?= esc($linked_grn['grn_number'] ?? $linked_grn['grn_id']) ?>
|
<i class="bi bi-box-arrow-left"></i> Back to GRN #<?= esc($linked_grn['grn_number'] ?? $linked_grn['grn_id']) ?>
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ if (!empty($inv->date_of_invoice) && $inv->date_of_invoice !== '0000-00-00') {
|
||||||
<i class="bi bi-card-checklist"></i> View Invoice Items
|
<i class="bi bi-card-checklist"></i> View Invoice Items
|
||||||
</button>
|
</button>
|
||||||
<?php if (!empty($linked_grn)): ?>
|
<?php if (!empty($linked_grn)): ?>
|
||||||
<button onclick="load_form_div('<?= base_url('Medical/grn_edit/' . (int) $linked_grn['grn_id']) ?>','medical-main','GRN Detail');" type="button" class="btn btn-outline-info btn-sm ms-1">
|
<button onclick="load_form_div('<?= base_url('Medical/grn_detail/' . (int) $linked_grn['grn_id']) ?>','medical-main','GRN Detail');" type="button" class="btn btn-outline-info btn-sm ms-1">
|
||||||
<i class="bi bi-box-arrow-left"></i> Back to GRN #<?= esc($linked_grn['grn_number'] ?? $linked_grn['grn_id']) ?>
|
<i class="bi bi-box-arrow-left"></i> Back to GRN #<?= esc($linked_grn['grn_number'] ?? $linked_grn['grn_id']) ?>
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user