diff --git a/src/backend/app/Http/Controllers/AgentAuthController.php b/src/backend/app/Http/Controllers/AgentAuthController.php index f945ffc5b..1feda46dd 100644 --- a/src/backend/app/Http/Controllers/AgentAuthController.php +++ b/src/backend/app/Http/Controllers/AgentAuthController.php @@ -3,7 +3,9 @@ namespace App\Http\Controllers; use App\Models\Agent; +use App\Models\MainSettings; use App\Services\AgentService; +use App\Services\MainSettingsService; use App\Utils\DemoCompany; use Dedoc\Scramble\Attributes\BodyParameter; use Dedoc\Scramble\Attributes\Group; @@ -18,6 +20,7 @@ class AgentAuthController extends Controller { */ public function __construct( private AgentService $agentService, + private MainSettingsService $mainSettingsService, ) {} /** @@ -83,6 +86,7 @@ public function me(): JsonResponse { 'agent' => $agent, 'roles' => $roles, 'permissions' => $permissions, + 'settings' => $this->mainSettings(), ]); } @@ -128,6 +132,24 @@ protected function respondWithToken($token) { 'agent' => $agent, 'roles' => $roles, 'permissions' => $permissions, + 'settings' => $this->mainSettings(), ]); } + + /** + * @return array|null + */ + private function mainSettings(): ?array { + $settings = $this->mainSettingsService->getAll()->first(); + if (!$settings instanceof MainSettings) { + return null; + } + + return [ + 'currency' => $settings->currency, + 'country' => $settings->country, + 'language' => $settings->language, + 'company_name' => $settings->company_name, + ]; + } } diff --git a/src/backend/app/Http/Controllers/AgentCustomerController.php b/src/backend/app/Http/Controllers/AgentCustomerController.php index 6be2762a4..aefa80629 100644 --- a/src/backend/app/Http/Controllers/AgentCustomerController.php +++ b/src/backend/app/Http/Controllers/AgentCustomerController.php @@ -2,10 +2,15 @@ namespace App\Http\Controllers; +use App\Http\Requests\CreateAgentCustomerRequest; use App\Http\Resources\ApiResource; use App\Services\AgentCustomerService; use App\Services\AgentService; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\ValidationException; class AgentCustomerController extends Controller { public function __construct( @@ -24,6 +29,12 @@ public function index(Request $request) { return ApiResource::make($this->agentCustomerService->list($agent)); } + public function show(int $customerId): ApiResource { + $agent = $this->agentService->getByAuthenticatedUser(); + + return ApiResource::make($this->agentCustomerService->findForAgent($agent, $customerId)); + } + public function search(Request $request): ApiResource { $term = $request->input('term'); $limit = $request->input('paginate', 15); @@ -31,4 +42,23 @@ public function search(Request $request): ApiResource { return ApiResource::make($this->agentCustomerService->search($term, $limit, $agent)); } + + public function store(CreateAgentCustomerRequest $request): JsonResponse { + $agent = $this->agentService->getByAuthenticatedUser(); + + try { + DB::connection('tenant')->beginTransaction(); + $person = $this->agentCustomerService->register($agent, $request); + DB::connection('tenant')->commit(); + + return ApiResource::make($person)->response()->setStatusCode(201); + } catch (ValidationException $e) { + DB::connection('tenant')->rollBack(); + throw $e; + } catch (\Exception $e) { + DB::connection('tenant')->rollBack(); + Log::critical('Error while an agent was registering a customer', ['message' => $e->getMessage()]); + throw $e; + } + } } diff --git a/src/backend/app/Http/Controllers/AgentTransactionsController.php b/src/backend/app/Http/Controllers/AgentTransactionsController.php index 53c8fe485..8c4eca9dc 100644 --- a/src/backend/app/Http/Controllers/AgentTransactionsController.php +++ b/src/backend/app/Http/Controllers/AgentTransactionsController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Http\Resources\ApiResource; +use App\Models\Transaction\Transaction; use App\Services\AgentService; use App\Services\AgentTransactionService; use Illuminate\Http\Request; @@ -27,4 +28,24 @@ public function show(int $customerId, Request $request): ApiResource { return ApiResource::make($this->agentTransactionService->getByCustomerId($agent->id, $customerId)); } + + /** + * Return the token generated for one of the agent's transactions, if any. + * Token generation is asynchronous (queued in ProcessPayment), so the + * field app polls this endpoint after a successful POST until the token + * appears or it gives up. + */ + public function token(int $transactionId): ApiResource { + $agent = $this->agentService->getByAuthenticatedUser(); + $transaction = $this->agentTransactionService->findForAgent($agent->id, $transactionId); + + if (!$transaction instanceof Transaction) { + abort(404, 'Transaction not found.'); + } + + return ApiResource::make([ + 'transaction_id' => $transaction->id, + 'token' => $transaction->token, + ]); + } } diff --git a/src/backend/app/Http/Controllers/TransactionController.php b/src/backend/app/Http/Controllers/TransactionController.php index 2ce215e08..0b3b05d12 100644 --- a/src/backend/app/Http/Controllers/TransactionController.php +++ b/src/backend/app/Http/Controllers/TransactionController.php @@ -27,7 +27,7 @@ public function show(int $id): ApiResource { return ApiResource::make($transaction); } - public function store(Request $request): void { + public function store(Request $request): ApiResource { /** * @var ITransactionProvider $transactionProvider */ @@ -44,5 +44,9 @@ public function store(Request $request): void { Log::warning('Company ID not found in request attributes. Payment transaction job not triggered for transaction '.$transaction->id); } } + + return ApiResource::make([ + 'id' => $transaction->id ?? null, + ]); } } diff --git a/src/backend/app/Http/Middleware/AgentBalanceMiddleware.php b/src/backend/app/Http/Middleware/AgentBalanceMiddleware.php index 846b8fd6b..d7730bf43 100644 --- a/src/backend/app/Http/Middleware/AgentBalanceMiddleware.php +++ b/src/backend/app/Http/Middleware/AgentBalanceMiddleware.php @@ -48,7 +48,7 @@ public function handle($request, \Closure $next) { throw new DownPaymentBiggerThanAmountException('Down payment is bigger than amount'); } } - if ($routeName === 'agent-transaction') { + if (in_array($routeName, ['agent-transaction', 'agent-app-transaction'], true)) { if ($transactionAmount = $request->input('amount')) { $agentBalance -= $transactionAmount; } else { diff --git a/src/backend/app/Http/Requests/CreateAgentCustomerRequest.php b/src/backend/app/Http/Requests/CreateAgentCustomerRequest.php new file mode 100644 index 000000000..df535816a --- /dev/null +++ b/src/backend/app/Http/Requests/CreateAgentCustomerRequest.php @@ -0,0 +1,24 @@ + + */ + public function rules(): array { + return [ + 'name' => ['required', 'string', 'min:3'], + 'surname' => ['required', 'string', 'min:3'], + 'phone' => ['required', 'string', 'phone:INTERNATIONAL'], + 'city_id' => ['required', 'integer', 'exists:tenant.cities,id'], + 'geo_points' => ['nullable', 'string'], + ]; + } +} diff --git a/src/backend/app/Http/Requests/CreateAgentSoldApplianceRequest.php b/src/backend/app/Http/Requests/CreateAgentSoldApplianceRequest.php index 6b11aaf03..8809089cd 100644 --- a/src/backend/app/Http/Requests/CreateAgentSoldApplianceRequest.php +++ b/src/backend/app/Http/Requests/CreateAgentSoldApplianceRequest.php @@ -2,6 +2,8 @@ namespace App\Http\Requests; +use App\Models\AgentAssignedAppliances; +use App\Models\ApplianceType; use Illuminate\Foundation\Http\FormRequest; class CreateAgentSoldApplianceRequest extends FormRequest { @@ -18,6 +20,10 @@ public function authorize(): bool { * @return array */ public function rules(): array { + $deviceSerialRules = $this->isShsAppliance() + ? ['required', 'string'] + : ['nullable', 'string']; + return [ 'person_id' => ['required'], 'payment_type' => ['nullable', 'string', 'in:installment,energy_service'], @@ -25,11 +31,31 @@ public function rules(): array { 'tenure' => ['required_unless:payment_type,energy_service', 'numeric', 'min:0'], 'first_payment_date' => ['required_unless:payment_type,energy_service'], 'agent_assigned_appliance_id' => ['required'], - 'device_serial' => ['nullable', 'string'], + 'device_serial' => $deviceSerialRules, 'address' => ['nullable', 'array'], 'points' => ['nullable', 'string'], 'minimum_payable_amount' => ['nullable', 'integer', 'min:0'], 'price_per_day' => ['nullable', 'integer', 'min:0'], ]; } + + /** + * @return array + */ + public function messages(): array { + return [ + 'device_serial.required' => 'Device serial is required for solar home system sales.', + ]; + } + + private function isShsAppliance(): bool { + $assignedId = $this->input('agent_assigned_appliance_id'); + if (!$assignedId) { + return false; + } + + $assigned = AgentAssignedAppliances::with('appliance')->find($assignedId); + + return $assigned?->appliance?->appliance_type_id === ApplianceType::APPLIANCE_TYPE_SHS; + } } diff --git a/src/backend/app/Services/AgentCustomerService.php b/src/backend/app/Services/AgentCustomerService.php index 59d52e7eb..13f3bf022 100644 --- a/src/backend/app/Services/AgentCustomerService.php +++ b/src/backend/app/Services/AgentCustomerService.php @@ -2,19 +2,69 @@ namespace App\Services; +use App\Http\Requests\CreateAgentCustomerRequest; use App\Models\Agent; +use App\Models\City; use App\Models\Person\Person; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Validation\ValidationException; class AgentCustomerService { - public function __construct(private Person $person) {} + public function __construct( + private Person $person, + private City $city, + private PersonService $personService, + private GeographicalInformationService $geographicalInformationService, + private AddressGeographicalInformationService $addressGeographicalInformationService, + ) {} + + public function register(Agent $agent, CreateAgentCustomerRequest $request): Person { + $cityId = $request->integer('city_id'); + $phone = $request->string('phone')->toString(); + $geoPoints = $request->string('geo_points')->toString(); + + $city = $this->city->newQuery()->findOrFail($cityId); + if ($city->mini_grid_id !== $agent->mini_grid_id) { + throw ValidationException::withMessages(['city_id' => ['Selected city does not belong to the agent\'s mini-grid.']]); + } + + if ($this->personService->getByPhoneNumber($phone) instanceof Person) { + throw ValidationException::withMessages(['phone' => ['A customer with this phone number already exists.']]); + } + + $request->merge(['is_customer' => 1]); + $person = $this->personService->createFromRequest($request); + + if ($geoPoints !== '') { + $address = $person->addresses()->where('is_primary', 1)->first(); + $geographicalInformation = $this->geographicalInformationService->make([ + 'points' => $geoPoints, + ]); + $this->addressGeographicalInformationService->setAssigned($geographicalInformation); + $this->addressGeographicalInformationService->setAssignee($address); + $this->addressGeographicalInformationService->assign(); + $this->geographicalInformationService->save($geographicalInformation); + } + + return $person->fresh(['addresses.city', 'addresses.geo']); + } /** * @return LengthAwarePaginator */ public function list(Agent $agent): LengthAwarePaginator { - $miniGridId = $agent->mini_grid_id; + return $this->scopedQuery($agent)->paginate(config('settings.paginate')); + } + + public function findForAgent(Agent $agent, int $customerId): Person { + return $this->scopedQuery($agent)->findOrFail($customerId); + } + /** + * @return Builder + */ + private function scopedQuery(Agent $agent): Builder { return $this->person->newQuery()->with([ 'devices', 'addresses' => fn ($q) => $q->where('is_primary', 1)->with('city'), @@ -22,9 +72,8 @@ public function list(Agent $agent): LengthAwarePaginator { ->where('is_customer', 1) ->whereHas( 'addresses', - fn ($q) => $q->whereHas('city', fn ($q) => $q->where('mini_grid_id', $miniGridId)) - ) - ->paginate(config('settings.paginate')); + fn ($q) => $q->whereHas('city', fn ($q) => $q->where('mini_grid_id', $agent->mini_grid_id)) + ); } /** diff --git a/src/backend/app/Services/AgentSoldApplianceService.php b/src/backend/app/Services/AgentSoldApplianceService.php index a7b241d3f..690f70e6c 100644 --- a/src/backend/app/Services/AgentSoldApplianceService.php +++ b/src/backend/app/Services/AgentSoldApplianceService.php @@ -205,7 +205,7 @@ public function processSaleFromRequest(AgentSoldAppliance $agentSoldAppliance, a 'street' => $addressFromCustomer->street, 'city_id' => $addressFromCustomer->city_id, ]; - $points = $requestData['points'] ?? $addressFromCustomer->geo()->first()->points; + $points = $requestData['points'] ?? $addressFromCustomer->geo()->first()?->points; $device = $this->deviceService->getBySerialNumber($deviceSerial); $this->deviceService->update($device, ['person_id' => $requestData['person_id']]); @@ -218,14 +218,16 @@ public function processSaleFromRequest(AgentSoldAppliance $agentSoldAppliance, a // Attach the new address to the buyer (person) rather than the device. $this->addressesService->assignAddressToOwner($appliancePerson->person, $address); - $geoInfo = $this->geographicalInformationService->make([ - 'points' => $points, - ]); + if ($points) { + $geoInfo = $this->geographicalInformationService->make([ + 'points' => $points, + ]); - $this->addressGeographicalInformationService->setAssigned($geoInfo); - $this->addressGeographicalInformationService->setAssignee($address); - $this->addressGeographicalInformationService->assign(); - $this->geographicalInformationService->save($geoInfo); + $this->addressGeographicalInformationService->setAssigned($geoInfo); + $this->addressGeographicalInformationService->setAssignee($address); + $this->addressGeographicalInformationService->assign(); + $this->geographicalInformationService->save($geoInfo); + } } // initalize appliance Rates diff --git a/src/backend/app/Services/AgentTransactionService.php b/src/backend/app/Services/AgentTransactionService.php index b6d795619..02f596538 100644 --- a/src/backend/app/Services/AgentTransactionService.php +++ b/src/backend/app/Services/AgentTransactionService.php @@ -70,6 +70,22 @@ public function getById(int $id): AgentTransaction { throw new \Exception('Method getById() not yet implemented.'); } + /** + * Find a transaction owned by the given agent and eager-load its token. + * Returns null if the transaction doesn't exist or doesn't belong to the agent. + */ + public function findForAgent(int $agentId, int $transactionId): ?Transaction { + return $this->transaction->newQuery() + ->with(['token']) + ->whereHasMorph( + 'originalTransaction', + [AgentTransaction::class], + fn ($q) => $q->where('agent_id', $agentId) + ) + ->where('id', $transactionId) + ->first(); + } + /** * @param array $transactionData */ diff --git a/src/backend/routes/resources/AgentApp.php b/src/backend/routes/resources/AgentApp.php index d682038e0..af3d684c0 100644 --- a/src/backend/routes/resources/AgentApp.php +++ b/src/backend/routes/resources/AgentApp.php @@ -13,6 +13,7 @@ use App\Http\Controllers\AgentSoldApplianceController; use App\Http\Controllers\AgentTicketController; use App\Http\Controllers\AgentTransactionsController; +use App\Http\Controllers\TransactionController; use Illuminate\Support\Facades\Route; // Android App Services @@ -30,6 +31,7 @@ Route::get('/balance', [AgentBalanceController::class, 'show']); Route::group(['prefix' => 'customers'], function () { Route::get('/', [AgentCustomerController::class, 'index']); + Route::post('/', [AgentCustomerController::class, 'store']); Route::get('/search', [AgentCustomerController::class, 'search']); Route::get( '/{customerId}/graph/{period}/{limit?}/{order?}', @@ -39,10 +41,18 @@ '/graph/{period}/{limit?}/{order?}', [AgentCustomersPaymentHistoryController::class, 'index'] ); + Route::get('/{customerId}', [AgentCustomerController::class, 'show']) + ->where('customerId', '[0-9]+'); }); Route::group(['prefix' => 'transactions'], function () { Route::get('/', [AgentTransactionsController::class, 'index']); - Route::get('/{customerId}', [AgentTransactionsController::class, 'show']); + Route::get('/{transactionId}/token', [AgentTransactionsController::class, 'token']) + ->where('transactionId', '[0-9]+'); + Route::get('/{customerId}', [AgentTransactionsController::class, 'show']) + ->where('customerId', '[0-9]+'); + Route::post('/', [TransactionController::class, 'store']) + ->name('agent-app-transaction') + ->middleware(['transaction.auth', 'transaction.request', 'agent.balance']); }); Route::group(['prefix' => 'appliances'], function () { Route::get('/', [AgentSoldApplianceController::class, 'index']); diff --git a/src/backend/tests/Feature/AgentAppTest.php b/src/backend/tests/Feature/AgentAppTest.php index 1115de6f3..e04a53ea4 100644 --- a/src/backend/tests/Feature/AgentAppTest.php +++ b/src/backend/tests/Feature/AgentAppTest.php @@ -2,8 +2,14 @@ namespace Tests\Feature; +use App\Jobs\ProcessPayment; use App\Models\AgentAssignedAppliances; +use App\Models\City; +use App\Models\MainSettings; use App\Models\Person\Person; +use App\Models\Transaction\AgentTransaction; +use App\Models\Transaction\Transaction; +use Illuminate\Support\Facades\Queue; use Tests\CreateEnvironments; use Tests\TestCase; @@ -44,6 +50,87 @@ public function testAgentGetsOwnData(): void { $this->assertEquals($agent->person->id, $response['agent']['person_id']); } + public function testAgentLoginResponseIncludesTenantSettings(): void { + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + $this->setMainSettings([ + 'currency' => 'TZS', + 'country' => 'Tanzania', + 'language' => 'sw', + 'company_name' => 'Acme Mini-Grid', + ]); + + $response = $this->post('/api/app/login', [ + 'email' => $this->agent->email, + 'password' => '123456', + ]); + + $response->assertStatus(200); + $response->assertJsonPath('settings.currency', 'TZS'); + $response->assertJsonPath('settings.country', 'Tanzania'); + $response->assertJsonPath('settings.language', 'sw'); + $response->assertJsonPath('settings.company_name', 'Acme Mini-Grid'); + } + + public function testAgentMeResponseIncludesTenantSettings(): void { + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + $this->setMainSettings([ + 'currency' => 'TZS', + 'country' => 'Tanzania', + 'language' => 'sw', + 'company_name' => 'Acme Mini-Grid', + ]); + + $response = $this->actingAs($this->agent)->get('/api/app/me'); + + $response->assertStatus(200); + $response->assertJsonPath('settings.currency', 'TZS'); + $response->assertJsonPath('settings.country', 'Tanzania'); + $response->assertJsonPath('settings.language', 'sw'); + $response->assertJsonPath('settings.company_name', 'Acme Mini-Grid'); + } + + public function testAgentAuthSettingsAreNullWhenMainSettingsAreMissing(): void { + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + MainSettings::query()->delete(); + + $response = $this->post('/api/app/login', [ + 'email' => $this->agent->email, + 'password' => '123456', + ]); + + $response->assertStatus(200); + $response->assertJsonPath('settings', null); + } + + /** + * @param array $attributes + */ + private function setMainSettings(array $attributes): MainSettings { + $settings = MainSettings::query()->first(); + if ($settings instanceof MainSettings) { + $settings->update($attributes); + + return $settings->fresh(); + } + + return MainSettings::factory()->create($attributes); + } + public function testAgentLogsOut(): void { $this->createTestData(); $this->createCluster(); @@ -337,6 +424,129 @@ public function testAgentGetsApplicationDashboardGraphValues(): void { $response->assertStatus(200); } + public function testAgentRegistersCustomer(): void { + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + + $postData = [ + 'name' => 'Jane', + 'surname' => 'Doe', + 'phone' => '+14155550100', + 'city_id' => $this->city->id, + 'geo_points' => '52.5200,13.4050', + ]; + $response = $this->actingAs($this->agent)->post('/api/app/agents/customers', $postData); + $response->assertStatus(201); + $this->assertEquals('Jane', $response['data']['name']); + $this->assertEquals(1, $response['data']['is_customer']); + + $person = Person::query()->where('name', 'Jane')->where('surname', 'Doe')->firstOrFail(); + $address = $person->addresses()->where('is_primary', 1)->firstOrFail(); + $this->assertEquals($this->city->id, $address->city_id); + $this->assertEquals('+14155550100', $address->phone); + $this->assertEquals('52.5200,13.4050', $address->geo->points); + } + + public function testAgentCannotRegisterCustomerWithDuplicatePhone(): void { + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + + $postData = [ + 'name' => 'Jane', + 'surname' => 'Doe', + 'phone' => '+14155550100', + 'city_id' => $this->city->id, + ]; + $this->actingAs($this->agent)->post('/api/app/agents/customers', $postData)->assertStatus(201); + $response = $this->actingAs($this->agent)->post('/api/app/agents/customers', $postData); + $response->assertStatus(422); + $response->assertJsonValidationErrors(['phone']); + } + + public function testAgentCannotRegisterCustomerInForeignMiniGrid(): void { + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(2); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + + $foreignMiniGrid = collect($this->miniGrids) + ->first(fn ($miniGrid): bool => $miniGrid->id !== $this->agent->mini_grid_id); + $foreignCity = City::query()->create([ + 'name' => 'Foreignville', + 'country_id' => 1, + 'mini_grid_id' => $foreignMiniGrid->id, + ]); + + $postData = [ + 'name' => 'Jane', + 'surname' => 'Doe', + 'phone' => '+14155550100', + 'city_id' => $foreignCity->id, + ]; + $response = $this->actingAs($this->agent)->post('/api/app/agents/customers', $postData); + $response->assertStatus(422); + $response->assertJsonValidationErrors(['city_id']); + } + + public function testAgentRecordsCashTransaction(): void { + Queue::fake(); + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + + $postData = [ + 'device_serial' => 'MTR-TX-001', + 'amount' => 500, + ]; + $response = $this->actingAs($this->agent) + ->post('/api/app/agents/transactions', $postData, ['device-id' => $this->agent->mobile_device_id]); + $response->assertStatus(200); + + $transaction = Transaction::query()->where('message', 'MTR-TX-001')->firstOrFail(); + $this->assertSame(500, (int) $transaction->amount); + $this->assertSame('Agent-'.$this->agent->id, $transaction->sender); + $this->assertSame('energy', $transaction->type); + $this->assertSame('agent_transaction', $transaction->original_transaction_type); + + $agentTransaction = AgentTransaction::query()->where('agent_id', $this->agent->id)->firstOrFail(); + $this->assertSame($agentTransaction->id, (int) $transaction->original_transaction_id); + + Queue::assertPushed(ProcessPayment::class); + } + + public function testAgentTransactionFailsWhenAmountExceedsFloat(): void { + Queue::fake(); + $this->createTestData(); + $this->createCluster(); + $this->createMiniGrid(); + $this->createCity(); + $this->createAgentCommission(); + $this->createAgent(); + + $postData = [ + 'device_serial' => 'MTR-TX-002', + 'amount' => 999_999_999, + ]; + $response = $this->actingAs($this->agent) + ->post('/api/app/agents/transactions', $postData, ['device-id' => $this->agent->mobile_device_id]); + $response->assertStatus(500); + $this->assertSame(0, Transaction::query()->where('message', 'MTR-TX-002')->count()); + Queue::assertNotPushed(ProcessPayment::class); + } + public function testAgentGetsApplicationDashboardWeeklyRevenues(): void { $this->createTestData(); $this->createCluster(); diff --git a/src/backend/tests/Unit/AgentSellApplianceTest.php b/src/backend/tests/Unit/AgentSellApplianceTest.php index 96e1de90a..8d06626da 100644 --- a/src/backend/tests/Unit/AgentSellApplianceTest.php +++ b/src/backend/tests/Unit/AgentSellApplianceTest.php @@ -7,9 +7,12 @@ use App\Models\AgentCommission; use App\Models\AgentSoldAppliance; use App\Models\Appliance; +use App\Models\City; use App\Models\Cluster; +use App\Models\Device; use App\Models\MiniGrid; use App\Models\PaymentHistory; +use App\Models\SolarHomeSystem; use Database\Factories\Person\PersonFactory; use Database\Factories\UserFactory; use Illuminate\Foundation\Testing\WithFaker; @@ -105,12 +108,38 @@ public function initData(): array { 'cost' => 100, ]); + $city = City::query()->create([ + 'name' => 'Test City', + 'country_id' => 1, + 'cluster_id' => $cluster->id, + 'mini_grid_id' => $miniGrid->id, + ]); + + $shs = SolarHomeSystem::query()->create([ + 'serial_number' => 'SHS-TEST-0001', + 'manufacturer_id' => 1, + 'appliance_id' => $appliance->id, + ]); + + Device::query()->create([ + 'person_id' => $person->id, + 'device_id' => $shs->id, + 'device_type' => SolarHomeSystem::class, + 'device_serial' => 'SHS-TEST-0001', + ]); + return [ 'agent_assigned_appliance_id' => $agentAssignedAppliance->id, 'person_id' => $person->id, 'first_payment_date' => '2020-12-29T20:53:38Z', 'down_payment' => 100, 'tenure' => 5, + 'device_serial' => 'SHS-TEST-0001', + 'address' => [ + 'street' => '1 Test Street', + 'city_id' => $city->id, + ], + 'points' => '0,0', ]; } }