From 2c52a2f7e6dff2656b29d47e572f4f9256b6ebe6 Mon Sep 17 00:00:00 2001 From: Nabeel Shahzad Date: Thu, 1 Mar 2018 16:20:13 -0600 Subject: [PATCH] Post fares/expenses on PIREP to Airline journal #130 --- app/Database/factories/FareFactory.php | 3 + ...018_02_26_185121_create_expenses_table.php | 2 +- app/Events/PirepAccepted.php | 5 +- .../Controllers/Admin/PirepController.php | 6 +- .../Controllers/Frontend/PirepController.php | 13 +- app/Listeners/ExpenseListener.php | 25 ++ app/Listeners/FinanceEvents.php | 39 +++ ...entListener.php => NotificationEvents.php} | 2 +- app/Models/Airline.php | 11 + app/Models/Expense.php | 2 +- app/Models/Pirep.php | 7 + app/Models/Traits/JournalTrait.php | 39 +++ app/Models/User.php | 5 +- app/Providers/EventServiceProvider.php | 14 +- app/Repositories/ExpenseRepository.php | 25 ++ app/Repositories/FlightRepository.php | 16 +- app/Repositories/JournalRepository.php | 9 +- app/Services/FareService.php | 45 +++ app/Services/FinanceService.php | 262 ++++++++++++++++-- app/Services/PIREPService.php | 52 ++-- app/Services/UserService.php | 6 +- tests/FinanceTest.php | 122 +++++++- 22 files changed, 625 insertions(+), 85 deletions(-) create mode 100644 app/Listeners/ExpenseListener.php create mode 100644 app/Listeners/FinanceEvents.php rename app/Listeners/{NotificationEventListener.php => NotificationEvents.php} (99%) create mode 100644 app/Models/Traits/JournalTrait.php diff --git a/app/Database/factories/FareFactory.php b/app/Database/factories/FareFactory.php index d097e48b..1721abb9 100644 --- a/app/Database/factories/FareFactory.php +++ b/app/Database/factories/FareFactory.php @@ -8,6 +8,9 @@ $factory->define(App\Models\Fare::class, function (Faker $faker) { 'code' => $faker->text(5), 'name' => $faker->text(20), 'price' => $faker->randomFloat(2, 100, 1000), + 'cost' => function (array $fare) { + return round($fare['price'] / 2); + }, 'capacity' => $faker->randomFloat(0, 20, 500), ]; }); diff --git a/app/Database/migrations/2018_02_26_185121_create_expenses_table.php b/app/Database/migrations/2018_02_26_185121_create_expenses_table.php index 1dcb81ed..bba538ed 100644 --- a/app/Database/migrations/2018_02_26_185121_create_expenses_table.php +++ b/app/Database/migrations/2018_02_26_185121_create_expenses_table.php @@ -17,7 +17,7 @@ class CreateExpensesTable extends Migration $table->increments('id'); $table->unsignedInteger('airline_id')->nullable(); $table->string('name'); - $table->unsignedDecimal('amount'); + $table->unsignedInteger('amount'); $table->unsignedTinyInteger('type'); $table->boolean('multiplier')->nullable()->default(0); $table->boolean('active')->nullable()->default(1); diff --git a/app/Events/PirepAccepted.php b/app/Events/PirepAccepted.php index 9e454848..37942b23 100644 --- a/app/Events/PirepAccepted.php +++ b/app/Events/PirepAccepted.php @@ -13,11 +13,8 @@ class PirepAccepted public $pirep; - /** - * Create a new event instance. - */ public function __construct(Pirep $pirep) { - // + $this->pirep = $pirep; } } diff --git a/app/Http/Controllers/Admin/PirepController.php b/app/Http/Controllers/Admin/PirepController.php index 01c75014..6bfacc7f 100644 --- a/app/Http/Controllers/Admin/PirepController.php +++ b/app/Http/Controllers/Admin/PirepController.php @@ -15,6 +15,7 @@ use App\Repositories\AirportRepository; use App\Repositories\PirepFieldRepository; use App\Repositories\PirepRepository; use App\Repositories\SubfleetRepository; +use App\Services\FareService; use App\Services\PIREPService; use App\Services\UserService; use App\Support\Units\Time; @@ -31,6 +32,7 @@ class PirepController extends BaseController private $airportRepo, $airlineRepo, $aircraftRepo, + $fareSvc, $pirepSvc, $pirepRepo, $pirepFieldRepo, @@ -51,6 +53,7 @@ class PirepController extends BaseController AirportRepository $airportRepo, AirlineRepository $airlineRepo, AircraftRepository $aircraftRepo, + FareService $fareSvc, PirepRepository $pirepRepo, PirepFieldRepository $pirepFieldRepo, PIREPService $pirepSvc, @@ -60,6 +63,7 @@ class PirepController extends BaseController $this->airportRepo = $airportRepo; $this->airlineRepo = $airlineRepo; $this->aircraftRepo = $aircraftRepo; + $this->fareSvc = $fareSvc; $this->pirepRepo = $pirepRepo; $this->pirepFieldRepo = $pirepFieldRepo; $this->pirepSvc = $pirepSvc; @@ -143,7 +147,7 @@ class PirepController extends BaseController ]; } - $this->pirepSvc->saveFares($pirep->id, $fares); + $this->fareSvc->saveForPirep($pirep, $fares); } /** diff --git a/app/Http/Controllers/Frontend/PirepController.php b/app/Http/Controllers/Frontend/PirepController.php index c0d4ab18..8967c004 100644 --- a/app/Http/Controllers/Frontend/PirepController.php +++ b/app/Http/Controllers/Frontend/PirepController.php @@ -15,6 +15,7 @@ use App\Repositories\AirportRepository; use App\Repositories\Criteria\WhereCriteria; use App\Repositories\PirepFieldRepository; use App\Repositories\PirepRepository; +use App\Services\FareService; use App\Services\GeoService; use App\Services\PIREPService; use App\Services\UserService; @@ -29,10 +30,11 @@ class PirepController extends Controller { private $aircraftRepo, $airlineRepo, + $fareSvc, + $geoSvc, $pirepRepo, $airportRepo, $pirepFieldRepo, - $geoSvc, $pirepSvc, $userSvc; @@ -41,9 +43,10 @@ class PirepController extends Controller * @param AircraftRepository $aircraftRepo * @param AirlineRepository $airlineRepo * @param AirportRepository $airportRepo + * @param FareService $fareSvc + * @param GeoService $geoSvc * @param PirepRepository $pirepRepo * @param PirepFieldRepository $pirepFieldRepo - * @param GeoService $geoSvc * @param PIREPService $pirepSvc * @param UserService $userSvc */ @@ -51,9 +54,10 @@ class PirepController extends Controller AircraftRepository $aircraftRepo, AirlineRepository $airlineRepo, AirportRepository $airportRepo, + FareService $fareSvc, + GeoService $geoSvc, PirepRepository $pirepRepo, PirepFieldRepository $pirepFieldRepo, - GeoService $geoSvc, PIREPService $pirepSvc, UserService $userSvc ) { @@ -63,6 +67,7 @@ class PirepController extends Controller $this->airportRepo = $airportRepo; $this->pirepFieldRepo = $pirepFieldRepo; + $this->fareSvc = $fareSvc; $this->geoSvc = $geoSvc; $this->pirepSvc = $pirepSvc; $this->userSvc = $userSvc; @@ -143,7 +148,7 @@ class PirepController extends Controller ]; } - $this->pirepSvc->saveFares($pirep->id, $fares); + $this->fareSvc->saveForPirep($pirep, $fares); } /** diff --git a/app/Listeners/ExpenseListener.php b/app/Listeners/ExpenseListener.php new file mode 100644 index 00000000..902ce9ec --- /dev/null +++ b/app/Listeners/ExpenseListener.php @@ -0,0 +1,25 @@ + ExpenseType::FLIGHT, + 'amount' => 15000 # $150 + ]), + ]; + } +} diff --git a/app/Listeners/FinanceEvents.php b/app/Listeners/FinanceEvents.php new file mode 100644 index 00000000..d86bb106 --- /dev/null +++ b/app/Listeners/FinanceEvents.php @@ -0,0 +1,39 @@ +financeSvc = $financeSvc; + } + + public function subscribe($events) + { + $events->listen( + PirepAccepted::class, + 'App\Listeners\FinanceEvents@onPirepAccept' + ); + } + + /** + * Kick off the finance events when a PIREP is accepted + * @param PirepAccepted $event + */ + public function onPirepAccept(PirepAccepted $event) + { + $this->financeSvc->processFinancesForPirep($event->pirep); + } +} diff --git a/app/Listeners/NotificationEventListener.php b/app/Listeners/NotificationEvents.php similarity index 99% rename from app/Listeners/NotificationEventListener.php rename to app/Listeners/NotificationEvents.php index f1b6894b..74f72eff 100644 --- a/app/Listeners/NotificationEventListener.php +++ b/app/Listeners/NotificationEvents.php @@ -12,7 +12,7 @@ use Log; * Handle sending emails on different events * @package App\Listeners */ -class NotificationEventListener +class NotificationEvents { public function subscribe($events) { diff --git a/app/Models/Airline.php b/app/Models/Airline.php index f3fd0825..06ef9a4d 100644 --- a/app/Models/Airline.php +++ b/app/Models/Airline.php @@ -2,12 +2,16 @@ namespace App\Models; +use App\Models\Traits\JournalTrait; + /** * Class Airline * @package App\Models */ class Airline extends BaseModel { + use JournalTrait; + public $table = 'airlines'; public $fillable = [ @@ -68,5 +72,12 @@ 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/Expense.php b/app/Models/Expense.php index 4ae4e802..8480cb3e 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -23,7 +23,7 @@ class Expense extends BaseModel 'active' => 'boolean', 'airline_id' => 'integer', 'amount' => 'float', - 'multiplier' => 'integer', + 'multiplier' => 'bool', 'type' => 'integer', ]; diff --git a/app/Models/Pirep.php b/app/Models/Pirep.php index 5379fc07..f0930793 100644 --- a/app/Models/Pirep.php +++ b/app/Models/Pirep.php @@ -13,6 +13,13 @@ use PhpUnitsOfMeasure\Exception\NonStringUnitName; /** * Class Pirep * + * @property integer airline_id + * @property Airline airline + * @property Airport arr_airport + * @property Airport dep_airport + * @property mixed flight_number + * @property mixed route_code + * @property mixed route_leg * @package App\Models */ class Pirep extends BaseModel diff --git a/app/Models/Traits/JournalTrait.php b/app/Models/Traits/JournalTrait.php new file mode 100644 index 00000000..f662893c --- /dev/null +++ b/app/Models/Traits/JournalTrait.php @@ -0,0 +1,39 @@ +morphOne(Journal::class, 'morphed'); + } + + /** + * Initialize a journal for a given model object + * + * @param string $currency_code + * @return Journal + * @throws \Exception + */ + public function initJournal($currency_code = 'USD') + { + if (!$this->journal) { + $journal = new Journal(); + $journal->currency = $currency_code; + $journal->balance = 0; + $this->journal()->save($journal); + + $journal->refresh(); + return $journal; + } + throw new \Exception('Journal already exists.'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 3e769d93..a755a632 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Models\Enums\PirepState; +use App\Models\Traits\JournalTrait; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laratrust\Traits\LaratrustUserTrait; @@ -23,9 +24,9 @@ use Laratrust\Traits\LaratrustUserTrait; */ class User extends Authenticatable { - use Notifiable; + use JournalTrait; use LaratrustUserTrait; - //use SoftDeletes; + use Notifiable; public $table = 'users'; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 33b25c2a..b2345a73 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,8 +2,11 @@ namespace App\Providers; -use App\Listeners\NotificationEventListener; +use App\Listeners\FinanceEvents; +use App\Listeners\NotificationEvents; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; +use App\Listeners\ExpenseListener; +use App\Events\Expenses; class EventServiceProvider extends ServiceProvider @@ -14,13 +17,14 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $listen = [ - /*'App\Events\TestEvent' => [ - 'App\Listeners\EventListener', - ],*/ + Expenses::class => [ + ExpenseListener::class + ], ]; protected $subscribe = [ - NotificationEventListener::class, + FinanceEvents::class, + NotificationEvents::class, ]; /** diff --git a/app/Repositories/ExpenseRepository.php b/app/Repositories/ExpenseRepository.php index 37a94dd4..f6ca28dc 100644 --- a/app/Repositories/ExpenseRepository.php +++ b/app/Repositories/ExpenseRepository.php @@ -3,6 +3,7 @@ namespace App\Repositories; use App\Models\Expense; +use Illuminate\Support\Collection; use Prettus\Repository\Contracts\CacheableInterface; use Prettus\Repository\Traits\CacheableRepository; @@ -18,4 +19,28 @@ class ExpenseRepository extends BaseRepository implements CacheableInterface { return Expense::class; } + + /** + * Get all of the expenses for a given type, and also + * include expenses for a given airline ID + * @param $type + * @return Collection + */ + public function getAllForType($type, $airline_id=null) + { + $expenses = $this->findWhere([ + 'type' => $type, + ]); + + if($airline_id) { + $airline_expenses = $this->findWhere([ + 'type' => $type, + 'airline_id' => $airline_id + ]); + + $expenses = $expenses->concat($airline_expenses); + } + + return $expenses; + } } diff --git a/app/Repositories/FlightRepository.php b/app/Repositories/FlightRepository.php index 68349425..b763fba2 100644 --- a/app/Repositories/FlightRepository.php +++ b/app/Repositories/FlightRepository.php @@ -31,24 +31,24 @@ class FlightRepository extends BaseRepository implements CacheableInterface * Find a flight based on the given criterea * @param $airline_id * @param $flight_num - * @param null $flight_code - * @param null $flight_leg + * @param null $route_code + * @param null $route_leg * @return mixed */ - public function findFlight($airline_id, $flight_num, $flight_code=null, $flight_leg=null) + public function findFlight($airline_id, $flight_num, $route_code=null, $route_leg=null) { $where = [ 'airline_id' => $airline_id, - 'flight_num' => $flight_num, + 'flight_number' => $flight_num, 'active' => true, ]; - if(filled($flight_code)) { - $where['flight_code'] = $flight_code; + if(filled($route_code)) { + $where['route_code'] = $route_code; } - if(filled('flight_leg')) { - $where['flight_leg'] = $flight_leg; + if(filled($route_leg)) { + $where['route_leg'] = $route_leg; } return $this->findWhere($where); diff --git a/app/Repositories/JournalRepository.php b/app/Repositories/JournalRepository.php index 2a1f80be..8af708c5 100644 --- a/app/Repositories/JournalRepository.php +++ b/app/Repositories/JournalRepository.php @@ -19,6 +19,9 @@ class JournalRepository extends BaseRepository implements CacheableInterface { use CacheableRepository; + /** + * @return string + */ public function model() { return JournalTransaction::class; @@ -58,7 +61,6 @@ class JournalRepository extends BaseRepository implements CacheableInterface 'currency' => config('phpvms.currency'), 'memo' => $memo, 'post_date' => $post_date ?: Carbon::now(), - 'transaction_group' => $transaction_group, ]; if($reference !== null) { @@ -66,6 +68,11 @@ class JournalRepository extends BaseRepository implements CacheableInterface $attrs['ref_class_id'] = $reference->id; } + if($transaction_group) { + $transaction_group = str_replace(' ', '_', $transaction_group); + $attrs['transaction_group'] = $transaction_group; + } + try { $transaction = $this->create($attrs); } catch (ValidatorException $e) { diff --git a/app/Services/FareService.php b/app/Services/FareService.php index 26340bab..dd94c763 100644 --- a/app/Services/FareService.php +++ b/app/Services/FareService.php @@ -4,6 +4,8 @@ namespace App\Services; use App\Models\Fare; use App\Models\Flight; +use App\Models\Pirep; +use App\Models\PirepFare; use App\Models\Subfleet; use App\Support\Math; use Illuminate\Support\Collection; @@ -184,4 +186,47 @@ class FareService extends BaseService $subfleet->refresh(); return $subfleet; } + + /** + * Get the fares for a PIREP, this just returns the PirepFare + * model which includes the counts for that particular fare + * @param Pirep $pirep + * @return Collection + */ + public function getForPirep(Pirep $pirep) + { + $fares = []; + $found_fares = PirepFare::where('pirep_id', $pirep->id)->get(); + return $found_fares; + /*foreach($found_fares as $fare) { + $fares[] = $fare->toArray(); + } + + return collect($fares);*/ + } + + /** + * Save the list of fares + * @param Pirep $pirep + * @param array $fares ['fare_id', 'count'] + * @throws \Exception + */ + public function saveForPirep(Pirep $pirep, array $fares) + { + if (!$fares) { + return; + } + + # Remove all the previous fares + PirepFare::where('pirep_id', $pirep->id)->delete(); + + # Add them in + foreach ($fares as $fare) { + $fare['pirep_id'] = $pirep->id; + # other fields: ['fare_id', 'count'] + + $field = new PirepFare($fare); + $field->save(); + } + } } diff --git a/app/Services/FinanceService.php b/app/Services/FinanceService.php index 3163a345..de65345b 100644 --- a/app/Services/FinanceService.php +++ b/app/Services/FinanceService.php @@ -1,41 +1,266 @@ expenseRepo = $expenseRepo; $this->fareSvc = $fareSvc; - $this->flightSvc = $flightSvc; + $this->journalRepo = $journalRepo; + $this->pirepSvc = $pirepSvc; + } + + /** + * Determine from the base rate, if we want to return the overridden rate + * or if the overridden rate is a percentage, then return that amount + * @param $base_rate + * @param $override_rate + * @return float|null + */ + public function applyAmountOrPercent($base_rate, $override_rate=null): ?float + { + if (!$override_rate) { + return $base_rate; + } + + # Not a percentage override + if (substr_count($override_rate, '%') === 0) { + return $override_rate; + } + + return Math::addPercent($base_rate, $override_rate); + } + + /** + * 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) + { + $journal = $pirep->airline->journal; + if(!$journal) { + $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 + */ + + $fares = $this->getReconciledFaresForPirep($pirep); + foreach($fares as $fare) { + + $credit = Money::createFromAmount($fare->count * $fare->price); + $debit = Money::createFromAmount($fare->count * $fare->cost); + + $this->journalRepo->post( + $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 + */ + $expenses = $this->getExpenses($pirep); + foreach($expenses as $expense) { + $debit = Money::createFromAmount($expense->amount); + $this->journalRepo->post( + $journal, + null, + $debit, + $pirep, + 'Expense: ' . $expense->name, + null, + 'expenses' + ); + } + + /* + * Collect and apply the ground handling cost + */ + $ground_handling_cost = $this->getGroundHandlingCost($pirep); + $this->journalRepo->post( + $journal, + null, + Money::createFromAmount($ground_handling_cost), + $pirep, + 'Ground handling', + null, + 'ground_handling' + ); + + return $journal; + } + + /** + * 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); + $all_fares = $this->fareSvc->getAllFares($flight, $pirep->aircraft->subfleet); + + $fares = $all_fares->map(function($fare, $i) use ($flight_fares) { + + $fare_count = $flight_fares->whereStrict('id', $fare->id)->first(); + + if($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; + } + } + + 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 $this->applyAmountOrPercent( + $pirep->arr_airport->ground_handling_cost, + $multiplier + ); + } + + return $pirep->arr_airport->ground_handling_cost; + } + + /** + * Send out an event called ExpensesEvent, which picks up any + * event listeners and check if they return a list of additional + * Expense model objects. + * @param Pirep $pirep + * @return mixed + */ + public function getExpenses(Pirep $pirep) + { + $event_expenses = []; + + $expenses = $this->expenseRepo + ->getAllForType(ExpenseType::FLIGHT, $pirep->airline_id); + + /** + * Go through the expenses and apply a mulitplier if present + */ + $expenses = $expenses->map(function($expense, $i) use ($pirep) { + if(!$expense->multiplier) { + return $expense; + } + + // TODO Apply the multiplier from the subfleet + }); + + $gathered_expenses = event(new ExpensesEvent($pirep)); + if (!\is_array($gathered_expenses)) { + return $expenses; + } + + 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; + } + + $event_expenses[] = $expense; + } + } + + $expenses = $expenses->concat($event_expenses); + + return $expenses; } /** * Return the pilot's hourly pay for the given PIREP * @param Pirep $pirep * @return float - * @throws \Money\Exception\UnknownCurrencyException * @throws \InvalidArgumentException */ - public function getPayRateForPirep(Pirep $pirep) + public function getPilotPayRateForPirep(Pirep $pirep) { # Get the base rate for the rank $rank = $pirep->user->rank; @@ -55,16 +280,10 @@ class FinanceService extends BaseService $override_rate = $override_rate->manual_pay; } - if(!$override_rate) { - return $base_rate; - } - - # Not a percentage override - if(substr_count($override_rate, '%') === 0) { - return $override_rate; - } - - return Math::addPercent($base_rate, $override_rate); + return $this->applyAmountOrPercent( + $base_rate, + $override_rate + ); } /** @@ -73,11 +292,10 @@ class FinanceService extends BaseService * @return Money * @throws \UnexpectedValueException * @throws \InvalidArgumentException - * @throws \Money\Exception\UnknownCurrencyException */ public function getPilotPilotPay(Pirep $pirep) { - $pilot_rate = $this->getPayRateForPirep($pirep) / 60; + $pilot_rate = $this->getPilotPayRateForPirep($pirep) / 60; $payment = round($pirep->flight_time * $pilot_rate, 2); return Money::createFromAmount($payment); diff --git a/app/Services/PIREPService.php b/app/Services/PIREPService.php index 29178e0a..f0e09ea2 100644 --- a/app/Services/PIREPService.php +++ b/app/Services/PIREPService.php @@ -16,6 +16,7 @@ use App\Models\PirepFare; use App\Models\PirepFieldValues; use App\Models\User; use App\Repositories\AcarsRepository; +use App\Repositories\FlightRepository; use App\Repositories\NavdataRepository; use App\Repositories\PirepRepository; use Carbon\Carbon; @@ -24,11 +25,12 @@ use Log; class PIREPService extends BaseService { - protected $acarsRepo, - $geoSvc, - $navRepo, - $pilotSvc, - $pirepRepo; + private $acarsRepo, + $flightRepo, + $geoSvc, + $navRepo, + $pilotSvc, + $pirepRepo; /** * PIREPService constructor. @@ -40,6 +42,7 @@ class PIREPService extends BaseService */ public function __construct( AcarsRepository $acarsRepo, + FlightRepository $flightRepo, GeoService $geoSvc, NavdataRepository $navRepo, PirepRepository $pirepRepo, @@ -47,6 +50,7 @@ class PIREPService extends BaseService ) { $this->acarsRepo = $acarsRepo; + $this->flightRepo = $flightRepo; $this->geoSvc = $geoSvc; $this->pilotSvc = $pilotSvc; $this->navRepo = $navRepo; @@ -54,6 +58,21 @@ class PIREPService extends BaseService } /** + * Find the flight that a PIREP is based on + * @param Pirep $pirep + * @return mixed + */ + public function findFlight(Pirep $pirep) + { + return $this->flightRepo->findFlight( + $pirep->airline_id, + $pirep->flight_number, + $pirep->route_code, + $pirep->route_leg + )->first(); + } + + /**π * Find if there are duplicates to a given PIREP. Ideally, the passed * in PIREP hasn't been saved or gone through the create() method * @param Pirep $pirep @@ -211,29 +230,6 @@ class PIREPService extends BaseService } } - /** - * Save the list of fares - * @param $pirep_id - * @param array $fares ['field_id', 'count'] - * @throws \Exception - */ - public function saveFares($pirep_id, array $fares) - { - if(!$fares) { return; } - - # Remove all the previous fares - PirepFare::where('pirep_id', $pirep_id)->delete(); - - # Add them in - foreach($fares as $fare) { - $fare['pirep_id'] = $pirep_id; - # other fields: ['fare_id', 'count'] - - $field = new PirepFare($fare); - $field->save(); - } - } - /** * @param Pirep $pirep * @param int $new_state diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 9671fd4a..43b61f2d 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -36,9 +36,10 @@ class UserService extends BaseService /** * Register a pilot. Also attaches the initial roles * required, and then triggers the UserRegistered event - * @param User $user User model - * @param array $groups Additional groups to assign + * @param User $user User model + * @param array $groups Additional groups to assign * @return mixed + * @throws \Exception */ public function createPilot(User $user, array $groups=null) { @@ -64,6 +65,7 @@ class UserService extends BaseService # Let's check their rank and where they should start $this->calculatePilotRank($user); + $user->initJournal(config('phpvms.currency')); $user->refresh(); diff --git a/tests/FinanceTest.php b/tests/FinanceTest.php index 11ed6f8c..73bacecc 100644 --- a/tests/FinanceTest.php +++ b/tests/FinanceTest.php @@ -1,5 +1,6 @@ fareSvc = app(FareService::class); $this->financeSvc = app(FinanceService::class); $this->fleetSvc = app(FleetService::class); + $this->pirepSvc = app(PIREPService::class); } + /** + * Create a user and a PIREP, that has all of the data filled out + * so that we can test all of the disparate parts of the finances + * @return array + * @throws Exception + */ + public function createFullPirep() + { + /** + * Setup tests + */ + $subfleet = $this->createSubfleetWithAircraft(2); + $rank = $this->createRank(10, [$subfleet['subfleet']->id]); + $this->fleetSvc->addSubfleetToRank($subfleet['subfleet'], $rank); + + $user = factory(App\Models\User::class)->create([ + 'rank_id' => $rank->id, + ]); + + $flight = factory(App\Models\Flight::class)->create([ + 'airline_id' => $user->airline_id + ]); + + $pirep = factory(App\Models\Pirep::class)->create([ + 'flight_number' => $flight->flight_number, + 'route_code' => $flight->route_code, + 'route_leg' => $flight->route_leg, + 'user_id' => $user->id, + 'airline_id' => $user->airline_id, + 'aircraft_id' => $subfleet['aircraft']->random(), + 'source' => PirepSource::ACARS, + ]); + + /** + * Add fares to the subfleet, and then add the fares + * to the PIREP when it's saved, and set the capacity + */ + $fare_counts = []; + $fares = factory(App\Models\Fare::class, 3)->create(); + foreach ($fares as $fare) { + $this->fareSvc->setForSubfleet($subfleet['subfleet'], $fare); + } + + $pirep = $this->pirepSvc->create($pirep, []); + + return [$user, $pirep, $fares]; + } + + /** + * + */ public function testFlightFaresNoOverride() { $flight = factory(App\Models\Flight::class)->create(); @@ -297,7 +354,7 @@ class FinanceTest extends TestCase 'source' => PirepSource::ACARS, ]); - $rate = $this->financeSvc->getPayRateForPirep($pirep); + $rate = $this->financeSvc->getPilotPayRateForPirep($pirep); $this->assertEquals($rank->acars_base_pay_rate, $rate); } @@ -324,7 +381,7 @@ class FinanceTest extends TestCase 'source' => PirepSource::ACARS, ]); - $rate = $this->financeSvc->getPayRateForPirep($pirep_acars); + $rate = $this->financeSvc->getPilotPayRateForPirep($pirep_acars); $this->assertEquals($acars_pay_rate, $rate); # Change to a percentage @@ -342,11 +399,11 @@ class FinanceTest extends TestCase 'source' => PirepSource::MANUAL, ]); - $rate = $this->financeSvc->getPayRateForPirep($pirep_manual); + $rate = $this->financeSvc->getPilotPayRateForPirep($pirep_manual); $this->assertEquals($manual_pay_adjusted, $rate); # And make sure the original acars override still works - $rate = $this->financeSvc->getPayRateForPirep($pirep_acars); + $rate = $this->financeSvc->getPilotPayRateForPirep($pirep_acars); $this->assertEquals($acars_pay_rate, $rate); } @@ -444,4 +501,59 @@ class FinanceTest extends TestCase $this->assertEquals(125, $transactions['credits']->getValue()); $this->assertEquals(25, $transactions['debits']->getValue()); } + + /** + * + * @throws Exception + */ + public function testPirepFares() + { + [$user, $pirep, $fares] = $this->createFullPirep(); + + # Override the fares + $fare_counts = []; + foreach ($fares as $fare) { + $fare_counts[] = [ + 'fare_id' => $fare->id, + 'price' => $fare->price, + 'count' => round($fare->capacity / 2), + ]; + } + + $this->fareSvc->saveForPirep($pirep, $fare_counts); + $all_fares = $this->financeSvc->getReconciledFaresForPirep($pirep); + + $fare_counts = collect($fare_counts); + foreach($all_fares as $fare) { + $set_fare = $fare_counts->where('fare_id', $fare->id)->first(); + $this->assertEquals($set_fare['count'], $fare->count); + $this->assertEquals($set_fare['price'], $fare->price); + } + } + + /** + * + * @throws Exception + */ + public function testPirepFinances() + { + [$user, $pirep, $fares] = $this->createFullPirep(); + + $journal = $user->airline->initJournal(config('phpvms.currency')); + + # Override the fares + $fare_counts = []; + foreach ($fares as $fare) { + $fare_counts[] = [ + 'fare_id' => $fare->id, + 'price' => $fare->price, + 'count' => round($fare->capacity / 2), + ]; + } + + $this->fareSvc->saveForPirep($pirep, $fare_counts); + + # This should process all of the + $pirep = $this->pirepSvc->accept($pirep); + } }