CaiWuXiTong/tests/Unit/EcommerceFinance/SupplierBookkeepingDeductionRuleTest.php
limeng 09b4ffcb8a 供应商应付-厂家记账:新增厂家记账配置并按配置判定厂家扣款
- 新增配置表 business_supplier_bookkeeping_configs(+ 操作日志表),迁移时导入财务提供的 209 家存量配置
- 记账方式:正常记账 / 仅代发仓发错货记账 / 不记账,支持生效期;配置页支持新增、编辑、删除恢复、Excel 导入导出
- 扣款判定:厂家记账按供应商配置,仅正常记账扣款;仓库记账仅订单发货仓库为代发仓且二级原因含“仓库发错货”时作为厂家扣款项,其余不扣
- 未配置/生效期外显示待配置,多供应商判定不一致显示待确认,均不计入扣款金额
- 明细页新增厂家扣款判定筛选、扣款工单数与扣款金额指标、导出列

涉及:数据库迁移(新建配置表并写入存量配置)、金额汇总展示;不涉及上游写入和权限模型。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JouFR6TJ7vGscRJanYScEo
2026-10-19 10:38:36 +08:00

87 lines
5.0 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 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']);
}
}