[OOP] contact,inventory,ics,dashboard
This commit is contained in:
@@ -488,23 +488,29 @@ class ReportManager
|
||||
$start = (clone $now)->modify('-12 months')->format('Y-m-d 00:00:00');
|
||||
$end = $now->format('Y-m-d 23:59:59');
|
||||
|
||||
$sth = $this->pdo->query(
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT DATE_FORMAT(date, '%b') AS label,
|
||||
DATE_FORMAT(date, '%Y-%m') AS sort_key,
|
||||
ROUND(SUM(`in`), 2) AS stock_in,
|
||||
ROUND(SUM(`out`), 2) AS stock_out
|
||||
FROM `{$table}`
|
||||
WHERE company_id = {$this->companyId}
|
||||
AND date >= '{$start}' AND date <= '{$end}'
|
||||
GROUP BY sort_key, label
|
||||
ORDER BY sort_key ASC"
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id
|
||||
AND date >= :start AND date <= :end
|
||||
GROUP BY sort_key, label
|
||||
ORDER BY sort_key ASC"
|
||||
);
|
||||
$sth->execute([
|
||||
':company_id' => $this->companyId,
|
||||
':start' => $start,
|
||||
':end' => $end,
|
||||
]);
|
||||
|
||||
$dataByMonth = [];
|
||||
foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$dataByMonth[$row['sort_key']] = $row;
|
||||
}
|
||||
|
||||
$labels = []; $stock_in = []; $stock_out = [];
|
||||
$labels = $stock_in = $stock_out = [];
|
||||
for ($i = 11; $i >= 0; $i--) {
|
||||
$dt = (clone $now)->modify("-{$i} months");
|
||||
$key = $dt->format('Y-m');
|
||||
@@ -525,29 +531,36 @@ class ReportManager
|
||||
$start = (clone $now)->modify('-12 months')->format('Y-m-d 00:00:00');
|
||||
$end = $now->format('Y-m-d 23:59:59');
|
||||
|
||||
$sth = $this->pdo->query(
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT ROUND(COALESCE(SUM(`in`) - SUM(`out`), 0), 2) AS balance
|
||||
FROM `{$table}`
|
||||
WHERE company_id = {$this->companyId} AND date < '{$start}'"
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id AND date < :start"
|
||||
);
|
||||
$running = (float) $sth->fetchColumn();
|
||||
$sth->execute([':company_id' => $this->companyId, ':start' => $start]);
|
||||
$running = (float)$sth->fetchColumn();
|
||||
|
||||
$sth = $this->pdo->query(
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT DATE_FORMAT(date, '%Y-%m') AS sort_key,
|
||||
ROUND(SUM(`in`), 2) AS stock_in,
|
||||
ROUND(SUM(`out`), 2) AS stock_out
|
||||
FROM `{$table}`
|
||||
WHERE company_id = {$this->companyId}
|
||||
AND date >= '{$start}' AND date <= '{$end}'
|
||||
GROUP BY sort_key
|
||||
ORDER BY sort_key ASC"
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id
|
||||
AND date >= :start AND date <= :end
|
||||
GROUP BY sort_key
|
||||
ORDER BY sort_key ASC"
|
||||
);
|
||||
$sth->execute([
|
||||
':company_id' => $this->companyId,
|
||||
':start' => $start,
|
||||
':end' => $end,
|
||||
]);
|
||||
|
||||
$dataByMonth = [];
|
||||
foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$dataByMonth[$row['sort_key']] = $row;
|
||||
}
|
||||
|
||||
$labels = []; $balance = [];
|
||||
$labels = $balance = [];
|
||||
for ($i = 11; $i >= 0; $i--) {
|
||||
$dt = (clone $now)->modify("-{$i} months");
|
||||
$key = $dt->format('Y-m');
|
||||
@@ -566,23 +579,24 @@ class ReportManager
|
||||
$table = $this->resolveWarehouseTable($warehouse_id);
|
||||
if (!$table) return [];
|
||||
|
||||
$sth = $this->pdo->query(
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT 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 = {$this->companyId}
|
||||
AND (a.`in` > 0 OR a.`out` > 0)
|
||||
ORDER BY a.date DESC, a.id DESC
|
||||
LIMIT 10"
|
||||
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->execute([':company_id' => $this->companyId]);
|
||||
|
||||
$activities = [];
|
||||
foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$is_in = (float)$row['stock_in'] > 0;
|
||||
$qty = $is_in ? (float)$row['stock_in'] : (float)$row['stock_out'];
|
||||
$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'],
|
||||
@@ -694,6 +708,207 @@ class ReportManager
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Warehouse balance summary
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Aggregate total_in, total_out and warehouse count across all warehouses.
|
||||
*/
|
||||
public function getWarehouseBalanceSummary(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
SUM(total_in) AS total_in,
|
||||
SUM(total_out) AS total_out,
|
||||
COUNT(DISTINCT warehouse_id) AS total_warehouse
|
||||
FROM warehouse_balance
|
||||
WHERE company_id = :company_id"
|
||||
);
|
||||
$sth->execute([':company_id' => $this->companyId]);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Dashboard report queries
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* All stock movements for a given product_sku + lot_number across all warehouses,
|
||||
* sorted by date DESC.
|
||||
*/
|
||||
public function getLotStockLog(string $product_sku, string $lot_number): array
|
||||
{
|
||||
if (!$product_sku || !$lot_number) return [];
|
||||
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT id, warehouse_name FROM md_warehouse
|
||||
WHERE company_id = :company_id AND status = 1"
|
||||
);
|
||||
$sth->execute([':company_id' => $this->companyId]);
|
||||
$warehouses = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($warehouses as $wh) {
|
||||
$safe = preg_replace('/[^a-zA-Z0-9_]/', '', $wh['warehouse_name']);
|
||||
$table = "td_stock_{$safe}";
|
||||
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
s.id,
|
||||
s.date,
|
||||
s.type,
|
||||
s.zone,
|
||||
s.aisle,
|
||||
s.rack,
|
||||
ROUND(COALESCE(s.`in`, 0), 2) AS stock_in,
|
||||
ROUND(COALESCE(s.`out`, 0), 2) AS stock_out,
|
||||
s.serial_number,
|
||||
s.description,
|
||||
'{$wh['warehouse_name']}' AS warehouse_name
|
||||
FROM `{$table}` s
|
||||
WHERE s.company_id = :company_id
|
||||
AND s.product_sku = :product_sku
|
||||
AND s.lot_number = :lot_number
|
||||
ORDER BY s.date DESC"
|
||||
);
|
||||
$sth->execute([
|
||||
':company_id' => $this->companyId,
|
||||
':product_sku' => $product_sku,
|
||||
':lot_number' => $lot_number,
|
||||
]);
|
||||
$rows = array_merge($rows, $sth->fetchAll(PDO::FETCH_ASSOC));
|
||||
}
|
||||
|
||||
usort($rows, fn($a, $b) => strcmp($b['date'], $a['date']));
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* All lots with running balance, expiry status, and days remaining.
|
||||
* Returns rows plus summary counts (total, active, expired, near).
|
||||
*/
|
||||
public function getProductLots(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
l.id,
|
||||
l.product_sku,
|
||||
l.lot_number,
|
||||
l.expiry_date,
|
||||
l.description,
|
||||
p.product_name,
|
||||
DATEDIFF(l.expiry_date, CURDATE()) AS days_remaining,
|
||||
COALESCE(
|
||||
(SELECT ROUND(SUM(wb.total_in) - SUM(wb.total_out), 2)
|
||||
FROM warehouse_balance wb
|
||||
WHERE wb.company_id = l.company_id
|
||||
AND wb.product_sku = l.product_sku),
|
||||
0
|
||||
) AS balance
|
||||
FROM md_lot l
|
||||
INNER JOIN md_product p
|
||||
ON p.company_id = l.company_id
|
||||
AND p.sku = l.product_sku
|
||||
WHERE l.company_id = :company_id
|
||||
ORDER BY l.product_sku, l.expiry_date ASC"
|
||||
);
|
||||
$sth->execute([':company_id' => $this->companyId]);
|
||||
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$active = $expired = $near = 0;
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
$days = (int)$row['days_remaining'];
|
||||
$balance = (float)$row['balance'];
|
||||
|
||||
if ($balance > 0) $active++;
|
||||
if ($days < 0) $expired++;
|
||||
if ($days >= 0 && $days <= 30) $near++;
|
||||
|
||||
if ($days < 0) $row['status'] = 'expired';
|
||||
elseif ($days <= 7) $row['status'] = 'critical';
|
||||
elseif ($days <= 30) $row['status'] = 'near';
|
||||
else $row['status'] = 'ok';
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return [
|
||||
'rows' => $rows,
|
||||
'total' => count($rows),
|
||||
'active' => $active,
|
||||
'expired' => $expired,
|
||||
'near' => $near,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Rack activity log for a given rack_id, with user name.
|
||||
*/
|
||||
public function getRackLog(int $rack_id): array
|
||||
{
|
||||
if (!$rack_id) return [];
|
||||
|
||||
$db = $this->pdo->query("SELECT DATABASE()")->fetchColumn();
|
||||
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
rl.id,
|
||||
rl.dt,
|
||||
rl.action,
|
||||
rl.product_sku,
|
||||
rl.td_stock_id,
|
||||
rl.login,
|
||||
u.name AS user_name
|
||||
FROM md_rack_log rl
|
||||
LEFT JOIN {$db}.user u
|
||||
ON u.user_id = rl.user_id
|
||||
WHERE rl.company_id = :company_id
|
||||
AND rl.md_rack_id = :rack_id
|
||||
ORDER BY rl.dt DESC"
|
||||
);
|
||||
$sth->execute([
|
||||
':company_id' => $this->companyId,
|
||||
':rack_id' => $rack_id,
|
||||
]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Full rack list with warehouse name, product info and occupancy status.
|
||||
*/
|
||||
public function getRackOccupancy(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
r.id,
|
||||
r.zone,
|
||||
r.aisle,
|
||||
r.rack,
|
||||
r.product_sku,
|
||||
r.td_stock_id,
|
||||
mw.warehouse_name,
|
||||
mw.id AS warehouse_id,
|
||||
p.product_name,
|
||||
CASE WHEN r.product_sku IS NOT NULL THEN 'occupied' ELSE 'empty' END AS status
|
||||
FROM md_rack r
|
||||
INNER JOIN md_warehouse mw
|
||||
ON mw.company_id = r.company_id
|
||||
AND mw.id = r.warehouse
|
||||
LEFT JOIN md_product p
|
||||
ON p.company_id = r.company_id
|
||||
AND p.sku = r.product_sku
|
||||
WHERE r.company_id = :company_id
|
||||
ORDER BY mw.warehouse_name, r.zone,
|
||||
CAST(r.aisle AS UNSIGNED), r.aisle,
|
||||
CAST(r.rack AS UNSIGNED), r.rack"
|
||||
);
|
||||
$sth->execute([':company_id' => $this->companyId]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user