109 lines
3.9 KiB
PHP
109 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Repositories;
|
|
|
|
use Carbon\CarbonImmutable;
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Database\DatabaseManager;
|
|
|
|
class VipshopJxOpeningBalanceRepository
|
|
{
|
|
public const TABLE = 'vipshop_jx_opening_balance_snapshots';
|
|
public const BASELINE_PERIOD_END = '2026-06-30';
|
|
|
|
private readonly ConnectionInterface $mirror;
|
|
private readonly ConnectionInterface $bills;
|
|
private readonly VipshopJxBankPaymentRepository $bankPayments;
|
|
|
|
public function __construct(
|
|
DatabaseManager $databases,
|
|
VipshopJxBankPaymentRepository $bankPayments,
|
|
)
|
|
{
|
|
$this->mirror = $databases->connection(
|
|
(string) config('upstream_mirrors.connection', 'business_mirror'),
|
|
);
|
|
$this->bills = $databases->connection();
|
|
$this->bankPayments = $bankPayments;
|
|
}
|
|
|
|
/** @return array{balance:float,date:?string,source:string,withdrawal_amount:float,withdrawal_count:int,withdrawal_latest_date:?string} */
|
|
public function openingBalance(?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 $this->none();
|
|
}
|
|
if (! $this->mirror->getSchemaBuilder()->hasTable(self::TABLE)) {
|
|
return $this->none();
|
|
}
|
|
|
|
$baseline = $this->mirror->table(self::TABLE)
|
|
->where('period_end', self::BASELINE_PERIOD_END)
|
|
->value('balance');
|
|
if ($baseline === null) {
|
|
return $this->none();
|
|
}
|
|
|
|
$withdrawnBefore = $this->bankPayments->netBefore($start->format('Y-m'));
|
|
if ($withdrawnBefore === null) {
|
|
return $this->none();
|
|
}
|
|
|
|
$balance = (float) $baseline - $withdrawnBefore;
|
|
if ($start->gt($baselineMonth)) {
|
|
$movement = $this->bills->table('ec_account_bills')
|
|
->where('company_id', (int) (company_id() ?: 1))
|
|
->where('bill_type', 'vipshop-jx')
|
|
->whereNull('deleted_at')
|
|
->where('payload->settle_period', '>=', '2026-07')
|
|
->where('payload->settle_period', '<', $start->format('Y-m'))
|
|
->selectRaw('COALESCE(SUM(income), 0) AS income, COALESCE(SUM(expense), 0) AS expense')
|
|
->first();
|
|
$balance += (float) ($movement->income ?? 0) - (float) ($movement->expense ?? 0);
|
|
}
|
|
$withdrawal = $this->bankPayments->forPeriod($start->format('Y-m'));
|
|
|
|
return [
|
|
'balance' => round($balance, 2),
|
|
'date' => $start->subDay()->format('Y-m-d'),
|
|
'source' => $start->equalTo($baselineMonth) ? 'bank_settled' : 'previous_period',
|
|
'withdrawal_amount' => $withdrawal['amount'],
|
|
'withdrawal_count' => $withdrawal['count'],
|
|
'withdrawal_latest_date' => $withdrawal['latest_pay_date'],
|
|
];
|
|
}
|
|
|
|
/** @return array{balance:float,date:null,source:string,withdrawal_amount:float,withdrawal_count:int,withdrawal_latest_date:null} */
|
|
private function none(): array
|
|
{
|
|
return [
|
|
'balance' => 0.0,
|
|
'date' => null,
|
|
'source' => 'none',
|
|
'withdrawal_amount' => 0.0,
|
|
'withdrawal_count' => 0,
|
|
'withdrawal_latest_date' => null,
|
|
];
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|