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' => '6.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); } // 后台接单:待接单 → 已接单(生成采购单的来源状态) StoreOrderModel::query()->update(['status' => StoreOrderModel::STATUS_SUMMARIZED]); return $product; } /** 头部汇总取订货明细合计:total_quantity = Σ数量,estimate_amount = Σ数量×成本价(预估成本) */ public function test_generate_aggregates_order_items_into_header(): void { $this->seedAcceptedOrders(); $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->assertSame('10.00', (string) $purchase->total_quantity, '3+3+4'); $this->assertSame('60.00', (string) $purchase->estimate_amount, '10 件 × 每包成本 6.00'); $this->assertSame('0.00', (string) $purchase->actual_amount); } /** 源订单状态回写为采购中,订单与订货明细均回写 purchase_id */ public function test_generate_writes_back_order_status(): void { $this->seedAcceptedOrders(); $this->actingAsSysUser(); $this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()]) ->assertJsonPath('success', true); $purchase = PurchaseOrderModel::first(); $this->assertSame(0, StoreOrderModel::where('status', StoreOrderModel::STATUS_SUMMARIZED)->count()); $this->assertSame(3, StoreOrderModel::where('status', StoreOrderModel::STATUS_DELIVERING)->count()); $this->assertSame(3, StoreOrderModel::where('purchase_id', $purchase->id)->count()); $this->assertSame( 3, StoreOrderItemModel::where('purchase_id', $purchase->id)->count(), '订货明细应全部回写 purchase_id(采购明细溯源依据)' ); $this->assertSame(0, StoreOrderItemModel::where('purchase_id', 0)->count()); } /** 仅有待接单订单时不可生成(待接单不再被归集)→ 报错 */ public function test_generate_without_accepted_orders_fails(): void { $level = CustomerLevelModel::factory()->create(); $store = StoreModel::factory()->create(['level_id' => $level->id]); $product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']); $this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create()); $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]) ->assertJsonPath('success', true); $this->actingAsSysUser(); $this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()]) ->assertJsonPath('success', false); $this->assertSame(0, PurchaseOrderModel::count()); $this->assertSame(1, StoreOrderModel::where('status', StoreOrderModel::STATUS_PENDING)->count()); } /** 指定订单合并:仅归集 order_ids 内的已接单订单,混入非已接单则整批拒绝 */ public function test_generate_with_selected_order_ids(): void { $this->seedAcceptedOrders(); $this->actingAsSysUser(); $selected = StoreOrderModel::query()->orderBy('id')->limit(2)->pluck('id')->all(); $this->postJson('/purchase/order/generate', [ 'purchase_date' => now()->toDateString(), 'order_ids' => $selected, ])->assertJsonPath('success', true); $purchase = PurchaseOrderModel::first(); $this->assertSame(2, StoreOrderModel::where('purchase_id', $purchase->id)->count()); $this->assertSame(1, StoreOrderModel::where('status', StoreOrderModel::STATUS_SUMMARIZED)->count(), '未选中的订单保持已接单'); // 混入非已接单订单 → 整批拒绝 $remaining = StoreOrderModel::where('status', StoreOrderModel::STATUS_SUMMARIZED)->value('id'); $done = StoreOrderModel::where('status', StoreOrderModel::STATUS_DELIVERING)->value('id'); $this->postJson('/purchase/order/generate', [ 'purchase_date' => now()->toDateString(), 'order_ids' => [$remaining, $done], ])->assertJsonPath('success', false); $this->assertSame(1, PurchaseOrderModel::count(), '整批拒绝,不产生新采购单'); } /** 幂等:已归集订单不会被重复归集 */ public function test_generate_is_idempotent(): void { $this->seedAcceptedOrders(); $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(), '第二次生成应被拒绝,不产生新采购单'); } }