CaiWuXiTong/tests/Feature/Business/PlantingWorkOrderPagesTest.php
limeng 4e174963e8 fix(种菜损耗): 透明挂钩/虚拟条码整单不扣款;拍A发B 从种菜报销直接挑出发运营
财务确认(2026-10-20):种菜损耗是查种菜报销的单有没有真实发出有价值的货——
- 发货数据中交易单号下出现信誉订单虚拟条码或透明挂钩(含条形码 GG 开头)即整单不扣款;
- 任务备注/卖家备注含拍A发B/拍A发A 的不扣款,且无论是否匹配到发货都从种菜报销中挑出发运营;
- “导出拍A发B(发运营)”直接按「种菜报销」页字段输出;其余真实发货的单按成本价×数量+运费扣款。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VANWYsNUbu2piMtCKymoyd
2026-10-20 10:10:29 +08:00

128 lines
6.7 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 Modules\EcommerceFinance\Repositories\PlantingReimbursementRepository;
use Modules\EcommerceFinance\Services\PlantingLossCalculator as Calculator;
use Modules\EcommerceFinance\View\PlantingLossPageData;
use Modules\EcommerceFinance\View\PlantingReimbursementPageData;
use Tests\Feature\FeatureTestCase;
class PlantingWorkOrderPagesTest extends FeatureTestCase
{
private function row(array $patch = []): array
{
$row = array_fill_keys(array_keys(PlantingLossPageData::COLUMNS + PlantingReimbursementPageData::COLUMNS), '');
return array_replace($row, [
'completed_at' => '2026-09-02 11:47:53', 'work_order_no' => 'GD565967381', 'status' => '已完成',
'shop_name' => '天猫-卓禾家具旗舰店', 'online_order_no' => '3316392626048007769', 'task_remark' => 'CCDD',
'seller_remark' => 'AABB', 'actual_paid_amount' => '37.79', 'task_payment' => '37.79', 'task_freight' => '0.00',
'task_commission' => '2.00', 'jingxi_price_diff' => '0.00', 'task_deduction' => '0.00',
'task_empty_package_fee' => '0.00', 'reimbursement_amount' => '39.79', 'payee_name' => '黄文梳',
'loss_result' => Calculator::RESULT_DEDUCT, 'loss_basis' => '已匹配发货,备注无拍A发B/拍A发A',
'shipped_amount' => '40.60', 'shipped_freight' => '3.20', 'express_no' => 'YT001',
'deduction_amount' => '207.40', 'deduction_detail' => "DNZ-01 × 2 × 100.5 = 201.00\n运费 6.40", 'cost_missing' => false,
], $patch);
}
private function mockData(array $rows): void
{
$repository = \Mockery::mock(PlantingReimbursementRepository::class);
$repository->shouldReceive('syncStatus')->andReturn(['code' => 'ready', 'last_succeeded_at' => '2026-10-19 13:00:00']);
$repository->shouldReceive('rows')->andReturn($rows);
$this->app->instance(PlantingReimbursementRepository::class, $repository);
$calculator = \Mockery::mock(Calculator::class);
$calculator->shouldReceive('rows')->andReturn($rows);
$calculator->shouldReceive('shippingStatus')->andReturn(['code' => 'succeeded', 'last_succeeded_at' => '2026-10-19 12:00:00']);
$this->app->instance(Calculator::class, $calculator);
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
}
private function url(string $page, array $query = []): string
{
return route('business.pages.show', ['company_id' => $this->company->id, 'section' => 'planting-data', 'page' => $page])
. ($query ? '?' . http_build_query($query) : '');
}
public function test_reimbursement_page_lists_month_rows_and_totals(): void
{
$this->mockData([$this->row(), $this->row(['work_order_no' => 'GD-2', 'shop_name' => '拼多多-归来舍下', 'reimbursement_amount' => '0.56', 'task_payment' => '0.06'])]);
$this->loginAs()->get($this->url('planting-reimbursements', ['month' => '2026-09']))
->assertOk()
->assertSee('种菜报销')
->assertSee('GD565967381')
->assertSee('GD-2')
->assertSee('40.35')
->assertSee('2026-09-02 00:00');
$this->loginAs()->get($this->url('planting-reimbursements', ['month' => '2026-09', 'shop_name' => '拼多多']))
->assertOk()->assertSee('GD-2')->assertDontSee('GD565967381');
$this->withExceptionHandling()->loginAs()->get($this->url('planting-reimbursements', ['month' => '2026-08']))->assertStatus(422);
}
public function test_loss_page_defaults_to_deduct_rows_and_splits_exempt(): void
{
$this->mockData([
$this->row(),
$this->row(['work_order_no' => 'GD-EXEMPT', 'task_remark' => '拍A发B+GG003', 'loss_result' => Calculator::RESULT_EXEMPT, 'loss_basis' => '备注含“拍A发B”']),
$this->row(['work_order_no' => 'GD-NONE', 'loss_result' => Calculator::RESULT_UNMATCHED, 'shipped_amount' => null]),
]);
$this->loginAs()->get($this->url('planting-loss', ['month' => '2026-09']))
->assertOk()
->assertSee('种菜损耗')
->assertSee('GD565967381')
->assertDontSee('GD-EXEMPT')
->assertDontSee('GD-NONE')
->assertSee('2026-08-01')
->assertSee('207.40')
->assertSee('DNZ-01 × 2 × 100.5 = 201.00');
$this->loginAs()->get($this->url('planting-loss', ['month' => '2026-09', 'result' => Calculator::RESULT_EXEMPT]))
->assertOk()->assertSee('GD-EXEMPT')->assertDontSee('GD565967381');
$this->withExceptionHandling()->loginAs()->get($this->url('planting-loss', ['month' => '2026-09', 'result' => 'x']))->assertStatus(422);
}
public function test_loss_export_for_operations_contains_only_exempt_rows(): void
{
$this->mockData([
$this->row(),
$this->row(['work_order_no' => 'GD-EXEMPT', 'task_remark' => '拍A发B', 'loss_result' => Calculator::RESULT_EXEMPT]),
]);
$response = $this->loginAs()->get(route('business.planting-data.planting-loss.export', [
'company_id' => $this->company->id, 'month' => '2026-09', 'result' => Calculator::RESULT_EXEMPT,
'columns' => 'work_order_no,online_order_no,task_remark,loss_result',
]));
$response->assertOk();
$csv = $response->streamedContent();
$this->assertStringContainsString('工单编号,线上订单号,任务备注,损耗判定', $csv);
$this->assertStringContainsString("GD-EXEMPT,'3316392626048007769,拍A发B", $csv);
$this->assertStringNotContainsString('GD565967381', $csv);
// 发运营:按种菜报销字段导出,忽略损耗页的列设置。
$operations = $this->loginAs()->get(route('business.planting-data.planting-loss.export', [
'company_id' => $this->company->id, 'month' => '2026-09', 'result' => Calculator::RESULT_EXEMPT, 'format' => 'reimbursement',
]));
$operations->assertOk();
$opsCsv = $operations->streamedContent();
$this->assertStringContainsString(implode(',', array_values(PlantingReimbursementPageData::COLUMNS)), $opsCsv);
$this->assertStringContainsString('GD-EXEMPT', $opsCsv);
$this->assertStringNotContainsString('GD565967381', $opsCsv);
$this->assertStringNotContainsString('损耗判定', $opsCsv);
$reimbursements = $this->loginAs()->get(route('business.planting-data.planting-reimbursements.export', [
'company_id' => $this->company->id, 'month' => '2026-09',
]));
$reimbursements->assertOk();
$this->assertStringContainsString('最终报销金额', $reimbursements->streamedContent());
}
}