*/ public function page(): array { $rows = $this->store->listRows( PayablesFinalActions::DATASET_PREFIX . 'supplier:' . self::DATASET, (int) (company_id() ?: 1), ); $negativeRows = []; foreach ($rows as $row) { $balance = $this->number($row['supplier_balance'] ?? null) ?? 0.0; $shouldPay = $this->number($row['should_pay'] ?? null) ?? 0.0; $estimatedPrice = $this->number($row['estimated_price'] ?? null) ?? 0.0; $alertAmount = $balance - $shouldPay + $estimatedPrice; if ($alertAmount >= 0) { continue; } $row['supplier_balance'] = $balance; $row['_negative_alert_amount'] = round($alertAmount, 2); $negativeRows[] = $row; } usort($negativeRows, static function (array $left, array $right): int { return strcmp( (string) ($right['summary_month'] ?? ''), (string) ($left['summary_month'] ?? ''), ) ?: strnatcasecmp( (string) ($left['supplier'] ?? ''), (string) ($right['supplier'] ?? ''), ); }); $months = []; foreach ($negativeRows as $row) { $month = trim((string) ($row['summary_month'] ?? '')); if (preg_match('/^\d{4}-\d{2}$/', $month) === 1) { $months[$month] = true; } } $months = array_keys($months); rsort($months); $requestedMonth = trim((string) request()->query('month', '')); $month = in_array($requestedMonth, $months, true) ? $requestedMonth : ''; if ($month !== '') { $negativeRows = array_values(array_filter( $negativeRows, static fn (array $row): bool => (string) ($row['summary_month'] ?? '') === $month, )); } $supplierNames = []; foreach ($negativeRows as $row) { $supplier = trim((string) ($row['supplier'] ?? '')); if ($supplier !== '') { $supplierNames[$supplier] = true; } } $supplierNames = array_keys($supplierNames); sort($supplierNames, SORT_NATURAL | SORT_FLAG_CASE); $supplierQuery = mb_substr(trim((string) request()->query('supplier', '')), 0, 191); if ($supplierQuery !== '') { $needle = mb_strtolower($supplierQuery); $negativeRows = array_values(array_filter( $negativeRows, static function (array $row) use ($needle): bool { $haystack = mb_strtolower(implode("\n", [ (string) ($row['supplier'] ?? ''), (string) ($row['company'] ?? ''), ])); return str_contains($haystack, $needle); }, )); } $supplierCounts = []; foreach ($negativeRows as $row) { $key = trim((string) ($row['company'] ?? '')) . "\0" . trim((string) ($row['supplier'] ?? '')); $supplierCounts[$key] = true; } $total = count($negativeRows); $pages = max((int) ceil($total / self::PER_PAGE), 1); $page = min(max((int) request()->query('page', 1), 1), $pages); $items = array_map( static fn (array $row): object => (object) $row, array_slice($negativeRows, ($page - 1) * self::PER_PAGE, self::PER_PAGE), ); return [ 'view' => 'business.pages.negative-alerts.supplier-negative-alert', 'negativeAlertFields' => collect($this->registry->fields( PayablesFinalRegistry::SECTION_SUPPLIER, self::DATASET, ))->map( static fn (array $definition, string $name): array => ['name' => $name] + $definition, )->values()->all(), 'negativeAlertRows' => new LengthAwarePaginator( $items, $total, self::PER_PAGE, $page, [ 'path' => request()->url(), 'query' => request()->except('page'), ], ), 'negativeAlertMonths' => $months, 'negativeAlertMonth' => $month, 'negativeAlertSupplier' => $supplierQuery, 'negativeAlertSupplierNames' => $supplierNames, 'negativeAlertSupplierCount' => count($supplierCounts), 'negativeAlertTotal' => $total, 'negativeAlertSourceUrl' => route('business.pages.show', [ 'section' => 'supplier-payables-final', 'page' => 'supplier-payables-report', ]), ]; } private function number(mixed $value): ?float { if ($value === null || trim((string) $value) === '') { return null; } $normalized = str_replace([',', ',', ' '], '', trim((string) $value)); return is_numeric($normalized) ? (float) $normalized : null; } }