create(); $store = StoreModel::factory()->create([ 'level_id' => $level->id, 'payment_cycle_days' => $cycleDays, ]); $product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON]); ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $level->id, 'price' => '10.00']); $user = UserModel::factory()->forStore($store->id)->create(); $this->actingAsMiniUser($user); $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => $qty]]]) ->assertJsonPath('success', true); return [$store, $user, $product]; } /** 回款周期快照:settlement_date = period_end + cycle 天 */ public function test_generate_snapshots_payment_cycle(): void { [$store] = $this->makeStoreWithOrder('2.00', 7); $today = now()->toDateString(); $response = $this->postJson('/mini/statement/generate', [ 'period_start' => $today, 'period_end' => $today, ]); $response->assertOk()->assertJsonPath('success', true); $statement = StatementModel::where('store_id', $store->id)->first(); $this->assertNotNull($statement); $this->assertStringStartsWith('ST', $statement->statement_no); $this->assertSame(7, $statement->payment_cycle_days, '快照生成时的回款周期'); $this->assertSame(now()->addDays(7)->toDateString(), $statement->settlement_date->toDateString()); $this->assertSame('20.00', (string) $statement->total_amount); $this->assertSame(1, $statement->items()->count()); // 生成后门店修改回款周期,不影响已生成的对账单(快照语义) $store->update(['payment_cycle_days' => 30]); $this->assertSame(7, $statement->fresh()->payment_cycle_days); } /** 门店隔离:只能查看与生成本店对账单 */ public function test_statement_isolated_between_stores(): void { [$storeA] = $this->makeStoreWithOrder(); $today = now()->toDateString(); $this->postJson('/mini/statement/generate', ['period_start' => $today, 'period_end' => $today]) ->assertJsonPath('success', true); $statementOfA = StatementModel::where('store_id', $storeA->id)->first(); // 门店 B 用户 $storeB = StoreModel::factory()->create(['level_id' => $storeA->level_id]); $this->actingAsMiniUser(UserModel::factory()->forStore($storeB->id)->create()); $this->getJson('/mini/statement')->assertJsonPath('data.total', 0); $this->getJson("/mini/statement/{$statementOfA->id}")->assertJsonPath('success', false); } /** 已取消订单不计入;重复生成时已入账明细被排除 */ public function test_generate_excludes_cancelled_and_used_items(): void { [$store] = $this->makeStoreWithOrder('2.00'); // 再下一单并取消 $this->postJson('/mini/order', ['items' => [['product_id' => ProductModel::first()->id, 'quantity' => 5]]]); $cancelledOrder = StoreOrderModel::where('store_id', $store->id) ->orderBy('id', 'desc') ->first(); $this->putJson("/mini/order/{$cancelledOrder->id}/cancel")->assertJsonPath('success', true); $today = now()->toDateString(); $this->postJson('/mini/statement/generate', ['period_start' => $today, 'period_end' => $today]) ->assertJsonPath('success', true); $statement = StatementModel::where('store_id', $store->id)->first(); $this->assertSame('20.00', (string) $statement->total_amount, '已取消订单不计入'); $this->assertSame(1, $statement->items()->count()); // 同周期重复生成 → 明细已全部入账,拒绝 $this->postJson('/mini/statement/generate', ['period_start' => $today, 'period_end' => $today]) ->assertJsonPath('success', false); $this->assertSame(1, StatementModel::where('store_id', $store->id)->count()); } }