CaiWuXiTong/tests/Unit/EcommerceFinance/StoreReceiptMappingPreviewOverrideTest.php
yueyang f7197b4976 财务分析新增开票/不开票费用,并补齐违约赔付预览 (#480)
## 任务占用与边界

- 任务名称:财务分析开票费用与业绩明细违约赔付预览
- 实际开发者:yueyang
- 任务来源/团队分工:负责人直接指定
- 目标结果:财务经营分析表按科目映射表拆分开票费用/不开票费用;业绩明细增加违约赔付(冲减收入)列并按指定顺序展示。
- 预计修改路径:业务 Pages Controller、财务分析视图、账单明细数据服务、业绩明细配置与相关测试。
- 将写入的公共契约:财务分析费用聚合、默认补偿预览命令、业绩明细列配置。
- 仅复用、明确不修改的公共契约:store_receipt_order_match_rows、ec_account_mapping_rules。
- 数据源、表/视图和字段:店铺收款账单镜像、store_receipt_order_match_rows、store_receipt_product_cost_rows、ec_account_mapping_rules。
- 明确不修改的范围:账单抓取与上游同步链路。

## 开工碰撞检查

- [x] Codex 已执行 scripts/codex-task-preflight.ps1
- [x] 已检查最新 main 和远程任务分支
- [x] 已搜索主线既有页面、接口和数据服务
- [x] 已将写入和复用契约分开
- [x] 预检列出的残留分支均无开放 PR;当前 2 个开放 PR 与本任务无关,确认无活跃写入冲突

## 实际改动

- 财务经营分析表增加“开票费用”“不开票费用”及合计;财务类型=费用,票据口径 0.06/0.09 归入开票,不开票归入不开票,金额按支出减收入净额。
- 费用聚合并入现有月度缓存,缓存键包含科目映射哈希,映射变化后自动失效。
- 业绩明细增加违约赔付(冲减收入)列,放在冲减金额后。
- CSV 导出列同步更新。

## 验证证据

- 正常流程:2026-07/08/09 实库数据计算成功;2026-09 开票费用 3,896,538.65,不开票费用 81,377.62。
- 实际页面:2026-09 财务经营分析表 438 行,两列与合计正常展示。
- 性能:单月费用聚合 7.0–13.6 秒,PHP 峰值约 65.5 MB;页面结果缓存 5 分钟。
- 自动测试:NavigationTest 目标 2 条通过(35 assertions),StoreReceiptMappingPreviewOverrideTest 2 条通过(5 assertions)。

## 风险检查

- [x] 不涉及数据库迁移或生产数据写入
- [x] 涉及金额口径,已用实库页面和固定总额核对
- [x] 未提交 .env、密码、Token、真实数据或构建产物
- [x] 前端符合 DESIGN.md 黑金表格样式

## 合并前复查

- [x] 已基于最新 main 53bd1dc4 变基
- [x] 完整 diff 自审,无冲突标记
- [x] 目标测试通过
- [x] 负责人已授权 Squash merge 和发布

Reviewed-on: #480
2026-10-23 19:05:35 +08:00

48 lines
1.8 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use App\Console\Commands\SyncStoreReceiptOrderMatches;
use Modules\EcommerceFinance\Services\AccountLedgerRemarkMap;
use ReflectionClass;
use ReflectionMethod;
use Tests\TestCase;
class StoreReceiptMappingPreviewOverrideTest extends TestCase
{
public function test_default_compensation_is_included_in_offset_remarks(): void
{
$reflection = new ReflectionClass(SyncStoreReceiptOrderMatches::class);
$remarks = $reflection->getConstant('OFFSET_REMARKS');
$this->assertContains('冲减收入', $remarks);
$this->assertContains('违约赔付(冲减收入)', $remarks);
}
public function test_local_mapping_override_file_is_used_when_configured(): void
{
$path = tempnam(sys_get_temp_dir(), 'caiwu-mapping-override-');
$this->assertNotFalse($path);
file_put_contents($path, json_encode([
['platform' => '京东', 'item' => '结算单', 'memo' => '订单收入'],
['platform' => '京东', 'item' => '售后卖家赔付费', 'memo' => '违约赔付(冲减收入)'],
], JSON_UNESCAPED_UNICODE));
$previous = getenv('ACCOUNT_MAPPING_OVERRIDE_FILE');
putenv('ACCOUNT_MAPPING_OVERRIDE_FILE=' . $path);
try {
$method = new ReflectionMethod(AccountLedgerRemarkMap::class, 'accountMappingOverrideRules');
$rules = $method->invoke(new AccountLedgerRemarkMap());
$this->assertCount(2, $rules);
$this->assertSame('违约赔付(冲减收入)', $rules[1]['memo']);
} finally {
putenv($previous === false
? 'ACCOUNT_MAPPING_OVERRIDE_FILE'
: 'ACCOUNT_MAPPING_OVERRIDE_FILE=' . $previous);
@unlink($path);
}
}
}