CaiWuXiTong/tests/Feature/Business/PlantingWorkOrderPagesTest.php
limeng cc9015cea6 fix(种菜损耗): 扣款金额改为 成本价×数量 + 该单发货运费
财务更正口径:种菜损耗扣款 = Σ 发货条码在普通商品资料的成本价 × 数量 + 该订单在发货数据中的运费合计,
不再按成本价×1.2;运费为空的订单标"运费缺失",缺失部分不计入扣款金额。

涉及:金额计算展示(只读);不涉及迁移、权限和上游写入。

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

117 lines
6.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\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);
$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());
}
}