CaiWuXiTong/tests/Unit/EcommerceFinance/SupplierBookkeepingWorkOrderRepositoryTest.php

181 lines
9.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Unit\EcommerceFinance;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\EcommerceFinance\Repositories\SupplierBookkeepingWorkOrderRepository as Repository;
use Modules\EcommerceFinance\View\SupplierBookkeepingPageData;
use Tests\TestCase;
class SupplierBookkeepingWorkOrderRepositoryTest extends TestCase
{
private const REASON = Repository::FIELD_REASON;
protected function setUp(): void
{
parent::setUp();
config([
'database.connections.business_mirror' => ['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => ''],
'upstream_mirrors.connection' => 'business_mirror',
'upstream_mirrors.datasets' => [
Repository::DATASET => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'jst_workOrderInfo',
'target' => 'fixture_work_orders', 'keys' => ['Id'], 'watermark' => 'updateTime',
'indexes' => [['woTypeId', 'woNo', 'Id']],
],
],
]);
Cache::setDefaultDriver('array');
Cache::flush();
DB::purge('business_mirror');
$schema = Schema::connection('business_mirror');
$schema->create('business_mirror_table_states', function (Blueprint $table): void {
$table->string('dataset_key')->primary();
$table->string('status')->nullable();
$table->dateTime('initial_completed_at')->nullable();
$table->dateTime('last_succeeded_at')->nullable();
});
$schema->create('fixture_work_orders', function (Blueprint $table): void {
$table->integer('Id');
foreach (['woNo', 'woTypeId', 'status', 'createby', 'executors', 'created', 'updated', 'updateTime', 'businessFieldList'] as $column) {
$table->text($column)->nullable();
}
});
}
private function state(?string $initialCompletedAt = '2026-10-01 00:00:00', string $status = 'succeeded'): void
{
DB::connection('business_mirror')->table('business_mirror_table_states')->updateOrInsert(
['dataset_key' => Repository::DATASET],
['status' => $status, 'initial_completed_at' => $initialCompletedAt, 'last_succeeded_at' => '2026-10-18 21:00:00'],
);
}
/** @param array<string,mixed> $fields */
private function order(int $id, string $woNo, array $fields, array $patch = [], int $jsonFlags = JSON_UNESCAPED_UNICODE): void
{
$list = [];
foreach ($fields as $fieldId => $value) {
$list[] = is_array($value) && array_key_exists('__raw', $value)
? ['fieldId' => $fieldId] + $value['__raw']
: ['fieldId' => $fieldId, 'value' => $value, 'valueLabel' => null];
}
DB::connection('business_mirror')->table('fixture_work_orders')->insert(array_replace([
'Id' => $id, 'woNo' => $woNo, 'woTypeId' => Repository::WORK_TYPE_ID, 'status' => 'WAIT_CONFIRM',
'createby' => '发起人甲', 'executors' => '简尚', 'created' => '2026-09-05 10:00:00',
'updated' => '2026-09-06 10:00:00', 'updateTime' => '2026-09-06 10:00:00',
'businessFieldList' => json_encode($list, $jsonFlags),
], $patch));
}
private function repository(): Repository
{
return $this->app->make(Repository::class);
}
public function test_reports_pending_until_initial_sync_and_refuses_empty_reads(): void
{
$this->assertSame('unavailable', $this->repository()->syncStatus()['code']);
$this->state(null, 'running');
$this->assertSame('initializing', $this->repository()->syncStatus()['code']);
$this->expectExceptionMessage('business_mirror_dataset_not_ready');
$this->repository()->rows();
}
public function test_applies_confirmed_scope_latest_version_and_parses_fields(): void
{
$this->state();
$base = [
self::REASON => '厂家记账/板材爆口/掉漆/开裂/发霉/色差',
'SELECTField1413856620250906100658' => '补偿款项',
'NUMBERField1413856620250906100530' => '120.5',
'orderField' => '6918600558615604325',
'shopField' => ['__raw' => ['value' => 18533663, 'valueLabel' => '抖音-艺煦白墙专卖店']],
'attachmentField' => ['https://img.example/a.png', 'https://img.example/b.png'],
'itemInfo' => ['__raw' => ['value' => null, 'childFieldList' => [
[['fieldId' => 'skuId', 'value' => 'CBG007215N16V01'], ['fieldId' => 'iid', 'value' => 'CBG0072'], ['fieldId' => 'supplierName', 'value' => '闻其'], ['fieldId' => 'qty', 'value' => 1]],
[['fieldId' => 'skuId', 'value' => 'CBG007216N16V01'], ['fieldId' => 'iid', 'value' => 'CBG0072'], ['fieldId' => 'supplierName', 'value' => '闻其'], ['fieldId' => 'qty', 'value' => 2]],
]]],
];
// 同一工单两版:只取最新一版(金额被修改为 130)。
$this->order(1, 'GD-A', $base);
$this->order(9, 'GD-A', array_replace($base, ['NUMBERField1413856620250906100530' => '130']), ['status' => 'CONFIRMING']);
// 仓库记账:数组形式的级联值 + \u 转义 JSON,API 形式的商品明细。
$this->order(2, 'GD-B', [
self::REASON => ['仓库记账', '退货运费'],
'NUMBERField1413856620250906100530' => '10.05',
'itemInfo' => [['skuId' => 'SKU-B', 'supplierName' => '聚尚', 'priceCount' => 99.5]],
], ['created' => '2026-09-01 00:00:00'], 0);
// 以下均不应接入。
$this->order(3, 'GD-C', [self::REASON => '买家原因/原单退回(导致退货,退回财务改记账)']);
$this->order(4, 'GD-D', $base, ['created' => '2026-08-31 23:59:59']);
$this->order(5, 'GD-E', $base);
$this->order(6, 'GD-E', $base, ['status' => 'CANCELED']);
$this->order(7, 'GD-F', $base, ['woTypeId' => 'OTHER']);
$this->order(8, 'GD-G', [self::REASON => '厂家记账/未勾选的新原因']);
$this->order(12, 'GD-I', $base, ['status' => '已取消']);
$this->order(13, 'GD-J', $base, ['status' => '已完成', 'created' => '2026-09-02 00:00:00']);
$this->order(10, 'GD-H', $base);
$this->order(11, 'GD-H', array_replace($base, [self::REASON => '快递记账/原单退回']));
$rows = $this->repository()->rows();
$this->assertSame(['GD-A', 'GD-J', 'GD-B'], array_column($rows, 'work_order_no'));
[$a, $j, $b] = $rows;
$this->assertSame('已完成', $j['status']);
$this->assertSame('130.00', $a['amount']);
$this->assertSame('处理中', $a['status']);
$this->assertSame('厂家记账', $a['reason_level_1']);
$this->assertSame('板材爆口/掉漆/开裂/发霉/色差', $a['reason_level_2']);
$this->assertSame('补偿款项', $a['bookkeeping_type']);
$this->assertSame('抖音-艺煦白墙专卖店', $a['shop_name']);
$this->assertSame("CBG007215N16V01\nCBG007216N16V01", $a['product_code']);
$this->assertSame("1\n2", $a['product_quantity']);
$this->assertSame(2, $a['attachment_count']);
$this->assertSame('6918600558615604325', $a['online_order_no']);
$this->assertSame('仓库记账/退货运费', $b['bookkeeping_reason']);
$this->assertSame('聚尚', $b['supplier']);
$this->assertSame('99.5', $b['product_amount']);
$this->assertSame('待处理', $b['status']);
$this->assertCount(17, Repository::reasonPaths());
}
public function test_page_filters_and_totals_use_cents(): void
{
$rows = [
['started_at' => '2026-09-02 08:00:00', 'bookkeeping_reason' => '厂家记账/缺货损失', 'reason_level_1' => '厂家记账', 'status' => '待处理', 'bookkeeping_type' => '记账全款', 'amount' => '0.10', 'work_order_no' => 'GD-1', 'online_order_no' => '', 'tracking_no' => '', 'shop_name' => '天猫-亿家达旗舰店', 'supplier' => '彩虹', 'product_code' => 'DNZ01'],
['started_at' => '2026-09-20 08:00:00', 'bookkeeping_reason' => '仓库记账/退货运费', 'reason_level_1' => '仓库记账', 'status' => '已完成', 'bookkeeping_type' => '运费损耗', 'amount' => '0.20', 'work_order_no' => 'GD-2', 'online_order_no' => '', 'tracking_no' => '', 'shop_name' => '拼多多-宜自然', 'supplier' => '胜友', 'product_code' => 'XG01'],
];
$page = $this->app->make(SupplierBookkeepingPageData::class);
$filters = ['started_from' => '', 'started_to' => '', 'reason' => '', 'status' => '', 'bookkeeping_type' => ''] + array_fill_keys(array_keys(SupplierBookkeepingPageData::TEXT_FILTERS), '');
$all = iterator_to_array($page->rows($filters, $rows), false);
$this->assertSame(30, array_sum(array_map([SupplierBookkeepingPageData::class, 'cents'], array_column($all, 'amount'))));
$this->assertSame(['GD-1'], array_column(iterator_to_array($page->rows(['reason' => '厂家记账'] + $filters, $rows), false), 'work_order_no'));
$this->assertSame(['GD-2'], array_column(iterator_to_array($page->rows(['started_from' => '2026-09-10', 'supplier' => '胜'] + $filters, $rows), false), 'work_order_no'));
$this->assertSame([], iterator_to_array($page->rows(['started_to' => '2026-09-01'] + $filters, $rows), false));
}
public function test_keeps_item_alignment_when_a_buyer_paid_amount_is_missing(): void
{
$this->state();
$this->order(1, 'GD-MULTI', [
self::REASON => '厂家记账/缺货损失',
'itemInfo' => [
['skuId' => 'SKU-A', 'qty' => 1, 'buyerPaidAmount' => 100],
['skuId' => 'SKU-B', 'qty' => 2],
],
]);
$row = $this->repository()->rows()[0];
$this->assertSame("100\n", $row['buyer_paid_amount']);
$this->assertSame("SKU-A\nSKU-B", $row['product_code']);
}
}