Files
xin-procurement/tests/Feature/PurchaseGenerateTest.php
T
2026-08-06 14:52:56 +08:00

140 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;
use App\Models\CustomerLevelModel;
use App\Models\ProductModel;
use App\Models\ProductPriceModel;
use App\Models\PurchaseOrderModel;
use App\Models\StoreModel;
use App\Models\StoreOrderModel;
use App\Models\UserModel;
/**
* C1 订单汇总生成采购单:多门店聚合、订单状态回写、无订单/重复生成防护
*/
class PurchaseGenerateTest extends ProcurementTestCase
{
/**
* 造当日待汇总订单:门店A 两单各 3 件 + 门店B 一单 4 件(同一商品)
* 商品设两个等级价 5.00 / 4.00,估算单价应取最低 4.00
*/
private function seedPendingOrders(): ProductModel
{
$level = CustomerLevelModel::factory()->create();
$levelLow = CustomerLevelModel::factory()->create();
$storeA = StoreModel::factory()->create(['level_id' => $level->id]);
$storeB = StoreModel::factory()->create(['level_id' => $level->id]);
$product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON]);
ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $level->id, 'price' => 5.00]);
ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $levelLow->id, 'price' => 4.00]);
foreach ([[$storeA, 3], [$storeA, 3], [$storeB, 4]] as [$store, $qty]) {
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => $qty]]])
->assertJsonPath('success', true);
}
return $product;
}
/** 多门店多订单按商品聚合,估算单价取最低等级价 */
public function test_generate_aggregates_orders_by_product(): void
{
$this->seedPendingOrders();
$admin = $this->actingAsSysUser();
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
->assertOk()
->assertJsonPath('success', true);
$purchase = PurchaseOrderModel::first();
$this->assertNotNull($purchase);
$this->assertStringStartsWith('PO', $purchase->purchase_no);
$this->assertSame(now()->toDateString(), $purchase->purchase_date->toDateString());
$this->assertSame($admin->id, $purchase->operator_id);
$this->assertSame(PurchaseOrderModel::STATUS_PENDING, $purchase->status);
$this->assertCount(1, $purchase->items, '单一商品应聚合为一行');
$item = $purchase->items->first();
$this->assertSame('10.00', (string) $item->quantity, '3+3+4');
$this->assertSame('4.00', (string) $item->price, '估算单价取最低等级价');
$this->assertSame('40.00', (string) $item->amount, '10 × 4.00');
$this->assertSame('40.00', (string) $purchase->estimate_amount);
}
/** 源订单状态回写为已汇总 */
public function test_generate_writes_back_order_status(): void
{
$this->seedPendingOrders();
$this->actingAsSysUser();
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
->assertJsonPath('success', true);
$this->assertSame(0, StoreOrderModel::where('status', StoreOrderModel::STATUS_PENDING)->count());
$this->assertSame(3, StoreOrderModel::where('status', StoreOrderModel::STATUS_SUMMARIZED)->count());
}
/** 当日无待汇总订单 → 报错 */
public function test_generate_without_pending_orders_fails(): void
{
$this->actingAsSysUser();
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
->assertJsonPath('success', false);
$this->assertSame(0, PurchaseOrderModel::count());
}
/** 幂等:已汇总订单不会被重复归集 */
public function test_generate_is_idempotent(): void
{
$this->seedPendingOrders();
$this->actingAsSysUser();
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
->assertJsonPath('success', true);
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
->assertJsonPath('success', false);
$this->assertSame(1, PurchaseOrderModel::count(), '第二次生成应被拒绝,不产生新采购单');
}
/** 估算单价取「最低实际价」:固定价与百分比计价混合时按换算后值比较 */
public function test_generate_uses_min_actual_price_across_types(): void
{
$level = CustomerLevelModel::factory()->create();
$levelFixed = CustomerLevelModel::factory()->create();
$store = StoreModel::factory()->create(['level_id' => $level->id]);
$product = ProductModel::factory()->create([
'status' => ProductModel::STATUS_ON,
'cost_price' => 10,
]);
// 百分比行:10 × (1+40%) = 14.00;固定价行 5.00 → 估算应取 5.00
ProductPriceModel::factory()->create([
'product_id' => $product->id,
'level_id' => $level->id,
'price_type' => ProductPriceModel::PRICE_TYPE_PERCENT,
'percent' => 40,
'price' => 14.00,
]);
ProductPriceModel::factory()->create([
'product_id' => $product->id,
'level_id' => $levelFixed->id,
'price' => 5.00,
]);
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 3]]])
->assertJsonPath('success', true);
$this->actingAsSysUser();
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
->assertJsonPath('success', true);
$item = PurchaseOrderModel::first()->items->first();
$this->assertSame('5.00', (string) $item->price, '估算单价应取换算后的最低实际价');
$this->assertSame('15.00', (string) $item->amount, '3 × 5.00');
}
}