106 lines
4.6 KiB
PHP
106 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\CustomerLevelModel;
|
|
use App\Models\ProductModel;
|
|
use App\Models\ProductPriceModel;
|
|
use App\Models\StatementModel;
|
|
use App\Models\StoreModel;
|
|
use App\Models\StoreOrderModel;
|
|
use App\Models\UserModel;
|
|
|
|
/**
|
|
* 门店对账单:回款周期快照(settlement_date = period_end + cycle)、门店数据隔离、防重复入账
|
|
*/
|
|
class StatementTest extends ProcurementTestCase
|
|
{
|
|
/**
|
|
* @return array{0: StoreModel, 1: UserModel, 2: ProductModel}
|
|
*/
|
|
private function makeStoreWithOrder(string $qty = '2.00', int $cycleDays = 7): array
|
|
{
|
|
$level = CustomerLevelModel::factory()->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());
|
|
}
|
|
}
|