connection = $database->connection( (string) config('upstream_mirrors.connection', 'business_mirror'), ); } /** * @return array{balance:?float,date:?string,source:string,store_count:int} */ public function openingBalance(?string $storeName, ?string $periodStart): array { if (! $this->hasTable(self::TABLE)) { return $this->unavailable(); } $query = $this->connection->table(self::TABLE) ->where('balance_date', self::BASELINE_DATE); if ($storeName !== null && $storeName !== '') { $query->where('store_name', $storeName); } $baseline = (clone $query)->selectRaw( 'COALESCE(SUM(balance), 0) AS balance, COUNT(*) AS store_count, ' . 'COUNT(DISTINCT source) AS source_count, MIN(source) AS source' )->first(); $storeCount = (int) ($baseline->store_count ?? 0); if ($storeCount === 0) { return $this->unavailable(); } $balance = round((float) $baseline->balance, 2); $date = self::BASELINE_DATE; $source = (int) ($baseline->source_count ?? 0) === 1 ? (string) $baseline->source : 'mixed_opening_sources'; $start = $this->date($periodStart); if ($start !== null && $start->gt('2026-07-01')) { $movement = $this->connection->table(self::BILL_TABLE) ->where('occurred_at', '>=', '2026-07-01 00:00:00') ->where('occurred_at', '<', $start->format('Y-m-d') . ' 00:00:00') ->when($storeName !== null && $storeName !== '', function ($query) use ($storeName): void { $query->where('store_name', $storeName); }) ->selectRaw( 'COALESCE(SUM(income_amount), 0) AS income, ' . 'COALESCE(SUM(expense_amount), 0) AS expense' ) ->first(); $balance = round( $balance + (float) ($movement->income ?? 0) - (float) ($movement->expense ?? 0), 2, ); $date = $start->subDay()->format('Y-m-d'); if (! str_ends_with($source, '_carried_forward')) { $source .= '_carried_forward'; } } return [ 'balance' => $balance, 'date' => $date, 'source' => $source, 'store_count' => $storeCount, ]; } /** @return array{balance:null,date:null,source:string,store_count:int} */ private function unavailable(): array { return [ 'balance' => null, 'date' => null, 'source' => 'unavailable', 'store_count' => 0, ]; } private function date(?string $value): ?CarbonImmutable { if ($value === null || preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) !== 1) { return null; } $date = CarbonImmutable::createFromFormat('!Y-m-d', $value); return $date !== false && $date->format('Y-m-d') === $value ? $date : null; } private function hasTable(string $table): bool { return Schema::connection($this->connection->getName())->hasTable($table); } }