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/(:num)', 'MedicalProcurement::grn_new/$1');
|
||||
$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_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->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()) {
|
||||
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)
|
||||
{
|
||||
if ($deny = $this->ensurePharmacyAccess()) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
<i class="bi bi-arrow-left"></i> Back to GRN List
|
||||
</a>
|
||||
<?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">
|
||||
<i class="bi bi-printer"></i> Print Slip
|
||||
</a>
|
||||
|
|
@ -225,6 +228,11 @@
|
|||
<?php endif; ?>
|
||||
</div>
|
||||
<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'])): ?>
|
||||
<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
|
||||
|
|
@ -245,7 +253,7 @@ function postGrnToLiveInventory(grnId) {
|
|||
$.post('<?= base_url('Medical/grn_post_inventory/') ?>' + grnId, {}, function(data) {
|
||||
alert(data.message || 'Done');
|
||||
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() {
|
||||
alert('Error posting GRN.');
|
||||
|
|
|
|||
|
|
@ -1,18 +1,34 @@
|
|||
<?php
|
||||
$poId = !empty($selectedPo) ? (int)$selectedPo['po_id'] : 0;
|
||||
$supplierId = !empty($selectedPo) ? (int)$selectedPo['supplier_id'] : 0;
|
||||
$nextGrnNumber = $nextGrnNumber ?? 'GRN-NEW';
|
||||
$isEdit = !empty($isEdit);
|
||||
$poId = !empty($grn['po_id']) ? (int)$grn['po_id'] : (!empty($selectedPo) ? (int)$selectedPo['po_id'] : 0);
|
||||
$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>
|
||||
<h4 class="mb-0 text-success">
|
||||
<i class="bi bi-box-arrow-in-down"></i> Create Goods Received Note (GRN)
|
||||
<span class="badge bg-light text-dark border ms-2 font-monospace"><?= esc($nextGrnNumber) ?></span>
|
||||
<i class="bi <?= $isEdit ? 'bi-pencil-square' : 'bi-box-arrow-in-down' ?>"></i>
|
||||
<?= $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>
|
||||
<small class="text-muted">Inward Stock Physical Inspection & Vendor Invoice Reconciliation</small>
|
||||
</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">
|
||||
<i class="bi bi-arrow-left"></i> Back to GRN List
|
||||
</a>
|
||||
|
|
@ -26,6 +42,9 @@
|
|||
<div class="card-body p-3">
|
||||
<form id="grn-create-form" onsubmit="event.preventDefault(); submitGrnForm();">
|
||||
<?= 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="col-md-3">
|
||||
<label class="form-label form-label-sm fw-semibold mb-1">Linked Purchase Order (PO)</label>
|
||||
|
|
@ -55,33 +74,34 @@
|
|||
</div>
|
||||
<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>
|
||||
<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 class="col-md-2">
|
||||
<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 class="col-md-2">
|
||||
<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 class="row g-2 mb-3">
|
||||
<div class="col-md-3">
|
||||
<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();">
|
||||
<option value="intra_state" selected>Intra-State (CGST + SGST)</option>
|
||||
<option value="inter_state">Inter-State (IGST)</option>
|
||||
<option value="intra_state" <?= ($gstType === 'intra_state') ? 'selected' : '' ?>>Intra-State (CGST + SGST)</option>
|
||||
<option value="inter_state" <?= ($gstType === 'inter_state') ? 'selected' : '' ?>>Inter-State (IGST)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<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 class="col-md-6">
|
||||
<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>
|
||||
|
||||
|
|
@ -155,7 +175,93 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<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
|
||||
$defGst = (float)($it['gst_rate'] ?? 5);
|
||||
|
|
@ -170,6 +276,8 @@
|
|||
<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="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_name]" value="<?= esc($it['item_name']) ?>">
|
||||
<input type="hidden" name="items[<?= $idx ?>][po_item_id]" value="<?= (int)$it['po_item_id'] ?>">
|
||||
|
|
@ -276,7 +384,7 @@
|
|||
<tr>
|
||||
<th colspan="13" class="text-end text-muted">Round Off (+ / -):</th>
|
||||
<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>
|
||||
</tr>
|
||||
|
|
@ -293,17 +401,29 @@
|
|||
|
||||
<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="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 if ($isEdit && $isPosted): ?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="sync_inventory" id="sync_inventory" value="1" checked>
|
||||
<label class="form-check-label fw-bold text-success" for="sync_inventory">
|
||||
<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 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">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -350,6 +470,11 @@ function initGrnDrugAutocomplete() {
|
|||
}
|
||||
|
||||
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) {
|
||||
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>
|
||||
<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_name]" value="${escapeHtml(name)}">
|
||||
<input type="hidden" name="items[${rowIdx}][po_item_id]" value="">
|
||||
|
|
@ -614,10 +741,13 @@ function submitGrnForm() {
|
|||
var formData = new FormData(form);
|
||||
|
||||
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.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',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
body: formData
|
||||
|
|
@ -625,18 +755,19 @@ function submitGrnForm() {
|
|||
.then(r => r.json())
|
||||
.then(data => {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.innerHTML = '<i class="bi bi-check-all"></i> Save & Complete Inward GRN';
|
||||
saveBtn.innerHTML = btnDefaultHtml;
|
||||
|
||||
if (data.status) {
|
||||
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 {
|
||||
alert('Error: ' + (data.message || 'Failed to save GRN.'));
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.innerHTML = '<i class="bi bi-check-all"></i> Save & Complete Inward GRN';
|
||||
saveBtn.innerHTML = btnDefaultHtml;
|
||||
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">
|
||||
<i class="bi bi-printer"></i>
|
||||
</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
|
||||
</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'): ?>
|
||||
<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
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<i class="bi bi-pencil-square"></i> Edit Invoice
|
||||
</button>
|
||||
<?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']) ?>
|
||||
</button>
|
||||
<?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
|
||||
</button>
|
||||
<?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']) ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user