diff --git a/app/assets/js/custom.js b/app/assets/js/custom.js index 69dd640..1f4337b 100644 --- a/app/assets/js/custom.js +++ b/app/assets/js/custom.js @@ -286,14 +286,16 @@ let isAjaxProcessing = false; function ajax_request(options) { - if (isAjaxProcessing) { + if (isAjaxProcessing && options.queueLock !== false) { // Instead of rejecting, we just return a "never-ending" promise // or a resolved promise that does nothing. console.warn("Request is busy... ignoring click."); return new Promise(() => { }); // This stays pending and won't trigger .then or .catch } - isAjaxProcessing = true; + if (options.queueLock !== false) { + isAjaxProcessing = true; + } // Auto prepare form data if (options.autoPrepare === true) { @@ -339,6 +341,7 @@ function ajax_request(options) { let isSendingFiles = (options.data instanceof FormData); if (options.debugMode) { + isAjaxProcessing = false; console.log("REQUEST DATA:", options.data); } diff --git a/app/assets/utils/db_auth.php b/app/assets/utils/db_auth.php index 931f080..242e77a 100644 --- a/app/assets/utils/db_auth.php +++ b/app/assets/utils/db_auth.php @@ -199,6 +199,14 @@ class WarehouseManager { $name = $this->getWarehouseName($warehouse_id); $table = "td_stock_" . $name; + if (empty($id)) { + return [ + 'table' => $table, + 'name' => $name, + 'row' => [] + ]; + } + $sql = "SELECT * FROM `$table` WHERE company_id = :company_id AND id = :id"; diff --git a/app/ics/api/engine_report/product_stats.php b/app/ics/api/engine_report/product_stats.php new file mode 100644 index 0000000..f3a52d5 --- /dev/null +++ b/app/ics/api/engine_report/product_stats.php @@ -0,0 +1,37 @@ +prepare($sql); + $sth->execute([ + ":company_id" => $company_id + ]); + db_check($sth, $answer); + + $output = $sth->fetch(PDO::FETCH_ASSOC); + + // calculate stock balance + $low_stock = 0; + foreach($output as $key => $value) { + $output[$key]["low_stock"] = ($value["total_in"] - $value["total_out"]) < $value["min_stock"]?true:false; + if ($output[$key]["low_stock"]) { + $low_stock++; + } + } + $answer["output"] = $output; + $answer["low_stock"] = $low_stock; + $answer["success"] = 1; + exit(json_encode($answer)); + +?> \ No newline at end of file diff --git a/app/ics/api/engine_report/stock_activity.php b/app/ics/api/engine_report/stock_activity.php new file mode 100644 index 0000000..a34bae6 --- /dev/null +++ b/app/ics/api/engine_report/stock_activity.php @@ -0,0 +1,65 @@ +getStockContext($warehouse_id, $id); + $table = $warehouse['table']; + + + // ── Last 10 stock in/out transactions ──────────────────────────────── + $sql = "SELECT + a.id, + a.date, + a.product_sku, + a.`in` AS stock_in, + a.`out` AS stock_out, + a.type, + a.description, + b.product_name + FROM `{$table}` a + LEFT JOIN md_product b ON + a.company_id = b.company_id and + a.product_sku = b.sku + WHERE + a.company_id = {$company_id} and + (a.`in` > 0 OR a.`out` > 0) + ORDER BY a.date DESC, a.id DESC + LIMIT 10"; + + $sth = $pdo2->prepare($sql); + $sth->execute(); + $rows = $sth->fetchAll(PDO::FETCH_ASSOC); + + // ── Build output ───────────────────────────────────────────────────── + $activities = []; + + foreach ($rows as $row) { + $is_in = (float)$row['stock_in'] > 0; + $qty = $is_in ? (float)$row['stock_in'] : (float)$row['stock_out']; + + $activities[] = [ + 'product_name' => $row['product_name'] ?: $row['product_sku'], + 'sku' => $row['product_sku'], + 'direction' => $is_in ? 'in' : 'out', + 'qty' => round($qty, 2), + 'type' => $row['type'], + 'description' => $row['description'], + 'date' => $row['date'], + ]; + } + + // ── Response ───────────────────────────────────────────────────────── + $output = [ + 'activities' => $activities, + ]; + + $answer["output"] = $output; + $answer["success"] = 1; + exit(json_encode($answer)); + +?> \ No newline at end of file diff --git a/app/ics/api/engine_report/stock_movement.php b/app/ics/api/engine_report/stock_movement.php new file mode 100644 index 0000000..82a71c3 --- /dev/null +++ b/app/ics/api/engine_report/stock_movement.php @@ -0,0 +1,69 @@ +getStockContext($warehouse_id, $id); + $table = $warehouse['table']; + + + // ── Always last 12 months ──────────────────────────────────────────── + $now = new DateTime(); + $start = (clone $now)->modify('-12 months')->format('Y-m-d 00:00:00'); + $end = $now->format('Y-m-d 23:59:59'); + + // ── Query: group by month ──────────────────────────────────────────── + $sql = "SELECT + DATE_FORMAT(a.date, '%b') AS `label`, + DATE_FORMAT(a.date, '%Y-%m') AS `sort_key`, + ROUND(SUM(a.`in`), 2) AS `stock_in`, + ROUND(SUM(a.`out`), 2) AS `stock_out` + FROM `{$table}` a + WHERE + a.company_id = {$company_id} and + a.date >= '{$start}' and + a.date <= '{$end}' + GROUP BY `sort_key`, `label` + ORDER BY `sort_key` ASC"; + + $sth = $pdo2->prepare($sql); + $sth->execute(); + $rows = $sth->fetchAll(PDO::FETCH_ASSOC); + + // ── Index DB results by sort_key (YYYY-MM) ────────────────────────── + $dataByMonth = []; + foreach ($rows as $row) { + $dataByMonth[$row['sort_key']] = $row; + } + + // ── Always build 12 months, fill zeros where no data ──────────────── + $labels = []; + $stock_in = []; + $stock_out = []; + + for ($i = 11; $i >= 0; $i--) { + $dt = (clone $now)->modify("-{$i} months"); + $key = $dt->format('Y-m'); // e.g. "2025-04" + $label = $dt->format('M'); // e.g. "Apr" + + $labels[] = $label; + $stock_in[] = isset($dataByMonth[$key]) ? round((float)$dataByMonth[$key]['stock_in'], 2) : 0; + $stock_out[] = isset($dataByMonth[$key]) ? round((float)$dataByMonth[$key]['stock_out'], 2) : 0; + } + + // ── Response ───────────────────────────────────────────────────────── + $output = [ + 'labels' => $labels, + 'stock_in' => $stock_in, + 'stock_out' => $stock_out, + ]; + + $answer["output"] = $output; + $answer["success"] = 1; + exit(json_encode($answer)); + +?> \ No newline at end of file diff --git a/app/ics/api/engine_report/stock_trend.php b/app/ics/api/engine_report/stock_trend.php new file mode 100644 index 0000000..aa6af5c --- /dev/null +++ b/app/ics/api/engine_report/stock_trend.php @@ -0,0 +1,83 @@ +getStockContext($warehouse_id, $id); + $table = $warehouse['table']; + + + // ── Always last 12 months ──────────────────────────────────────────── + $now = new DateTime(); + $start = (clone $now)->modify('-12 months')->format('Y-m-d 00:00:00'); + $end = $now->format('Y-m-d 23:59:59'); + + // ── Get cumulative balance BEFORE the 12-month window ──────────────── + $sql_prior = "SELECT + ROUND(COALESCE(SUM(a.`in`), 0) - COALESCE(SUM(a.`out`), 0), 2) AS `balance` + FROM `{$table}` a + WHERE + a.company_id = {$company_id} and + a.date < '{$start}'"; + + $sth = $pdo2->prepare($sql_prior); + $sth->execute(); + $prior_balance = (float)$sth->fetchColumn(); + + // ── Get monthly in/out for the 12-month window ────────────────────── + $sql = "SELECT + DATE_FORMAT(a.date, '%Y-%m') AS `sort_key`, + ROUND(SUM(a.`in`), 2) AS `stock_in`, + ROUND(SUM(a.`out`), 2) AS `stock_out` + FROM `{$table}` a + WHERE + a.company_id = {$company_id} and + a.date >= '{$start}' and + a.date <= '{$end}' + GROUP BY `sort_key` + ORDER BY `sort_key` ASC"; + + $sth = $pdo2->prepare($sql); + $sth->execute(); + $rows = $sth->fetchAll(PDO::FETCH_ASSOC); + + // ── Index DB results by sort_key (YYYY-MM) ────────────────────────── + $dataByMonth = []; + foreach ($rows as $row) { + $dataByMonth[$row['sort_key']] = $row; + } + + // ── Always build 12 months, accumulate running balance ────────────── + $labels = []; + $balance = []; + $running = $prior_balance; + + for ($i = 11; $i >= 0; $i--) { + $dt = (clone $now)->modify("-{$i} months"); + $key = $dt->format('Y-m'); + $label = $dt->format('M'); + + $month_in = isset($dataByMonth[$key]) ? (float)$dataByMonth[$key]['stock_in'] : 0; + $month_out = isset($dataByMonth[$key]) ? (float)$dataByMonth[$key]['stock_out'] : 0; + + $running += ($month_in - $month_out); + + $labels[] = $label; + $balance[] = round($running, 2); + } + + // ── Response ───────────────────────────────────────────────────────── + $output = [ + 'labels' => $labels, + 'balance' => $balance, + ]; + + $answer["output"] = $output; + $answer["success"] = 1; + exit(json_encode($answer)); + +?> \ No newline at end of file diff --git a/app/ics/api/engine_report/warehouse_balance.php b/app/ics/api/engine_report/warehouse_balance.php new file mode 100644 index 0000000..c4e9abd --- /dev/null +++ b/app/ics/api/engine_report/warehouse_balance.php @@ -0,0 +1,24 @@ +prepare($sql); + $sth->execute([ + ":company_id" => $company_id + ]); + db_check($sth, $answer); + + $output = $sth->fetch(PDO::FETCH_ASSOC); + + $answer["output"] = $output; + $answer["success"] = 1; + exit(json_encode($answer)); + +?> \ No newline at end of file diff --git a/app/ics/api/engine/warehouse_stats.php b/app/ics/api/engine_report/warehouse_stats.php similarity index 100% rename from app/ics/api/engine/warehouse_stats.php rename to app/ics/api/engine_report/warehouse_stats.php diff --git a/app/ics/stock_overview.php b/app/ics/stock_overview.php index f59264c..ab307af 100644 --- a/app/ics/stock_overview.php +++ b/app/ics/stock_overview.php @@ -16,8 +16,16 @@
Unit added
Unit removed
Stock In - -
-Stock Out - -
-Stock Out - -
-Stock Out - -
-