expenseRepo = $expenseRepo; $this->fareSvc = $fareSvc; $this->journalRepo = $journalRepo; $this->pirepSvc = $pirepSvc; } /** * Process all of the finances for a pilot report. This is called * from a listener (FinanceEvents) * @param Pirep $pirep * @return mixed * @throws \UnexpectedValueException * @throws \InvalidArgumentException * @throws \Prettus\Validator\Exceptions\ValidatorException * @throws \Exception */ public function processFinancesForPirep(Pirep $pirep) { if(!$pirep->airline->journal) { $pirep->airline->journal = $pirep->airline->initJournal(config('phpvms.currency')); } if (!$pirep->user->journal) { $pirep->user->journal = $pirep->user->initJournal(config('phpvms.currency')); } # Clean out the expenses first $this->deleteFinancesForPirep($pirep); # Now start and pay from scratch $this->payFaresForPirep($pirep); $this->payExpensesForPirep($pirep); $this->payExpensesEventsForPirep($pirep); $this->payGroundHandlingForPirep($pirep); $this->payPilotForPirep($pirep); $pirep->airline->journal->refresh(); $pirep->user->journal->refresh(); return $pirep; } /** * @param Pirep $pirep */ public function deleteFinancesForPirep(Pirep $pirep) { $this->journalRepo->deleteAllForObject($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 \UnexpectedValueException * @throws \InvalidArgumentException * @throws \Prettus\Validator\Exceptions\ValidatorException */ public function payFaresForPirep($pirep): void { $fares = $this->getReconciledFaresForPirep($pirep); /** @var \App\Models\Fare $fare */ foreach ($fares as $fare) { Log::info('Finance: PIREP: '.$pirep->id.', fare:', $fare->toArray()); $credit = Money::createFromAmount($fare->count * $fare->price); $debit = Money::createFromAmount($fare->count * $fare->cost); $this->journalRepo->post( $pirep->airline->journal, $credit, $debit, $pirep, 'Fares ' . $fare->code . $fare->count .'; price: '.$fare->price.', cost: '.$fare->cost, null, 'Fares' ); } } /** * Collect all of the expenses and apply those to the journal * @param Pirep $pirep * @throws \UnexpectedValueException * @throws \InvalidArgumentException */ public function payExpensesForPirep(Pirep $pirep): void { $expenses = $this->expenseRepo->getAllForType( ExpenseType::FLIGHT, $pirep->airline_id ); /** * Go through the expenses and apply a mulitplier if present */ $expenses->map(function ($expense, $i) use ($pirep) { if ($expense->multiplier) { # TODO: Modify the amount } Log::info('Finance: PIREP: ' . $pirep->id . ', expense:', $expense->toArray()); # Get the transaction group name from the ref_class name # This way it can be more dynamic and don't have to add special # tables or specific expense calls to accomodate all of these $transaction_group = 'Expense'; if($expense->ref_class) { $ref = explode('\\', $expense->ref_class); $transaction_group = end($ref); } $debit = Money::createFromAmount($expense->amount); $this->journalRepo->post( $pirep->airline->journal, null, $debit, $pirep, 'Expense: ' . $expense->name, null, $transaction_group ); }); } /** * Collect all of the expenses from the listeners and apply those to the journal * @param Pirep $pirep * @throws \UnexpectedValueException * @throws \InvalidArgumentException * @throws \Prettus\Validator\Exceptions\ValidatorException */ public function payExpensesEventsForPirep(Pirep $pirep): void { /** * Throw an event and collect any expenses returned from it */ $gathered_expenses = event(new ExpensesEvent($pirep)); if (!\is_array($gathered_expenses)) { return; } foreach ($gathered_expenses as $event_expense) { if (!\is_array($event_expense)) { continue; } foreach ($event_expense as $expense) { # Make sure it's of type expense Model if (!($expense instanceof Expense)) { continue; } # If an airline_id is filled, then see if it matches if ($expense->airline_id !== $pirep->airline_id) { continue; } $debit = Money::createFromAmount($expense->amount); $this->journalRepo->post( $pirep->airline->journal, null, $debit, $pirep, 'Expense: ' . $expense->name, null, $expense->transaction_group ?? 'Expenses' ); } } } /** * Collect and apply the ground handling cost * @param Pirep $pirep * @throws \UnexpectedValueException * @throws \InvalidArgumentException * @throws \Prettus\Validator\Exceptions\ValidatorException */ public function payGroundHandlingForPirep(Pirep $pirep) { $ground_handling_cost = $this->getGroundHandlingCost($pirep); Log::info('Finance: PIREP: '.$pirep->id.'; ground handling: '.$ground_handling_cost); $this->journalRepo->post( $pirep->airline->journal, null, Money::createFromAmount($ground_handling_cost), $pirep, 'Ground Handling', null, 'Ground Handling' ); } /** * 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; Log::info('Finance: PIREP: '.$pirep->id .'; pilot pay: '.$pilot_pay_rate.', total: '.$pilot_pay); $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' ); } /** * Return all of the fares for the PIREP. Reconcile the list; * Get the fares that have been filled out for the PIREP, and * then get the fares for the flight and subfleet. Then merge * them together, and return the final list of: * count = number of pax * price = how much each pax unit paid * capacity = max number of pax units * * If count > capacity, count will be adjusted to capacity * @param $pirep * @return \Illuminate\Support\Collection */ public function getReconciledFaresForPirep($pirep) { $flight = $this->pirepSvc->findFlight($pirep); # Collect all of the fares and prices $flight_fares = $this->fareSvc->getForPirep($pirep); Log::info('Finance: PIREP: ' . $pirep->id . ', flight fares: ', $flight_fares->toArray()); $all_fares = $this->fareSvc->getAllFares($flight, $pirep->aircraft->subfleet); $fares = $all_fares->map(function($fare, $i) use ($flight_fares, $pirep) { $fare_count = $flight_fares ->where('fare_id', $fare->id) ->first(); if($fare_count) { Log::info('Finance: PIREP: ' . $pirep->id . ', fare count: '. $fare_count); # If the count is greater than capacity, then just set it # to the maximum amount if($fare_count->count > $fare->capacity) { $fare->count = $fare->capacity; } else { $fare->count = $fare_count->count; } } else { Log::info('Finance: PIREP: ' . $pirep->id . ', no fare count found', $fare->toArray()); } return $fare; }); return $fares; } /** * Return the costs for the ground handling, with the multiplier * being applied from the subfleet * @param Pirep $pirep * @return float|null */ public function getGroundHandlingCost(Pirep $pirep) { if(filled($pirep->aircraft->subfleet->ground_handling_multiplier)) { // force into percent mode $multiplier = $pirep->aircraft->subfleet->ground_handling_multiplier.'%'; return Math::applyAmountOrPercent( $pirep->arr_airport->ground_handling_cost, $multiplier ); } return $pirep->arr_airport->ground_handling_cost; } /** * Return the pilot's hourly pay for the given PIREP * @param Pirep $pirep * @return float * @throws \InvalidArgumentException */ public function getPilotPayRateForPirep(Pirep $pirep) { # Get the base rate for the rank $rank = $pirep->user->rank; $subfleet_id = $pirep->aircraft->subfleet_id; # find the right subfleet $override_rate = $rank->subfleets() ->where('subfleet_id', $subfleet_id) ->first(); if($override_rate) { $override_rate = $override_rate->pivot; } if($pirep->source === PirepSource::ACARS) { Log::debug('Source is ACARS'); $base_rate = $rank->acars_base_pay_rate; if($override_rate) { $override_rate = $override_rate->acars_pay; } } else { Log::debug('Source is Manual'); $base_rate = $rank->manual_base_pay_rate; if($override_rate) { $override_rate = $override_rate->manual_pay; } } Log::debug('pilot pay: base rate=' . $base_rate . ', override=' . $override_rate); return Math::applyAmountOrPercent( $base_rate, $override_rate ); } /** * Get the user's payment amount for a PIREP * @param Pirep $pirep * @return Money * @throws \UnexpectedValueException * @throws \InvalidArgumentException */ public function getPilotPay(Pirep $pirep) { $pilot_rate = $this->getPilotPayRateForPirep($pirep) / 60; $payment = round($pirep->flight_time * $pilot_rate, 2); Log::info('Pilot Payment: rate='.$pilot_rate); $payment = Money::convertToSubunit($payment); return new Money($payment); } }