mirror = $databases->connection( (string) config('upstream_mirrors.connection', 'business_mirror'), ); $this->bills = $databases->connection(); } /** @return array{balance:float,date:?string,source:string} */ public function openingBalance(?string $storeName, ?string $startDate, ?string $endDate): array { $start = $this->monthStart($startDate) ?? $this->monthStart($endDate); $baselineMonth = CarbonImmutable::create(2026, 7, 1)->startOfDay(); if ($start === null || $start->lt($baselineMonth)) { return ['balance' => 0.0, 'date' => null, 'source' => 'none']; } if (! $this->mirror->getSchemaBuilder()->hasTable(VipshopMpOpeningBalanceImporter::TABLE)) { return ['balance' => 0.0, 'date' => null, 'source' => 'none']; } $store = trim((string) $storeName); $snapshot = $this->mirror->table(VipshopMpOpeningBalanceImporter::TABLE) ->where('period_end', '2026-06-30') ->when($store !== '', static fn ($query) => $query->where('store_name', $store)); if ((clone $snapshot)->doesntExist()) { return ['balance' => 0.0, 'date' => null, 'source' => 'none']; } $balance = round((float) (clone $snapshot)->sum('balance'), 2); if ($start->gt($baselineMonth)) { $movement = $this->bills->table('ec_account_bills') ->where('company_id', (int) (company_id() ?: 1)) ->where('bill_type', 'vipshop') ->whereNull('deleted_at') ->where('payload->settle_period', '>=', '2026-07') ->where('payload->settle_period', '<', $start->format('Y-m')) ->when($store !== '', static fn ($query) => $query->where('store_name', $store)) ->selectRaw('COALESCE(SUM(income), 0) AS income, COALESCE(SUM(expense), 0) AS expense') ->first(); $balance = round( $balance + (float) ($movement->income ?? 0) - (float) ($movement->expense ?? 0), 2, ); } return [ 'balance' => $balance, 'date' => $start->subDay()->format('Y-m-d'), 'source' => $start->equalTo($baselineMonth) ? 'june_ending' : 'previous_period', ]; } private function monthStart(?string $value): ?CarbonImmutable { $date = trim((string) $value); if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) !== 1) { return null; } try { $parsed = CarbonImmutable::createFromFormat('!Y-m-d', $date); } catch (\Throwable) { return null; } return $parsed instanceof CarbonImmutable && $parsed->format('Y-m-d') === $date ? $parsed->startOfMonth() : null; } }