From 5f95e762d47fa832ca6d29bd5190f1e6161c8573 Mon Sep 17 00:00:00 2001 From: Daniel Mohns Date: Thu, 12 Dec 2024 17:48:35 +0100 Subject: [PATCH] More Transaction generation to Seeder (#422) * More Transaction generation to Seeder * Fix bugs in seeding --- docs/development/development-environment.md | 7 +- .../app/Console/Commands/DemoDataCreator.php | 253 +-------------- .../database/seeders/DatabaseSeeder.php | 3 +- .../database/seeders/TransactionSeeder.php | 288 ++++++++++++++++++ 4 files changed, 297 insertions(+), 254 deletions(-) create mode 100644 src/backend/database/seeders/TransactionSeeder.php diff --git a/docs/development/development-environment.md b/docs/development/development-environment.md index 8b7864af..97d6222a 100644 --- a/docs/development/development-environment.md +++ b/docs/development/development-environment.md @@ -68,16 +68,15 @@ password: 123123 The Demo Company protected page password of this company is `123123`. -## Generating transaction data +## Generating ticket data -To generate transaction and ticket for the Demo Company data, run: +To generate ticket for the Demo Company data, run: ```sh -php artisan demo:create-data 250 php artisan demo:create-data --type=ticket 25 ``` -This commands will create 250 transactions and 25 tickets within the past 30 days respectively. +This commands will generate 25 tickets within the past 365 days respectively. It can be run multiple times to generate more data as required. ## Reseting the Demo data diff --git a/src/backend/app/Console/Commands/DemoDataCreator.php b/src/backend/app/Console/Commands/DemoDataCreator.php index 9d70379d..1054dfef 100644 --- a/src/backend/app/Console/Commands/DemoDataCreator.php +++ b/src/backend/app/Console/Commands/DemoDataCreator.php @@ -2,43 +2,21 @@ namespace App\Console\Commands; -use App\Helpers\TokenGenerator; -use App\Models\MainSettings; use App\Models\MaintenanceUsers; -use App\Models\Meter\Meter; -use App\Models\Meter\MeterToken; use App\Models\Person\Person; -use App\Models\Token; -use App\Models\Transaction\AgentTransaction; -use App\Models\Transaction\AirtelTransaction; -use App\Models\Transaction\Transaction; -use App\Models\Transaction\VodacomTransaction; use App\Models\User; -use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Console\View\Components\Warn; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; -use Inensus\CalinMeter\Models\CalinTransaction; -use Inensus\SwiftaPaymentProvider\Models\SwiftaTransaction; use Inensus\Ticket\Models\Ticket; use Inensus\Ticket\Models\TicketCategory; use Inensus\Ticket\Models\TicketOutsource; use Inensus\Ticket\Models\TicketUser; -use Inensus\WavecomPaymentProvider\Models\WaveComTransaction; -use Inensus\WaveMoneyPaymentProvider\Models\WaveMoneyTransaction; class DemoDataCreator extends AbstractSharedCommand { protected $signature = 'demo:create-data {amount} {--company-id=} {--type=}'; protected $description = 'Creates transaction and ticket data for Demo Company'; - private $transactionTypes = [ - SwiftaTransaction::class, - WaveComTransaction::class, - WaveMoneyTransaction::class, - AgentTransaction::class, - VodacomTransaction::class, - AirtelTransaction::class, - ]; - public function handle() { $companyId = $this->option('company-id'); $type = $this->option('type') ?? 'transaction'; @@ -55,7 +33,9 @@ public function handle() { try { DB::connection('shard')->beginTransaction(); if ($type == 'transaction') { - $this->generateTransaction(); + (new Warn($this->getOutput()))->render( + 'Generating Transaction data using script is no longer supported. Use `artisan db:seed --TransactionSeeder` instead.`' + ); } else { $this->generateTicket(); } @@ -68,231 +48,6 @@ public function handle() { } } - private function generateTransaction(): void { - try { - // get randomly a user - $randomMeter = Meter::inRandomOrder()->with([ - 'device', - 'tariff', - ])->limit(1)->firstOrFail(); - } catch (ModelNotFoundException $x) { - echo 'failed to find a random meter'; - - return; - } catch (\Exception $x) { - echo 'boom'; - - return; - } - - $demoDate = date('Y-m-d', strtotime('-'.mt_rand(0, 365).' days')); - - try { - $meterOwnerPhoneNumber = $randomMeter->device->person->addresses()->firstOrFail(); - } catch (\Exception $x) { - echo 'failed to get meter owner address'; - - return; - } - - try { - $amount = random_int(1000, 20000); - } catch (\Exception $e) { - $amount = 300; - } - - $randomTransactionType = $this->getTransactionTypeRandomlyFromTransactionTypes(); - $transactionType = app()->make($randomTransactionType); - - $transaction = Transaction::query()->make([ - 'amount' => $amount, - 'type' => 'energy', - 'message' => $randomMeter['serial_number'], - 'sender' => $meterOwnerPhoneNumber['phone'], - 'created_at' => $demoDate, - 'updated_at' => $demoDate, - ]); - $subTransaction = null; - - // FIXME: What is this? - $manufacturerTransaction = CalinTransaction::query()->create([]); - - if ($transactionType instanceof AgentTransaction) { - $city = $randomMeter->device->person->addresses()->first()->city()->first(); - $miniGrid = $city->miniGrid()->first(); - $agent = $miniGrid->agent()->first(); - $subTransaction = AgentTransaction::query()->create([ - 'agent_id' => $agent->id, - 'device_id' => 'test-device', - 'status' => 1, - 'manufacturer_transaction_id' => $manufacturerTransaction->id, - 'manufacturer_transaction_type' => 'calin_transaction', - 'created_at' => $demoDate, - 'updated_at' => $demoDate, - ]); - } - - if ($transactionType instanceof SwiftaTransaction) { - $subTransaction = SwiftaTransaction::query()->create([ - 'transaction_reference' => Str::random(10), - 'status' => 1, - 'amount' => $amount, - 'cipher' => Str::random(10), - 'timestamp' => strval(time()), - 'manufacturer_transaction_id' => $manufacturerTransaction->id, - 'manufacturer_transaction_type' => 'calin_transaction', - 'created_at' => $demoDate, - 'updated_at' => $demoDate, - ]); - } - - if ($transactionType instanceof WaveMoneyTransaction) { - $mainSettings = MainSettings::query()->first(); - $subTransaction = WaveMoneyTransaction::query()->create([ - 'transaction_reference' => Str::random(10), - 'status' => 1, - 'amount' => $amount, - 'order_id' => Str::random(10), - 'reference_id' => Str::random(10), - 'currency' => $mainSettings ? $mainSettings->currency : '$', - 'customer_id' => $randomMeter->device->person->id, - 'meter_serial' => $randomMeter['serial_number'], - 'external_transaction_id' => Str::random(10), - 'attempts' => 1, - 'created_at' => $demoDate, - 'updated_at' => $demoDate, - 'manufacturer_transaction_id' => $manufacturerTransaction->id, - 'manufacturer_transaction_type' => 'calin_transaction', - ]); - } - - if ($transactionType instanceof WaveComTransaction) { - $subTransaction = WaveComTransaction::query()->create([ - 'transaction_id' => Str::random(10), - 'sender' => $meterOwnerPhoneNumber['phone'], - 'message' => $randomMeter['serial_number'], - 'status' => 1, - 'amount' => $amount, - 'manufacturer_transaction_id' => $manufacturerTransaction->id, - 'manufacturer_transaction_type' => 'calin_transaction', - 'created_at' => $demoDate, - 'updated_at' => $demoDate, - ]); - } - - if ($transactionType instanceof VodacomTransaction) { - $subTransaction = VodacomTransaction::query()->create([ - 'conversation_id' => Str::random(20), - 'originator_conversation_id' => Str::random(20), - 'mpesa_receipt' => Str::random(10), - 'transaction_date' => $demoDate, - 'transaction_id' => Str::random(10), - 'status' => 1, - 'manufacturer_transaction_id' => $manufacturerTransaction->id, - 'manufacturer_transaction_type' => 'calin_transaction', - 'created_at' => $demoDate, - 'updated_at' => $demoDate, - ]); - } - - if ($transactionType instanceof AirtelTransaction) { - $subTransaction = AirtelTransaction::query()->create([ - 'interface_id' => Str::random(20), - 'business_number' => Str::random(20), - 'trans_id' => Str::random(10), - 'tr_id' => Str::random(10), - 'status' => 1, - 'manufacturer_transaction_id' => $manufacturerTransaction->id, - 'manufacturer_transaction_type' => 'calin_transaction', - 'created_at' => $demoDate, - 'updated_at' => $demoDate, - ]); - } - - $transaction->originalTransaction()->associate($subTransaction); - $transaction->save(); - - try { - // create an object for the token job - $transactionData = \App\Misc\TransactionDataContainer::initialize($transaction); - } catch (\Exception $exception) { - event('transaction.failed', [$transaction, $exception->getMessage()]); - throw $exception; - } - - // pay access rate - $accessRatePayer = resolve('AccessRatePayer'); - $accessRatePayer->initialize($transactionData); - $transactionData = $accessRatePayer->pay(); - - // pay appliance installments - $applianceInstallmentPayer = resolve('ApplianceInstallmentPayer'); - $applianceInstallmentPayer->initialize($transactionData); - $transactionData->transaction->amount = $applianceInstallmentPayer->payInstallments(); - $transactionData->totalAmount = $transactionData->transaction->amount; - $transactionData->paidRates = $applianceInstallmentPayer->paidRates; - $transactionData->shsLoan = $applianceInstallmentPayer->shsLoan; - - // generate random token - if ($transactionData->transaction->amount > 0) { - $tokenData = [ - 'token' => TokenGenerator::generate(), - 'load' => round( - $transactionData->transaction->amount / - $randomMeter['tariff']['price'], - 2 - ), - ]; - $token = Token::query()->make([ - 'token' => $tokenData['token'], - 'load' => $tokenData['load'], - ]); - $token->transaction()->associate($transaction); - $token->save(); - $transactionData->token = $token; - - // generate meter_token - $meterTokenData = [ - 'meter_id' => $randomMeter->id, - 'token' => TokenGenerator::generate(), - 'energy' => round( - $transactionData->transaction->amount / - $randomMeter['tariff']['price'], - 2 - ), - 'transaction_id' => $transaction->id, - ]; - $meterToken = MeterToken::query()->make([ - 'meter_id' => $meterTokenData['meter_id'], - 'token' => $meterTokenData['token'], - 'energy' => $meterTokenData['energy'], - 'transaction_id' => $meterTokenData['transaction_id'], - ]); - $meterToken->save(); - - // payment event - event( - 'payment.successful', - [ - 'amount' => $transactionData->transaction->amount, - 'paymentService' => $transactionData->transaction->original_transaction_type, - 'paymentType' => 'energy', - 'sender' => $transactionData->transaction->sender, - 'paidFor' => $token, - 'payer' => $transactionData->device->person, - 'transaction' => $transactionData->transaction, - ] - ); - - // TODO: This currently doesn't work, it throws error that SMS is not configured. - // event('transaction.successful', [$transactionData->transaction]); - } - } - - private function getTransactionTypeRandomlyFromTransactionTypes() { - return $this->transactionTypes[array_rand($this->transactionTypes)]; - } - private function generateTicket() { $randomCategory = TicketCategory::query()->inRandomOrder()->first(); $fakeSentence = $this->generateFakeSentence(); diff --git a/src/backend/database/seeders/DatabaseSeeder.php b/src/backend/database/seeders/DatabaseSeeder.php index 21382ccc..a513714e 100644 --- a/src/backend/database/seeders/DatabaseSeeder.php +++ b/src/backend/database/seeders/DatabaseSeeder.php @@ -26,8 +26,9 @@ public function run() { CustomerSeeder::class, MeterSeeder::class, SolarHomeSystemSeeder::class, - TicketSeeder::class, AgentSeeder::class, + TicketSeeder::class, + TransactionSeeder::class, ]); } else { // If the database already includes the Demo data we don't throw an error, diff --git a/src/backend/database/seeders/TransactionSeeder.php b/src/backend/database/seeders/TransactionSeeder.php new file mode 100644 index 00000000..4af6ad6a --- /dev/null +++ b/src/backend/database/seeders/TransactionSeeder.php @@ -0,0 +1,288 @@ +databaseProxyManagerService->buildDatabaseConnectionDummyCompany(); + } + + private $transactionTypes = [ + SwiftaTransaction::class, + WaveComTransaction::class, + WaveMoneyTransaction::class, + AgentTransaction::class, + VodacomTransaction::class, + AirtelTransaction::class, + ]; + + private $amount = 1000; + + /** + * Run the database seeds. + * + * @return void + */ + public function run() { + (new Info($this->command->getOutput()))->render( + "Running TransactionSeeder to generate $this->amount transactions. This may take some time." + ); + + for ($i = 1; $i <= $this->amount; ++$i) { + try { + DB::connection('shard')->beginTransaction(); + $this->generateTransaction(); + DB::connection('shard')->commit(); + } catch (\Exception $e) { + DB::connection('shard')->rollBack(); + echo $e->getMessage(); + } + } + } + + private function getTransactionTypeRandomlyFromTransactionTypes() { + return $this->transactionTypes[array_rand($this->transactionTypes)]; + } + + private function generateTransaction(): void { + try { + // get randomly a user + $randomMeter = Meter::inRandomOrder()->with([ + 'device', + 'tariff', + ])->limit(1)->firstOrFail(); + } catch (ModelNotFoundException $x) { + echo 'failed to find a random meter'; + + return; + } catch (\Exception $x) { + echo 'boom'; + + return; + } + + $demoDate = date('Y-m-d', strtotime('-'.mt_rand(0, 365).' days')); + + try { + $meterOwnerPhoneNumber = $randomMeter->device->person->addresses()->firstOrFail(); + } catch (\Exception $x) { + echo 'failed to get meter owner address'; + + return; + } + + try { + $amount = random_int(1000, 20000); + } catch (\Exception $e) { + $amount = 300; + } + + $randomTransactionType = $this->getTransactionTypeRandomlyFromTransactionTypes(); + $transactionType = app()->make($randomTransactionType); + + $transaction = Transaction::query()->make([ + 'amount' => $amount, + 'type' => 'energy', + 'message' => $randomMeter['serial_number'], + 'sender' => $meterOwnerPhoneNumber['phone'], + 'created_at' => $demoDate, + 'updated_at' => $demoDate, + ]); + $subTransaction = null; + + // FIXME: What is this? + $manufacturerTransaction = CalinTransaction::query()->create([]); + + if ($transactionType instanceof AgentTransaction) { + $city = $randomMeter->device->person->addresses()->first()->city()->first(); + $miniGrid = $city->miniGrid()->first(); + $agent = $miniGrid->agent()->first(); + $subTransaction = AgentTransaction::query()->create([ + 'agent_id' => $agent->id, + 'device_id' => 'test-device', + 'status' => 1, + 'manufacturer_transaction_id' => $manufacturerTransaction->id, + 'manufacturer_transaction_type' => 'calin_transaction', + 'created_at' => $demoDate, + 'updated_at' => $demoDate, + ]); + } + + if ($transactionType instanceof SwiftaTransaction) { + $subTransaction = SwiftaTransaction::query()->create([ + 'transaction_reference' => Str::random(10), + 'status' => 1, + 'amount' => $amount, + 'cipher' => Str::random(10), + 'timestamp' => strval(time()), + 'manufacturer_transaction_id' => $manufacturerTransaction->id, + 'manufacturer_transaction_type' => 'calin_transaction', + 'created_at' => $demoDate, + 'updated_at' => $demoDate, + ]); + } + + if ($transactionType instanceof WaveMoneyTransaction) { + $mainSettings = MainSettings::query()->first(); + $subTransaction = WaveMoneyTransaction::query()->create([ + 'status' => 1, + 'amount' => $amount, + 'order_id' => Str::random(10), + 'reference_id' => Str::random(10), + 'currency' => $mainSettings ? $mainSettings->currency : '$', + 'customer_id' => $randomMeter->device->person->id, + 'meter_serial' => $randomMeter['serial_number'], + 'external_transaction_id' => Str::random(10), + 'attempts' => 1, + 'created_at' => $demoDate, + 'updated_at' => $demoDate, + 'manufacturer_transaction_id' => $manufacturerTransaction->id, + 'manufacturer_transaction_type' => 'calin_transaction', + ]); + } + + if ($transactionType instanceof WaveComTransaction) { + $subTransaction = WaveComTransaction::query()->create([ + 'transaction_id' => Str::random(10), + 'sender' => $meterOwnerPhoneNumber['phone'], + 'message' => $randomMeter['serial_number'], + 'status' => 1, + 'amount' => $amount, + 'manufacturer_transaction_id' => $manufacturerTransaction->id, + 'manufacturer_transaction_type' => 'calin_transaction', + 'created_at' => $demoDate, + 'updated_at' => $demoDate, + ]); + } + + if ($transactionType instanceof VodacomTransaction) { + $subTransaction = VodacomTransaction::query()->create([ + 'conversation_id' => Str::random(20), + 'originator_conversation_id' => Str::random(20), + 'mpesa_receipt' => Str::random(10), + 'transaction_date' => $demoDate, + 'transaction_id' => Str::random(10), + 'status' => 1, + 'manufacturer_transaction_id' => $manufacturerTransaction->id, + 'manufacturer_transaction_type' => 'calin_transaction', + 'created_at' => $demoDate, + 'updated_at' => $demoDate, + ]); + } + + if ($transactionType instanceof AirtelTransaction) { + $subTransaction = AirtelTransaction::query()->create([ + 'interface_id' => Str::random(20), + 'business_number' => Str::random(20), + 'trans_id' => Str::random(10), + 'tr_id' => Str::random(10), + 'status' => 1, + 'manufacturer_transaction_id' => $manufacturerTransaction->id, + 'manufacturer_transaction_type' => 'calin_transaction', + 'created_at' => $demoDate, + 'updated_at' => $demoDate, + ]); + } + + $transaction->originalTransaction()->associate($subTransaction); + $transaction->save(); + + try { + // create an object for the token job + $transactionData = \App\Misc\TransactionDataContainer::initialize($transaction); + } catch (\Exception $exception) { + event('transaction.failed', [$transaction, $exception->getMessage()]); + throw $exception; + } + + // pay access rate + $accessRatePayer = resolve('AccessRatePayer'); + $accessRatePayer->initialize($transactionData); + $transactionData = $accessRatePayer->pay(); + + // pay appliance installments + $applianceInstallmentPayer = resolve('ApplianceInstallmentPayer'); + $applianceInstallmentPayer->initialize($transactionData); + $transactionData->transaction->amount = $applianceInstallmentPayer->payInstallments(); + $transactionData->totalAmount = $transactionData->transaction->amount; + $transactionData->paidRates = $applianceInstallmentPayer->paidRates; + $transactionData->shsLoan = $applianceInstallmentPayer->shsLoan; + + // generate random token + if ($transactionData->transaction->amount > 0) { + $tokenData = [ + 'token' => TokenGenerator::generate(), + 'load' => round( + $transactionData->transaction->amount / + $randomMeter['tariff']['price'], + 2 + ), + ]; + $token = Token::query()->make([ + 'token' => $tokenData['token'], + 'load' => $tokenData['load'], + ]); + $token->transaction()->associate($transaction); + $token->save(); + $transactionData->token = $token; + + // generate meter_token + $meterTokenData = [ + 'meter_id' => $randomMeter->id, + 'token' => TokenGenerator::generate(), + 'energy' => round( + $transactionData->transaction->amount / + $randomMeter['tariff']['price'], + 2 + ), + 'transaction_id' => $transaction->id, + ]; + $meterToken = MeterToken::query()->make([ + 'meter_id' => $meterTokenData['meter_id'], + 'token' => $meterTokenData['token'], + 'energy' => $meterTokenData['energy'], + 'transaction_id' => $meterTokenData['transaction_id'], + ]); + $meterToken->save(); + + // payment event + event( + 'payment.successful', + [ + 'amount' => $transactionData->transaction->amount, + 'paymentService' => $transactionData->transaction->original_transaction_type, + 'paymentType' => 'energy', + 'sender' => $transactionData->transaction->sender, + 'paidFor' => $token, + 'payer' => $transactionData->device->person, + 'transaction' => $transactionData->transaction, + ] + ); + + // TODO: This currently doesn't work, it throws error that SMS is not configured. + // event('transaction.successful', [$transactionData->transaction]); + } + } +}