Files
xin-procurement/tests/Feature/PurchaseEditTest.php
T
2026-08-12 14:38:20 +08:00

172 lines
7.3 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\StoreOrderItemModel;
use App\Models\StoreOrderModel;
use App\Models\UserModel;
/**
* C4 采购单数据修改:详情矩阵(商品行 × 门店列)、门店单元格数量、行级成本/称重,
* 修改同步订货明细并重算订单/采购单汇总
*/
class PurchaseEditTest extends ProcurementTestCase
{
/**
* 造采购链路:门店A 2 件 + 门店B 3 件(同一商品,等级价 10.00,成本 20,包规 10斤/箱 → 单价 2.00),
* 接单后生成采购单
*
* @return array{0: PurchaseOrderModel, 1: ProductModel, 2: array<int, StoreModel>}
*/
private function buildPurchase(): array
{
$level = 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,
'cost_price' => 20,
'spec' => '10斤/箱',
'unit' => '斤',
]);
ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $level->id, 'price' => 10.00]);
foreach ([[$storeA, 2], [$storeB, 3]] 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);
}
StoreOrderModel::query()->update(['status' => StoreOrderModel::STATUS_SUMMARIZED]);
$this->actingAsSysUser();
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
->assertJsonPath('success', true);
return [PurchaseOrderModel::first(), $product, [$storeA, $storeB]];
}
/** 详情返回「商品行 × 门店列」矩阵:单价 = 成本/包规,单元格金额 = 数量×单价 */
public function test_detail_returns_store_matrix(): void
{
[$purchase, $product, $stores] = $this->buildPurchase();
$this->actingAsSysUser();
$response = $this->getJson("/purchase/order/{$purchase->id}")
->assertOk()
->assertJsonPath('success', true);
$data = $response->json('data');
$this->assertCount(2, $data['stores']);
$this->assertCount(1, $data['items']);
$row = $data['items'][0];
$this->assertSame($product->id, $row['product_id']);
$this->assertSame('10斤/箱', $row['product_spec']);
$this->assertSame('斤', $row['unit']);
$this->assertEquals(20.0, $row['cost_price']);
$this->assertEquals(2.0, $row['unit_cost'], '单价 = 20 ÷ 10');
$this->assertEquals(5.0, $row['quantity'], '2+3');
$this->assertEquals(10.0, $row['amount'], '5 × 2.00');
$cells = collect($row['cells'])->keyBy('store_id');
$this->assertSame(2, $cells[$stores[0]->id]['quantity']);
$this->assertEquals(4.0, $cells[$stores[0]->id]['amount'], '2 × 2.00');
$this->assertSame(3, $cells[$stores[1]->id]['quantity']);
$this->assertEquals(6.0, $cells[$stores[1]->id]['amount'], '3 × 2.00');
}
/** 门店单元格数量修改 → 明细金额重算(数量×单价),订单与采购单汇总同步 */
public function test_update_cell_syncs_order_item_and_totals(): void
{
[$purchase, , $stores] = $this->buildPurchase();
$this->actingAsSysUser();
$item = StoreOrderItemModel::where('store_id', $stores[0]->id)->first();
$this->putJson("/purchase/order/cell/{$item->id}", ['quantity' => 5])
->assertJsonPath('success', true)
->assertJsonPath('data.amount', 50);
$item = $item->fresh();
$this->assertSame(5, $item->quantity);
$this->assertSame('50.00', (string) $item->amount, '5 × 订货单价 10.00');
$order = StoreOrderModel::find($item->order_id);
$this->assertSame(5, $order->total_quantity);
$this->assertSame('50.00', (string) $order->product_amount);
$this->assertSame('50.00', (string) $order->total_amount);
$purchase = $purchase->fresh();
$this->assertSame('8.00', (string) $purchase->total_quantity, '5+3');
$this->assertSame('80.00', (string) $purchase->estimate_amount, '50+30');
}
/** 行级成本修改 → 同步该商品全部订货明细,采购单实际金额按 数量×单价 重算 */
public function test_update_row_cost_syncs_all_order_items(): void
{
[$purchase, $product] = $this->buildPurchase();
$this->actingAsSysUser();
$this->putJson("/purchase/order/{$purchase->id}/row/{$product->id}", ['cost_price' => 30])
->assertJsonPath('success', true)
->assertJsonPath('data.count', 2);
$this->assertSame(2, StoreOrderItemModel::where('cost_price', '30.00')->count());
$purchase = $purchase->fresh();
// 单价 = 30 ÷ 10 = 3.00,实际金额 = 5 × 3.00
$this->assertSame('15.00', (string) $purchase->actual_amount);
$this->assertSame('50.00', (string) $purchase->estimate_amount, '订货金额不受成本修改影响');
}
/** 行级称重修改 → 按各店数量比例分摊写入明细(尾差修正守恒),实际金额按 称重×单价 计 */
public function test_update_row_weight_distributed_by_quantity(): void
{
[$purchase, $product, $stores] = $this->buildPurchase();
$this->actingAsSysUser();
$this->putJson("/purchase/order/{$purchase->id}/row/{$product->id}", ['weight' => 10])
->assertJsonPath('success', true);
$items = StoreOrderItemModel::query()->orderBy('store_id')->get()->keyBy('store_id');
$this->assertSame('4.000', (string) $items[$stores[0]->id]->weight, '10 × 2/5');
$this->assertSame('6.000', (string) $items[$stores[1]->id]->weight, '10 × 3/5');
$this->assertSame(
'10.000',
bcadd((string) $items[$stores[0]->id]->weight, (string) $items[$stores[1]->id]->weight, 3),
'分摊守恒'
);
$purchase = $purchase->fresh();
$this->assertSame('10.000', (string) $purchase->total_weight);
// 称重>0 → 金额按 称重×单价:10 × 2.00
$this->assertSame('20.00', (string) $purchase->actual_amount);
}
/** 行级修改:成本与称重至少一项 */
public function test_update_row_requires_at_least_one_field(): void
{
[$purchase, $product] = $this->buildPurchase();
$this->actingAsSysUser();
$this->putJson("/purchase/order/{$purchase->id}/row/{$product->id}", [])
->assertJsonPath('success', false);
}
/** 单元格数量校验:负数拒绝 */
public function test_update_cell_rejects_negative_quantity(): void
{
[$purchase, , $stores] = $this->buildPurchase();
$this->actingAsSysUser();
$item = StoreOrderItemModel::where('store_id', $stores[0]->id)->first();
$this->putJson("/purchase/order/cell/{$item->id}", ['quantity' => -1])
->assertJsonPath('success', false);
}
}