CaiWuXiTong/tests/Unit/EcommerceFinance/PinduoduoSettlementAccountBillsRepositoryTest.php
qiuxiang 55f3e7d10f feat: 拼多多账户按平台余额滚动计算期初期末
以6月平台实际期末余额为基准,按财务系统每月收支滚动计算期初余额;期末余额=期初余额+收入合计-支出合计。
2026-11-06 11:37:23 +08:00

224 lines
9.9 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\EcommerceFinance\Repositories\JushuitanShopBalanceRepository;
use Modules\EcommerceFinance\Repositories\PinduoduoSettlementBillRepository;
use RuntimeException;
use Tests\TestCase;
class PinduoduoSettlementAccountBillsRepositoryTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
config([
'database.connections.business_mirror' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
'upstream_mirrors.connection' => 'business_mirror',
]);
DB::purge('business_mirror');
$this->createTables();
}
public function test_account_bill_page_reads_authoritative_rows_with_filters_and_totals(): void
{
$this->insertSucceededImport();
$this->insertRow('REC-INCOME', '拼多多一店', '交易收款', '2026-08-01 00:00:00', '100.00', '0.00', '100.00', 'M-100', 'O-100');
$this->insertRow('REC-EXPENSE', '拼多多一店', '退款', '2026-08-31 23:59:59', '0.00', '20.00', '-20.00', 'M-200', 'O-200');
$this->insertRow('REC-OTHER', '拼多多二店', '交易收款', '2026-08-15 12:00:00', '50.00', '0.00', '50.00', 'M-300', 'O-300');
$result = app(PinduoduoSettlementBillRepository::class)->paginateAccountBills([
'store_name' => '拼多多一店',
'start' => '2026-08-01',
'end' => '2026-08-31',
], 'occurred_at', 'desc', 1, 10);
$this->assertSame(2, $result['total']);
$this->assertSame(100.0, $result['summary']['income']);
$this->assertSame(20.0, $result['summary']['expense']);
$this->assertSame(0.0, $result['summary']['opening_balance']);
$this->assertSame(80.0, $result['summary']['balance']);
$this->assertSame('REC-EXPENSE', $result['data'][0]['record_code']);
$this->assertSame('M-200', $result['data'][0]['order_no']);
$this->assertSame('O-200', $result['data'][0]['payload']['online_order_no']);
$this->assertSame('拼多多货款账户', $result['data'][0]['payload']['type']);
$this->assertSame(20.0, $result['data'][0]['amount']);
$this->assertSame(-20.0, $result['data'][0]['balance']);
$filtered = app(PinduoduoSettlementBillRepository::class)->paginateAccountBills([
'bill_item' => '退款',
'keyword' => 'O-200',
], 'record_code', 'asc', 1, 10);
$this->assertSame(1, $filtered['total']);
$this->assertSame('REC-EXPENSE', $filtered['data'][0]['record_code']);
$options = app(PinduoduoSettlementBillRepository::class)->accountBillFilterOptions();
$this->assertSame(['拼多多一店', '拼多多二店'], $options['shops']);
$this->assertSame(['交易收款', '退款'], $options['bill_items']);
$export = iterator_to_array(app(PinduoduoSettlementBillRepository::class)->exportAccountBills([
'bill_item' => '退款',
], 'record_code', 'asc'));
$this->assertCount(1, $export);
$this->assertSame('REC-EXPENSE', $export[0]['record_code']);
}
public function test_platform_balance_rolls_forward_from_june_into_later_months(): void
{
$this->insertSucceededImport();
$this->insertBalanceSnapshot('拼多多-一店', '100.00');
$this->insertBalanceSnapshot('拼多多-二店', '50.00');
$this->insertBalanceSnapshot('拼多多-平台独有店', '20.00');
$this->insertRow('JUL-A-IN', '拼多多-一店', '交易收款', '2026-07-10 10:00:00', '30.00', '0.00', '30.00', 'M-1', 'O-1');
$this->insertRow('JUL-A-OUT', '拼多多-一店', '退款', '2026-07-11 10:00:00', '0.00', '5.00', '-5.00', 'M-2', 'O-2');
$this->insertRow('JUL-B-IN', '拼多多-二店', '交易收款', '2026-07-12 10:00:00', '8.00', '0.00', '8.00', 'M-3', 'O-3');
$this->insertRow('AUG-A-IN', '拼多多-一店', '交易收款', '2026-08-10 10:00:00', '10.00', '0.00', '10.00', 'M-4', 'O-4');
$repository = app(PinduoduoSettlementBillRepository::class);
$july = $repository->paginateAccountBills([
'start' => '2026-07-01',
'end' => '2026-07-31',
], 'occurred_at', 'desc', 1, 10);
$august = $repository->paginateAccountBills([
'start' => '2026-08-01',
'end' => '2026-08-31',
], 'occurred_at', 'desc', 1, 10);
$this->assertSame(170.0, $july['summary']['opening_balance']);
$this->assertSame('2026-06-30', $july['summary']['opening_balance_date']);
$this->assertSame('platform_snapshot', $july['summary']['opening_balance_source']);
$this->assertSame(38.0, $july['summary']['income']);
$this->assertSame(5.0, $july['summary']['expense']);
$this->assertSame(203.0, $july['summary']['balance']);
$this->assertSame($july['summary']['balance'], $august['summary']['opening_balance']);
$this->assertSame('2026-07-31', $august['summary']['opening_balance_date']);
$this->assertSame('previous_period', $august['summary']['opening_balance_source']);
$this->assertSame(213.0, $august['summary']['balance']);
$oneStore = $repository->paginateAccountBills([
'store_name' => '拼多多-一店',
'start' => '2026-08-01',
'end' => '2026-08-31',
], 'occurred_at', 'desc', 1, 10);
$this->assertSame(125.0, $oneStore['summary']['opening_balance']);
$this->assertSame(135.0, $oneStore['summary']['balance']);
}
public function test_account_bill_page_fails_closed_until_a_successful_import_exists(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('pinduoduo_authoritative_bills_not_ready');
app(PinduoduoSettlementBillRepository::class)->paginateAccountBills(
[],
'occurred_at',
'desc',
1,
10,
);
}
private function createTables(): void
{
$schema = Schema::connection('business_mirror');
$schema->create('pdd_settlement_bill_imports', function (Blueprint $table): void {
$table->uuid('generation')->primary();
$table->string('status', 32);
$table->timestamp('updated_at')->nullable();
});
$schema->create('pdd_settlement_bill_rows', function (Blueprint $table): void {
$table->string('record_code', 191)->primary();
$table->string('store_name')->nullable();
$table->string('custom_remark')->nullable();
$table->string('bill_item')->nullable();
$table->string('merchant_order_no', 191)->nullable();
$table->string('online_order_no', 191)->nullable();
$table->string('business_type')->nullable();
$table->string('business_description', 1000)->nullable();
$table->string('account_name')->nullable();
$table->dateTime('occurred_at');
$table->decimal('income_amount', 20, 2);
$table->decimal('expense_amount', 20, 2);
$table->decimal('signed_amount', 20, 2);
$table->string('remark', 1000)->nullable();
$table->dateTime('source_updated_at')->nullable();
$table->string('source_modified_by')->nullable();
});
$schema->create('pdd_platform_balance_snapshots', function (Blueprint $table): void {
$table->bigIncrements('id');
$table->date('period_end');
$table->string('store_name');
$table->string('normalized_store_name');
$table->string('account_name')->nullable();
$table->decimal('balance', 20, 6);
$table->string('source_filename');
$table->char('source_sha256', 64);
$table->dateTime('imported_at');
});
}
private function insertSucceededImport(): void
{
DB::connection('business_mirror')->table('pdd_settlement_bill_imports')->insert([
'generation' => '11111111-1111-1111-1111-111111111111',
'status' => 'succeeded',
]);
}
private function insertBalanceSnapshot(string $storeName, string $balance): void
{
DB::connection('business_mirror')->table('pdd_platform_balance_snapshots')->insert([
'period_end' => '2026-06-30',
'store_name' => $storeName,
'normalized_store_name' => JushuitanShopBalanceRepository::normalizeStoreName($storeName),
'account_name' => '拼多多货款账户',
'balance' => $balance,
'source_filename' => '拼多多6月平台期末余额.xlsx',
'source_sha256' => str_repeat('a', 64),
'imported_at' => '2026-11-06 10:00:00',
]);
}
private function insertRow(
string $recordCode,
string $storeName,
string $billItem,
string $occurredAt,
string $income,
string $expense,
string $signed,
string $merchantOrderNo,
string $onlineOrderNo,
): void {
DB::connection('business_mirror')->table('pdd_settlement_bill_rows')->insert([
'record_code' => $recordCode,
'store_name' => $storeName,
'custom_remark' => '自定义备注',
'bill_item' => $billItem,
'merchant_order_no' => $merchantOrderNo,
'online_order_no' => $onlineOrderNo,
'business_type' => '货款',
'business_description' => '业务描述',
'account_name' => '拼多多货款账户',
'occurred_at' => $occurredAt,
'income_amount' => $income,
'expense_amount' => $expense,
'signed_amount' => $signed,
'remark' => '账单备注',
'source_updated_at' => '2026-09-01 08:00:00',
'source_modified_by' => '聚胜算',
]);
}
}