客户等级设置

This commit is contained in:
liu
2026-08-17 16:27:15 +08:00
parent 2eb27127c9
commit a06c45dc49
31 changed files with 548 additions and 1142 deletions
+107 -42
View File
@@ -5,33 +5,34 @@ namespace Tests\Feature;
use App\Models\CustomerLevelModel;
use App\Models\NoticeModel;
use App\Models\ProductModel;
use App\Models\ProductPriceModel;
use App\Models\StoreModel;
use App\Models\UserModel;
/**
* A2 等级价格体系:等级价匹配、批量调价事务与调价通知
* A2 等级价格体系(等级上浮比例):列表按等级换算价、批量调价(成本价)事务与调价通知
* 等级上浮比例变更通知
*/
class ProductPriceTest extends ProcurementTestCase
{
/** 小程序商品列表返回当前门店等级对应的价格 */
/** 小程序商品列表按门店等级的上浮比例返回换算价(不同等级不同价) */
public function test_product_list_returns_price_for_store_level(): void
{
$levelA = CustomerLevelModel::factory()->create();
$levelB = CustomerLevelModel::factory()->create();
$store = StoreModel::factory()->create(['level_id' => $levelA->id]);
$product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON]);
ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $levelA->id, 'price' => 5.50]);
ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $levelB->id, 'price' => 3.00]);
$levelA = CustomerLevelModel::factory()->create(['percent' => 30]);
$levelB = CustomerLevelModel::factory()->create(['percent' => 10]);
$storeA = StoreModel::factory()->create(['level_id' => $levelA->id]);
$storeB = StoreModel::factory()->create(['level_id' => $levelB->id]);
$product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => 10.00]);
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->actingAsMiniUser(UserModel::factory()->forStore($storeA->id)->create());
$rowA = collect($this->getJson('/mini/product/list')->assertOk()->json('data.data'))
->firstWhere('id', $product->id);
$this->assertNotNull($rowA, '商品应出现在列表中');
$this->assertSame(13.00, (float) $rowA['price'], 'A 等级:10 元上浮 30% = 13.00');
$response = $this->getJson('/mini/product/list');
$response->assertOk()->assertJsonPath('success', true);
$row = collect($response->json('data.data'))->firstWhere('id', $product->id);
$this->assertNotNull($row, '商品应出现在列表中');
$this->assertSame(5.50, (float) $row['price'], '应返回门店所在等级的价格');
$this->actingAsMiniUser(UserModel::factory()->forStore($storeB->id)->create());
$rowB = collect($this->getJson('/mini/product/list')->assertOk()->json('data.data'))
->firstWhere('id', $product->id);
$this->assertSame(11.00, (float) $rowB['price'], 'B 等级:10 元上浮 10% = 11.00');
}
/** 门店未设置客户等级:仍可浏览商品,但价格不可见(price=null */
@@ -71,59 +72,123 @@ class ProductPriceTest extends ProcurementTestCase
->assertJsonPath('msg', '尚未绑定门店,请联系客服处理');
}
/** 批量调价:事务写入 + 通知受影响门店用户(不影响无关门店 */
public function test_batch_price_updates_and_notifies_affected_stores(): void
/** 批量调价(成本价):事务写入 + 通知全部正常门店用户(成本变更影响所有等级售价 */
public function test_batch_price_updates_cost_and_notifies_all_stores(): void
{
$this->actingAsSysUser();
$level = CustomerLevelModel::factory()->create();
$affectedStore = StoreModel::factory()->create(['level_id' => $level->id]);
$unaffectedStore = StoreModel::factory()->create(['level_id' => CustomerLevelModel::factory()->create()->id]);
$affectedUser = UserModel::factory()->forStore($affectedStore->id)->create();
$unaffectedUser = UserModel::factory()->forStore($unaffectedStore->id)->create();
$storeA = StoreModel::factory()->create(['level_id' => CustomerLevelModel::factory()->create()->id]);
$storeB = StoreModel::factory()->create(['level_id' => CustomerLevelModel::factory()->create()->id]);
$userA = UserModel::factory()->forStore($storeA->id)->create();
$userB = UserModel::factory()->forStore($storeB->id)->create();
$product = ProductModel::factory()->create();
ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $level->id, 'price' => 5.00]);
$product = ProductModel::factory()->create(['cost_price' => 10]);
$this->putJson('/product/goods/batchPrice', [
'updates' => [
['product_id' => $product->id, 'level_id' => $level->id, 'price' => 8.80],
['product_id' => $product->id, 'cost_price' => 12],
],
])->assertOk()->assertJsonPath('success', true);
$this->assertSame(
8.80,
(float) ProductPriceModel::forProductLevel($product->id, $level->id)->first()->price,
'等级价格应已更新'
);
$this->assertSame('12.00', (string) $product->fresh()->cost_price, '成本价应已更新');
$this->assertSame(
1,
NoticeModel::where('user_id', $affectedUser->id)->where('type', NoticeModel::TYPE_PRICE)->count(),
'受影响门店用户应收到价格变更通知'
NoticeModel::where('user_id', $userA->id)->where('type', NoticeModel::TYPE_PRICE)->count(),
'成本价变更影响所有等级售价,门店用户应收到价格变更通知'
);
$this->assertSame(
0,
NoticeModel::where('user_id', $unaffectedUser->id)->count(),
'无关门店用户应收到通知'
1,
NoticeModel::where('user_id', $userB->id)->where('type', NoticeModel::TYPE_PRICE)->count(),
'成本价变更影响所有等级售价,门店用户应收到价格变更通知'
);
}
/** 价格矩阵:行=商品 × 列=启用等级 */
/** 价格矩阵:行=商品 × 列=启用等级,等级格为按上浮比例换算的售价 */
public function test_price_matrix_structure(): void
{
$this->actingAsSysUser();
$level = CustomerLevelModel::factory()->create();
$product = ProductModel::factory()->create();
ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $level->id, 'price' => 6.00]);
$level = CustomerLevelModel::factory()->create(['percent' => 20]);
$product = ProductModel::factory()->create(['cost_price' => 10]);
$response = $this->getJson('/product/goods/priceMatrix');
$response->assertOk()->assertJsonPath('success', true);
$this->assertNotEmpty($response->json('data.levels'));
$levels = $response->json('data.levels');
$this->assertNotEmpty($levels);
$levelRow = collect($levels)->firstWhere('id', $level->id);
$this->assertSame(20.0, (float) $levelRow['percent'], '矩阵等级列应含上浮比例');
$row = collect($response->json('data.rows'))->firstWhere('id', $product->id);
$this->assertNotNull($row);
$this->assertSame(6.0, (float) $row['price_' . $level->id]);
$this->assertSame(12.0, (float) $row['price_' . $level->id], '矩阵等级格应为换算价:10 元上浮 20% = 12.00');
}
/** 等级上浮比例变更:通知该等级下正常门店用户(不影响其他等级门店) */
public function test_level_percent_update_notifies_level_stores(): void
{
$this->actingAsSysUser();
$level = CustomerLevelModel::factory()->create(['percent' => 10]);
$otherLevel = CustomerLevelModel::factory()->create(['percent' => 20]);
$user = UserModel::factory()->forStore(
StoreModel::factory()->create(['level_id' => $level->id])->id
)->create();
$otherUser = UserModel::factory()->forStore(
StoreModel::factory()->create(['level_id' => $otherLevel->id])->id
)->create();
$this->putJson('/customer/level/' . $level->id, [
'name' => $level->name,
'percent' => 30,
])->assertOk()->assertJsonPath('success', true);
$this->assertSame('30.00', (string) $level->fresh()->percent, '上浮比例应已更新');
$this->assertSame(
1,
NoticeModel::where('user_id', $user->id)->where('type', NoticeModel::TYPE_PRICE)->count(),
'该等级门店用户应收到价格变更通知'
);
$this->assertSame(
0,
NoticeModel::where('user_id', $otherUser->id)->count(),
'其他等级门店用户不应收到通知'
);
}
/** 上浮比例未变化时不重复通知 */
public function test_level_update_without_percent_change_sends_no_notice(): void
{
$this->actingAsSysUser();
$level = CustomerLevelModel::factory()->create(['percent' => 10]);
$user = UserModel::factory()->forStore(
StoreModel::factory()->create(['level_id' => $level->id])->id
)->create();
$this->putJson('/customer/level/' . $level->id, [
'name' => $level->name . '(改)',
'percent' => 10,
])->assertOk()->assertJsonPath('success', true);
$this->assertSame(0, NoticeModel::where('user_id', $user->id)->count(), '上浮比例未变不应发通知');
}
/** 等级表单:上浮比例校验(负数/超限拒绝) */
public function test_level_percent_validation(): void
{
$this->actingAsSysUser();
$this->postJson('/customer/level', ['name' => '负比例等级', 'percent' => -1])
->assertOk()
->assertJsonPath('success', false)
->assertJsonPath('msg', '价格上浮比例不能小于 0');
$this->postJson('/customer/level', ['name' => '超比例等级', 'percent' => 1000])
->assertOk()
->assertJsonPath('success', false)
->assertJsonPath('msg', '价格上浮比例不能超过 999.99');
$this->assertSame(0, CustomerLevelModel::count(), '校验失败不应落库');
}
}