From b9993b9c239e8ea7ef81fbccf235b120434eae09 Mon Sep 17 00:00:00 2001 From: Nabeel S Date: Wed, 12 Feb 2020 10:40:52 -0500 Subject: [PATCH] Set expenses on specific flight types #348 (#549) * Set expenses on specific flight types #348 * Formatting * Use strict check for in_array --- ..._02_12_141152_expenses_add_flight_type.php | 32 ++++++ .../Controllers/Admin/ExpenseController.php | 5 +- app/Models/Expense.php | 35 +++++- app/Models/Pirep.php | 1 + app/Models/Traits/ReferenceTrait.php | 2 +- app/Services/Finance/PirepFinanceService.php | 25 +++-- .../views/admin/expenses/fields.blade.php | 14 ++- tests/FinanceTest.php | 105 +++++++++++++++++- 8 files changed, 199 insertions(+), 20 deletions(-) create mode 100644 app/Database/migrations/2020_02_12_141152_expenses_add_flight_type.php diff --git a/app/Database/migrations/2020_02_12_141152_expenses_add_flight_type.php b/app/Database/migrations/2020_02_12_141152_expenses_add_flight_type.php new file mode 100644 index 00000000..b3a3e98b --- /dev/null +++ b/app/Database/migrations/2020_02_12_141152_expenses_add_flight_type.php @@ -0,0 +1,32 @@ +string('flight_type', 50) + ->nullable() + ->after('type'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('expenses', function (Blueprint $table) { + $table->dropColumn('flight_type'); + }); + } +} diff --git a/app/Http/Controllers/Admin/ExpenseController.php b/app/Http/Controllers/Admin/ExpenseController.php index 79fc218f..a331dd58 100644 --- a/app/Http/Controllers/Admin/ExpenseController.php +++ b/app/Http/Controllers/Admin/ExpenseController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin; use App\Contracts\Controller; use App\Http\Controllers\Admin\Traits\Importable; use App\Models\Enums\ExpenseType; +use App\Models\Enums\FlightType; use App\Models\Enums\ImportExportType; use App\Models\Expense; use App\Repositories\AirlineRepository; @@ -69,6 +70,7 @@ class ExpenseController extends Controller return view('admin.expenses.create', [ 'airlines_list' => $this->airlineRepo->selectBoxList(true), 'expense_types' => ExpenseType::select(), + 'flight_types' => FlightType::select(), ]); } @@ -135,6 +137,7 @@ class ExpenseController extends Controller 'expense' => $expense, 'airlines_list' => $this->airlineRepo->selectBoxList(true), 'expense_types' => ExpenseType::select(), + 'flight_types' => FlightType::select(), ]); } @@ -154,14 +157,12 @@ class ExpenseController extends Controller if (empty($expenses)) { Flash::error('Expense not found'); - return redirect(route('admin.expenses.index')); } $this->expenseRepo->update($request->all(), $id); Flash::success('Expense updated successfully.'); - return redirect(route('admin.expenses.index')); } diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 15e0fd85..3a328487 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -6,13 +6,15 @@ use App\Contracts\Model; use App\Models\Traits\ReferenceTrait; /** - * Class Expense - * * @property int airline_id * @property float amount * @property string name + * @property string type + * @property string flight_type * @property string ref_model * @property string ref_model_id + * + * @mixin \Illuminate\Database\Eloquent\Builder */ class Expense extends Model { @@ -25,6 +27,7 @@ class Expense extends Model 'name', 'amount', 'type', + 'flight_type', 'multiplier', 'charge_to_user', 'ref_model', @@ -40,6 +43,34 @@ class Expense extends Model 'charge_to_user' => 'bool', ]; + /** + * flight_type is stored a comma delimited list in table. Retrieve it as an array + * + * @return array + */ + public function getFlightTypeAttribute() + { + if (empty(trim($this->attributes['flight_type']))) { + return []; + } + + return explode(',', $this->attributes['flight_type']); + } + + /** + * Make sure the flight type is stored a comma-delimited list in the table + * + * @param string $value + */ + public function setFlightTypeAttribute($value) + { + if (is_array($value)) { + $this->attributes['flight_type'] = implode(',', $value); + } else { + $this->attributes['flight_type'] = trim($value); + } + } + /** * Foreign Keys */ diff --git a/app/Models/Pirep.php b/app/Models/Pirep.php index 90d013d3..5b176e69 100644 --- a/app/Models/Pirep.php +++ b/app/Models/Pirep.php @@ -17,6 +17,7 @@ use Illuminate\Support\Collection; * @property string flight_number * @property string route_code * @property string route_leg + * @property string flight_type * @property int airline_id * @property int user_id * @property int aircraft_id diff --git a/app/Models/Traits/ReferenceTrait.php b/app/Models/Traits/ReferenceTrait.php index f7305830..bb6c4bdf 100644 --- a/app/Models/Traits/ReferenceTrait.php +++ b/app/Models/Traits/ReferenceTrait.php @@ -27,7 +27,7 @@ trait ReferenceTrait /** * Return an instance of the object or null * - * @return \App\Contracts\Model|null + * @return \App\Contracts\Model|$this|null */ public function getReferencedObject() { diff --git a/app/Services/Finance/PirepFinanceService.php b/app/Services/Finance/PirepFinanceService.php index 2cd99bbc..e79f0b28 100644 --- a/app/Services/Finance/PirepFinanceService.php +++ b/app/Services/Finance/PirepFinanceService.php @@ -4,10 +4,13 @@ namespace App\Services\Finance; use App\Contracts\Service; use App\Events\Expenses as ExpensesEvent; +use App\Models\Aircraft; +use App\Models\Airport; use App\Models\Enums\ExpenseType; use App\Models\Enums\PirepSource; use App\Models\Expense; use App\Models\Pirep; +use App\Models\Subfleet; use App\Repositories\ExpenseRepository; use App\Repositories\JournalRepository; use App\Services\FareService; @@ -222,13 +225,19 @@ class PirepFinanceService extends Service /* * Go through the expenses and apply a mulitplier if present */ - $expenses->map(function ($expense, $i) use ($pirep) { - /*if ($expense->multiplier) { - # TODO: Modify the amount - }*/ - + $expenses->map(function (/** @var \App\Models\Expense */ $expense, $i) use ($pirep) { Log::info('Finance: PIREP: '.$pirep->id.', expense:', $expense->toArray()); + // Check to see if there is a certain fleet or flight type set on this expense + // if there is and it doesn't match up the flight type for the PIREP, skip it + if ($expense->ref_model === Expense::class) { + if (is_array($expense->flight_type) && count($expense->flight_type) > 0) { + if (!in_array($pirep->flight_type, $expense->flight_type, true)) { + return; + } + } + } + // Get the transaction group name from the ref_model 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 @@ -239,13 +248,13 @@ class PirepFinanceService extends Service } // Form the memo, with some specific ones depending on the group - if ($klass === 'Airport') { + if ($expense->ref_model === Airport::class) { $memo = "Airport Expense: {$expense->name} ({$expense->ref_model_id})"; $transaction_group = "Airport: {$expense->ref_model_id}"; - } elseif ($klass === 'Subfleet') { + } elseif ($expense->ref_model === Subfleet::class) { $memo = "Subfleet Expense: {$expense->name} ({$pirep->aircraft->subfleet->name})"; $transaction_group = "Subfleet: {$expense->name} ({$pirep->aircraft->subfleet->name})"; - } elseif ($klass === 'Aircraft') { + } elseif ($expense->ref_model === Aircraft::class) { $memo = "Aircraft Expense: {$expense->name} ({$pirep->aircraft->name})"; $transaction_group = "Aircraft: {$expense->name} " ."({$pirep->aircraft->name}-{$pirep->aircraft->registration})"; diff --git a/resources/views/admin/expenses/fields.blade.php b/resources/views/admin/expenses/fields.blade.php index 87072a89..8b8253df 100644 --- a/resources/views/admin/expenses/fields.blade.php +++ b/resources/views/admin/expenses/fields.blade.php @@ -1,6 +1,6 @@
-
+
{{ Form::label('airline_id', 'Airline:') }} {{ Form::select('airline_id', $airlines_list, null , ['class' => 'form-control select2']) }} @@ -11,12 +11,20 @@ @endcomponent
- -
+
{{ Form::label('type', 'Expense Type:') }} * {{ Form::select('type', $expense_types, null , ['class' => 'form-control select2']) }}

{{ $errors->first('type') }}

+ +
+ {{ Form::label('flight_type', 'Flight Types:') }}  + {{ Form::select('flight_type[]', $flight_types, null , ['class' => 'form-control select2', 'multiple']) }} +

{{ $errors->first('type') }}

+ @component('admin.components.info') + If selected and the expense type is "flight", this expense will only apply to the specified flight types + @endcomponent +
diff --git a/tests/FinanceTest.php b/tests/FinanceTest.php index ed654c5d..0d200625 100644 --- a/tests/FinanceTest.php +++ b/tests/FinanceTest.php @@ -1,6 +1,9 @@ create([ 'flight_number' => $flight->flight_number, + 'flight_type' => FlightType::SCHED_PAX, 'route_code' => $flight->route_code, 'route_leg' => $flight->route_leg, 'dpt_airport_id' => $dpt_apt->id, @@ -114,7 +118,7 @@ class FinanceTest extends TestCase // Add a subfleet expense factory(App\Models\Expense::class)->create([ - 'ref_model' => \App\Models\Subfleet::class, + 'ref_model' => Subfleet::class, 'ref_model_id' => $subfleet['subfleet']->id, 'amount' => 200, ]); @@ -594,7 +598,7 @@ class FinanceTest extends TestCase $expenses = $this->expenseRepo->getAllForType( ExpenseType::FLIGHT, $airline->id, - \App\Models\Expense::class + Expense::class ); $this->assertCount(2, $expenses); @@ -615,7 +619,7 @@ class FinanceTest extends TestCase $subfleet = factory(App\Models\Subfleet::class)->create(); factory(App\Models\Expense::class)->create([ 'airline_id' => null, - 'ref_model' => \App\Models\Subfleet::class, + 'ref_model' => Subfleet::class, 'ref_model_id' => $subfleet->id, ]); @@ -628,7 +632,7 @@ class FinanceTest extends TestCase $this->assertCount(1, $expenses); $expense = $expenses->random(); - $this->assertEquals(\App\Models\Subfleet::class, $expense->ref_model); + $this->assertEquals(Subfleet::class, $expense->ref_model); $obj = $expense->getReferencedObject(); $this->assertEquals($obj->id, $expense->ref_model_id); } @@ -680,4 +684,97 @@ class FinanceTest extends TestCase $this->assertEquals($count, $find->count()); } } + + /** + * @throws Exception + */ + public function testPirepFinancesSpecificExpense() + { + $journalRepo = app(JournalRepository::class); + + // Add an expense that's only for a cargo flight + factory(App\Models\Expense::class)->create([ + 'airline_id' => null, + 'amount' => 100, + 'flight_type' => FlightType::SCHED_CARGO, + ]); + + [$user, $pirep, $fares] = $this->createFullPirep(); + $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' => 100, + ]; + } + + $this->fareSvc->saveForPirep($pirep, $fare_counts); + + // This should process all of the + $pirep = $this->pirepSvc->accept($pirep); + + $transactions = $journalRepo->getAllForObject($pirep); + +// $this->assertCount(9, $transactions['transactions']); + $this->assertEquals(3020, $transactions['credits']->getValue()); + $this->assertEquals(1960, $transactions['debits']->getValue()); + + // Check that all the different transaction types are there + // test by the different groups that exist + $transaction_tags = [ + 'fuel' => 1, + 'expense' => 1, + 'subfleet' => 2, + 'fare' => 3, + 'ground_handling' => 1, + 'pilot_pay' => 2, // debit on the airline, credit to the pilot + ]; + + foreach ($transaction_tags as $type => $count) { + $find = $transactions['transactions']->where('tags', $type); + $this->assertEquals($count, $find->count()); + } + + // Add a new PIREP; + $pirep2 = factory(App\Models\Pirep::class)->create([ + 'flight_number' => 100, + 'flight_type' => FlightType::SCHED_CARGO, + 'dpt_airport_id' => $pirep->dpt_airport_id, + 'arr_airport_id' => $pirep->arr_airport_id, + 'user_id' => $user->id, + 'airline_id' => $user->airline_id, + 'aircraft_id' => $pirep->aircraft_id, + 'source' => PirepSource::ACARS, + 'flight_time' => 120, + 'block_fuel' => 10, + 'fuel_used' => 9, + ]); + + $this->fareSvc->saveForPirep($pirep2, $fare_counts); + $pirep2 = $this->pirepSvc->accept($pirep2); + + $transactions = $journalRepo->getAllForObject($pirep2); + $this->assertEquals(3020, $transactions['credits']->getValue()); + $this->assertEquals(2060, $transactions['debits']->getValue()); + + // Check that all the different transaction types are there + // test by the different groups that exist + $transaction_tags = [ + 'fuel' => 1, + 'expense' => 2, + 'subfleet' => 2, + 'fare' => 3, + 'ground_handling' => 1, + 'pilot_pay' => 2, // debit on the airline, credit to the pilot + ]; + + foreach ($transaction_tags as $type => $count) { + $find = $transactions['transactions']->where('tags', $type); + $this->assertEquals($count, $find->count()); + } + } }