- 新增配置表 business_supplier_bookkeeping_configs(+ 操作日志表),迁移时导入财务提供的 209 家存量配置 - 记账方式:正常记账 / 仅代发仓发错货记账 / 不记账,支持生效期;配置页支持新增、编辑、删除恢复、Excel 导入导出 - 扣款判定:厂家记账按供应商配置,仅正常记账扣款;仓库记账仅订单发货仓库为代发仓且二级原因含“仓库发错货”时作为厂家扣款项,其余不扣 - 未配置/生效期外显示待配置,多供应商判定不一致显示待确认,均不计入扣款金额 - 明细页新增厂家扣款判定筛选、扣款工单数与扣款金额指标、导出列 涉及:数据库迁移(新建配置表并写入存量配置)、金额汇总展示;不涉及上游写入和权限模型。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JouFR6TJ7vGscRJanYScEo
87 lines
5.0 KiB
PHP
87 lines
5.0 KiB
PHP
<?php
|
||
|
||
namespace Tests\Unit\EcommerceFinance;
|
||
|
||
use InvalidArgumentException;
|
||
use Modules\EcommerceFinance\Services\SupplierBookkeepingDeductionRule as Rule;
|
||
use PHPUnit\Framework\TestCase;
|
||
|
||
require_once dirname(__DIR__, 3) . '/modules/EcommerceFinance/Services/SupplierBookkeepingDeductionRule.php';
|
||
|
||
class SupplierBookkeepingDeductionRuleTest extends TestCase
|
||
{
|
||
public function test_parses_every_rule_text_in_finance_sheet(): void
|
||
{
|
||
$this->assertSame(['mode' => Rule::MODE_NONE, 'effective_from' => null, 'effective_to' => null], Rule::parseRuleText('不记账'));
|
||
$this->assertSame(Rule::MODE_NORMAL, Rule::parseRuleText('正常记账')['mode']);
|
||
$this->assertSame(Rule::MODE_DROPSHIP_WRONG, Rule::parseRuleText('代发仓发货错需记账,其他原因均不记账')['mode']);
|
||
$this->assertSame(Rule::MODE_DROPSHIP_WRONG, Rule::parseRuleText('厂家暂未出货,后期若建立代发仓,发货错需记账,其他原因均不记账')['mode']);
|
||
$this->assertSame(['mode' => Rule::MODE_NONE, 'effective_from' => '2025-03-01', 'effective_to' => '2026-02-28'], Rule::parseRuleText('不记账(2025.3月-2026.2月)'));
|
||
$this->assertSame(['mode' => Rule::MODE_NONE, 'effective_from' => '2025-03-01', 'effective_to' => '2027-12-30'], Rule::parseRuleText('不记账(2025.3月-2027.12.30)'));
|
||
$this->assertSame(Rule::MODE_DROPSHIP_WRONG, Rule::parseRuleText('仅代发仓发错货记账')['mode']);
|
||
}
|
||
|
||
public function test_rejects_unknown_rule_text(): void
|
||
{
|
||
$this->expectException(InvalidArgumentException::class);
|
||
Rule::parseRuleText('看情况');
|
||
}
|
||
|
||
private function row(array $patch = []): array
|
||
{
|
||
return array_replace([
|
||
'started_at' => '2026-09-05 10:00:00', 'reason_level_1' => '厂家记账', 'reason_level_2' => '孔位错误',
|
||
'shipping_warehouse' => '常州主仓', 'supplier' => '甲厂',
|
||
], $patch);
|
||
}
|
||
|
||
private function configs(): array
|
||
{
|
||
return [
|
||
'甲厂' => ['mode' => Rule::MODE_NORMAL, 'effective_from' => null, 'effective_to' => null],
|
||
'乙厂' => ['mode' => Rule::MODE_DROPSHIP_WRONG, 'effective_from' => null, 'effective_to' => null],
|
||
'丙厂' => ['mode' => Rule::MODE_NONE, 'effective_from' => null, 'effective_to' => null],
|
||
'丁厂' => ['mode' => Rule::MODE_NONE, 'effective_from' => '2025-03-01', 'effective_to' => '2026-02-28'],
|
||
];
|
||
}
|
||
|
||
private function judge(array $patch): string
|
||
{
|
||
return Rule::decide($this->row($patch), $this->configs())['deduction_status'];
|
||
}
|
||
|
||
public function test_supplier_reasons_follow_supplier_config(): void
|
||
{
|
||
$this->assertSame(Rule::STATUS_DEDUCT, $this->judge([]));
|
||
$this->assertSame(Rule::STATUS_SKIP, $this->judge(['supplier' => '乙厂']));
|
||
$this->assertSame(Rule::STATUS_SKIP, $this->judge(['supplier' => '丙厂']));
|
||
$this->assertSame(Rule::STATUS_UNCONFIGURED, $this->judge(['supplier' => '未知厂']));
|
||
$this->assertSame(Rule::STATUS_UNCONFIGURED, $this->judge(['supplier' => '']));
|
||
// 限期不记账:期外不自动推断,提示补配置。
|
||
$this->assertSame(Rule::STATUS_UNCONFIGURED, $this->judge(['supplier' => '丁厂']));
|
||
$this->assertSame(Rule::STATUS_SKIP, $this->judge(['supplier' => '丁厂', 'started_at' => '2026-02-28 23:00:00']));
|
||
}
|
||
|
||
public function test_warehouse_reasons_only_deduct_dropship_wrong_shipment(): void
|
||
{
|
||
$wrong = ['reason_level_1' => '仓库记账', 'reason_level_2' => '仓库发错货(面单照片,实物照片,条形码照片)', 'shipping_warehouse' => '曹县洪耀代发仓'];
|
||
$this->assertSame(Rule::STATUS_DEDUCT, $this->judge($wrong + ['supplier' => '乙厂']));
|
||
$this->assertSame(Rule::STATUS_DEDUCT, $this->judge($wrong));
|
||
$this->assertSame(Rule::STATUS_SKIP, $this->judge($wrong + ['supplier' => '丙厂']));
|
||
$this->assertSame(Rule::STATUS_UNCONFIGURED, $this->judge($wrong + ['supplier' => '未知厂']));
|
||
// 非代发仓、或代发仓的其他仓库记账原因:一律不作为厂家扣款项,无需配置。
|
||
$this->assertSame(Rule::STATUS_SKIP, $this->judge(['shipping_warehouse' => '常州主仓'] + $wrong + ['supplier' => '乙厂']));
|
||
$this->assertSame(Rule::STATUS_SKIP, $this->judge(['reason_level_2' => '仓库双面单'] + $wrong + ['supplier' => '乙厂']));
|
||
$this->assertSame(Rule::STATUS_SKIP, $this->judge(['reason_level_2' => '退货运费'] + $wrong + ['supplier' => '未知厂']));
|
||
}
|
||
|
||
public function test_multiple_suppliers_need_review_when_outcomes_differ(): void
|
||
{
|
||
$this->assertSame(Rule::STATUS_DEDUCT, $this->judge(['supplier' => "甲厂\n甲厂"]));
|
||
$result = Rule::decide($this->row(['supplier' => "甲厂\n丙厂"]), $this->configs());
|
||
$this->assertSame(Rule::STATUS_REVIEW, $result['deduction_status']);
|
||
$this->assertStringContainsString('甲厂:扣款', $result['deduction_basis']);
|
||
$this->assertSame("正常记账\n不记账", $result['supplier_config']);
|
||
}
|
||
}
|