diff --git a/NginxProxy/conf.p/app.conf b/NginxProxy/conf.p/app.conf index 44ffc438e..0329d7b94 100644 --- a/NginxProxy/conf.p/app.conf +++ b/NginxProxy/conf.p/app.conf @@ -1,23 +1,45 @@ - server { - listen 80; - server_name staging.micropowermanager.com; - root /var/www/html/dist; - index index.php index.html index.htm; - include /etc/nginx/mime.types; - - location / { +server { + listen 80; + server_tokens off; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_tokens off; + + ssl_certificate /etc/letsencrypt/live/demo.micropowermanager.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/demo.micropowermanager.com/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + error_log /var/log/nginx/error.log; + access_log /var/log/nginx/access.log; + root /var/www/html/dist; + index index.php index.html index.htm; + + + location / { try_files $uri /index.html; - } + } - location /api/ { + location /tickets/ { try_files $uri /index.php?$args; gzip_static on; } - location /tickets/ { + + location /api/ { try_files $uri /index.php?$args; gzip_static on; - } - location ~ \.php$ { + } + + location ~ \.php$ { root /var/www/html/mpmanager/public; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass laravel:9000; @@ -25,6 +47,8 @@ include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; - } - } + fastcgi_buffer_size 128k; + fastcgi_buffers 4 256k; + } +} \ No newline at end of file diff --git a/Website/htdocs/mpmanager/app/Http/Controllers/CustomerRegistrationAppController.php b/Website/htdocs/mpmanager/app/Http/Controllers/CustomerRegistrationAppController.php new file mode 100644 index 000000000..846845a0e --- /dev/null +++ b/Website/htdocs/mpmanager/app/Http/Controllers/CustomerRegistrationAppController.php @@ -0,0 +1,32 @@ +beginTransaction(); + $person = $this->customerRegistrationAppService->createCustomer($request); + DB::connection('shard')->commit(); + + return ApiResource::make($person)->response()->setStatusCode(201); + }catch (\Exception $e){ + DB::connection('shard')->rollBack(); + Log::critical('Error while adding new Customer', ['message' => $e->getMessage()]); + throw new \Exception($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/Website/htdocs/mpmanager/app/Jobs/ApplianceTransactionProcessor.php b/Website/htdocs/mpmanager/app/Jobs/ApplianceTransactionProcessor.php index 8dd598a5e..eb3fd5503 100644 --- a/Website/htdocs/mpmanager/app/Jobs/ApplianceTransactionProcessor.php +++ b/Website/htdocs/mpmanager/app/Jobs/ApplianceTransactionProcessor.php @@ -29,7 +29,7 @@ public function executeJob() try { $this->checkForMinimumPurchaseAmount($container); - $this->payApplianceInstallments($container); + $container = $this->payApplianceInstallments($container); $this->processToken($container); } catch (\Exception $e) { Log::info('Transaction failed.: ' . $e->getMessage()); @@ -68,6 +68,7 @@ private function payApplianceInstallments(TransactionDataContainer $container): $applianceInstallmentPayer = resolve('ApplianceInstallmentPayer'); $applianceInstallmentPayer->initialize($container); $applianceInstallmentPayer->payInstallmentsForDevice($container); + $container->paidRates = $applianceInstallmentPayer->paidRates; return $container; } diff --git a/Website/htdocs/mpmanager/app/Jobs/TokenProcessor.php b/Website/htdocs/mpmanager/app/Jobs/TokenProcessor.php index 6036cfd0b..629ec01a6 100644 --- a/Website/htdocs/mpmanager/app/Jobs/TokenProcessor.php +++ b/Website/htdocs/mpmanager/app/Jobs/TokenProcessor.php @@ -3,6 +3,7 @@ namespace App\Jobs; use App\Misc\TransactionDataContainer; +use App\Models\AssetRate; use App\Models\Token; use Exception; use Illuminate\Bus\Queueable; @@ -24,7 +25,7 @@ class TokenProcessor extends AbstractJob public function __construct( TransactionDataContainer $container, bool $reCreate = false, - int $counter = self::MAX_TRIES + int $counter = self::MAX_TRIES, ) { $this->transactionContainer = $container; $this->reCreate = $reCreate; @@ -46,7 +47,7 @@ public function executeJob(): void if ($token === null) { $this->generateToken($api); } - if ($token !== null){ + if ($token !== null) { $this->handlePaymentEvents($token); } @@ -91,13 +92,14 @@ private function handleTokenGenerationFailure(Exception $e): void $this->retryTokenGeneration(); return; } - Log::critical( $this->transactionContainer->manufacturer->name . ' Token listener failed after ' . $this->counter . ' times ', ['message' => $e->getMessage()] ); + $this->handleRollbackInFailure(); + event('transaction.failed', [ $this->transactionContainer->transaction, 'Manufacturer Api did not succeed after 3 times with the following error: ' . $e->getMessage() @@ -119,6 +121,8 @@ private function saveToken(array $tokenData): void $token = Token::query()->make(['token' => $tokenData['token'], 'load' => $tokenData['load']]); $token->transaction()->associate($this->transactionContainer->transaction); $token->save(); + + $this->handlePaymentEvents($token); } private function handlePaymentEvents($token): void @@ -137,4 +141,19 @@ private function handlePaymentEvents($token): void event('transaction.successful', [$this->transactionContainer->transaction]); } + + private function handleRollbackInFailure() + { + $paidRates = $this->transactionContainer->paidRates; + collect($paidRates)->map(function ($paidRate) { + $assetRate = AssetRate::query()->find($paidRate['asset_rate_id']); + $assetRate->remaining += $paidRate['paid']; + $assetRate->update(); + $assetRate->save(); + }); + $paymentHistories = $this->transactionContainer->transaction->paymentHistories()->get(); + $paymentHistories->map(function ($paymentHistory) { + $paymentHistory->delete(); + }); + } } diff --git a/Website/htdocs/mpmanager/app/Listeners/AccessRateListener.php b/Website/htdocs/mpmanager/app/Listeners/AccessRateListener.php index bca8cf8e7..c73699201 100644 --- a/Website/htdocs/mpmanager/app/Listeners/AccessRateListener.php +++ b/Website/htdocs/mpmanager/app/Listeners/AccessRateListener.php @@ -3,20 +3,28 @@ namespace App\Listeners; use App\Exceptions\AccessRates\NoAccessRateFound; -use App\Models\Meter\MeterParameter; -use App\PaymentHandler\AccessRate; +use App\Models\AccessRate\AccessRate; +use App\Models\AccessRate\AccessRatePayment; +use App\Models\Meter\Meter; use Illuminate\Events\Dispatcher; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Log; class AccessRateListener { - public function initializeAccessRatePayment(MeterParameter $meterParameter): void + public function initializeAccessRatePayment(Meter $meter): void { try { - $accessRatePayment = AccessRate::withMeterParameters($meterParameter); - $accessRatePayment->initializeAccessRatePayment()->save(); + $accessRate = $meter->tariff()->first()->accessRate; + if (!$accessRate) { + throw new NoAccessRateFound('Access Rate is not set'); + } + $nextPaymentDate = Carbon::now()->addDays($accessRate->period)->toDateString(); + $accessRatePayment = new AccessRatePayment(); + $accessRatePayment->accessRate()->associate($accessRate); + $accessRatePayment->meter()->associate($meter); + $accessRatePayment->due_date = $nextPaymentDate; + $accessRatePayment->debt = 0; } catch (NoAccessRateFound $exception) { Log::error($exception->getMessage(), ['id' => 'fj3g98suiq3z89fdhfjlsa']); } diff --git a/Website/htdocs/mpmanager/app/Listeners/PaymentListener.php b/Website/htdocs/mpmanager/app/Listeners/PaymentListener.php index 6f138b834..3599dfcfc 100644 --- a/Website/htdocs/mpmanager/app/Listeners/PaymentListener.php +++ b/Website/htdocs/mpmanager/app/Listeners/PaymentListener.php @@ -6,10 +6,9 @@ use App\Models\Asset; use App\Models\AssetRate; use App\Models\Meter\MeterParameter; -use App\Models\Meter\MeterToken; +use App\Models\Token; use App\Services\AccessRatePaymentHistoryService; use App\Services\ApplianceRatePaymentHistoryService; -use App\Services\MeterTokenPaymentHistoryService; use App\Services\PaymentHistoryService; use App\Services\PersonPaymentHistoryService; use App\Services\TransactionPaymentHistoryService; @@ -25,7 +24,6 @@ public function __construct( private PersonPaymentHistoryService $personPaymentHistoryService, private ApplianceRatePaymentHistoryService $applianceRatePaymentHistoryService, private AccessRatePaymentHistoryService $accessRatePaymentHistoryService, - private MeterTokenPaymentHistoryService $meterTokenPaymentHistoryService, private TransactionPaymentHistoryService $transactionPaymentHistoryService, ) { } @@ -99,10 +97,9 @@ public function onPaymentSuccess( $paymentHistory->paid_for_type = Asset::class; $paymentHistory->paid_for_id = $paidFor->id; break; - case $paidFor instanceof MeterToken: - $this->meterTokenPaymentHistoryService->setAssignee($paidFor); - $this->meterTokenPaymentHistoryService->setAssigned($paymentHistory); - $this->meterTokenPaymentHistoryService->assign(); + case $paidFor instanceof Token: + $paymentHistory->paid_for_type = Token::class; + $paymentHistory->paid_for_id = $paidFor->id; break; } diff --git a/Website/htdocs/mpmanager/app/Misc/TransactionDataContainer.php b/Website/htdocs/mpmanager/app/Misc/TransactionDataContainer.php index d61b21d72..d8141b73b 100644 --- a/Website/htdocs/mpmanager/app/Misc/TransactionDataContainer.php +++ b/Website/htdocs/mpmanager/app/Misc/TransactionDataContainer.php @@ -25,7 +25,7 @@ class TransactionDataContainer public float $amount; public float $totalAmount; public float $rawAmount; - public AssetPerson $appliancePerson; + public ?AssetPerson $appliancePerson; public float $installmentCost; public string $dayDifferenceBetweenTwoInstallments; diff --git a/Website/htdocs/mpmanager/app/Models/Token.php b/Website/htdocs/mpmanager/app/Models/Token.php index 873ccbd7a..aa12354c8 100644 --- a/Website/htdocs/mpmanager/app/Models/Token.php +++ b/Website/htdocs/mpmanager/app/Models/Token.php @@ -4,11 +4,17 @@ use App\Models\Transaction\Transaction; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\MorphOne; class Token extends BaseModel { + public const RELATION_NAME = 'token'; public function transaction(): BelongsTo { return $this->belongsTo(Transaction::class); } + public function paymentHistories(): MorphOne + { + return $this->morphOne(PaymentHistory::class, 'paid_for'); + } } diff --git a/Website/htdocs/mpmanager/app/Providers/AppServiceProvider.php b/Website/htdocs/mpmanager/app/Providers/AppServiceProvider.php index 668dcf8b1..5740c3eac 100644 --- a/Website/htdocs/mpmanager/app/Providers/AppServiceProvider.php +++ b/Website/htdocs/mpmanager/app/Providers/AppServiceProvider.php @@ -27,6 +27,7 @@ use App\Models\MiniGrid; use App\Models\Person\Person; use App\Models\SolarHomeSystem; +use App\Models\Token; use App\Models\Transaction\AgentTransaction; use App\Models\Transaction\AirtelTransaction; use App\Models\Transaction\CashTransaction; @@ -89,6 +90,7 @@ public function boot() City::RELATION_NAME => City::class, Address::RELATION_NAME => Address::class, SolarHomeSystem::RELATION_NAME => SolarHomeSystem::class, + Token::RELATION_NAME => Token::class, ] ); } diff --git a/Website/htdocs/mpmanager/app/Services/AgentCustomerService.php b/Website/htdocs/mpmanager/app/Services/AgentCustomerService.php index 2af45efe1..5245b9387 100644 --- a/Website/htdocs/mpmanager/app/Services/AgentCustomerService.php +++ b/Website/htdocs/mpmanager/app/Services/AgentCustomerService.php @@ -19,71 +19,27 @@ public function list(Agent $agent): LengthAwarePaginator { $miniGridId = $agent->mini_grid_id; - return $this->person->newQuery()->with( - [ - 'addresses' => function ($q) { - return $q->where('is_primary', 1); - }, - 'addresses.city', - 'meters.meter', - ] - ) + return $this->person->newQuery()->with([ + 'devices', + 'addresses' => fn($q) => $q->where('is_primary', 1)->with('city'), + ]) ->where('is_customer', 1) ->whereHas( 'addresses', - function ($q) use ($miniGridId) { - $q->whereHas( - 'city', - function ($q) use ($miniGridId) { - $q->where('mini_grid_id', $miniGridId); - } - ); - } - ) + fn($q) => $q->whereHas('city', fn($q) => $q->where('mini_grid_id', $miniGridId))) ->paginate(config('settings.paginate')); } public function search($searchTerm, $limit, $agent) { - $miniGridId = $agent->mini_grid_id; - - return $this->person->newQuery()->with( - [ - 'addresses' => function ($q) { - return $q->where('is_primary', 1); - }, - 'addresses.city', - 'meters.meter', - - ] - )->where('is_customer', 1) - ->where('name', 'LIKE', '%' . $searchTerm . '%') - ->whereHas( - 'addresses.city', - function ($q) use ($searchTerm, $miniGridId) { - $q->where('mini_grid_id', $miniGridId); - } - ) + return $this->person->newQuery()->with(['addresses.city', 'devices'])->whereHas( + 'addresses', fn($q) => $q->where('phone', 'LIKE', '%' . $searchTerm . '%') + )->orWhereHas( + 'devices', + fn($q) => $q->where('device_serial', 'LIKE', '%' . $searchTerm . '%') + )->orWhere('name', 'LIKE', '%' . $searchTerm . '%') ->orWhere('surname', 'LIKE', '%' . $searchTerm . '%') - ->orWhereHas( - 'addresses', - function ($q) use ($searchTerm) { - $q->where('email', 'LIKE', '%' . $searchTerm . '%'); - $q->where('phone', 'LIKE', '%' . $searchTerm . '%'); - } - ) - ->orWhereHas( - 'addresses.city', - function ($q) use ($searchTerm) { - $q->where('name', 'LIKE', '%' . $searchTerm . '%'); - } - ) - ->orWhereHas( - 'meters.meter', - function ($q) use ($searchTerm) { - $q->where('serial_number', 'LIKE', '%' . $searchTerm . '%'); - } - )->paginate($limit); + ->paginate($limit); } } diff --git a/Website/htdocs/mpmanager/app/Services/AgentTransactionService.php b/Website/htdocs/mpmanager/app/Services/AgentTransactionService.php index 67375b371..f04cd191f 100644 --- a/Website/htdocs/mpmanager/app/Services/AgentTransactionService.php +++ b/Website/htdocs/mpmanager/app/Services/AgentTransactionService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Models\Device; use App\Models\Meter\Meter; use App\Models\Meter\MeterParameter; use App\Models\Transaction\AgentTransaction; @@ -15,8 +16,7 @@ class AgentTransactionService implements IBaseService public function __construct( private AgentTransaction $agentTransaction, private Transaction $transaction, - private Meter $meter, - private MeterParameter $meterParameter + private Device $device ) { } @@ -27,9 +27,9 @@ public function getAll($limit = null, $agentId = null, $forApp = false) $query = $this->transaction->newQuery(); if ($forApp) { - $query->with(['originalAgent', 'meter.meterParameter.owner']); + $query->with(['originalAgent', 'device' => fn($q) => $q->whereHas('person')->with(['device','person'])]); } else { - $query->with(['meter.meterParameter.owner']); + $query->with(['device' => fn($q) => $q->whereHas('person')->with(['device','person'])]); } $query->whereHasMorph( @@ -48,37 +48,18 @@ static function ($q) use ($agentId) { public function getById($agentId, $customerId = null) { - $customerMeters = $this->meterParameter->newQuery()->select('meter_id')->where('owner_id', $customerId)->get(); - if ($customerMeters->count() === 0) { + $customerDeviceSerials = $this->device->newQuery()->where('person_id', $customerId) + ->get()->pluck('device_serial'); + + if (!$customerDeviceSerials->count()) { return null; } - $meterIds = array(); - foreach ($customerMeters as $key => $item) { - $meterIds[] = $item->meter_id; - } - - $customerMeterSerialNumbers = $this->meter->newQuery()->has('meterParameter') - ->whereHas( - 'meterParameter', - static function ($q) use ($meterIds) { - $q->whereIn('meter_id', $meterIds); - } - )->get('serial_number'); - - return $this->transaction->newQuery()->with(['originalAgent', 'meter.meterParameter.owner']) + return $this->transaction->newQuery() + ->with(['originalAgent', 'device' => fn($q) => $q->whereHas('person')->with(['device','person'])]) ->whereHasMorph( 'originalTransaction', - [AgentTransaction::class], - static function ($q) use ($agentId) { - $q->where('agent_id', $agentId); - } - ) - ->whereHas( - 'meter', - static function ($q) use ($customerMeterSerialNumbers) { - $q->whereIn('serial_number', $customerMeterSerialNumbers); - } - ) + [AgentTransaction::class], fn ($q) => $q->where('agent_id', $agentId)) + ->whereHas('device', fn ($q) => $q->whereIn('device_serial', $customerDeviceSerials)) ->latest()->paginate(); } diff --git a/Website/htdocs/mpmanager/app/Services/MeterService.php b/Website/htdocs/mpmanager/app/Services/MeterService.php index 0c8c80caf..f95975bdf 100644 --- a/Website/htdocs/mpmanager/app/Services/MeterService.php +++ b/Website/htdocs/mpmanager/app/Services/MeterService.php @@ -79,6 +79,9 @@ public function create($meterData) 'meter_type_id' => $meterData['meter_type_id'], 'in_use' => $meterData['in_use'], 'manufacturer_id' => $meterData['manufacturer_id'], + 'connection_group_id' => $meterData['connection_group_id'], + 'connection_type_id' => $meterData['connection_type_id'], + 'tariff_id' => $meterData['tariff_id'], ]); } diff --git a/Website/htdocs/mpmanager/app/Services/PersonService.php b/Website/htdocs/mpmanager/app/Services/PersonService.php index 1431e25ed..76629fdba 100644 --- a/Website/htdocs/mpmanager/app/Services/PersonService.php +++ b/Website/htdocs/mpmanager/app/Services/PersonService.php @@ -40,7 +40,8 @@ public function getDetails(int $personID, bool $allRelations = false) return $this->person->newQuery()->with( [ - 'addresses' => fn($q) => $q->orderBy('is_primary')->with('city',fn($q) => $q->whereHas('location'))->get(), + 'addresses' => fn($q) => $q->orderBy('is_primary')->with('city', fn($q) => $q->whereHas('location')) + ->get(), 'citizenship', 'roleOwner.definitions', 'devices' => fn($q) => $q->whereHas('address')->with('address.geo'), @@ -196,4 +197,10 @@ public function createFromRequest(Request $request): Model return $person; } + + public function getByPhoneNumber($phoneNumber):?Person + { + return $this->person->newQuery()->whereHas('addresses', fn($q) => $q->where('phone', $phoneNumber)) + ->first(); + } } diff --git a/Website/htdocs/mpmanager/app/Traits/RestExceptionHandler.php b/Website/htdocs/mpmanager/app/Traits/RestExceptionHandler.php index f6ebea43f..03a9d7f54 100644 --- a/Website/htdocs/mpmanager/app/Traits/RestExceptionHandler.php +++ b/Website/htdocs/mpmanager/app/Traits/RestExceptionHandler.php @@ -15,6 +15,9 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Validation\ValidationException; use Throwable; +use Tymon\JWTAuth\Exceptions\JWTException; +use Tymon\JWTAuth\Exceptions\TokenExpiredException; +use Tymon\JWTAuth\Exceptions\TokenInvalidException; trait RestExceptionHandler { @@ -27,9 +30,13 @@ trait RestExceptionHandler */ protected function getJsonResponseForException(Request $request, Exception|Throwable $e) { - - $response = null; switch (true) { + case $e instanceof TokenExpiredException: + return response()->json(['error' => 'Token is expired'], 401); + case $e instanceof TokenInvalidException: + return response()->json(['error' => 'Token is invalid'], 401); + case $e instanceof JWTException: + return response()->json(['error' => 'There was an issue with the token'], 401); case $this->isModelNotFoundException($e): $response = $this->modelNotFound(['model not found ' . implode(' ', $e->getIds()) , $e->getMessage(), $e->getTrace()]); break; diff --git a/Website/htdocs/mpmanager/app/Utils/ApplianceInstallmentPayer.php b/Website/htdocs/mpmanager/app/Utils/ApplianceInstallmentPayer.php index 7a5f505a8..855d57c3b 100644 --- a/Website/htdocs/mpmanager/app/Utils/ApplianceInstallmentPayer.php +++ b/Website/htdocs/mpmanager/app/Utils/ApplianceInstallmentPayer.php @@ -10,6 +10,7 @@ use App\Services\AppliancePaymentService; use App\Services\AppliancePersonService; use App\Services\ApplianceRateService; +use Illuminate\Support\Collection; use MPM\Device\DeviceService; class ApplianceInstallmentPayer @@ -38,21 +39,66 @@ public function initialize(TransactionDataContainer $transactionData): void //This function pays the installments for the device number that provided in transaction public function payInstallmentsForDevice(TransactionDataContainer $container) { - $applianceOwner = $container->appliancePerson->person; + $customer = $container->appliancePerson->person; $this->appliancePaymentService->setPaymentAmount($container->transaction->amount); - $container->appliancePerson->rates->map(fn($installment - ) => $this->appliancePaymentService->payInstallment($installment, $applianceOwner, $this->transaction)); + $installments = $container->appliancePerson->rates; + $this->pay($installments, $customer); } // This function processes the payment of all installments (excluding device-recorded ones) that are due, right before generating the meter token. // If meter number is provided in transaction - public function payInstallments(TransactionDataContainer $container) + public function payInstallments() { $customer = $this->customer; $appliancePersonIds = $this->appliancePersonService->getLoanIdsForCustomerId($customer->id); - $installmentsAreDue = $this->applianceRateService->getByLoanIdsForDueDate($appliancePersonIds); - $installmentsAreDue->each(function ($installment) use ($customer) { + $installments = $this->applianceRateService->getByLoanIdsForDueDate($appliancePersonIds); + $this->pay($installments, $customer); + + return $this->transaction->amount; + } + + public function consumeAmount() + { + $installments = $this->getInstallments($this->customer); + $installments->each(function ($installment) { + if ($installment->remaining > $this->consumableAmount) {// money is not enough to cover the + // whole rate + $this->consumableAmount = 0; + + return false; + } else { + $this->consumableAmount -= $installment->remaining; + + return true; + } + }); + + return $this->consumableAmount; + } + + private function getCustomerByDeviceSerial(string $serialNumber): Person + { + $device = $this->deviceService->getBySerialNumber($serialNumber); + + if (!$device) { + throw new DeviceIsNotAssignedToCustomer('Device is not assigned to customer'); + } + + + return $device->person; + } + + private function getInstallments($customer) + { + $loans = $this->appliancePersonService->getLoanIdsForCustomerId($customer->id); + + return $this->applianceRateService->getByLoanIdsForDueDate($loans); + } + + private function pay(Collection $installments, mixed $customer): void + { + $installments->map(function ($installment) use ($customer) { if ($installment->remaining > $this->transaction->amount) {// money is not enough to cover the whole rate //add payment history for the installment event('payment.successful', [ @@ -69,7 +115,7 @@ public function payInstallments(TransactionDataContainer $container) $installment->save(); $this->paidRates[] = [ - 'asset_type_name' => $installment->assetPerson->asset->name, + 'asset_rate_id' => $installment->id, 'paid' => $this->transaction->amount, ]; $this->transaction->amount = 0; @@ -87,7 +133,7 @@ public function payInstallments(TransactionDataContainer $container) 'transaction' => $this->transaction, ]); $this->paidRates[] = [ - 'asset_type_name' => $installment->assetPerson->asset->name, + 'asset_rate_id' => $installment->id, 'paid' => $installment->remaining, ]; $this->transaction->amount -= $installment->remaining; @@ -97,47 +143,9 @@ public function payInstallments(TransactionDataContainer $container) return true; } - }); - - return $this->transaction->amount; - } - - public function consumeAmount() - { - $installments = $this->getInstallments($this->customer); - $installments->each(function ($installment) { - if ($installment->remaining > $this->consumableAmount) {// money is not enough to cover the - // whole rate - $this->consumableAmount = 0; - - return false; - } else { - $this->consumableAmount -= $installment->remaining; - - return true; - } - }); - - return $this->consumableAmount; - } - - private function getCustomerByDeviceSerial(string $serialNumber): Person - { - $device = $this->deviceService->getBySerialNumber($serialNumber); - - if (!$device) { - throw new DeviceIsNotAssignedToCustomer('Device is not assigned to customer'); } - - - return $device->person; + ); } - private function getInstallments($customer) - { - $loans = $this->appliancePersonService->getLoanIdsForCustomerId($customer->id); - - return $this->applianceRateService->getByLoanIdsForDueDate($loans); - } } diff --git a/Website/htdocs/mpmanager/app/modules/Apps/CustomerRegistration/CustomerRegistrationAppService.php b/Website/htdocs/mpmanager/app/modules/Apps/CustomerRegistration/CustomerRegistrationAppService.php new file mode 100644 index 000000000..2cfb16e23 --- /dev/null +++ b/Website/htdocs/mpmanager/app/modules/Apps/CustomerRegistration/CustomerRegistrationAppService.php @@ -0,0 +1,87 @@ +input('serial_number'); + $meter = $this->meterService->getBySerialNumber($serialNumber); + $phone = $request->input('phone'); + if ($meter) { + throw new \Exception('Meter already exists'); + } + + $person = $this->personService->getByPhoneNumber($phone); + $manufacturerId = $request->input('manufacturer'); + $meterTypeId = $request->input('meter_type'); + $connectionTypeId = $request->input('connection_type_id'); + $connectionGroupId = $request->input('connection_group_id'); + $tariffId = $request->input('tariff_id'); + $cityId = $request->input('city_id'); + $geoPoints = $request->input('geo_points'); + if ($person === null) { + $request->attributes->add(['is_customer' => 1]); + $person = $this->personService->createFromRequest($request); + } + $meterData = [ + 'serial_number' => $serialNumber, + 'connection_group_id' => $connectionGroupId, + 'manufacturer_id' => $manufacturerId, + 'meter_type_id' => $meterTypeId, + 'connection_type_id' => $connectionTypeId, + 'tariff_id' => $tariffId, + 'in_use' => 1, + ]; + $meter = $this->meterService->create($meterData); + $device = $this->deviceService->make([ + 'person_id' => $person->id, + 'device_serial' => $meter->serial_number, + ]); + $this->meterDeviceService->setAssigned($device); + $this->meterDeviceService->setAssignee($meter); + $this->meterDeviceService->assign(); + $this->deviceService->save($device); + $addressData = [ + 'city_id' => $cityId ?? 1, + ]; + $address = $this->addressService->make($addressData); + $this->deviceAddressService->setAssigned($address); + $this->deviceAddressService->setAssignee($device); + $this->deviceAddressService->assign(); + $this->addressService->save($address); + $geographicalInformation = $this->geographicalInformationService->make([ + 'points' => $geoPoints, + ]); + $this->addressGeographicalInformationService->setAssigned($geographicalInformation); + $this->addressGeographicalInformationService->setAssignee($address); + $this->addressGeographicalInformationService->assign(); + $this->geographicalInformationService->save($geographicalInformation); + //initializes a new Access Rate Payment for the next Period + event('accessRatePayment.initialize', $meter); + + return $person; + } +} \ No newline at end of file diff --git a/Website/htdocs/mpmanager/app/modules/Transaction/Provider/AgentTransaction.php b/Website/htdocs/mpmanager/app/modules/Transaction/Provider/AgentTransaction.php index 94db76737..7d4fe909d 100644 --- a/Website/htdocs/mpmanager/app/modules/Transaction/Provider/AgentTransaction.php +++ b/Website/htdocs/mpmanager/app/modules/Transaction/Provider/AgentTransaction.php @@ -43,7 +43,7 @@ private function assignData(array $data): void // common transaction data $this->transaction->amount = (float)$data['amount']; $this->transaction->sender = 'Agent-' . $data['agent_id']; - $this->transaction->message = $data['meter_serial_number']; + $this->transaction->message = $data['device_serial']; $this->transaction->type = 'energy'; $this->transaction->original_transaction_type = 'agent_transaction'; } @@ -146,7 +146,7 @@ public function validateRequest($request): void if ($agentId !== $agent->id) { throw new \Exception('Agent authorization failed.'); } - $this->validData = request()->only(['meter_serial_number', 'amount']); + $this->validData = request()->only(['device_serial', 'amount']); $this->validData['device_id'] = $deviceId; $this->validData['agent_id'] = $agentId; } diff --git a/Website/htdocs/mpmanager/app/modules/Transaction/TransactionPaymentProcessor.php b/Website/htdocs/mpmanager/app/modules/Transaction/TransactionPaymentProcessor.php index 559ae89ac..72f9f3c79 100644 --- a/Website/htdocs/mpmanager/app/modules/Transaction/TransactionPaymentProcessor.php +++ b/Website/htdocs/mpmanager/app/modules/Transaction/TransactionPaymentProcessor.php @@ -30,7 +30,7 @@ public static function process(int $transactionId): void $processorClass = self::PROCESSORS_BY_DEVICE_TYPE[$deviceType]; // Instantiate the processor class - $processor = new $processorClass; + $processor = new $processorClass($transactionId); $queue = self::QUEUE_BY_DEVICE_TYPE[$deviceType]; // Dispatch the job diff --git a/Website/htdocs/mpmanager/app/modules/Transaction/TransactionService.php b/Website/htdocs/mpmanager/app/modules/Transaction/TransactionService.php index 900d00bad..23bba9813 100644 --- a/Website/htdocs/mpmanager/app/modules/Transaction/TransactionService.php +++ b/Website/htdocs/mpmanager/app/modules/Transaction/TransactionService.php @@ -254,13 +254,13 @@ public function save($transaction) public function getById($id) { - return $this->transaction->newQuery()->with( + return $this->transaction->newQuery()->with([ 'token', 'originalTransaction', 'originalTransaction.conflicts', 'sms', 'paymentHistories', - 'device.device' + 'device' => fn($q) => $q->whereHas('person')->with(['device','person'])] )->find($id); } diff --git a/Website/htdocs/mpmanager/routes/api.php b/Website/htdocs/mpmanager/routes/api.php index 84420e23c..55441ff4f 100644 --- a/Website/htdocs/mpmanager/routes/api.php +++ b/Website/htdocs/mpmanager/routes/api.php @@ -1,25 +1,8 @@ 'api', 'prefix' => 'auth'], static function () { @@ -199,13 +184,11 @@ }); // People Route::group(['prefix' => 'people', 'middleware' => 'jwt.verify'], static function () { - Route::get('/{personId}/meters', 'PersonMeterController@show'); Route::get('/{personId}/meters/geo', 'MeterGeographicalInformationController@show'); Route::get('/', 'PersonController@index'); Route::post('/', 'PersonController@store'); - Route::get('/all', 'PersonController@list'); Route::get('/search', 'PersonController@search'); Route::get('/{personId}', 'PersonController@show'); Route::get('/{personId}/transactions', 'PersonController@transactions'); @@ -215,9 +198,9 @@ Route::get('/{personId}/addresses', 'PersonAddressesController@show'); Route::post('/{personId}/addresses', 'PersonAddressesController@store'); Route::put('/{personId}/addresses', 'PersonAddressesController@update'); +}); -}); // PV Route::group(['prefix' => 'pv'], static function () { Route::get('/{miniGridId}', ['middleware' => 'jwt.verify', 'uses' => 'PVController@show']); @@ -360,79 +343,6 @@ static function () { Route::put('/{mpmPluginId}', 'PluginController@update'); }); -Route::post('androidApp', static function (AndroidAppRequest $r) { - try { - - DB::connection('shard')->beginTransaction(); - //check if the meter id or the phone already exists - $meter = Meter::query()->where('serial_number', $r->get('serial_number'))->first(); - $person = null; - - if ($meter === null) { - $meter = new Meter(); - $meterParameter = new MeterParameter(); - $geoLocation = new GeographicalInformation(); - } else { - - $meterParameter = MeterParameter::query()->where('meter_id', $meter->id)->first(); - $geoLocation = $meterParameter->geo()->first(); - if ($geoLocation === null) { - $geoLocation = new GeographicalInformation(); - } - - $person = Person::query()->whereHas('meters', static function ($q) use ($meterParameter) { - return $q->where('id', $meterParameter->id); - })->first(); - } - - if ($person === null) { - $r->attributes->add(['is_customer' => 1]); - $personService = App::make(PersonService::class); - $person = $personService->createFromRequest($r); - } - - $meter->serial_number = $r->get('serial_number'); - $meter->manufacturer()->associate(Manufacturer::query()->findOrFail($r->get('manufacturer'))); - $meter->meterType()->associate(MeterType::query()->findOrFail($r->get('meter_type'))); - $meter->updated_at = date('Y-m-d h:i:s'); - $meter->save(); - - $geoLocation->points = $r->get('geo_points'); - - $meterParameter->meter()->associate($meter); - $meterParameter->connection_type_id = $r->get('connection_type_id'); - $meterParameter->connection_group_id = $r->get('connection_group_id'); - $meterParameter->owner()->associate($person); - $meterParameter->tariff()->associate(MeterTariff::query()->findOrFail($r->get('tariff_id'))); - $meterParameter->save(); - $meterParameter->geo()->save($geoLocation); - - - $address = new Address(); - $address = $address->newQuery()->create([ - 'city_id' => request()->input('city_id') ?? 1, - ]); - $address->owner()->associate($meterParameter); - - $address->geo()->associate($meterParameter->geo); - $address->save(); - - //initializes a new Access Rate Payment for the next Period - event('accessRatePayment.initialize', $meterParameter); - // changes in_use parameter of the meter - event('meterparameter.saved', $meterParameter->meter_id); - DB::connection('shard')->commit(); - - return ApiResource::make($person)->response()->setStatusCode(201); - - } catch (\Exception $e) { - DB::connection('shard')->rollBack(); - Log::critical('Error while adding new Customer', ['message' => $e->getMessage()]); - - return Response::make($e->getMessage())->setStatusCode(409); - } -}); - Route::get('/clusterlist', 'ClusterController@index'); Route::post('/restrictions', 'RestrictionController@store'); diff --git a/Website/htdocs/mpmanager/routes/resources/CustomerRegistrationApp.php b/Website/htdocs/mpmanager/routes/resources/CustomerRegistrationApp.php new file mode 100644 index 000000000..d96e6bc4f --- /dev/null +++ b/Website/htdocs/mpmanager/routes/resources/CustomerRegistrationApp.php @@ -0,0 +1,29 @@ + 'customer-registration-app'], static function () { + Route::get('/people', 'PersonController@index'); + Route::get('/manufacturers', 'ManufacturerController@index'); + Route::get('/meter-types', 'MeterTypeController@index'); + Route::get('/tariffs', 'MeterTariffController@index'); + Route::get('/cities', 'CityController@index'); + Route::get('/connection-groups', 'ConnectionGroupController@index'); + Route::get('/connection-types', 'ConnectionTypeController@index'); + Route::get('/sub-connection-types', 'SubConnectionTypeController@index'); + Route::post('/', 'CustomerRegistrationAppController@store'); +}); \ No newline at end of file diff --git a/Website/ui/src/modules/Agent/AgentTransactionList.vue b/Website/ui/src/modules/Agent/AgentTransactionList.vue index e93fbf0b9..b2758b461 100644 --- a/Website/ui/src/modules/Agent/AgentTransactionList.vue +++ b/Website/ui/src/modules/Agent/AgentTransactionList.vue @@ -13,7 +13,7 @@ {{item.id}} {{item.amount}} - {{item.meter}} + {{item.meter}} {{item.customer}} {{item.createdAt}} @@ -30,7 +30,7 @@ export default { return { subscriber: 'agent-transactions', agentTransactionService: new AgentTransactionService(this.agentId), - headers: [this.$tc('words.id'), this.$tc('words.amount'), this.$tc('words.meter'), this.$tc('words.meter'), this.$tc('words.date')], + headers: [this.$tc('words.id'), this.$tc('words.amount'), this.$tc('words.device'), this.$tc('words.person'), this.$tc('words.date')], } }, mounted () { diff --git a/Website/ui/src/services/AgentTransactionService.js b/Website/ui/src/services/AgentTransactionService.js index c3adb48a4..789b01dd2 100644 --- a/Website/ui/src/services/AgentTransactionService.js +++ b/Website/ui/src/services/AgentTransactionService.js @@ -1,7 +1,7 @@ import Repository from '../repositories/RepositoryFactory' import { Paginator } from '@/classes/paginator' -export class AgentTransactionService{ +export class AgentTransactionService { constructor (agentId) { this.repository = Repository.get('agentTransactions') @@ -11,22 +11,22 @@ export class AgentTransactionService{ id: null, amount: null, meter: null, - customer:null, + customer: null, createdAt: null } this.paginator = new Paginator(resources.agents.transactions + agentId) } fromJson (data) { - const meterParameter = data.meter ? data.meter.meter_parameter: null - let transaction = { + + return { id: data.id, amount: data.amount, meter: data.message, - customer: meterParameter ? meterParameter.owner.name + ' ' + meterParameter.owner.surname : '', + customer: data.device.person.name + ' ' + data.device.person.surname, createdAt: data.created_at.toString().replace(/T/, ' ').replace(/\..+/, '') } - return transaction + } updateList (data) {