diff --git a/debts.php b/debts.php index f28f5ed..f9d3a3b 100644 --- a/debts.php +++ b/debts.php @@ -8,6 +8,11 @@ $pdo = db(); $debtsLoadError = ''; $unpaidSales = []; $debtsByCustomer = []; +$customerNameFilter = trim((string) ($_GET['customer_name'] ?? '')); +$phoneFilter = trim((string) ($_GET['phone'] ?? '')); +$dateFrom = trim((string) ($_GET['date_from'] ?? '')); +$dateTo = trim((string) ($_GET['date_to'] ?? '')); +$hasFilters = $customerNameFilter !== '' || $phoneFilter !== '' || $dateFrom !== '' || $dateTo !== ''; // Handle legacy mark-as-paid shortcut if (isset($_GET['mark_paid'])) { @@ -53,6 +58,7 @@ try { 'COALESCE(s.due_amount, s.total_amount) AS due_amount', isset($salesColumns['customer_id']) ? 's.customer_id' : 'NULL AS customer_id', isset($salesColumns['customer_name']) ? 's.customer_name' : 'NULL AS customer_name', + isset($salesColumns['status']) ? "COALESCE(s.status, 'completed') AS status" : "'completed' AS status", 'NULL AS c_name', 'NULL AS c_phone', ]; @@ -61,28 +67,95 @@ try { if ($hasCustomersTable && isset($salesColumns['customer_id']) && isset($customerColumns['id'])) { $joinSql = ' LEFT JOIN customers c ON s.customer_id = c.id '; if (isset($customerColumns['name'])) { - $selectParts[9] = 'c.name AS c_name'; + $selectParts[10] = 'c.name AS c_name'; } if (isset($customerColumns['phone'])) { - $selectParts[10] = 'c.phone AS c_phone'; + $selectParts[11] = 'c.phone AS c_phone'; } } + $params = []; + $whereParts = []; if (isset($salesColumns['payment_status'])) { - $whereSql = " WHERE s.payment_status IN ('unpaid', 'partial')"; + $whereParts[] = "s.payment_status IN ('unpaid', 'partial')"; } elseif (isset($salesColumns['payment_method'])) { - $whereSql = " WHERE s.payment_method = 'pay_later'"; + $whereParts[] = "s.payment_method = 'pay_later'"; } else { - $whereSql = ' WHERE 1 = 0'; + $whereParts[] = '1 = 0'; } + if ($customerNameFilter !== '') { + $nameConditions = []; + if ($hasCustomersTable && isset($customerColumns['name'])) { + $nameConditions[] = 'c.name LIKE :customer_name_customer'; + $params[':customer_name_customer'] = '%' . $customerNameFilter . '%'; + } + if (isset($salesColumns['customer_name'])) { + $nameConditions[] = 's.customer_name LIKE :customer_name_sale'; + $params[':customer_name_sale'] = '%' . $customerNameFilter . '%'; + } + if ($nameConditions !== []) { + $whereParts[] = '(' . implode(' OR ', $nameConditions) . ')'; + } + } + + if ($phoneFilter !== '') { + $rawDigits = phone_digits($phoneFilter); + $normalizedPhone = normalize_oman_phone($phoneFilter); + $phoneVariants = []; + if ($rawDigits !== '') { + $phoneVariants[] = $rawDigits; + } + if ($normalizedPhone !== '') { + $phoneVariants[] = $normalizedPhone; + $phoneVariants[] = '0' . $normalizedPhone; + $phoneVariants[] = '968' . $normalizedPhone; + $phoneVariants[] = '00968' . $normalizedPhone; + } + $phoneVariants = array_values(array_unique(array_filter($phoneVariants, static fn($value) => $value !== ''))); + + $phoneColumns = []; + if ($hasCustomersTable && isset($customerColumns['phone'])) { + $phoneColumns[] = "REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(c.phone, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '')"; + } + if (isset($salesColumns['customer_name'])) { + $phoneColumns[] = "REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(s.customer_name, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '')"; + } + + if ($phoneColumns !== [] && $phoneVariants !== []) { + $phoneConditions = []; + foreach ($phoneColumns as $columnIndex => $columnExpression) { + foreach ($phoneVariants as $variantIndex => $variant) { + $paramKey = ':phone_' . $columnIndex . '_' . $variantIndex; + $phoneConditions[] = $columnExpression . ' LIKE ' . $paramKey; + $params[$paramKey] = '%' . $variant . '%'; + } + } + $whereParts[] = '(' . implode(' OR ', $phoneConditions) . ')'; + } + } + + if ($dateFrom !== '') { + $whereParts[] = 'DATE(s.sale_date) >= :date_from'; + $params[':date_from'] = $dateFrom; + } + if ($dateTo !== '') { + $whereParts[] = 'DATE(s.sale_date) <= :date_to'; + $params[':date_to'] = $dateTo; + } + + $whereSql = ' WHERE ' . implode(' AND ', $whereParts); $sqlUnpaid = 'SELECT ' . implode(', ', $selectParts) . ' FROM sales_orders s' . $joinSql . $whereSql . ' ORDER BY s.sale_date DESC'; - $stmtUnpaid = $pdo->query($sqlUnpaid); - $unpaidSales = $stmtUnpaid ? $stmtUnpaid->fetchAll(PDO::FETCH_ASSOC) : []; + $stmtUnpaid = $pdo->prepare($sqlUnpaid); + foreach ($params as $key => $value) { + $stmtUnpaid->bindValue($key, $value); + } + $stmtUnpaid->execute(); + $unpaidSales = $stmtUnpaid->fetchAll(PDO::FETCH_ASSOC); } catch (Throwable $e) { $debtsLoadError = tr( 'تعذر تحميل صفحة الديون بسبب اختلاف في هيكل قاعدة البيانات. احفظ التعديلات ثم أنشئ نسخة جديدة وأعد التحديث.', @@ -90,16 +163,48 @@ try { ); } +$extractCustomerContact = static function (array $sale): array { + $sourceName = trim((string) ($sale['customer_name'] ?? '')); + $displayName = trim((string) ($sale['c_name'] ?? '')); + if ($displayName === '') { + $displayName = $sourceName; + } + + $displayPhone = trim((string) ($sale['c_phone'] ?? '')); + if ($displayPhone === '' && str_contains($sourceName, ' - ')) { + $parts = explode(' - ', $sourceName); + $lastPart = trim((string) end($parts)); + if (preg_match('/^[0-9+\s]+$/', $lastPart)) { + $displayPhone = $lastPart; + array_pop($parts); + if ($displayName === '' || $displayName === $sourceName) { + $displayName = trim(implode(' - ', $parts)); + } + } + } + + $displayPhone = phone_display($displayPhone); + if ($displayName === '') { + $displayName = tr('عميل غير معروف', 'Unknown Customer'); + } + + return [ + 'name' => $displayName, + 'phone' => $displayPhone, + ]; +}; + // Aggregate by customer foreach ($unpaidSales as $sale) { + $customerContact = $extractCustomerContact($sale); $cId = $sale['customer_id'] ?? 'unknown'; if (!isset($debtsByCustomer[$cId])) { $debtsByCustomer[$cId] = [ - 'name' => $sale['c_name'] ?: $sale['customer_name'] ?: tr('عميل غير معروف', 'Unknown Customer'), - 'phone' => $sale['c_phone'] ?: '', + 'name' => $customerContact['name'], + 'phone' => $customerContact['phone'], 'total' => 0.0, 'open_invoices' => 0, - 'partial_invoices' => 0 + 'partial_invoices' => 0, ]; } $saleSummary = sale_payment_summary($sale); @@ -126,6 +231,60 @@ require_once 'includes/header.php'; +