CaiWuXiTong/tests/Feature/Business/PlantingReimbursementWorkbenchTest.php
limeng 951e553fae feat(种菜报销): 新增种菜报销工作台,逐单校验并生成打款明细与钉钉流程明细 (#1140)
- 种菜数据汇总新增「种菜报销工作台」:按聚工单锁单口径取单(待处理 WAIT_CONFIRM、付款 D-7~D-1、ERP订单状态非取消/删除/空、售后单号为空;可切换处理中 CONFIRMING 和付款起始日),逐单校验重复报销、金额公式、佣金资格、收款人在职、以旧换新买家实付(取买家实付/看板订单实付中最低,查不到标签的待复查)、看板超额(同日按付款先后报到日限额)与看板未匹配
- 导出前须勾选「已在聚工单核对条数和金额一致」;页面给出应核对的条数/金额、各口径合计自检,聚工单镜像停更超过 20 分钟时提示先别报销
- 页面给出可报销合计(三大天猫/其他)、钉钉「种菜报销流程」明细行、按发起人的发群通知与取消清单
- 导出种菜对账、种菜报销明细(打款卡号明细表)、聚工单种菜任务工单导入模版,格式对齐手工文件
- 财务可上传种菜监控看板、报销佣金人员、花名册在职、店铺信息;名单存 storage 持久卷 JSON,花名册只存姓名
- 聚工单镜像读取补充 ERP订单标签、待报销工单和已报销订单查询
涉及:只读聚工单镜像;不涉及迁移、上游写入、钉钉和聚工单写操作。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VANWYsNUbu2piMtCKymoyd
2026-11-12 18:54:21 +08:00

118 lines
7.1 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\Feature\Business;
use App\Models\Auth\Permission;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Modules\EcommerceFinance\Repositories\PlantingReimbursementRepository;
use Modules\EcommerceFinance\Services\PlantingReimbursementReferences;
use Tests\Feature\FeatureTestCase;
class PlantingReimbursementWorkbenchTest extends FeatureTestCase
{
private function row(string $orderNo, string $amount, array $patch = []): array
{
return array_replace([
'work_order_no' => 'GD' . $orderNo, 'online_order_no' => $orderNo, 'status' => '待处理', 'paid_at' => '2026-11-11 10:00:00',
'started_at' => '2026-11-11 12:00:00', 'completed_at' => '', 'initiator' => '白菜', 'executor' => '李斯', 'completer' => '',
'platform' => '天猫', 'shop_name' => '天猫-欧意朗旗舰店', 'internal_order_no' => '1', 'task_remark' => '', 'seller_remark' => 'CCDD',
'actual_paid_amount' => $amount, 'task_payment' => $amount, 'task_freight' => '0.00', 'task_commission' => '0.00',
'jingxi_price_diff' => '0.00', 'task_deduction' => '0.00', 'task_empty_package_fee' => '0.00', 'reimbursement_amount' => $amount,
'payee_name' => '姚佳炜', 'payee_account' => '6228760405006454377', 'payee_bank' => '农业银行', 'payee_mobile' => '13800000000',
'erp_order_label' => '工单同步', 'erp_order_status' => '已发货', 'platform_order_status' => '', 'product_code' => '',
'product_name' => '', 'product_quantity' => '', 'platform_product_id' => '',
], $patch);
}
private function mockRepository(array $rows): void
{
$repository = \Mockery::mock(PlantingReimbursementRepository::class);
$repository->shouldReceive('syncStatus')->andReturn(['code' => 'ready', 'last_succeeded_at' => '2026-11-12 12:57:00']);
$repository->shouldReceive('pendingRows')->with('2026-11-05 00:00:00', '2026-11-11 23:59:59', ['WAIT_CONFIRM'])->andReturn($rows);
$repository->shouldReceive('reimbursedOrders')->andReturn(['900' => '2026-11-03']);
$this->app->instance(PlantingReimbursementRepository::class, $repository);
Storage::fake('local');
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
}
private function upload(string $kind, string $name, string $content)
{
return $this->loginAs()->post(route('business.planting-data.planting-reimbursement-workbench.upload', [
'company_id' => $this->company->id, 'kind' => $kind,
]), ['date' => '2026-11-12', 'file' => UploadedFile::fake()->createWithContent($name, $content)]);
}
private function url(array $query = []): string
{
return route('business.pages.show', ['company_id' => $this->company->id, 'section' => 'planting-data', 'page' => 'planting-reimbursement-workbench'])
. ($query ? '?' . http_build_query($query) : '');
}
public function test_workbench_audits_pending_rows_and_exports(): void
{
$this->mockRepository([
$this->row('101', '60.00'),
$this->row('102', '40.50', ['shop_name' => '京喜-星光闪闪京喜自营工厂店', 'initiator' => '星璃', 'payee_name' => '王敏佳']),
$this->row('900', '10.00', ['initiator' => '星璃']),
$this->row('103', '20.00', ['payee_name' => '离职的人']),
$this->row('104', '30.00'),
]);
$this->upload('commission', '报销佣金人员.csv', "平台,人员,佣金,人员,平台\n天猫,王也,10元,白菜,天猫\n")->assertRedirect();
$this->upload('roster', '花名册.csv', "序号,花名,姓名\n1,白,姚佳炜\n2,星,王敏佳\n")->assertRedirect();
$this->upload('shop', '店铺信息.csv', "开店时间,ERP系统店铺名称,店铺所属利润中心\n2021-01-01,天猫-欧意朗旗舰店,运营中心A\n2021-01-01,京喜-星光闪闪京喜自营工厂店,运营中心A\n")->assertRedirect();
// 看板是 GBK 编码导出;104 没匹配到,应待复查。
$monitor = mb_convert_encoding("订单号,风险,提示,风险类型,ID负责人,日限额,支付时间\n101,正常,,,白菜,,2026/11/11 10:00\n102,正常,,,星璃,,2026/11/11 10:00\n900,正常,,,星璃,,2026/11/11 10:00\n103,正常,,,白菜,,2026/11/11 10:00\n", 'GB18030', 'UTF-8');
$this->upload('monitor', '种菜报销明细.csv', $monitor)->assertRedirect()->assertSessionHas('success');
$this->assertSame(['白菜' => true], app(PlantingReimbursementReferences::class)->nameSet($this->company->id, 'commission'));
$this->assertSame(['姚佳炜' => true, '王敏佳' => true], app(PlantingReimbursementReferences::class)->nameSet($this->company->id, 'roster'));
$this->loginAs()->get($this->url(['date' => '2026-11-12']))
->assertOk()
->assertSee('种菜报销工作台', false)
->assertSee('2026-11-05 00:00:00')
->assertSee('100.50')
->assertSee('11.11种菜报销')
->assertSee('欧意朗')
->assertSee('重复报销')
->assertSee('不在在职花名册')
->assertSee('@星璃 以上订单本次不予报销', false)
->assertSee('待复查')
->assertSee('应为')
->assertSee('5 条', false)
->assertSee('钉钉明细合计 = 可报销合计');
$response = $this->loginAs()->get(route('business.planting-data.planting-reimbursement-workbench.export', [
'company_id' => $this->company->id, 'type' => 'reconciliation', 'date' => '2026-11-12', 'verified' => '1',
]));
$response->assertOk();
$this->assertStringContainsString('11.11.xlsx', (string) $response->headers->get('content-disposition'));
$this->assertStringStartsWith('PK', $response->streamedContent());
foreach (['details', 'import-template'] as $type) {
$this->loginAs()->get(route('business.planting-data.planting-reimbursement-workbench.export', [
'company_id' => $this->company->id, 'type' => $type, 'date' => '2026-11-12', 'verified' => '1',
]))->assertOk();
}
// 没勾「已核对」不给导出。
$this->loginAs()->get(route('business.planting-data.planting-reimbursement-workbench.export', [
'company_id' => $this->company->id, 'type' => 'details', 'date' => '2026-11-12',
]))->assertRedirect()->assertSessionHas('error');
$this->withExceptionHandling()->loginAs()->get($this->url(['date' => '2026-13-40']))->assertStatus(422);
$this->withExceptionHandling()->loginAs()->get($this->url(['date' => '2026-11-12', 'paid_from' => '2026-11-12']))->assertStatus(422);
$this->withExceptionHandling()->loginAs()->get($this->url(['date' => '2026-11-12', 'scope' => 'x']))->assertStatus(422);
}
public function test_bad_upload_reports_error(): void
{
$this->mockRepository([]);
$this->upload('monitor', 'x.csv', "a,b\n1,2\n")->assertRedirect()->assertSessionHas('error');
$this->upload('roster', 'x.txt', "姓名\n张三\n")->assertRedirect()->assertSessionHas('error');
}
}