db = \Config\Database::connect();
}
// -------------------------------------------------------------------------
// Auth helper
// -------------------------------------------------------------------------
private function ensureStoreAccess()
{
$user = service('auth')->user();
$allowed = false;
if ($user && method_exists($user, 'can')) {
$allowed = $user->can('hospital_stock.access') || $user->can('pharmacy.access');
}
if (! $allowed && $user && method_exists($user, 'inGroup')) {
$allowed = $user->inGroup(
'superadmin', 'admin', 'developer',
'stock_manager', 'stock_requester', 'stock_issuer', 'storekeeper'
);
}
if ($allowed) {
return null;
}
return $this->response
->setStatusCode(403)
->setBody('
Access denied for Hospital Stock module.
');
}
private function actorLabel(): string
{
$user = service('auth')->user();
if (! $user) {
return 'System[' . date('Y-m-d H:i:s') . ']';
}
$name = trim(($user->first_name ?? '') . ' ' . ($user->last_name ?? ''));
if ($name === '' || $name === ' ') {
$name = (string) ($user->username ?? ($user->email ?? 'User'));
}
$userId = (int) ($user->id ?? 0);
return $name . '[' . $userId . '][' . date('d-m-Y H:i:s') . ']';
}
private function safeScalar(string $sql, array $bindings = [], $default = 0)
{
try {
$row = $this->db->query($sql, $bindings)->getRowArray();
if (! is_array($row) || empty($row)) {
return $default;
}
$val = reset($row);
return $val !== null ? $val : $default;
} catch (\Throwable $e) {
return $default;
}
}
private function safeRows(string $sql, array $bindings = []): array
{
try {
return $this->db->query($sql, $bindings)->getResultArray();
} catch (\Throwable $e) {
return [];
}
}
private function buildDashboardInsights(): array
{
$todayIndents = (int) $this->safeScalar(
"SELECT COUNT(*) AS c FROM invoice_stock_master WHERE DATE(indent_date) = CURDATE()",
[],
0
);
$monthIndents = (int) $this->safeScalar(
"SELECT COUNT(*) AS c FROM invoice_stock_master WHERE DATE_FORMAT(indent_date, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')",
[],
0
);
$pendingPurchase = (int) $this->safeScalar(
"SELECT COUNT(*) AS c FROM purchase_invoice_stock WHERE IFNULL(inv_status, 0) = 0",
[],
0
);
$lowStockCount = (int) $this->safeScalar(
"SELECT COUNT(*) AS c FROM (
SELECT p.id,
COALESCE(SUM(s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit), 0) AS cur_units,
COALESCE(MAX(p.re_order_qty), 0) AS re_order_qty,
COALESCE(MAX(NULLIF(s.packing, 0)), 1) AS packing
FROM med_store_product_master p
LEFT JOIN purchase_invoice_item_stock s
ON p.id = s.item_code
AND s.remove_item = 0
AND s.item_return = 0
WHERE IFNULL(p.is_continue, 1) = 1
GROUP BY p.id
HAVING cur_units <= (re_order_qty * packing)
) x",
[],
0
);
$nearExpiryCount = (int) $this->safeScalar(
"SELECT COUNT(*) AS c
FROM purchase_invoice_item_stock s
WHERE s.remove_item = 0
AND s.item_return = 0
AND (s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit) > 0
AND s.expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 60 DAY)",
[],
0
);
$stockValue = (float) $this->safeScalar(
"SELECT ROUND(SUM(
(s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit)
* IFNULL(s.purchase_unit_rate, s.purchase_price)
), 2) AS v
FROM purchase_invoice_item_stock s
WHERE s.remove_item = 0
AND s.item_return = 0
AND (s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit) > 0",
[],
0
);
$lowStockItems = $this->safeRows(
"SELECT p.item_name,
COALESCE(MAX(p.re_order_qty), 0) AS re_order_qty,
COALESCE(MAX(NULLIF(s.packing, 0)), 1) AS packing,
ROUND(COALESCE(SUM(s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit), 0), 2) AS cur_units
FROM med_store_product_master p
LEFT JOIN purchase_invoice_item_stock s
ON p.id = s.item_code
AND s.remove_item = 0
AND s.item_return = 0
WHERE IFNULL(p.is_continue, 1) = 1
GROUP BY p.id, p.item_name
HAVING cur_units <= (re_order_qty * packing)
ORDER BY cur_units ASC
LIMIT 8"
);
$nearExpiryItems = $this->safeRows(
"SELECT s.item_name,
s.batch_no,
DATE_FORMAT(s.expiry_date, '%d-%m-%Y') AS expiry_str,
DATEDIFF(s.expiry_date, CURDATE()) AS days_left,
ROUND((s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit) / NULLIF(s.packing, 0), 2) AS cur_packs
FROM purchase_invoice_item_stock s
WHERE s.remove_item = 0
AND s.item_return = 0
AND (s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit) > 0
AND s.expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 60 DAY)
ORDER BY s.expiry_date ASC
LIMIT 8"
);
return [
'stats' => [
'today_indents' => $todayIndents,
'month_indents' => $monthIndents,
'pending_purchase' => $pendingPurchase,
'low_stock_count' => $lowStockCount,
'near_expiry_count' => $nearExpiryCount,
'stock_value' => $stockValue,
],
'lists' => [
'low_stock_items' => $lowStockItems,
'near_expiry_items' => $nearExpiryItems,
],
];
}
// -------------------------------------------------------------------------
// Dashboard
// -------------------------------------------------------------------------
public function index()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$data = $this->buildDashboardInsights();
return view('storestock/dashboard', $data);
}
// -------------------------------------------------------------------------
// Indent List
// -------------------------------------------------------------------------
public function indent_list()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
return view('storestock/indent_list');
}
// -------------------------------------------------------------------------
// Indent DataTable (server-side)
// -------------------------------------------------------------------------
public function getIndentTable()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$columns = [
0 => 'indent_code',
1 => 'issued_name',
2 => 'indent_date_str',
3 => 'id',
];
$draw = (int) ($this->request->getPost('draw') ?? $this->request->getGet('draw') ?? 1);
$start = (int) ($this->request->getPost('start') ?? $this->request->getGet('start') ?? 0);
$length = (int) ($this->request->getPost('length') ?? $this->request->getGet('length') ?? 10);
$orderColIdx = (int) ($this->request->getPost('order[0][column]') ?? 0);
$orderDir = $this->request->getPost('order[0][dir]') === 'asc' ? 'asc' : 'desc';
$orderCol = $columns[$orderColIdx] ?? 'id';
// Column search values
$searchIndentCode = (string) ($this->request->getPost('columns[0][search][value]') ?? '');
$searchIssuedName = (string) ($this->request->getPost('columns[1][search][value]') ?? '');
$searchIndentDate = (string) ($this->request->getPost('columns[2][search][value]') ?? '');
// Total records
$totalData = (int) $this->db->query("SELECT COUNT(*) AS no_rec FROM invoice_stock_master")->getRow()->no_rec;
$totalFiltered = $totalData;
// Build parameterised WHERE
$whereSQL = ' WHERE 1=1';
$whereParams = [];
if ($searchIndentCode !== '') {
$esc = $this->db->escapeLikeString($searchIndentCode);
$whereSQL .= " AND indent_code LIKE '%{$esc}%'";
}
if ($searchIssuedName !== '') {
$esc = $this->db->escapeLikeString($searchIssuedName);
$whereSQL .= " AND issued_name LIKE '%{$esc}%'";
}
if ($searchIndentDate !== '') {
// Date column — use parameterised bind
$whereSQL .= ' AND DATE(indent_date) = ?';
$whereParams[] = $searchIndentDate;
}
if ($searchIndentCode !== '' || $searchIssuedName !== '' || $searchIndentDate !== '') {
$countSql = "SELECT COUNT(*) AS no_rec FROM invoice_stock_master{$whereSQL}";
$totalFiltered = (int) $this->db->query($countSql, $whereParams)->getRow()->no_rec;
}
$orderClause = " ORDER BY {$orderCol} {$orderDir}, id desc";
$limitClause = ' LIMIT ' . $start . ', ' . $length;
$selectSQL = "SELECT m.id, m.indent_code, m.issued_name, m.indent_date,
DATE_FORMAT(m.indent_date,'%d-%m-%Y') AS indent_date_str
FROM invoice_stock_master m{$whereSQL}{$orderClause}{$limitClause}";
$rdata = $this->db->query($selectSQL, $whereParams)->getResultArray();
$rows = [];
foreach ($rdata as $aRow) {
$rows[] = [
$aRow['indent_code'],
$aRow['issued_name'],
$aRow['indent_date_str'],
$aRow['id'],
];
}
return $this->response->setJSON([
'draw' => $draw,
'recordsTotal' => $totalData,
'recordsFiltered' => $totalFiltered,
'data' => $rows,
]);
}
// -------------------------------------------------------------------------
// New Indent form
// -------------------------------------------------------------------------
public function new_indent()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$data['location_master'] = $this->db->query("SELECT * FROM location_master ORDER BY loc_name")->getResult();
$data['employee_master'] = $this->db->query("SELECT * FROM employee_master ORDER BY emp_name")->getResult();
return view('storestock/new_indent', $data);
}
// -------------------------------------------------------------------------
// Create Indent
// -------------------------------------------------------------------------
public function indent_create(int $location_type)
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$date_indent = $this->request->getPost('date_indent');
$loc_id = (int) $this->request->getPost('loc_id');
// Convert dd/mm/yyyy → yyyy-mm-dd
$mysql_date = date('Y-m-d');
if ($date_indent) {
$parts = explode('/', trim($date_indent));
if (count($parts) === 3) {
$mysql_date = $parts[2] . '-' . $parts[1] . '-' . $parts[0];
} else {
$mysql_date = date('Y-m-d', strtotime($date_indent));
}
}
$issued_name = '';
if ($location_type == 1) {
$row = $this->db->query("SELECT * FROM location_master WHERE l_id = ?", [$loc_id])->getRow();
if ($row) {
$issued_name = $row->loc_name;
}
} elseif ($location_type == 2) {
$row = $this->db->query("SELECT * FROM employee_master WHERE emp_id = ?", [$loc_id])->getRow();
if ($row) {
$issued_name = $row->emp_name . ' [' . $row->emp_code . ']';
}
}
$this->db->table('invoice_stock_master')->insert([
'indent_date' => $mysql_date,
'location_type' => $location_type,
'location_id' => $loc_id,
'issued_name' => $issued_name,
]);
$insert_id = $this->db->insertID();
// Generate indent_code like S{ym}{padded_id}
$ym = date('ym');
$indent_code = 'S' . $ym . str_pad($insert_id, 5, '0', STR_PAD_LEFT);
$this->db->table('invoice_stock_master')->where('id', $insert_id)->update(['indent_code' => $indent_code]);
return redirect()->to(site_url('Storestock/Indent_show/' . $insert_id));
}
// -------------------------------------------------------------------------
// Show / Edit Indent
// -------------------------------------------------------------------------
public function indent_show(int $indent_id)
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$data['invoice_stock_master'] = $this->db->query(
"SELECT * FROM invoice_stock_master WHERE id = ?",
[$indent_id]
)->getResult();
$data['inv_stock_item'] = $this->db->query(
"SELECT i.*, IFNULL(DATEDIFF(expiry, CURDATE()), 1000) AS no_day,
SUM(p.total_unit - p.total_sale_unit - p.total_return_unit - p.total_lost_unit) / p.packing AS cur_qty
FROM inv_stock_item i
JOIN purchase_invoice_item_stock p ON i.item_code = p.item_code AND p.remove_item = 0
WHERE i.indent_id = ?
GROUP BY i.id",
[$indent_id]
)->getResult();
$data['invoiceGtotal'] = $this->db->query(
"SELECT SUM(amount) AS Gtotal, SUM(tamount) AS tamt, SUM(CGST) AS TCGST, SUM(SGST) AS TSGST
FROM inv_stock_item WHERE indent_id = ?",
[$indent_id]
)->getResult();
$data['content'] = view('storestock/indent_item_list', $data);
return view('storestock/indent_edit', $data);
}
// -------------------------------------------------------------------------
// Indent Items List (AJAX partial refresh)
// -------------------------------------------------------------------------
public function indent_items_list(int $indent_id)
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$data['inv_stock_item'] = $this->db->query(
"SELECT * FROM inv_stock_item WHERE indent_id = ? ORDER BY id",
[$indent_id]
)->getResult();
$data['invoiceGtotal'] = $this->db->query(
"SELECT SUM(amount) AS Gtotal, SUM(tamount) AS tamt, SUM(CGST) AS TCGST, SUM(SGST) AS TSGST
FROM inv_stock_item WHERE indent_id = ?",
[$indent_id]
)->getResult();
$html = view('storestock/indent_item_list', $data);
return $this->response->setBody($html);
}
// -------------------------------------------------------------------------
// Add / Update / Delete item from indent
// -------------------------------------------------------------------------
public function add_item(int $type)
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$insert_id = 1;
$error_show = '';
$exist = 0;
$input_id = 0;
$inv_id = 0;
$user = service('auth')->user();
$user_id = $user ? (int) ($user->id ?? 0) : 0;
$user_name_info = $this->actorLabel();
$store_stock_id = (int) $this->request->getPost('l_ssno');
if ($type == 1) {
// ---- Add new item ----
$inv_id = (int) $this->request->getPost('inv_id');
$qty = (float) $this->request->getPost('qty');
$disc = (float) $this->request->getPost('disc');
$product_unit_rate = (float) $this->request->getPost('product_unit_rate');
$item = $this->db->query(
"SELECT p.item_name, t.batch_no, t.expiry_date,
(t.total_unit - t.total_sale_unit) AS S_qty,
t.mrp, t.selling_unit_rate, p.id AS master_product_id,
t.id AS purchase_item_id, p.formulation,
t.HSNCODE, t.CGST_per, t.SGST_per, t.packing
FROM med_store_product_master p
JOIN purchase_invoice_item_stock t ON p.id = t.item_code
WHERE t.id = ?",
[$store_stock_id]
)->getRow();
if (! $item) {
$insert_id = 0;
$error_show = 'Item not found in stock';
} else {
$item_exists = $this->db->query(
"SELECT * FROM inv_stock_item WHERE indent_id = ? AND store_stock_id = ?",
[$inv_id, $store_stock_id]
)->getResult();
$stock_qty = (float) $item->S_qty;
$amount_value = $qty * $product_unit_rate;
$disc_amount = $amount_value * $disc / 100;
$tamount_value = $amount_value - $disc_amount;
if ($qty > $stock_qty) {
$insert_id = 0;
$error_show = 'Stock Qty. is less than Required Qty : Current Qty :' . $stock_qty;
} elseif ($stock_qty == 0) {
$insert_id = 0;
$error_show = 'Stock is Empty';
}
if ($insert_id > 0) {
if (count($item_exists) > 0) {
$exist = count($item_exists);
$input_id = 0;
$error_show = 'Please Check Also Same Item List';
}
$this->db->table('inv_stock_item')->insert([
'indent_id' => $inv_id,
'item_code' => $item->master_product_id,
'item_Name' => $item->item_name,
'formulation' => $item->formulation,
'qty' => $qty,
'batch_no' => $item->batch_no,
'expiry' => $item->expiry_date,
'price' => $product_unit_rate,
'price2' => $product_unit_rate,
'mrp' => $item->mrp,
'disc_per' => $disc,
'disc_amount' => $disc_amount,
'amount' => $amount_value,
'tamount' => $tamount_value,
'CGST_per' => $item->CGST_per,
'SGST_per' => $item->SGST_per,
'HSNCODE' => $item->HSNCODE,
'store_stock_id' => $item->purchase_item_id,
'update_by_id' => $user_id,
'update_by_remark' => $user_name_info,
'packing' => $item->packing,
]);
$insert_id = $this->db->insertID();
}
}
} elseif ($type == 2) {
// ---- Update qty ----
$update_qty = (float) $this->request->getPost('u_qty');
$item_id = (int) $this->request->getPost('itemid');
$inv_item = $this->db->query(
"SELECT * FROM inv_stock_item WHERE id = ?",
[$item_id]
)->getRow();
if (! $inv_item) {
$insert_id = 0;
$error_show = 'Item not found';
} else {
$inv_id = (int) $inv_item->indent_id;
$store_stock_id = (int) $inv_item->store_stock_id;
$disc = (float) $inv_item->disc_per;
$old_qty = (float) $inv_item->qty;
$diff_qty = $update_qty - $old_qty;
if ($diff_qty > 0) {
$stock_item = $this->db->query(
"SELECT (t.total_unit - t.total_sale_unit) AS S_qty
FROM purchase_invoice_item_stock t WHERE t.id = ?",
[$store_stock_id]
)->getRow();
$stock_qty = $stock_item ? (float) $stock_item->S_qty : 0;
if ($diff_qty > $stock_qty) {
$insert_id = 0;
$error_show = 'Stock Qty. is less than Required Qty : Current Qty :' . $stock_qty;
}
}
if ($insert_id > 0) {
$item_rate = (float) $inv_item->price;
$amount_value = $update_qty * $item_rate;
$disc_amount = $amount_value * $disc / 100;
$tamount = $amount_value - $disc_amount;
$this->db->table('inv_stock_item')->where('id', $item_id)->update([
'qty' => $update_qty,
'disc_amount' => $disc_amount,
'amount' => $amount_value,
'tamount' => $tamount,
'store_stock_id'=> $store_stock_id,
'packing' => $inv_item->packing,
'indent_id' => $inv_id,
]);
$insert_id = $item_id;
}
}
} else {
// ---- Delete item ----
$item_id = (int) $this->request->getPost('itemid');
$inv_item = $this->db->query(
"SELECT * FROM inv_stock_item WHERE id = ?",
[$item_id]
)->getRowArray();
if ($inv_item) {
$inv_id = (int) $inv_item['indent_id'];
// Archive to delete table
$del_data = $inv_item;
$del_data['del_by'] = $user_name_info;
unset($del_data['id']);
$this->db->table('inv_stock_item_delete')->insert($del_data);
$this->db->table('inv_stock_item')->where('id', $item_id)->delete();
$insert_id = 1;
} else {
$insert_id = 0;
$error_show = 'Item not found';
}
}
$content = '';
if ($insert_id > 0) {
// Call stored procs
$this->db->query("CALL p_stock_update_purchase_id_store({$inv_id})");
$this->db->query("CALL p_update_med_GST_store({$inv_id})");
$data['inv_stock_item'] = $this->db->query(
"SELECT i.*, IFNULL(DATEDIFF(expiry, CURDATE()), 1000) AS no_day,
SUM(p.total_unit - p.total_sale_unit - p.total_return_unit - p.total_lost_unit) / p.packing AS cur_qty
FROM inv_stock_item i
JOIN purchase_invoice_item_stock p ON i.item_code = p.item_code AND p.remove_item = 0
WHERE i.indent_id = ?
GROUP BY i.id",
[$inv_id]
)->getResult();
$data['invoiceGtotal'] = $this->db->query(
"SELECT SUM(amount) AS Gtotal, SUM(tamount) AS tamt,
SUM(CGST) AS TCGST, SUM(SGST) AS TSGST, SUM(disc_amount) AS t_dis_amt
FROM inv_stock_item WHERE indent_id = ?",
[$inv_id]
)->getResult();
$content = view('storestock/indent_item_list', $data);
}
return $this->response->setJSON([
'exist' => $exist,
'insertid' => $insert_id,
'content' => $content,
'error' => $error_show,
'input_id' => $input_id,
'csrf_token_value' => csrf_hash(),
]);
}
// -------------------------------------------------------------------------
// Drug autocomplete
// -------------------------------------------------------------------------
public function get_drug()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$term = trim((string) $this->request->getGet('term'));
if ($term === '') {
return $this->response->setJSON([]);
}
$q = strtolower($term);
$esc = $this->db->escapeLikeString($q);
$sql = "SELECT p.item_name, p.formulation, t.batch_no,
t.expiry_date AS expiry_date_str, t.expiry_date,
(t.total_unit - t.total_sale_unit - t.total_lost_unit - t.total_return_unit) AS c_qty,
t.id, t.mrp, t.selling_unit_rate, p.id AS item_code, t.packing
FROM med_store_product_master p
JOIN purchase_invoice_item_stock t ON p.id = t.item_code
AND (t.total_unit - t.total_sale_unit - t.total_lost_unit - t.total_return_unit) > 0
AND (expiry_date > DATE_ADD(SYSDATE(), INTERVAL 1 DAY) OR p.exp_date_applicable = 0)
AND t.remove_item = 0 AND t.item_return = 0
WHERE p.item_name LIKE '{$esc}%'
ORDER BY p.item_name, t.id
LIMIT 100";
$rows = $this->db->query($sql)->getResultArray();
$row_set = [];
foreach ($rows as $row) {
$row_set[] = [
'label' => htmlentities($row['item_name'] . ' ' . $row['formulation'])
. ' |B:' . htmlentities($row['batch_no'])
. ' |Pak:' . htmlentities($row['packing'])
. ' |Rs.' . htmlentities($row['mrp'])
. ' |Qty:' . htmlentities($row['c_qty']),
'value' => htmlentities($row['item_name']),
'l_item_code' => htmlentities($row['item_code']),
'l_ss_no' => htmlentities($row['id']),
'l_Batch' => htmlentities($row['batch_no']),
'l_Expiry' => htmlentities($row['expiry_date_str']),
'l_mrp' => htmlentities($row['mrp']),
'l_unit_rate' => htmlentities($row['selling_unit_rate']),
'l_c_qty' => htmlentities($row['c_qty']),
'l_packing' => htmlentities($row['packing']),
];
}
return $this->response->setJSON($row_set);
}
// -------------------------------------------------------------------------
// Final Invoice
// -------------------------------------------------------------------------
public function final_invoice(int $inv_id)
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
// Call stored procedure to recalculate GST
$this->db->query("CALL p_update_med_GST_store({$inv_id})");
$data['invoice_stock_master'] = $this->db->query(
"SELECT *, (discount_amount + item_discount_amount) AS inv_disc_total,
(CGST_Tamount + SGST_Tamount) AS TGST
FROM invoice_stock_master WHERE id = ?",
[$inv_id]
)->getResult();
$data['inv_items'] = $this->db->query(
"SELECT * FROM inv_stock_item WHERE indent_id = ?",
[$inv_id]
)->getResult();
$data['invoiceGtotal'] = $this->db->query(
"SELECT SUM(amount) AS Gtotal, SUM(tamount) AS tamt,
SUM(CGST) AS TCGST, SUM(SGST) AS TSGST, SUM(disc_amount) AS t_dis_amt
FROM inv_stock_item WHERE indent_id = ?",
[$inv_id]
)->getResult();
return view('storestock/final_invoice', $data);
}
// -------------------------------------------------------------------------
// Print single indent (mPDF)
// -------------------------------------------------------------------------
public function print_single_indent(int $inv_id)
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$this->db->query("CALL p_update_med_GST_store({$inv_id})");
$data['inv_items'] = $this->db->query(
"SELECT i.indent_id, i.id, i.item_Name, i.formulation,
i.batch_no, DATE_FORMAT(i.expiry, '%m-%y') AS expiry,
i.price, i.qty, (i.amount) AS amount,
i.HSNCODE, m.indent_code, m.id AS m_id,
DATE_FORMAT(m.indent_date, '%d-%m-%Y') AS str_inv_date,
(i.disc_amount) AS d_amt,
(i.CGST + i.SGST) AS gst,
(i.CGST_per + i.SGST_per) AS gst_per, i.sale_return
FROM inv_stock_item i
JOIN invoice_stock_master m ON i.indent_id = m.id
WHERE m.id = ?
ORDER BY i.sale_return, i.id",
[$inv_id]
)->getResult();
$data['invoice_stock_master'] = $this->db->query(
"SELECT *, DATE_FORMAT(indent_date,'%d-%m-%Y') AS str_inv_date,
(discount_amount + item_discount_amount) AS inv_disc_total,
(CGST_Tamount + SGST_Tamount) AS TGST
FROM invoice_stock_master WHERE id = ?",
[$inv_id]
)->getResult();
$html = view('storestock/store_print', $data);
$mpdf = new Mpdf([
'margin_top' => 40,
'margin_bottom' => 12,
'margin_left' => 10,
'margin_right' => 5,
]);
$mpdf->showWatermarkText = false;
$mpdf->WriteHTML($html);
$filename = 'Indent-' . $inv_id . '-' . date('Ymdhis') . '.pdf';
$mpdf->Output($filename, 'I');
}
// -------------------------------------------------------------------------
// Store Stock Search
// -------------------------------------------------------------------------
public function store_stock()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$data['supplier_data'] = $this->db->query("SELECT * FROM stock_supplier ORDER BY name_supplier")->getResult();
$data['med_formulation'] = $this->db->query("SELECT * FROM med_formulation ORDER BY formulation_length")->getResult();
return view('storestock/store_stock_search', $data);
}
// -------------------------------------------------------------------------
// Store Stock Result (POST)
// -------------------------------------------------------------------------
public function store_stock_result()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$supplier_id = (int) $this->request->getPost('input_supplier');
$item_name = (string) $this->request->getPost('txtsearch');
$chk_reorder = (string) $this->request->getPost('chk_reorder');
$having = '';
if ($chk_reorder === 'on') {
$having = ' HAVING SUM(s.total_unit - s.total_lost_unit - s.total_sale_unit - s.total_return_unit) <= p.re_order_qty';
}
$where = ' s.remove_item = 0';
$whereParams = [];
if ($supplier_id > 0) {
$where .= ' AND m.sid = ?';
$whereParams[] = $supplier_id;
}
if ($item_name !== '') {
$esc = $this->db->escapeLikeString($item_name);
$where .= " AND p.item_name LIKE '%{$esc}%'";
}
$sql = "SELECT p.id, p.item_name,
IFNULL(s.item_code, 0) AS item_found,
IFNULL(s.packing, p.packing) AS packing,
SUM(s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit) AS C_Unit_Stock_Qty,
CONCAT(
TRUNCATE(SUM(s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit) / s.packing, 0),
':', MOD(SUM(s.total_unit - s.total_sale_unit - s.total_lost_unit - s.total_return_unit), s.packing)
) AS C_Pak_Qty,
SUM(s.total_unit) AS P_Unit_Qty,
SUM(s.total_unit) / s.packing AS Pak_qty,
SUM(s.total_sale_unit) AS sale_unit,
SUM(s.total_sale_unit) / s.packing AS C_Pak_Sale_Qty,
p.re_order_qty,
s.total_lost_unit
FROM med_store_product_master p
JOIN purchase_invoice_item_stock s ON p.id = s.item_code AND s.remove_item = 0 AND s.item_return = 0
JOIN purchase_invoice_stock m ON s.purchase_id = m.id AND p.is_continue = 1
WHERE {$where}
GROUP BY p.id, s.packing{$having}
ORDER BY p.item_name";
$data['stock_list'] = $this->db->query($sql, $whereParams)->getResult();
$html = view('storestock/stock_search_result', $data);
return $this->response->setBody($html);
}
// -------------------------------------------------------------------------
// Get product stock detail (batch-wise)
// -------------------------------------------------------------------------
public function get_product_stock(int $product_id)
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
$supplier_id = (int) $this->request->getPost('input_supplier');
$date_range = (string) $this->request->getPost('date_range');
$product_pak = (float) $this->request->getPost('product_pak');
$where = '1 = 1';
$whereParams = [];
if ($date_range !== '' && $date_range !== '0') {
$rangeArray = explode('S', $date_range);
if (count($rangeArray) === 2) {
$where .= ' AND p.date_of_invoice BETWEEN ? AND ?';
$whereParams[] = $rangeArray[0];
$whereParams[] = $rangeArray[1];
}
}
if ($supplier_id > 0) {
$where .= ' AND m.sid = ?';
$whereParams[] = $supplier_id;
}
$where .= ' AND i.item_code = ?';
$whereParams[] = $product_id;
if ($product_pak > 0) {
$where .= ' AND i.packing = ?';
$whereParams[] = $product_pak;
}
$sql = "SELECT i.id, s.name_supplier, s.short_name, i.Item_name,
p.Invoice_no, p.date_of_invoice, i.mrp, i.packing,
i.batch_no, i.expiry_date, i.purchase_price, i.purchase_unit_rate,
i.qty, i.qty_free, i.tqty, i.total_unit, i.total_sale_unit,
i.total_return_unit, i.total_lost_unit,
(i.total_unit - i.total_sale_unit - i.total_lost_unit - i.total_return_unit) AS cur_unit
FROM purchase_invoice_stock p
JOIN purchase_invoice_item_stock i ON p.id = i.purchase_id AND i.item_return = 0
JOIN stock_supplier s ON p.sid = s.sid
WHERE {$where} AND i.remove_item = 0
ORDER BY p.id";
$data['product_purchase_detail'] = $this->db->query($sql, $whereParams)->getResult();
$html = view('storestock/stock_item_history', $data);
return $this->response->setBody($html);
}
// -------------------------------------------------------------------------
// Day Report (stub)
// -------------------------------------------------------------------------
public function report_2()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
return view('storestock/day_report');
}
// -------------------------------------------------------------------------
// Main Store Dashboard
// -------------------------------------------------------------------------
public function main_store()
{
if ($err = $this->ensureStoreAccess()) {
return $err;
}
return view('storestock/main_store_dashboard');
}
}