204 lines
8.8 KiB
PHP
204 lines
8.8 KiB
PHP
<?php
|
||
|
||
namespace Tests\Unit\EcommerceFinance;
|
||
|
||
use Illuminate\Database\Schema\Blueprint;
|
||
use Illuminate\Contracts\Console\Kernel;
|
||
use Illuminate\Support\Facades\Cache;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Schema;
|
||
use Modules\EcommerceFinance\Repositories\TaobaoAlipaySettlementBillRepository;
|
||
use PHPUnit\Framework\TestCase;
|
||
|
||
class TaobaoAlipaySettlementBillRepositoryTest extends TestCase
|
||
{
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
$app = require dirname(__DIR__, 3) . '/bootstrap/app.php';
|
||
$app->make(Kernel::class)->bootstrap();
|
||
config([
|
||
'database.connections.business_mirror' => [
|
||
'driver' => 'sqlite',
|
||
'database' => ':memory:',
|
||
'prefix' => '',
|
||
],
|
||
'upstream_mirrors.connection' => 'business_mirror',
|
||
]);
|
||
DB::purge('business_mirror');
|
||
Cache::flush();
|
||
$this->createTables();
|
||
}
|
||
|
||
public function test_month_balance_uses_previous_account_balances_plus_month_flow(): void
|
||
{
|
||
$rows = DB::connection('business_mirror')->table('taobao_alipay_bill_rows');
|
||
$rows->insert([
|
||
$this->row('A-SEP-1', '天猫-A店', '20880001', '2026-09-30 23:58:00', '0', '0', '100', 10),
|
||
$this->row('A-SEP-2', '天猫-A店', '20880001', '2026-09-30 23:59:59', '0', '0', '150', 20),
|
||
// 同一时间的两笔按源表行号取后一笔。
|
||
$this->row('A-SEP-3', '天猫-A店', '20880001', '2026-09-30 23:59:59', '0', '0', '180', 30),
|
||
$this->row('B-SEP', '淘宝-B店', '20880002', '2026-09-29 18:00:00', '0', '0', '50', 40),
|
||
$this->row('A-OCT', '天猫-A店', '20880001', '2026-10-15 10:00:00', '40', '10', '210', 50),
|
||
$this->row('B-OCT', '淘宝-B店', '20880002', '2026-10-20 10:00:00', '20', '5', '65', 60),
|
||
]);
|
||
|
||
$result = app(TaobaoAlipaySettlementBillRepository::class)->paginate(
|
||
['start' => '2026-10-01', 'end' => '2026-10-31'],
|
||
'occurred_at',
|
||
'desc',
|
||
1,
|
||
20,
|
||
);
|
||
|
||
$this->assertSame(2, $result['summary']['count']);
|
||
$this->assertSame(60.0, $result['summary']['income']);
|
||
$this->assertSame(15.0, $result['summary']['expense']);
|
||
$this->assertSame(230.0, $result['summary']['opening_balance']);
|
||
$this->assertSame('2026-09-30', $result['summary']['opening_balance_date']);
|
||
$this->assertSame('previous_period', $result['summary']['opening_balance_source']);
|
||
$this->assertSame(275.0, $result['summary']['balance']);
|
||
$this->assertSame(
|
||
$result['summary'],
|
||
app(TaobaoAlipaySettlementBillRepository::class)->summary([
|
||
'start' => '2026-10-01',
|
||
'end' => '2026-10-31',
|
||
]),
|
||
);
|
||
}
|
||
|
||
public function test_month_balance_respects_store_filter(): void
|
||
{
|
||
$rows = DB::connection('business_mirror')->table('taobao_alipay_bill_rows');
|
||
$rows->insert([
|
||
$this->row('A-SEP', '天猫-A店', '20880001', '2026-09-30 23:59:59', '0', '0', '150', 10),
|
||
$this->row('B-SEP', '淘宝-B店', '20880002', '2026-09-30 23:59:59', '0', '0', '50', 20),
|
||
$this->row('A-OCT', '天猫-A店', '20880001', '2026-10-15 10:00:00', '40', '10', '180', 30),
|
||
$this->row('B-OCT', '淘宝-B店', '20880002', '2026-10-20 10:00:00', '20', '5', '65', 40),
|
||
]);
|
||
|
||
$result = app(TaobaoAlipaySettlementBillRepository::class)->paginate(
|
||
['store_name' => '天猫-A店', 'start' => '2026-10-01', 'end' => '2026-10-31'],
|
||
'occurred_at',
|
||
'desc',
|
||
1,
|
||
20,
|
||
);
|
||
|
||
$this->assertSame(1, $result['summary']['count']);
|
||
$this->assertSame(40.0, $result['summary']['income']);
|
||
$this->assertSame(10.0, $result['summary']['expense']);
|
||
$this->assertSame(150.0, $result['summary']['opening_balance']);
|
||
$this->assertSame(180.0, $result['summary']['balance']);
|
||
}
|
||
|
||
public function test_first_imported_month_infers_opening_balance_from_first_account_rows(): void
|
||
{
|
||
$rows = DB::connection('business_mirror')->table('taobao_alipay_bill_rows');
|
||
$rows->insert([
|
||
$this->row('A-JUL-1', '天猫-A店', 'account-a', '2026-07-01 09:00:00', 10, 0, 210, 10),
|
||
$this->row('A-JUL-2', '天猫-A店', 'account-a', '2026-07-02 09:00:00', 20, 5, 225, 20),
|
||
$this->row('B-JUL-1', '淘宝-B店', 'account-b', '2026-07-01 10:00:00', 0, 10, 90, 30),
|
||
$this->row('B-JUL-2', '淘宝-B店', 'account-b', '2026-07-03 10:00:00', 5, 0, 95, 40),
|
||
]);
|
||
|
||
$result = app(TaobaoAlipaySettlementBillRepository::class)->paginate(
|
||
['start' => '2026-07-01', 'end' => '2026-07-31'],
|
||
'occurred_at',
|
||
'desc',
|
||
1,
|
||
20,
|
||
);
|
||
|
||
// A 首笔倒推期初 200,B 首笔倒推期初 100。
|
||
$this->assertSame(300.0, $result['summary']['opening_balance']);
|
||
$this->assertSame('first_row_estimate', $result['summary']['opening_balance_source']);
|
||
$this->assertSame(35.0, $result['summary']['income']);
|
||
$this->assertSame(15.0, $result['summary']['expense']);
|
||
$this->assertSame(320.0, $result['summary']['balance']);
|
||
}
|
||
|
||
private function createTables(): void
|
||
{
|
||
$schema = Schema::connection('business_mirror');
|
||
$schema->create('taobao_alipay_bill_imports', function (Blueprint $table): void {
|
||
$table->uuid('generation');
|
||
$table->string('status');
|
||
$table->timestamp('completed_at')->nullable();
|
||
});
|
||
$schema->create('taobao_alipay_bill_rows', function (Blueprint $table): void {
|
||
$table->string('record_code')->primary();
|
||
$table->string('store_name')->nullable();
|
||
$table->string('custom_remark')->nullable();
|
||
$table->string('bill_item')->nullable();
|
||
$table->string('alipay_account')->nullable();
|
||
$table->string('account_order_no')->nullable();
|
||
$table->string('business_serial_no')->nullable();
|
||
$table->string('merchant_order_no')->nullable();
|
||
$table->string('business_base_order_no')->nullable();
|
||
$table->string('goods_name')->nullable();
|
||
$table->string('counterparty_account')->nullable();
|
||
$table->dateTime('occurred_at');
|
||
$table->decimal('income_amount', 20, 6)->default(0);
|
||
$table->decimal('expense_amount', 20, 6)->default(0);
|
||
$table->decimal('balance_amount', 20, 6)->default(0);
|
||
$table->string('business_type')->nullable();
|
||
$table->string('remark')->nullable();
|
||
$table->string('online_order_no')->nullable();
|
||
$table->string('online_real_store')->nullable();
|
||
$table->string('business_description')->nullable();
|
||
$table->string('business_bill_source')->nullable();
|
||
$table->string('business_order_no')->nullable();
|
||
$table->dateTime('source_created_at')->nullable();
|
||
$table->dateTime('source_updated_at')->nullable();
|
||
$table->string('source_modified_by')->nullable();
|
||
$table->unsignedBigInteger('source_row_number');
|
||
});
|
||
DB::connection('business_mirror')->table('taobao_alipay_bill_imports')->insert([
|
||
'generation' => 'test-generation',
|
||
'status' => 'succeeded',
|
||
'completed_at' => '2026-11-01 09:00:00',
|
||
]);
|
||
}
|
||
|
||
private function row(
|
||
string $recordCode,
|
||
string $storeName,
|
||
string $account,
|
||
string $occurredAt,
|
||
string $income,
|
||
string $expense,
|
||
string $balance,
|
||
int $sourceRowNumber,
|
||
): array {
|
||
return [
|
||
'record_code' => $recordCode,
|
||
'store_name' => $storeName,
|
||
'custom_remark' => null,
|
||
'bill_item' => '交易收款',
|
||
'alipay_account' => $account,
|
||
'account_order_no' => null,
|
||
'business_serial_no' => null,
|
||
'merchant_order_no' => null,
|
||
'business_base_order_no' => null,
|
||
'goods_name' => null,
|
||
'counterparty_account' => null,
|
||
'occurred_at' => $occurredAt,
|
||
'income_amount' => $income,
|
||
'expense_amount' => $expense,
|
||
'balance_amount' => $balance,
|
||
'business_type' => '交易付款',
|
||
'remark' => null,
|
||
'online_order_no' => null,
|
||
'online_real_store' => null,
|
||
'business_description' => null,
|
||
'business_bill_source' => '支付宝',
|
||
'business_order_no' => null,
|
||
'source_created_at' => null,
|
||
'source_updated_at' => null,
|
||
'source_modified_by' => null,
|
||
'source_row_number' => $sourceRowNumber,
|
||
];
|
||
}
|
||
}
|