CaiWuXiTong/tests/Feature/Business/InternalFlowReportPageTest.php
DoDo 19af680c19 fix: 心选不向买家开票
心选买家开票固定为0,收入倒推和全额内部开票保持不变。
2026-11-09 15:47:28 +08:00

286 lines
12 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 Illuminate\Support\Carbon;
use Mockery;
use Modules\EcommerceFinance\Repositories\InternalFlowReportRepository;
use Modules\EcommerceFinance\Services\InternalFlowProjection;
use Tests\Feature\FeatureTestCase;
class InternalFlowReportPageTest extends FeatureTestCase
{
protected function setUp(): void
{
parent::setUp();
Carbon::setTestNow('2026-11-09 12:00:00');
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
public function testInternalFlowReportPageRendersRowsWithQueryAndExportTools(): void
{
$projection = Mockery::mock(InternalFlowProjection::class);
$projection->shouldReceive('forMonth')->once()->andReturn([
'forecast_month' => '2026-11',
'forecast_month_label' => '11月',
'source_month' => '2026-10',
'source_month_label' => '10月',
'sales_by_company' => ['常州心选供应链有限公司' => 1200000.0],
]);
$this->app->instance(InternalFlowProjection::class, $projection);
$repository = Mockery::mock(InternalFlowReportRepository::class);
$repository->shouldReceive('ready')->once()->andReturn(true);
$repository->shouldReceive('paginate')->once()->andReturn([
'data' => [[
'id' => 1,
'sort_order' => 1,
'company_name' => '常州心选供应链有限公司',
'tag' => '1',
'projected_sales' => '1200000.000000',
'invoiced_to_buyer' => '240000.000000',
'internal_invoiced' => '300000.000000',
'required_input_cost' => '960000.000000',
'receipt_from_supplier' => '100000.000000',
'receipt_from_suofeiya' => '900000.000000',
'receipt_from_gujia' => null,
'internal_transfer_1' => '0.000000',
'internal_transfer_2' => '0.000000',
'internal_transfer_3' => '0.000000',
'receipt_total' => '100000.000000',
'remaining_gap' => '860000.000000',
'invoicing_ratio' => '1',
]],
'total' => 1,
'summary' => [
'companies' => 1,
'projected_sales' => 1200000.0,
'receipt_total' => 100000.0,
'remaining_gap' => 860000.0,
'average_invoicing_ratio' => 100.0,
],
]);
$this->app->instance(InternalFlowReportRepository::class, $repository);
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'store-management',
'page' => 'internal-flow-report',
]))
->assertOk()
->assertSeeText('内部流转报表')
->assertSeeText('按抬头、标签查询内部流转明细并导出')
->assertSeeText('常州心选供应链有限公司')
->assertSeeText('预计11月销量')
->assertSeeText('取自主体维度经营报表10月业绩')
->assertSeeText('内部开票按标签 1→2→3→4 顺延')
->assertSeeText('心选不向买家开票')
->assertSeeText('票不足时计入剩余缺口')
->assertSee('data-internal-flow-receipt-input', false)
->assertSee('data-receipt-field="receipt_from_suofeiya"', false)
->assertSee('suofeiya-receipt', false)
->assertSee('value="900000.00"', false)
->assertSee('data-internal-flow-row-id="1"', false)
->assertSee('value="100000.00"', false)
->assertSeeText('收票-内部流转(来源)1')
->assertSeeText('剩余缺口')
->assertSeeText('开票比例')
->assertSeeText('公司数')
->assertSeeText('预计销量合计')
->assertSeeText('已收票合计')
->assertSeeText('剩余缺口合计')
->assertSeeText('平均开票比例')
->assertSee('data-directory-query-picker', false)
->assertSee('data-directory-query-export', false)
->assertSee('data-directory-query-field="company_name"', false)
->assertSee('data-directory-query-select="tag"', false)
->assertSeeText('筛选字段')
->assertSeeText('导出 Excel')
->assertSee('排序和字段操作', false)
->assertSee('sort=projected_sales', false);
}
public function testSupplierReceiptUpdateReturnsRecalculatedRowAndSummary(): void
{
$projectionPayload = [
'forecast_month' => '2026-11',
'forecast_month_label' => '11月',
'source_month' => '2026-10',
'source_month_label' => '10月',
'sales_by_company' => ['示例甲供应链有限公司' => 1200000.0],
];
$projection = Mockery::mock(InternalFlowProjection::class);
$projection->shouldReceive('forMonth')->once()->andReturn($projectionPayload);
$this->app->instance(InternalFlowProjection::class, $projection);
$repository = Mockery::mock(InternalFlowReportRepository::class);
$repository->shouldReceive('updateReceiptOverride')
->once()
->with(7, 'receipt_from_supplier', 500000.0, (int) $this->user->id)
->andReturn('示例甲供应链有限公司');
$repository->shouldReceive('paginate')
->once()
->with(1, 100, ['company_name' => '示例甲', 'tag' => ''], null, 'asc', $projectionPayload)
->andReturn([
'data' => [[
'id' => 7,
'receipt_from_supplier' => 500000.0,
'internal_transfer_1' => 0.0,
'internal_transfer_2' => 250000.0,
'internal_transfer_3' => 0.0,
'receipt_total' => 500000.0,
'remaining_gap' => 460000.0,
]],
'total' => 1,
'summary' => [
'companies' => 1,
'projected_sales' => 1200000.0,
'receipt_total' => 500000.0,
'remaining_gap' => 460000.0,
'average_invoicing_ratio' => 100.0,
],
]);
$this->app->instance(InternalFlowReportRepository::class, $repository);
$this->loginAs()
->patchJson(
route('business.internal-flow-report.supplier-receipt', ['record' => 7]),
[
'receipt_from_supplier' => '500000',
'company_name' => '示例甲',
'tag' => '',
],
)
->assertOk()
->assertJsonPath('ok', true)
->assertJsonPath('record.receipt_field', 'receipt_from_supplier')
->assertJsonPath('record.receipt_amount', 500000)
->assertJsonPath('record.receipt_total', 500000)
->assertJsonPath('record.remaining_gap', 460000)
->assertJsonPath('rows.0.internal_transfer_2', 250000)
->assertJsonPath('summary.receipt_total', 500000);
}
public function testXinxuanSuofeiyaReceiptUsesCostToIncomeFormulaAndReturnsAllRows(): void
{
$projectionPayload = [
'forecast_month' => '2026-11',
'forecast_month_label' => '11月',
'source_month' => '2026-10',
'source_month_label' => '10月',
'sales_by_company' => [],
];
$projection = Mockery::mock(InternalFlowProjection::class);
$projection->shouldReceive('forMonth')->once()->andReturn($projectionPayload);
$this->app->instance(InternalFlowProjection::class, $projection);
$repository = Mockery::mock(InternalFlowReportRepository::class);
$repository->shouldReceive('companyNameForRecord')
->once()
->with(8)
->andReturn('常州心选供应链有限公司');
$repository->shouldReceive('updateReceiptOverride')
->once()
->with(8, 'receipt_from_suofeiya', 900000.0, (int) $this->user->id)
->andReturn('常州心选供应链有限公司');
$repository->shouldReceive('paginate')
->once()
->with(1, 100, ['company_name' => '', 'tag' => ''], null, 'asc', $projectionPayload)
->andReturn([
'data' => [
[
'id' => 8,
'projected_sales' => 1000000.0,
'invoiced_to_buyer' => 0.0,
'internal_invoiced' => 1000000.0,
'required_input_cost' => 900000.0,
'receipt_from_suofeiya' => 900000.0,
'internal_transfer_1' => 0.0,
'internal_transfer_2' => 0.0,
'internal_transfer_3' => 0.0,
'receipt_total' => 900000.0,
'remaining_gap' => 0.0,
],
[
'id' => 9,
'projected_sales' => 2000000.0,
'invoiced_to_buyer' => 400000.0,
'internal_invoiced' => 500000.0,
'required_input_cost' => 1600000.0,
'internal_transfer_1' => 1000000.0,
'internal_transfer_2' => 0.0,
'internal_transfer_3' => 0.0,
'receipt_total' => 1000000.0,
'remaining_gap' => 600000.0,
],
],
'total' => 2,
'summary' => [
'companies' => 2,
'projected_sales' => 3000000.0,
'receipt_total' => 1900000.0,
'remaining_gap' => 600000.0,
'average_invoicing_ratio' => 0.0,
],
]);
$this->app->instance(InternalFlowReportRepository::class, $repository);
$this->loginAs()
->patchJson(
route('business.internal-flow-report.suofeiya-receipt', ['record' => 8]),
[
'receipt_from_suofeiya' => '900000',
'company_name' => '',
'tag' => '',
],
)
->assertOk()
->assertJsonPath('record.receipt_field', 'receipt_from_suofeiya')
->assertJsonPath('record.receipt_amount', 900000)
->assertJsonPath('record.projected_sales', 1000000)
->assertJsonPath('rows.0.invoiced_to_buyer', 0)
->assertJsonPath('rows.0.internal_invoiced', 1000000)
->assertJsonPath('rows.1.internal_transfer_1', 1000000)
->assertJsonPath('summary.projected_sales', 3000000);
}
public function testSuofeiyaReceiptIsRejectedForOtherCompanies(): void
{
$repository = Mockery::mock(InternalFlowReportRepository::class);
$repository->shouldReceive('companyNameForRecord')
->once()
->with(8)
->andReturn('其他公司有限公司');
$this->app->instance(InternalFlowReportRepository::class, $repository);
$this->withExceptionHandling();
$this->loginAs()
->patchJson(
route('business.internal-flow-report.suofeiya-receipt', ['record' => 8]),
['receipt_from_suofeiya' => '900000'],
)
->assertForbidden();
}
public function testInternalFlowReportPageExplainsMissingDataset(): void
{
$repository = Mockery::mock(InternalFlowReportRepository::class);
$repository->shouldReceive('ready')->once()->andReturn(false);
$this->app->instance(InternalFlowReportRepository::class, $repository);
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'store-management',
'page' => 'internal-flow-report',
]))
->assertOk()
->assertSeeText('内部流转数据尚未导入');
}
}