CaiWuXiTong/tests/Unit/EcommerceFinance/JdPromotionAccountBalanceRepositoryTest.php
2026-09-14 11:40:06 +08:00

223 lines
9.7 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Carbon\CarbonImmutable;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\EcommerceFinance\Repositories\JdPromotionAccountBalanceRepository;
use RuntimeException;
use Tests\TestCase;
class JdPromotionAccountBalanceRepositoryTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
CarbonImmutable::setTestNow('2026-09-12 12:00:00');
config([
'database.connections.business_mirror' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
'upstream_mirrors.connection' => 'business_mirror',
'upstream_mirrors.datasets' => [
JdPromotionAccountBalanceRepository::DATASET_KEY => [
'source_key' => 'js_data_middle_platform',
'schema' => 'dbo',
'object' => 'jd_jzt_finance_records',
'target' => 'fixture_jd_jzt_finance_records',
'keys' => ['id'],
'watermark' => 'updated_at',
],
],
]);
DB::purge('business_mirror');
Schema::connection('business_mirror')->create('business_mirror_table_states', function (Blueprint $table): void {
$table->string('dataset_key')->primary();
$table->dateTime('initial_completed_at')->nullable();
});
Schema::connection('business_mirror')->create('fixture_jd_jzt_finance_records', function (Blueprint $table): void {
$table->integer('id');
$table->string('data_month')->nullable();
$table->string('shop_name')->nullable();
$table->string('in_out_type')->nullable();
$table->string('in_out_type_name')->nullable();
$table->decimal('amount', 18, 2)->nullable();
$table->decimal('balance_amount', 18, 2)->nullable();
$table->dateTime('settle_datetime')->nullable();
$table->date('show_date')->nullable();
$table->dateTime('updated_at')->nullable();
});
}
protected function tearDown(): void
{
CarbonImmutable::setTestNow();
DB::purge('business_mirror');
parent::tearDown();
}
public function test_it_calculates_completed_month_balances_from_jd_jzt_rows(): void
{
$this->markReady();
DB::connection('business_mirror')->table('fixture_jd_jzt_finance_records')->insert([
$this->row(1, '京东A店', '2026-05-31', '2026-05-31 23:59:00', 'IN', '入账', 1000, 1000),
$this->row(2, '京东A店', '2026-06-01', '2026-06-01 09:00:00', 'IN', '入账', 500, 1500),
$this->row(3, '京东A店', '2026-06-30', '2026-06-30 10:00:00', 'OUT', '出账', 100, 1400),
$this->row(4, '京东A店', '2026-06-30', '2026-06-30 20:00:00', 'OUT', '出账', 100, 1300),
$this->row(5, '京东B店', '2026-06-30', '2026-06-30 18:00:00', 'OUT', '出账', 25, 75),
$this->row(6, '京东A店', '2026-09-01', '2026-09-01 09:00:00', 'IN', '入账', 10, 1310),
]);
$repository = app(JdPromotionAccountBalanceRepository::class);
$result = $repository->paginateMonthlyBalances(null, '2026-06', 1, 25);
$this->assertTrue($repository->usesBusinessMirror());
$this->assertSame(['京东A店', '京东B店'], $repository->storeOptions());
$this->assertSame(2, $result['total']);
$this->assertSame([
'date' => '2026-06-30',
'store_name' => '京东A店',
'opening_balance' => 1000.0,
'income_total' => 500.0,
'expense_total' => 200.0,
'closing_balance' => 1300.0,
'system_closing_balance' => 1300.0,
'balance_difference' => 0.0,
'difference_reason' => '',
'verification_status' => '已核对',
'notes' => '',
], $result['rows'][0]);
$this->assertNull($result['rows'][1]['opening_balance']);
$this->assertNull($result['rows'][1]['closing_balance']);
$this->assertSame('缺少上月期末余额', $result['rows'][1]['difference_reason']);
$this->assertSame('待核对', $result['rows'][1]['verification_status']);
}
public function test_store_and_month_filters_share_the_same_monthly_result(): void
{
$this->markReady();
DB::connection('business_mirror')->table('fixture_jd_jzt_finance_records')->insert([
$this->row(1, '京东A店', '2026-05-31', '2026-05-31 23:59:00', 'IN', '入账', 100, 100),
$this->row(2, '京东A店', '2026-06-30', '2026-06-30 23:59:00', 'IN', '入账', 20, 120),
$this->row(3, '京东B店', '2026-06-30', '2026-06-30 23:59:00', 'IN', '入账', 30, 30),
]);
$result = app(JdPromotionAccountBalanceRepository::class)
->paginateMonthlyBalances('京东A店', '2026-06', 1, 25);
$this->assertSame(1, $result['total']);
$this->assertSame('京东A店', $result['rows'][0]['store_name']);
$this->assertSame(100.0, $result['rows'][0]['opening_balance']);
$this->assertSame(20.0, $result['rows'][0]['income_total']);
}
public function test_it_does_not_treat_non_month_end_balances_as_month_end_balances(): void
{
$this->markReady();
DB::connection('business_mirror')->table('fixture_jd_jzt_finance_records')->insert([
$this->row(1, '京东A店', '2026-05-30', '2026-05-30 23:59:00', 'IN', '入账', 100, 100),
$this->row(2, '京东A店', '2026-06-01', '2026-06-01 09:00:00', 'IN', '入账', 20, 120),
$this->row(3, '京东A店', '2026-06-29', '2026-06-29 23:59:00', 'OUT', '出账', 5, 115),
]);
$row = app(JdPromotionAccountBalanceRepository::class)
->paginateMonthlyBalances('京东A店', '2026-06', 1, 25)['rows'][0];
$this->assertNull($row['opening_balance']);
$this->assertNull($row['system_closing_balance']);
$this->assertNull($row['closing_balance']);
$this->assertNull($row['balance_difference']);
$this->assertSame('缺少期初余额和系统期末余额', $row['difference_reason']);
$this->assertSame('待核对', $row['verification_status']);
}
public function test_it_identifies_a_missing_current_month_end_balance(): void
{
$this->markReady();
DB::connection('business_mirror')->table('fixture_jd_jzt_finance_records')->insert([
$this->row(1, '京东A店', '2026-05-31', '2026-05-31 23:59:00', 'IN', '入账', 100, 100),
$this->row(2, '京东A店', '2026-06-01', '2026-06-01 09:00:00', 'IN', '入账', 20, 120),
$this->row(3, '京东A店', '2026-06-29', '2026-06-29 23:59:00', 'OUT', '出账', 5, 115),
]);
$row = app(JdPromotionAccountBalanceRepository::class)
->paginateMonthlyBalances('京东A店', '2026-06', 1, 25)['rows'][0];
$this->assertSame(100.0, $row['opening_balance']);
$this->assertSame(115.0, $row['closing_balance']);
$this->assertNull($row['system_closing_balance']);
$this->assertNull($row['balance_difference']);
$this->assertSame('缺少本月系统期末余额', $row['difference_reason']);
$this->assertSame('待核对', $row['verification_status']);
}
public function test_it_explains_a_non_zero_balance_difference(): void
{
$this->markReady();
DB::connection('business_mirror')->table('fixture_jd_jzt_finance_records')->insert([
$this->row(1, '京东A店', '2026-05-31', '2026-05-31 23:59:00', 'IN', '入账', 100, 100),
$this->row(2, '京东A店', '2026-06-30', '2026-06-30 23:59:00', 'IN', '入账', 20, 125),
]);
$row = app(JdPromotionAccountBalanceRepository::class)
->paginateMonthlyBalances('京东A店', '2026-06', 1, 25)['rows'][0];
$this->assertSame(120.0, $row['closing_balance']);
$this->assertSame(125.0, $row['system_closing_balance']);
$this->assertSame(5.0, $row['balance_difference']);
$this->assertSame('系统期末余额与计算期末余额相差 5.00 元', $row['difference_reason']);
$this->assertSame('待核对', $row['verification_status']);
}
public function test_it_rejects_an_incomplete_shared_mirror(): void
{
$repository = app(JdPromotionAccountBalanceRepository::class);
$this->assertFalse($repository->usesBusinessMirror());
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('京东账户余额共享业务镜像尚未完成同步。');
$repository->paginateMonthlyBalances(null, '2026-06', 1, 25);
}
private function markReady(): void
{
DB::connection('business_mirror')->table('business_mirror_table_states')->insert([
'dataset_key' => JdPromotionAccountBalanceRepository::DATASET_KEY,
'initial_completed_at' => '2026-09-12 10:00:00',
]);
}
/** @return array<string,mixed> */
private function row(
int $id,
string $shop,
string $date,
string $settledAt,
string $type,
string $typeName,
float $amount,
float $balance,
): array {
return [
'id' => $id,
'data_month' => substr($date, 0, 7),
'shop_name' => $shop,
'in_out_type' => $type,
'in_out_type_name' => $typeName,
'amount' => $amount,
'balance_amount' => $balance,
'settle_datetime' => $settledAt,
'show_date' => $date,
'updated_at' => $settledAt,
];
}
}