implement JS password scoring, td_stock approve logicc
This commit is contained in:
@@ -25,18 +25,20 @@
|
||||
* - getRecentActivity: warehouse_name in SQL now uses $safe (was unescaped)
|
||||
* - getExpiredStock: warehouse_id cast to (int) before SQL fragment injection;
|
||||
* warehouse_name in UNION now uses $safe (was unescaped)
|
||||
* - getRackLog: DB name from SELECT DATABASE() bound via PDO (was raw interpolation)
|
||||
* - getRackLog: cross-DB join uses $mainDb injected at construction time
|
||||
* - getRackOccupancy: (already safe — no dynamic identifiers)
|
||||
*/
|
||||
class ReportManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
private string $mainDb;
|
||||
|
||||
public function __construct(PDO $pdo, int $companyId)
|
||||
public function __construct(PDO $pdo, int $companyId, string $mainDb = '')
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $companyId;
|
||||
$this->mainDb = $mainDb;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
@@ -600,7 +602,8 @@ class ReportManager
|
||||
LEFT JOIN md_product p
|
||||
ON p.company_id = s.company_id
|
||||
AND p.sku = s.product_sku
|
||||
WHERE s.company_id = {$cid}";
|
||||
WHERE s.company_id = {$cid}
|
||||
AND s.status = 1";
|
||||
},
|
||||
$warehouses
|
||||
));
|
||||
@@ -740,7 +743,8 @@ class ReportManager
|
||||
ROUND(SUM(`out`), 2) AS stock_out
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id
|
||||
AND date >= :start AND date <= :end
|
||||
AND status = 1
|
||||
AND date >= :start AND date <= :end
|
||||
GROUP BY sort_key, label
|
||||
ORDER BY sort_key ASC"
|
||||
);
|
||||
@@ -790,7 +794,7 @@ class ReportManager
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT ROUND(COALESCE(SUM(`in`) - SUM(`out`), 0), 2) AS balance
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id AND date < :start"
|
||||
WHERE company_id = :company_id AND status = 1 AND date < :start"
|
||||
);
|
||||
$sth->execute([':company_id' => $this->companyId, ':start' => $start]);
|
||||
$running = (float)$sth->fetchColumn();
|
||||
@@ -801,7 +805,8 @@ class ReportManager
|
||||
ROUND(SUM(`out`), 2) AS stock_out
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id
|
||||
AND date >= :start AND date <= :end
|
||||
AND status = 1
|
||||
AND date >= :start AND date <= :end
|
||||
GROUP BY sort_key
|
||||
ORDER BY sort_key ASC"
|
||||
);
|
||||
@@ -939,7 +944,8 @@ class ReportManager
|
||||
FROM `td_stock_{$safe}` s
|
||||
WHERE s.company_id = {$cid}
|
||||
AND s.type = 'in'
|
||||
AND s.lot_number IS NOT NULL";
|
||||
AND s.lot_number IS NOT NULL
|
||||
AND s.status = 1";
|
||||
},
|
||||
$warehouses
|
||||
));
|
||||
@@ -1058,6 +1064,7 @@ class ReportManager
|
||||
WHERE s.company_id = :company_id
|
||||
AND s.product_sku = :product_sku
|
||||
AND s.lot_number = :lot_number
|
||||
AND s.status = 1
|
||||
ORDER BY s.date DESC"
|
||||
);
|
||||
$sth->execute([
|
||||
@@ -1089,6 +1096,37 @@ class ReportManager
|
||||
*/
|
||||
public function getProductLots(): array
|
||||
{
|
||||
$warehouses = $this->pdo->prepare(
|
||||
"SELECT id, warehouse_name FROM md_warehouse
|
||||
WHERE company_id = :company_id AND status = 1"
|
||||
);
|
||||
$warehouses->execute([':company_id' => $this->companyId]);
|
||||
$wh_list = $warehouses->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Build per-lot balance by summing approved td_stock rows across all warehouses.
|
||||
// warehouse_balance is per-product, not per-lot, so we query td_stock directly.
|
||||
$lot_balance = [];
|
||||
$cid = (int) $this->companyId;
|
||||
|
||||
foreach ($wh_list as $wh) {
|
||||
$safe = preg_replace('/[^a-zA-Z0-9_]/', '', $wh['warehouse_name']);
|
||||
$table = "td_stock_{$safe}";
|
||||
|
||||
$sth = $this->pdo->query(
|
||||
"SELECT lot_number,
|
||||
ROUND(SUM(COALESCE(`in`, 0)) - SUM(COALESCE(`out`, 0)), 2) AS lot_balance
|
||||
FROM `{$table}`
|
||||
WHERE company_id = {$cid}
|
||||
AND status = 1
|
||||
AND lot_number IS NOT NULL
|
||||
GROUP BY lot_number"
|
||||
);
|
||||
foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $lb) {
|
||||
$key = $lb['lot_number'];
|
||||
$lot_balance[$key] = ($lot_balance[$key] ?? 0) + (float)$lb['lot_balance'];
|
||||
}
|
||||
}
|
||||
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
l.id,
|
||||
@@ -1097,14 +1135,7 @@ class ReportManager
|
||||
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
|
||||
DATEDIFF(l.expiry_date, CURDATE()) AS days_remaining
|
||||
FROM md_lot l
|
||||
INNER JOIN md_product p
|
||||
ON p.company_id = l.company_id
|
||||
@@ -1117,18 +1148,24 @@ class ReportManager
|
||||
|
||||
$active = $expired = $near = 0;
|
||||
|
||||
// Exclude lots that have no td_stock record at all (e.g. all rows were soft-deleted)
|
||||
$rows = array_values(array_filter($rows, fn($r) => array_key_exists($r['lot_number'], $lot_balance)));
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
$days = (int)$row['days_remaining'];
|
||||
$balance = (float)$row['balance'];
|
||||
$balance = round($lot_balance[$row['lot_number']] ?? 0, 2);
|
||||
|
||||
$row['balance'] = $balance;
|
||||
$row['is_active'] = $balance > 0 ? 1 : 0;
|
||||
|
||||
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';
|
||||
if ($days < 0) $row['status'] = 'expired';
|
||||
elseif ($days <= 7) $row['status'] = 'critical';
|
||||
elseif ($days <= 30) $row['status'] = 'near';
|
||||
else $row['status'] = 'ok';
|
||||
}
|
||||
unset($row);
|
||||
|
||||
@@ -1158,9 +1195,7 @@ class ReportManager
|
||||
{
|
||||
if (!$rack_id) return [];
|
||||
|
||||
// Fetch DB name safely — used as a backtick-quoted identifier, not user input
|
||||
$db_name = $this->pdo->query("SELECT DATABASE()")->fetchColumn();
|
||||
$db_name = preg_replace('/[^a-zA-Z0-9_]/', '', (string)$db_name);
|
||||
$main_db = preg_replace('/[^a-zA-Z0-9_]/', '', $this->mainDb);
|
||||
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
@@ -1172,7 +1207,7 @@ class ReportManager
|
||||
rl.login,
|
||||
u.name AS user_name
|
||||
FROM md_rack_log rl
|
||||
LEFT JOIN `{$db_name}`.user u
|
||||
LEFT JOIN `{$main_db}`.user u
|
||||
ON u.user_id = rl.user_id
|
||||
WHERE rl.company_id = :company_id
|
||||
AND rl.md_rack_id = :rack_id
|
||||
@@ -1299,6 +1334,7 @@ class ReportManager
|
||||
"SELECT DISTINCT product_sku
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id
|
||||
AND status = 1
|
||||
AND date BETWEEN :date_from AND :date_to
|
||||
ORDER BY product_sku ASC"
|
||||
);
|
||||
@@ -1313,6 +1349,7 @@ class ReportManager
|
||||
ROUND(SUM(COALESCE(`out`, 0)), 2) AS total_out
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id
|
||||
AND status = 1
|
||||
AND date BETWEEN :date_from AND :date_to"
|
||||
);
|
||||
$sth->execute([':company_id' => $this->companyId, ':date_from' => $date_from, ':date_to' => $date_to]);
|
||||
@@ -1366,6 +1403,7 @@ class ReportManager
|
||||
FROM `{$table}`
|
||||
WHERE company_id = :company_id
|
||||
AND product_sku = :sku
|
||||
AND status = 1
|
||||
AND date < :date_from"
|
||||
);
|
||||
$sth->execute([':company_id' => $this->companyId, ':sku' => $product_sku, ':date_from' => $date_from]);
|
||||
@@ -1404,6 +1442,7 @@ class ReportManager
|
||||
AND c.id = s.contact_id
|
||||
WHERE s.company_id = :company_id
|
||||
AND s.product_sku = :sku
|
||||
AND s.status = 1
|
||||
AND s.date BETWEEN :date_from AND :date_to
|
||||
ORDER BY s.date ASC, s.id ASC"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user