defaultTenant is already created and set in parent::setUp() $this->admin = User::factory()->create(['tenant_id' => $this->defaultTenant->id, 'role' => 'admin']); $owner = User::factory()->create(['tenant_id' => $this->defaultTenant->id, 'role' => 'store_partner']); $this->store = PartnerStore::factory()->create([ 'tenant_id' => $this->defaultTenant->id, 'owner_user_id' => $owner->id, 'commission_rate_percent' => 10, 'status' => 'active', ]); $this->ops = app(StoreOperations::class); } public function test_calculate_balance_due_consignment_model() { // 1. Issue inventory (already in setup) // 2. Record sales (Retail Price 100, Commission 10%) // Total Sold: 2 codes @ 100 each = 200 Gross // Commission: 10% of 200 = 20 // Net Owed to City: 200 - 20 = 180 StoreSale::factory()->count(2)->create([ 'store_id' => $this->store->id, 'retail_price_centavos' => 10000, 'commission_centavos' => 1000, ]); $balance = $this->ops->calculateBalanceDue($this->store); $this->assertEquals(18000, $balance); // 180.00 in centavos // 3. Record payment $response = $this->actingAs($this->admin) ->postJson("/api/v1/admin/partner-stores/{$this->store->uuid}/settle", [ 'amount_pesos' => 100, 'payment_method' => 'cash', 'reference_number' => 'REF123', ]) ->assertSuccessful(); // 4. Check new balance: 180 - 100 = 80 $newBalance = $this->ops->calculateBalanceDue($this->store); $this->assertEquals(8000, $newBalance); } public function test_report_defective_with_replacement() { // Setup: Issue a specific code $qr = QrCode::factory()->create([ 'tenant_id' => $this->defaultTenant->id, 'assigned_to_store_id' => $this->store->id, 'status' => 'allocated', ]); \App\Models\StoreInventory::create([ 'store_id' => $this->store->id, 'current_code_balance' => 1, 'prepaid_balance' => 0 ]); $this->actingAs($this->admin) ->postJson("/api/v1/admin/partner-stores/{$this->store->uuid}/report-issue", [ 'serial' => $qr->serial, 'reason' => 'Defective Adhesive', ]) ->assertSuccessful(); // Original should be voided $qr->refresh(); $this->assertEquals('voided', $qr->status); // A new replacement QR should exist $replacement = QrCode::where('replacement_for_id', $qr->id)->first(); $this->assertNotNull($replacement); $this->assertEquals('allocated', $replacement->status); $this->assertEquals($this->store->id, $replacement->assigned_to_store_id); // Inventory balance should be 1 (started at 1, -1 for defective, +1 for replacement) $this->store->refresh()->load('inventory'); $this->assertEquals(1, $this->store->inventory?->current_code_balance ?? 0); } public function test_print_replacement_endpoint() { $qr = QrCode::factory()->create([ 'tenant_id' => $this->defaultTenant->id, 'assigned_to_store_id' => $this->store->id, 'status' => 'voided', ]); $replacement = QrCode::factory()->create([ 'tenant_id' => $this->defaultTenant->id, 'assigned_to_store_id' => $this->store->id, 'status' => 'allocated', 'replacement_for_id' => $qr->id, ]); $this->actingAs($this->admin) ->getJson("/api/v1/admin/partner-stores/{$this->store->uuid}/qr-codes/{$replacement->serial}/print") ->assertSuccessful() ->assertJsonStructure(['data' => ['qr_data', 'serial']]); } public function test_analytics_includes_financials() { // 1 sale: 100 retail, 10 comm -> 90 owed StoreSale::factory()->create([ 'store_id' => $this->store->id, 'retail_price_centavos' => 10000, 'commission_centavos' => 1000, ]); $this->actingAs($this->admin) ->getJson("/api/v1/admin/partner-stores/{$this->store->uuid}/analytics") ->assertSuccessful() ->assertJsonFragment([ 'balance_due_pesos' => '90.00', 'total_settled_pesos' => '0.00', ]); } }