CaiWuXiTong/tests/Feature/Business/PayablesUnpricedCycleTest.php
苏木 c658bbed99 feat: 增加未定价账期汇总与导出 (#1179)
Co-authored-by: 苏木 <594326199@qq.com>
Co-committed-by: 苏木 <594326199@qq.com>
2026-11-15 11:28:34 +08:00

203 lines
9.4 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use Illuminate\Validation\ValidationException;
use Modules\EcommerceFinance\Services\PayablesUnpricedCycleProcessor;
use Modules\EcommerceFinance\Services\SupplierPayableOverrideStore;
use Tests\Feature\FeatureTestCase;
class PayablesUnpricedCycleTest extends FeatureTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
}
public function test_cycle_matches_history_before_carrying_in_new_unpriced_rows(): void
{
$store = app(SupplierPayableOverrideStore::class);
$companyId = (int) $this->company->id;
$userId = (int) $this->user->id;
$store->saveBatch(PayablesUnpricedCycleProcessor::DELIVERY_HISTORY_DATASET, [
'id:history:delivery:matched-1' => [
'supplier' => '供应商甲', 'barCode' => 'M-1', 'qty' => '2', 'unitPrice' => null, 'amount' => null,
],
'id:history:delivery:matched-2' => [
'supplier' => '供应商甲', 'barCode' => 'M-1', 'qty' => '3', 'unitPrice' => null, 'amount' => null,
],
'id:history:delivery:unmatched' => [
'supplier' => '供应商乙', 'barCode' => 'NO-PRICE', 'qty' => '4', 'unitPrice' => null, 'amount' => null,
],
], $companyId, $userId, 'test');
$store->saveBatch(PayablesUnpricedCycleProcessor::RETURN_HISTORY_DATASET, [
'id:history:return:matched' => [
'supplier' => '退货甲', 'barcode' => 'R-1', 'qty' => '3', 'unit_price' => null, 'amount' => null,
],
'id:history:return:unmatched' => [
'supplier' => '退货乙', 'barcode' => 'NO-PRICE', 'qty' => '5', 'unit_price' => null, 'amount' => null,
],
], $companyId, $userId, 'test');
$store->saveBatch(PayablesUnpricedCycleProcessor::DELIVERY_RECONCILIATION_DATASET, [
'id:recon:delivery:new' => [
'summary_month' => '2026-10', 'purchaseNo' => 'CG-NEW', 'stockNo' => 'RK-NEW',
'productCode' => 'NEW-D', 'qty' => '2', 'amount' => '', 'guessAmount' => '50',
],
'id:recon:delivery:priced' => [
'summary_month' => '2026-10', 'purchaseNo' => 'CG-OLD', 'stockNo' => 'RK-OLD',
'productCode' => 'M-1', 'qty' => '2', 'amount' => '20', 'guessAmount' => '25',
],
], $companyId, $userId, 'test');
$store->saveBatch(PayablesUnpricedCycleProcessor::RETURN_RECONCILIATION_DATASET, [
'id:recon:return:new' => [
'summary_month' => '2026-10', 'supplier' => '退货甲', 'barcode' => 'NEW-R',
'qty' => '1', 'amount' => '', 'guess_amount' => '30',
],
'id:recon:return:priced' => [
'summary_month' => '2026-10', 'supplier' => '退货甲', 'barcode' => 'R-1',
'qty' => '1', 'amount' => '8', 'guess_amount' => '10',
],
], $companyId, $userId, 'test');
$priceFile = $this->makeFile(
'prices.csv',
"\xEF\xBB\xBF商品编码,价格类型,变价后价格,生效开始时间,生效结束时间,流程完成时间\n"
. "M-1,变价,10.50,2026-01-01,2026-09-30,\n"
. "M-1,变价,12.00,2026-10-01,,2026-10-01\n"
. "R-1,定价,8.00,2026-09-01,,\n"
. "OLD,变价,99.00,2026-01-01,2026-09-30,\n",
);
try {
$result = app(PayablesUnpricedCycleProcessor::class)->process(
$priceFile,
'2026-10',
'2026-11-02',
$companyId,
$userId,
);
} finally {
@unlink($priceFile);
}
$this->assertSame(2, $result['matched_delivery_rows']);
$this->assertSame(1, $result['matched_return_rows']);
$this->assertSame(1, $result['new_delivery_rows']);
$this->assertSame(1, $result['new_return_rows']);
$pricedDelivery = $store->listRows(PayablesUnpricedCycleProcessor::DELIVERY_PRICED_DATASET, $companyId);
$pricedReturn = $store->listRows(PayablesUnpricedCycleProcessor::RETURN_PRICED_DATASET, $companyId);
$this->assertCount(2, $pricedDelivery);
$this->assertCount(1, $pricedReturn);
$this->assertSame('12', $pricedDelivery[0]['unitPrice']);
$this->assertSame('24.00', $pricedDelivery[0]['amount']);
$this->assertSame('8', $pricedReturn[0]['unit_price']);
$this->assertSame('24.00', $pricedReturn[0]['amount']);
$this->assertSame('2026-10', $pricedReturn[0]['_accounting_period']);
$remainingDelivery = $store->listRows(PayablesUnpricedCycleProcessor::DELIVERY_HISTORY_DATASET, $companyId);
$remainingReturn = $store->listRows(PayablesUnpricedCycleProcessor::RETURN_HISTORY_DATASET, $companyId);
$this->assertCount(2, $remainingDelivery);
$this->assertCount(2, $remainingReturn);
$this->assertSame(['NO-PRICE', 'NEW-D'], array_column($remainingDelivery, 'barCode'));
$this->assertSame(['NO-PRICE', 'NEW-R'], array_column($remainingReturn, 'barcode'));
$again = $this->makeFile('prices-again.csv', "商品编码,变价后价格,生效开始时间\nM-1,12,2026-10-01\n");
try {
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('账期 2026-10 已完成定价批次,不能重复处理。');
app(PayablesUnpricedCycleProcessor::class)->process(
$again,
'2026-10',
'2026-11-02',
$companyId,
$userId,
);
} finally {
@unlink($again);
}
}
public function test_previous_unpriced_current_priced_page_shows_two_source_tables(): void
{
$store = app(SupplierPayableOverrideStore::class);
$companyId = (int) $this->company->id;
$store->saveBatch(PayablesUnpricedCycleProcessor::DELIVERY_PRICED_DATASET, [
'id:page:delivery' => [
'barCode' => 'DELIVERY-PRICE-1', 'qty' => '2', 'unitPrice' => '10', 'amount' => '20', 'guessAmount' => '30',
'_accounting_period' => '2026-10',
],
], $companyId, (int) $this->user->id, 'test');
$store->saveBatch(PayablesUnpricedCycleProcessor::RETURN_PRICED_DATASET, [
'id:page:return' => [
'barcode' => 'RETURN-PRICE-1', 'qty' => '1', 'unit_price' => '8', 'amount' => '8', 'guess_amount' => '10',
'_accounting_period' => '2026-10',
],
], $companyId, (int) $this->user->id, 'test');
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'supplier-payables-final',
'page' => 'previous-unpriced-current-priced',
'month' => '2026-10',
]))
->assertOk()
->assertSeeText('之前月未定价-本月定价')
->assertSee('data-unpriced-cycle-tab="delivery"', false)
->assertSee('data-unpriced-cycle-tab="return"', false)
->assertSeeText('规格码')
->assertSeeText('采购金额')
->assertSeeText('DELIVERY-PRICE-1')
->assertSeeText('RETURN-PRICE-1')
->assertSee('value="2026-10"', false)
->assertSee('data-cycle-summary="delivery-record-count">1</strong>', false)
->assertSee('data-cycle-summary="delivery-quantity">2.00</strong>', false)
->assertSee('data-cycle-summary="delivery-amount">¥20.00</strong>', false)
->assertSee('data-cycle-summary="return-estimated-amount">¥10.00</strong>', false)
->assertSee('>导出</span>', false);
$export = $this->loginAs()->get(route(
'business.supplier-payables-final.previous-unpriced-current-priced.export',
['month' => '2026-10'],
))->assertOk();
$path = tempnam(sys_get_temp_dir(), 'caiwu-cycle-export') . '.xlsx';
try {
file_put_contents($path, $export->streamedContent());
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($path);
$this->assertSame(['账期汇总', '送货未定价', '退货未定价'], $spreadsheet->getSheetNames());
$summary = $spreadsheet->getSheetByName('账期汇总')->rangeToArray('A1:E3');
$this->assertSame(['类型', '记录数', '件数', '金额', '预估金额'], $summary[0]);
$this->assertSame('送货未定价', $summary[1][0]);
$this->assertSame(2.0, (float) $summary[1][2]);
$this->assertSame(20.0, (float) $summary[1][3]);
$deliveryHeader = $spreadsheet->getSheetByName('送货未定价')->rangeToArray('A1:T1')[0];
$this->assertContains('计划单号', $deliveryHeader);
$this->assertContains('预估金额', $deliveryHeader);
$returnHeader = $spreadsheet->getSheetByName('退货未定价')->rangeToArray('A1:Q1')[0];
$this->assertContains('采购金额', $returnHeader);
$spreadsheet->disconnectWorksheets();
} finally {
@unlink($path);
}
}
private function makeFile(string $name, string $content): string
{
$path = sys_get_temp_dir() . '/caiwu-cycle-test-' . bin2hex(random_bytes(6)) . '-' . $name;
file_put_contents($path, $content);
return $path;
}
}