138 lines
7.6 KiB
PHP
138 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\EcommerceFinance;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Modules\EcommerceFinance\AccountBillColumns;
|
|
use Modules\EcommerceFinance\Repositories\JdDailyBillRepository;
|
|
use Modules\EcommerceFinance\Services\JdDailyBillImporter;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class JdDailyBillTest extends TestCase
|
|
{
|
|
private array $files = [];
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$app = require dirname(__DIR__, 3) . '/bootstrap/app.php';
|
|
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
|
config(['database.connections.business_mirror' => ['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '']]);
|
|
DB::purge('business_mirror');
|
|
(require database_path('migrations/2026_09_18_140000_create_jd_daily_bill_mirror_tables.php'))->up();
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
foreach ($this->files as $file) unlink($file);
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function test_complete_month_preserves_identifiers_and_unifies_paging_filters_and_export(): void
|
|
{
|
|
$rows = [$this->row('2000000000000000001', '40.12'), $this->row('2000000000000000002', '0.00', '2.03')];
|
|
$this->import($rows, 2, '40.12', '2.03');
|
|
$repo = app(JdDailyBillRepository::class);
|
|
$filters = ['start' => '2026-08-01', 'end' => '2026-08-31'];
|
|
$first = $repo->paginate($filters, 'record_code', 'asc', 1, 1);
|
|
$second = $repo->paginate($filters, 'record_code', 'asc', 2, 1);
|
|
$this->assertSame(2, $first['total']);
|
|
$this->assertSame(40.12, $first['summary']['income']);
|
|
$this->assertSame(2.03, $first['summary']['expense']);
|
|
$this->assertSame(38.09, $first['summary']['balance']);
|
|
$this->assertSame('2000000000000000001', $first['data'][0]['record_code']);
|
|
$this->assertSame('10100126080000000000000001', $first['data'][0]['payload']['document_no']);
|
|
$this->assertSame('2026-07-31 10:00:00', $first['data'][0]['payload']['cost_happened_at']);
|
|
$this->assertSame([$first['data'][0], $second['data'][0]], iterator_to_array($repo->exportRows($filters, 'record_code', 'asc')));
|
|
$this->assertSame(1, $repo->paginate(['keyword' => '2000000000000000002'], 'occurred_at', 'asc', 1, 10)['total']);
|
|
$this->assertSame(0, $repo->paginate(['start' => '2026-09-01'], 'occurred_at', 'asc', 1, 10)['total']);
|
|
$this->assertSame(2, $repo->paginate(['store_name' => '测试京东店', 'bill_item' => '货款'], 'income', 'desc', 1, 10)['total']);
|
|
$this->assertSame(['测试京东店'], $repo->filterOptions()['shops']);
|
|
$this->assertSame(['2026-08'], $first['sync']['periods']);
|
|
$this->assertSame('京东账单', AccountBillColumns::for('jd-transactions')['title']);
|
|
$this->assertNotContains('balance', array_column(AccountBillColumns::for('jd-transactions')['columns'], 'field'));
|
|
}
|
|
|
|
public function test_invalid_import_does_not_replace_active_month(): void
|
|
{
|
|
$row = $this->row('2000000000000000001', '10.00');
|
|
$initial = $this->import([$row], 1, '10.00', '0.00');
|
|
$cases = [
|
|
[[$row], 2, '10.00', '0.00'], [[$row], 1, '11.00', '0.00'],
|
|
[[$row, $row], 2, '20.00', '0.00'],
|
|
[[array_replace($row, ['费用结算时间' => '2026-09-01 00:00:00'])], 1, '10.00', '0.00'],
|
|
[[array_replace($row, ['费用结算时间' => '2026-08-32 00:00:00'])], 1, '10.00', '0.00'],
|
|
[[array_replace($row, ['收入金额' => '1.005'])], 1, '1.00', '0.00'],
|
|
[[array_replace($row, ['币种' => 'USD'])], 1, '10.00', '0.00'],
|
|
[[array_replace($row, ['记录编码' => '2E+18'])], 1, '10.00', '0.00'],
|
|
];
|
|
foreach ($cases as $case) {
|
|
$caught = false;
|
|
try { $this->import(...$case); } catch (\InvalidArgumentException|\Illuminate\Database\QueryException) { $caught = true; }
|
|
$this->assertTrue($caught);
|
|
$this->assertSame($initial['active_generation'], DB::connection('business_mirror')->table('jd_daily_bill_periods')->value('active_generation'));
|
|
$this->assertSame(1, DB::connection('business_mirror')->table('jd_daily_bill_rows')->count());
|
|
}
|
|
}
|
|
|
|
public function test_reimport_is_atomic_and_keeps_old_version(): void
|
|
{
|
|
$first = $this->import([$this->row('2000000000000000001', '10.00')], 1, '10.00', '0.00');
|
|
$second = $this->import([$this->row('2000000000000000001', '12.00')], 1, '12.00', '0.00');
|
|
$this->assertNotSame($first['active_generation'], $second['active_generation']);
|
|
$this->assertSame(2, DB::connection('business_mirror')->table('jd_daily_bill_rows')->count());
|
|
$this->assertSame(12.0, app(JdDailyBillRepository::class)->paginate([], 'occurred_at', 'asc', 1, 10)['summary']['income']);
|
|
}
|
|
|
|
public function test_uninitialized_mirror_is_not_reported_as_empty_success(): void
|
|
{
|
|
$this->expectExceptionMessage('jd_daily_bill_mirror_not_ready');
|
|
app(JdDailyBillRepository::class)->paginate([], 'occurred_at', 'asc', 1, 10);
|
|
}
|
|
|
|
public function test_cutoff_validates_rows_and_refuses_to_shorten_existing_coverage(): void
|
|
{
|
|
$row = array_replace($this->row('123', '10.00'), ['费用结算时间' => '2026-08-21 23:59:59']);
|
|
$initial = $this->import([$row], 1, '10.00', '0.00', '2026-08-21');
|
|
$this->assertSame('2026-08-22', $initial['period_end']);
|
|
foreach (['2026-08-20', '2026-09-21', '2026-08-32'] as $cutoff) {
|
|
try {
|
|
$this->import([$row], 1, '10.00', '0.00', $cutoff);
|
|
$this->fail('Invalid cutoff accepted');
|
|
} catch (\InvalidArgumentException $e) {
|
|
$this->assertSame($initial['active_generation'], DB::connection('business_mirror')->table('jd_daily_bill_periods')->value('active_generation'));
|
|
}
|
|
}
|
|
$full = $this->import([$row, $this->row('124', '2.00')], 2, '12.00', '0.00');
|
|
try {
|
|
$this->import([$row], 1, '10.00', '0.00', '2026-08-21');
|
|
$this->fail('Existing later records would be lost');
|
|
} catch (\InvalidArgumentException $e) {
|
|
$repo = app(JdDailyBillRepository::class);
|
|
$this->assertSame(2, $repo->paginate([], 'occurred_at', 'asc', 1, 10)['total']);
|
|
$this->assertSame(2.0, $repo->paginate(['start' => '2026-08-22'], 'occurred_at', 'asc', 1, 10)['summary']['income']);
|
|
$this->assertSame($full['active_generation'], DB::connection('business_mirror')->table('jd_daily_bill_periods')->value('active_generation'));
|
|
}
|
|
}
|
|
|
|
private function import(array $rows, int $count, string $income, string $expense, ?string $through = null): array
|
|
{
|
|
$path = tempnam(sys_get_temp_dir(), 'jd-daily-');
|
|
$this->files[] = $path;
|
|
$file = fopen($path, 'wb');
|
|
fputcsv($file, array_keys($rows[0]));
|
|
foreach ($rows as $row) fputcsv($file, array_values($row));
|
|
fclose($file);
|
|
return app(JdDailyBillImporter::class)->import($path, '2026-08', $count, $income, $expense, $through);
|
|
}
|
|
|
|
private function row(string $code, string $income, string $expense = '0.00'): array
|
|
{
|
|
return array_replace(array_fill_keys(array_values(JdDailyBillImporter::PAYLOAD_FIELDS), ''), [
|
|
'记录编码' => $code, '店铺' => '测试京东店', '账单项目' => '货款', '订单编号' => '3606404000000001',
|
|
'单据编号' => '10100126080000000000000001', '费用结算时间' => '2026-08-31 23:59:59',
|
|
'费用发生时间' => '2026-07-31 10:00:00', '收入金额' => $income, '支出金额' => $expense, '币种' => 'CNY',
|
|
]);
|
|
}
|
|
}
|