[ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', ], 'upstream_mirrors.connection' => 'business_mirror', ]); DB::purge('business_mirror'); $this->createTables(); } public function test_july_opening_uses_the_june_ending_balance(): void { $this->insertOpening('淘系微信一店', '669385.40'); $result = app(TaobaoWechatOpeningBalanceRepository::class) ->openingBalance('淘系微信一店', '2026-07-01'); $this->assertSame(669385.4, $result['balance']); $this->assertSame('2026-06-30', $result['date']); $this->assertSame(TaobaoWechatBackofficeJuneEndingBalances::SOURCE, $result['source']); $this->assertSame(1, $result['store_count']); } public function test_later_periods_carry_the_opening_balance_forward(): void { $this->insertOpening('淘系微信一店', '100.00'); $this->insertBill('淘系微信一店', '2026-07-05 10:00:00', '50.00', '20.00'); $this->insertBill('淘系微信一店', '2026-08-02 10:00:00', '10.00', '5.00'); $result = app(TaobaoWechatOpeningBalanceRepository::class) ->openingBalance('淘系微信一店', '2026-08-01'); $this->assertSame(130.0, $result['balance']); $this->assertSame('2026-07-31', $result['date']); $this->assertSame( TaobaoWechatBackofficeJuneEndingBalances::SOURCE . '_carried_forward', $result['source'], ); } public function test_all_store_openings_are_summed_when_no_store_is_selected(): void { $this->insertOpening('淘系微信一店', '100.00'); $this->insertOpening('淘系微信二店', '200.25'); $result = app(TaobaoWechatOpeningBalanceRepository::class) ->openingBalance(null, '2026-07-01'); $this->assertSame(300.25, $result['balance']); $this->assertSame(2, $result['store_count']); } public function test_backoffice_dataset_contains_the_twenty_one_confirmed_store_balances(): void { $balances = TaobaoWechatBackofficeJuneEndingBalances::balances(); $totalCents = array_sum(array_map( static fn (string $balance): int => (int) round((float) $balance * 100), array_values($balances), )); $this->assertCount(21, $balances); $this->assertSame(81463042, $totalCents); $this->assertSame('27846.22', $balances['天猫-HX旗舰店101']); $this->assertSame('290881.84', $balances['天猫-亿家达旗舰店']); $this->assertSame('5155.88', $balances['天猫-亿家达宜彩专卖店']); $this->assertSame('86802.00', $balances['淘宝-全颐家具工厂店']); } public function test_missing_opening_balance_is_unavailable(): void { $result = app(TaobaoWechatOpeningBalanceRepository::class) ->openingBalance('未录入店铺', '2026-07-01'); $this->assertNull($result['balance']); $this->assertNull($result['date']); $this->assertSame('unavailable', $result['source']); $this->assertSame(0, $result['store_count']); } private function createTables(): void { $schema = Schema::connection('business_mirror'); $schema->create(TaobaoWechatOpeningBalanceRepository::TABLE, function (Blueprint $table): void { $table->id(); $table->date('balance_date'); $table->string('store_name', 191); $table->decimal('balance', 20, 2); $table->string('source', 191); $table->text('source_note')->nullable(); $table->timestamp('imported_at'); $table->timestamps(); $table->unique(['balance_date', 'store_name']); }); $schema->create('taobao_wechat_bill_rows', function (Blueprint $table): void { $table->string('record_code', 191)->primary(); $table->string('store_name')->nullable(); $table->dateTime('occurred_at'); $table->decimal('income_amount', 20, 2); $table->decimal('expense_amount', 20, 2); }); } private function insertOpening(string $storeName, string $balance): void { DB::connection('business_mirror')->table(TaobaoWechatOpeningBalanceRepository::TABLE)->insert([ 'balance_date' => '2026-06-30', 'store_name' => $storeName, 'balance' => $balance, 'source' => TaobaoWechatBackofficeJuneEndingBalances::SOURCE, 'source_note' => TaobaoWechatBackofficeJuneEndingBalances::SOURCE_NOTE, 'imported_at' => '2026-11-09 10:00:00', ]); } private function insertBill( string $storeName, string $occurredAt, string $income, string $expense, ): void { static $sequence = 0; $sequence++; DB::connection('business_mirror')->table('taobao_wechat_bill_rows')->insert([ 'record_code' => 'TEST-' . $sequence, 'store_name' => $storeName, 'occurred_at' => $occurredAt, 'income_amount' => $income, 'expense_amount' => $expense, ]); } }