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); } public function test_sale_omitting_price_uses_global_srp(): void { Sanctum::actingAs($this->admin); $store = PartnerStore::factory()->create(['status' => 'active', 'commission_rate_percent' => 10]); app(StoreOperations::class)->issueWholesale($store, 10, 50000); $household = Household::factory()->create(); $response = $this->postJson("/api/v1/admin/partner-stores/{$store->uuid}/sales", [ 'household_id' => $household->id, 'quantity' => 5, ]); $response->assertCreated() ->assertJsonPath('data.quantity', 5) ->assertJsonPath('data.retail_price_centavos', 5000) ->assertJsonPath('data.commission_centavos', 500); } public function test_sale_with_incorrect_price_fails_validation(): void { Sanctum::actingAs($this->admin); $store = PartnerStore::factory()->create(['status' => 'active']); app(StoreOperations::class)->issueWholesale($store, 10, 50000); $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' => 1200, ]); $response->assertStatus(422) ->assertJsonValidationErrors(['retail_price_per_code_centavos']); } public function test_admin_can_view_analytics_and_history_endpoints(): void { Sanctum::actingAs($this->admin); $store = PartnerStore::factory()->create(['status' => 'active', 'commission_rate_percent' => 10]); // Issue wholesale (creates purchase) app(StoreOperations::class)->issueWholesale($store, 20, 100000); // Sell to household (creates sale) $household = Household::factory()->create(); app(StoreOperations::class)->sellToHousehold($store, $household, 5, 1000); // Test purchases endpoint $response = $this->getJson("/api/v1/admin/partner-stores/{$store->uuid}/purchases"); $response->assertOk() ->assertJsonStructure(['data', 'meta']) ->assertJsonCount(1, 'data') ->assertJsonPath('data.0.quantity', 20) ->assertJsonPath('data.0.wholesale_price_pesos', '1,000.00'); // Test sales-history endpoint $response = $this->getJson("/api/v1/admin/partner-stores/{$store->uuid}/sales-history"); $response->assertOk() ->assertJsonStructure(['data', 'meta']) ->assertJsonCount(1, 'data') ->assertJsonPath('data.0.quantity', 5) ->assertJsonPath('data.0.retail_price_pesos', '50.00') ->assertJsonPath('data.0.commission_pesos', '5.00'); // Test analytics endpoint $response = $this->getJson("/api/v1/admin/partner-stores/{$store->uuid}/analytics"); $response->assertOk() ->assertJson([ 'data' => [ 'total_wholesale_cost_pesos' => '1,000.00', 'total_retail_revenue_pesos' => '50.00', 'total_commission_pesos' => '5.00', 'gross_profit_pesos' => '-950.00', 'total_issued_codes' => 20, 'total_sold_codes' => 5, ] ]); } }