Files
Verde-Web/tests/Feature/Api/V1/Store/PartnerStoreTest.php
admin 989f4b87b9 feat(backend): complete Module 12 (partner stores)
partner_stores, store_inventories, store_purchases, store_sales tables.
Promotes qr_code_batches.target_store_id and qr_codes.assigned_to_store_id
to real FKs. StoreOperations service handles wholesale issuance
(generates fresh batch -> codes go allocated to store -> inventory tops
up -> StorePurchase recorded) and resident sales (codes flip allocated
-> active to a household, commission computed at the store's rate).
Admin endpoints: store CRUD, issue-inventory, record-sale.

160 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:55:29 +08:00

120 lines
4.0 KiB
PHP

<?php
namespace Tests\Feature\Api\V1\Store;
use App\Models\Household;
use App\Models\PartnerStore;
use App\Models\QrCode;
use App\Models\StoreInventory;
use App\Models\User;
use App\Services\Store\StoreOperations;
use Database\Seeders\RoleSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class PartnerStoreTest extends TestCase
{
use RefreshDatabase;
private User $admin;
protected function setUp(): void
{
parent::setUp();
$this->seed(RoleSeeder::class);
$this->admin = User::factory()->create(['role' => User::ROLE_ADMIN, 'status' => 'active']);
}
public function test_admin_can_create_partner_store(): void
{
Sanctum::actingAs($this->admin);
$owner = User::factory()->create(['role' => User::ROLE_STORE_PARTNER]);
$response = $this->postJson('/api/v1/admin/partner-stores', [
'owner_user_id' => $owner->id,
'business_name' => 'Diliman Grocery',
'commission_rate_percent' => 15,
'status' => 'active',
]);
$response->assertCreated()
->assertJsonPath('data.business_name', 'Diliman Grocery')
->assertJsonPath('data.commission_rate_percent', 15);
}
public function test_admin_can_issue_inventory_to_store(): void
{
Sanctum::actingAs($this->admin);
$store = PartnerStore::factory()->create(['status' => 'active']);
$response = $this->postJson("/api/v1/admin/partner-stores/{$store->uuid}/issue-inventory", [
'quantity' => 50,
'wholesale_price_centavos' => 250000,
]);
$response->assertCreated()
->assertJsonPath('data.quantity', 50)
->assertJsonPath('data.inventory_balance', 50);
$allocated = QrCode::where('assigned_to_store_id', $store->id)
->where('status', 'allocated')->count();
$this->assertSame(50, $allocated);
}
public function test_inactive_store_cannot_receive_inventory(): void
{
Sanctum::actingAs($this->admin);
$store = PartnerStore::factory()->create(['status' => 'pending_kyc']);
$response = $this->postJson("/api/v1/admin/partner-stores/{$store->uuid}/issue-inventory", [
'quantity' => 10, 'wholesale_price_centavos' => 50000,
]);
$response->assertStatus(422);
}
public function test_admin_can_record_sale_and_codes_activate(): void
{
Sanctum::actingAs($this->admin);
$store = PartnerStore::factory()->create(['status' => 'active', 'commission_rate_percent' => 10]);
app(StoreOperations::class)->issueWholesale($store, 20, 100000);
$household = Household::factory()->create();
$response = $this->postJson("/api/v1/admin/partner-stores/{$store->uuid}/sales", [
'household_id' => $household->id,
'quantity' => 5,
'retail_price_per_code_centavos' => 1000,
]);
$response->assertCreated()
->assertJsonPath('data.quantity', 5)
->assertJsonPath('data.retail_price_centavos', 5000)
->assertJsonPath('data.commission_centavos', 500);
$active = QrCode::where('assigned_to_household_id', $household->id)
->where('status', 'active')->count();
$this->assertSame(5, $active);
$balance = StoreInventory::where('store_id', $store->id)->value('current_code_balance');
$this->assertSame(15, (int) $balance);
}
public function test_oversold_request_fails(): void
{
Sanctum::actingAs($this->admin);
$store = PartnerStore::factory()->create(['status' => 'active']);
app(StoreOperations::class)->issueWholesale($store, 5, 25000);
$household = Household::factory()->create();
$response = $this->postJson("/api/v1/admin/partner-stores/{$store->uuid}/sales", [
'household_id' => $household->id,
'quantity' => 10,
'retail_price_per_code_centavos' => 1000,
]);
$response->assertStatus(422);
}
}