From 9f89447070164d8c63ae7e1c1203d697922e6085 Mon Sep 17 00:00:00 2001 From: Nabeel Shahzad Date: Fri, 2 Mar 2018 13:12:39 -0600 Subject: [PATCH] Add pilot pay to journals; cleanup payment code #130 --- .../2017_06_21_165410_create_ranks_table.php | 2 + app/Database/seeds/sample.yml | 249 ++++++++++-------- app/Listeners/ExpenseListener.php | 18 +- app/Models/Airline.php | 7 - app/Models/Traits/HashId.php | 3 +- app/Models/Traits/JournalTrait.php | 11 + app/Services/FinanceService.php | 115 ++++++-- app/Services/PIREPService.php | 2 + app/Services/UserService.php | 2 - tests/FinanceTest.php | 4 +- 10 files changed, 262 insertions(+), 151 deletions(-) diff --git a/app/Database/migrations/2017_06_21_165410_create_ranks_table.php b/app/Database/migrations/2017_06_21_165410_create_ranks_table.php index fe719df7..8d3dd7a5 100644 --- a/app/Database/migrations/2017_06_21_165410_create_ranks_table.php +++ b/app/Database/migrations/2017_06_21_165410_create_ranks_table.php @@ -36,6 +36,8 @@ class CreateRanksTable extends Migration 'id' => 1, 'name' => 'New Pilot', 'hours' => 0, + 'acars_base_pay_rate' => 50, + 'manual_base_pay_rate' => 25, ] ]; diff --git a/app/Database/seeds/sample.yml b/app/Database/seeds/sample.yml index c9fb5fa4..73443c38 100644 --- a/app/Database/seeds/sample.yml +++ b/app/Database/seeds/sample.yml @@ -333,8 +333,9 @@ pireps: dpt_airport_id: KAUS arr_airport_id: KJFK flight_time: 180 # 6 hours - state: 2 + state: 2 # accepted status: 0 + source: 1 route: KAUS SID TNV J87 IAH J2 LCH J22 MEI J239 ATL J52 AJFEB J14 BYJAC Q60 JAXSN J14 COLIN J61 HUBBS J55 SIE STAR KJFK notes: just a pilot report created_at: NOW @@ -350,7 +351,7 @@ pireps: state: 1 route: PLMMR2 SPA Q22 BEARI FAK PHLBO3 notes: just a pilot report - source: 1 + source: 1 # pending source_name: sample created_at: NOW updated_at: NOW @@ -362,7 +363,8 @@ pireps: dpt_airport_id: KJFK arr_airport_id: KAUS flight_time: 180 # 6 hours - state: 1 + state: 1 # pending + source: 0 # manual route: PLMMR2 SPA Q22 BEARI FAK PHLBO3 notes: just a pilot report created_at: NOW @@ -429,117 +431,150 @@ pirep_comments: updated_at: now journals: - - id: '1' + - id: 1 ledger_id: null - balance: '7965000' + balance: 7950000 currency: USD morphed_type: App\Models\Airline - morphed_id: '1' - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - + morphed_id: 1 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + - id: 2 + ledger_id: null + balance: 15000 + currency: USD + morphed_type: App\Models\User + morphed_id: 1 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' journal_transactions: - - id: d2875dd2-e329-4d75-9883-120ef88f343a - transaction_group: fares - journal_id: '1' - credit: '6000000' - debit: '0' - currency: USD - memo: 'Fares Y300; price:200, cost: 0' - ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' - deleted_at: null - - id: 3912bedb-138e-47f4-9a1f-bcd29c3c00ad - transaction_group: fares - journal_id: '1' - credit: '1100000' - debit: '0' - currency: USD - memo: 'Fares B10; price:1100, cost: 0' - ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' - deleted_at: null - - id:: 55bbc2b8-646e-4d07-88d4-54c11c721f35 - transaction_group: fares - journal_id: '1' - credit: '1000000' - debit: '0' - currency: USD - memo: 'Fares F10; price:1000, cost: 0' - ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' - deleted_at: null - - id:: d9a353ea-5e40-4c7c-b976-305b89957185 - transaction_group: expenses - journal_id: '1' - credit: null - debit: '10000' - currency: USD - memo: 'Expense: Per-Flight (no muliplier)' - ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' - deleted_at: null - - id:: 9d1f321a-7fdb-402b-af90-ebf6cd5717a3 - transaction_group: expenses - journal_id: '1' - credit: null - debit: '10000' - currency: USD - memo: 'Expense: Per-Flight (multiplier)' - ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' - deleted_at: null - - id:: 7583cd5f-e75d-472d-b913-af724dc30f30 - transaction_group: expenses - journal_id: '1' - credit: null - debit: '20000' - currency: USD - memo: 'Expense: Per-Flight (multiplier, on airline)' - ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' - deleted_at: null - - id:: 9cf2fe46-124c-40a1-8cb8-1d1940c18704 - transaction_group: expenses - journal_id: '1' - credit: null - debit: '20000' - currency: USD - memo: 'Expense: Per-Flight (multiplier, on airline)' - ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' - deleted_at: null - - id:: 200af4e2-50fd-43f9-bcf3-93cc29800679 + - id: 4c708dff-653a-41ab-8df8-9b2cd11430a1 transaction_group: ground_handling - journal_id: '1' + journal_id: 1 credit: null - debit: '75000' + debit: 75000 currency: USD memo: 'Ground handling' ref_class: App\Models\Pirep - ref_class_id: pirepid_1 - created_at: '2018-03-02 03:31:59' - updated_at: '2018-03-02 03:31:59' - post_date: '2018-03-02 03:31:59' + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: 6c4d3882-01cd-4f71-8b6c-6c7d97a338da + transaction_group: expenses + journal_id: 1 + credit: null + debit: 20000 + currency: USD + memo: 'Expense: Per-Flight (multiplier, on airline)' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: 830a5479-8c32-47e5-90c3-5a85e46e93a7 + transaction_group: expenses + journal_id: 1 + credit: null + debit: 10000 + currency: USD + memo: 'Expense: Per-Flight (multiplier)' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: 8fb9adcc-c4aa-4ef2-aebd-9e9834c260f8 + transaction_group: expenses + journal_id: 1 + credit: null + debit: 20000 + currency: USD + memo: 'Expense: Per-Flight (multiplier, on airline)' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: 93ffaa27-4fb3-4a03-99e3-316d80ffdade + transaction_group: expenses + journal_id: 1 + credit: null + debit: 10000 + currency: USD + memo: 'Expense: Per-Flight (no muliplier)' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: b1d694ee-a390-4640-a370-b512677c3d8a + transaction_group: pilot_pay + journal_id: 1 + credit: null + debit: 15000 + currency: USD + memo: 'Pilot Payment @ 50' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: b4d873b1-0548-404d-ace0-5503fa68f01c + transaction_group: pilot_pay + journal_id: 2 + credit: 15000 + debit: null + currency: USD + memo: 'Pilot Payment @ 50' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: b91bf014-6e60-44a5-8551-71e080ae38d5 + transaction_group: fares + journal_id: 1 + credit: 1100000 + debit: 0 + currency: USD + memo: 'Fares B10; price:1100, cost: 0' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: e0c39035-13ec-4cfa-9f0e-d1dea9fd6087 + transaction_group: fares + journal_id: 1 + credit: 1000000 + debit: 0 + currency: USD + memo: 'Fares F10; price:1000, cost: 0' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' + deleted_at: null + - id: f524150c-0843-4ba6-ad95-a288b61af2ff + transaction_group: fares + journal_id: 1 + credit: 6000000 + debit: 0 + currency: USD + memo: 'Fares Y300; price:200, cost: 0' + ref_class: App\Models\Pirep + ref_class_id: 0 + created_at: '2018-03-02 19:10:10' + updated_at: '2018-03-02 19:10:10' + post_date: '2018-03-02 19:10:10' deleted_at: null diff --git a/app/Listeners/ExpenseListener.php b/app/Listeners/ExpenseListener.php index 902ce9ec..2718e59b 100644 --- a/app/Listeners/ExpenseListener.php +++ b/app/Listeners/ExpenseListener.php @@ -11,15 +11,19 @@ class ExpenseListener /** * Return a list of additional expenses * @param Expenses $event - * @return array + * @return mixed */ public function handle(Expenses $event) { - return [ - new Expense([ - 'type' => ExpenseType::FLIGHT, - 'amount' => 15000 # $150 - ]), - ]; + $expenses = []; + + # This is an example of an expense to return + # You have the pirep on $event->pirep, and any associated data + /*$expenses[] = new Expense([ + 'type' => ExpenseType::FLIGHT, + 'amount' => 15000 # $150 + ]);*/ + + return $expenses; } } diff --git a/app/Models/Airline.php b/app/Models/Airline.php index 06ef9a4d..982f4840 100644 --- a/app/Models/Airline.php +++ b/app/Models/Airline.php @@ -72,12 +72,5 @@ class Airline extends BaseModel $model->icao = strtoupper($model->icao); } }); - - /** - * Make sure a new journal object is created - */ - static::created(function(Airline $model) { - $model->initJournal(config('phpvms.currency')); - }); } } diff --git a/app/Models/Traits/HashId.php b/app/Models/Traits/HashId.php index f3a48868..aef618de 100644 --- a/app/Models/Traits/HashId.php +++ b/app/Models/Traits/HashId.php @@ -20,9 +20,8 @@ trait HashId /** * Register callbacks */ - protected static function boot() + protected static function bootHashId() { - parent::boot(); static::creating(function ($model) { if (empty($model->id)) { $model->id = static::createNewHashId(); diff --git a/app/Models/Traits/JournalTrait.php b/app/Models/Traits/JournalTrait.php index ceab7cb9..3979445c 100644 --- a/app/Models/Traits/JournalTrait.php +++ b/app/Models/Traits/JournalTrait.php @@ -6,6 +6,17 @@ use App\Models\Journal; trait JournalTrait { + + /** + * Initialize a new journal when a new record is created + */ + public static function bootJournalTrait() + { + static::created(function ($model) { + $model->initJournal(config('phpvms.currency')); + }); + } + /** * Morph to Journal. * diff --git a/app/Services/FinanceService.php b/app/Services/FinanceService.php index a5549014..ca9f58ac 100644 --- a/app/Services/FinanceService.php +++ b/app/Services/FinanceService.php @@ -77,18 +77,35 @@ class FinanceService extends BaseService */ public function processFinancesForPirep(Pirep $pirep) { - $journal = $pirep->airline->journal; - if(!$journal) { - $journal = $pirep->airline->initJournal(config('phpvms.currency')); + if(!$pirep->airline->journal) { + $pirep->airline->journal = $pirep->airline->initJournal(config('phpvms.currency')); } - /* - * Collect all of the fares and then post each fare class's profit and - * the costs for each seat and post it to the journal - */ + if (!$pirep->user->journal) { + $pirep->user->journal = $pirep->user->initJournal(config('phpvms.currency')); + } + $this->payFaresForPirep($pirep); + $this->payExpensesForPirep($pirep); + $this->payGroundHandlingForPirep($pirep); + $this->payPilotForPirep($pirep); + + $pirep->airline->journal->refresh(); + $pirep->user->journal->refresh(); + + return $pirep; + } + + /** + * Collect all of the fares and then post each fare class's profit and + * the costs for each seat and post it to the journal + * @param $pirep + * @throws \Prettus\Validator\Exceptions\ValidatorException + */ + public function payFaresForPirep($pirep): void + { $fares = $this->getReconciledFaresForPirep($pirep); - foreach($fares as $fare) { + foreach ($fares as $fare) { Log::info('Finance: PIREP: ' . $pirep->id . ', fare:', $fare->toArray()); @@ -96,28 +113,34 @@ class FinanceService extends BaseService $debit = Money::createFromAmount($fare->count * $fare->cost); $this->journalRepo->post( - $journal, + $pirep->airline->journal, $credit, $debit, $pirep, - 'Fares '. $fare->code.$fare->count - . '; price:'.$fare->price .', cost: '. $fare->cost, + 'Fares ' . $fare->code . $fare->count + . '; price:' . $fare->price . ', cost: ' . $fare->cost, null, 'fares' ); } + } - /* - * Collect all of the expenses and apply those to the journal - */ + /** + * Collect all of the expenses and apply those to the journal + * @param Pirep $pirep + * @throws \InvalidArgumentException + * @throws \Prettus\Validator\Exceptions\ValidatorException + */ + public function payExpensesForPirep(Pirep $pirep): void + { $expenses = $this->getExpenses($pirep); - foreach($expenses as $expense) { + foreach ($expenses as $expense) { - Log::info('Finance: PIREP: '.$pirep->id.', expense:', $expense->toArray()); + Log::info('Finance: PIREP: ' . $pirep->id . ', expense:', $expense->toArray()); $debit = Money::createFromAmount($expense->amount); $this->journalRepo->post( - $journal, + $pirep->airline->journal, null, $debit, $pirep, @@ -126,13 +149,18 @@ class FinanceService extends BaseService 'expenses' ); } + } - /* - * Collect and apply the ground handling cost - */ + /** + * Collect and apply the ground handling cost + * @param Pirep $pirep + * @throws \Prettus\Validator\Exceptions\ValidatorException + */ + public function payGroundHandlingForPirep(Pirep $pirep) + { $ground_handling_cost = $this->getGroundHandlingCost($pirep); $this->journalRepo->post( - $journal, + $pirep->airline->journal, null, Money::createFromAmount($ground_handling_cost), $pirep, @@ -140,8 +168,41 @@ class FinanceService extends BaseService null, 'ground_handling' ); + } - return $journal; + /** + * Figure out what the pilot pay is. Debit it from the airline journal + * But also reference the PIREP + * @param Pirep $pirep + * @throws \UnexpectedValueException + * @throws \InvalidArgumentException + * @throws \Prettus\Validator\Exceptions\ValidatorException + */ + public function payPilotForPirep(Pirep $pirep) + { + $pilot_pay = $this->getPilotPay($pirep); + $pilot_pay_rate = $this->getPilotPayRateForPirep($pirep); + $memo = 'Pilot Payment @ ' . $pilot_pay_rate; + + $this->journalRepo->post( + $pirep->airline->journal, + null, + $pilot_pay, + $pirep, + $memo, + null, + 'pilot_pay' + ); + + $this->journalRepo->post( + $pirep->user->journal, + $pilot_pay, + null, + $pirep, + $memo, + null, + 'pilot_pay' + ); } /** @@ -280,13 +341,16 @@ class FinanceService extends BaseService ->pivot; if($pirep->source === PirepSource::ACARS) { + Log::debug('Source is ACARS'); $base_rate = $rank->acars_base_pay_rate; $override_rate = $override_rate->acars_pay; } else { + Log::debug('Source is Manual'); $base_rate = $rank->manual_base_pay_rate; $override_rate = $override_rate->manual_pay; } + Log::debug('pilot pay: base rate=' . $base_rate . ', override=' . $override_rate); return $this->applyAmountOrPercent( $base_rate, $override_rate @@ -300,11 +364,14 @@ class FinanceService extends BaseService * @throws \UnexpectedValueException * @throws \InvalidArgumentException */ - public function getPilotPilotPay(Pirep $pirep) + public function getPilotPay(Pirep $pirep) { $pilot_rate = $this->getPilotPayRateForPirep($pirep) / 60; $payment = round($pirep->flight_time * $pilot_rate, 2); - return Money::createFromAmount($payment); + Log::info('Pilot Payment: rate='.$pilot_rate); + $payment = Money::convertToSubunit($payment); + + return new Money($payment); } } diff --git a/app/Services/PIREPService.php b/app/Services/PIREPService.php index f0e09ea2..1e7e0d3e 100644 --- a/app/Services/PIREPService.php +++ b/app/Services/PIREPService.php @@ -305,6 +305,8 @@ class PIREPService extends BaseService $pirep->aircraft->landing_time = $pirep->updated_at; $pirep->aircraft->save(); + $pirep->refresh(); + event(new PirepAccepted($pirep)); return $pirep; diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 43b61f2d..e719c2d7 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -65,8 +65,6 @@ class UserService extends BaseService # Let's check their rank and where they should start $this->calculatePilotRank($user); - $user->initJournal(config('phpvms.currency')); - $user->refresh(); event(new UserRegistered($user)); diff --git a/tests/FinanceTest.php b/tests/FinanceTest.php index 50ab3845..326b7b20 100644 --- a/tests/FinanceTest.php +++ b/tests/FinanceTest.php @@ -446,7 +446,7 @@ class FinanceTest extends TestCase 'flight_time' => 60, ]); - $payment = $this->financeSvc->getPilotPilotPay($pirep_acars); + $payment = $this->financeSvc->getPilotPay($pirep_acars); $this->assertEquals(100, $payment->getValue()); $pirep_acars = factory(App\Models\Pirep::class)->create([ @@ -456,7 +456,7 @@ class FinanceTest extends TestCase 'flight_time' => 90, ]); - $payment = $this->financeSvc->getPilotPilotPay($pirep_acars); + $payment = $this->financeSvc->getPilotPay($pirep_acars); $this->assertEquals($payment->getValue(), 150); }