Remove subfleet_expenses and combine into main expenses table; select expense type on subfleet #130 #136

This commit is contained in:
Nabeel Shahzad
2018-03-05 21:24:49 -06:00
parent 8c05ad134e
commit 88a8fd2bbd
20 changed files with 160 additions and 138 deletions

View File

@@ -13,6 +13,8 @@ $factory->define(App\Models\Expense::class, function (Faker $faker) {
'amount' => $faker->randomFloat(2, 100, 1000), 'amount' => $faker->randomFloat(2, 100, 1000),
'type' => ExpenseType::FLIGHT, 'type' => ExpenseType::FLIGHT,
'multiplier' => false, 'multiplier' => false,
'ref_class' => \App\Models\Expense::class,
'ref_class_id' => null,
'active' => true, 'active' => true,
]; ];
}); });

View File

@@ -1,11 +0,0 @@
<?php
use Faker\Generator as Faker;
$factory->define(App\Models\SubfleetExpense::class, function (Faker $faker) {
return [
'subfleet_id' => null,
'name' => $faker->text(20),
'amount' => $faker->randomFloat(2, 100, 1000),
];
});

View File

@@ -3,14 +3,11 @@
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateSubfleetTables
*/
class CreateSubfleetTables extends Migration class CreateSubfleetTables extends Migration
{ {
/**
* Run the migrations.
*
* @return void
*/
public function up() public function up()
{ {
Schema::create('subfleets', function (Blueprint $table) { Schema::create('subfleets', function (Blueprint $table) {
@@ -26,17 +23,6 @@ class CreateSubfleetTables extends Migration
$table->timestamps(); $table->timestamps();
}); });
Schema::create('subfleet_expenses', function(Blueprint $table) {
$table->increments('id');
$table->unsignedBigInteger('subfleet_id');
$table->string('name', 50);
$table->unsignedTinyInteger('type'); # ExpenseType
$table->unsignedDecimal('amount');
$table->timestamps();
$table->index('subfleet_id');
});
Schema::create('subfleet_fare', function (Blueprint $table) { Schema::create('subfleet_fare', function (Blueprint $table) {
$table->unsignedInteger('subfleet_id'); $table->unsignedInteger('subfleet_id');
$table->unsignedInteger('fare_id'); $table->unsignedInteger('fare_id');
@@ -76,7 +62,6 @@ class CreateSubfleetTables extends Migration
public function down() public function down()
{ {
Schema::dropIfExists('subfleets'); Schema::dropIfExists('subfleets');
Schema::dropIfExists('subfleet_expenses');
Schema::dropIfExists('subfleet_fare'); Schema::dropIfExists('subfleet_fare');
Schema::dropIfExists('subfleet_flight'); Schema::dropIfExists('subfleet_flight');
Schema::dropIfExists('subfleet_rank'); Schema::dropIfExists('subfleet_rank');

View File

@@ -16,15 +16,19 @@ class CreateExpensesTable extends Migration
Schema::create('expenses', function (Blueprint $table) { Schema::create('expenses', function (Blueprint $table) {
$table->increments('id'); $table->increments('id');
$table->unsignedInteger('airline_id')->nullable(); $table->unsignedInteger('airline_id')->nullable();
$table->string('name'); $table->string('name');
$table->unsignedInteger('amount'); $table->unsignedInteger('amount');
$table->unsignedTinyInteger('type'); $table->unsignedTinyInteger('type');
$table->boolean('multiplier')->nullable()->default(0); $table->boolean('multiplier')->nullable()->default(0);
$table->boolean('active')->nullable()->default(true); $table->boolean('active')->nullable()->default(true);
# Internal fields are expenses tied to some system object # ref fields are expenses tied to some model object
# EG, the airports has an internal expense for gate costs # EG, the airports has an internal expense for gate costs
$table->nullableMorphs('expensable'); $table->string('ref_class')->nullable();
$table->string('ref_class_id', 36)->nullable();
$table->index(['ref_class', 'ref_class_id']);
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@@ -184,25 +184,38 @@ expenses:
amount: 100 amount: 100
type: 0 type: 0
active: 1 active: 1
ref_class: App\Models\Expense
- name: Per-Flight (multiplier) - name: Per-Flight (multiplier)
amount: 100 amount: 100
type: 0 type: 0
multiplier: 1 multiplier: 1
active: 1 active: 1
ref_class: App\Models\Expense
- name: Per-Flight (multiplier, on airline) - name: Per-Flight (multiplier, on airline)
airline_id: 1 airline_id: 1
amount: 200 amount: 200
type: 0 type: 0
multiplier: 1 multiplier: 1
active: 1 active: 1
ref_class: App\Models\Expense
- name: A daily fee - name: A daily fee
amount: 800 amount: 800
type: 1 type: 1
active: 1 active: 1
ref_class: App\Models\Expense
- name: A monthly fee - name: A monthly fee
amount: 5000 amount: 5000
type: 2 type: 2
active: 1 active: 1
ref_class: App\Models\Expense
- name: Catering
amount: 1000
type: 0
active: 1
ref_class: App\Models\Subfleet
ref_class_id: 1
created_at: now
updated_at: now
fares: fares:
- id: 1 - id: 1
@@ -238,13 +251,14 @@ subfleets:
type: 772-36ER-GE90-115B type: 772-36ER-GE90-115B
ground_handling_multiplier: 150 ground_handling_multiplier: 150
subfleet_expenses: #subfleet_expenses:
- id: 1 # - id: 1
subfleet_id: 1 # subfleet_id: 1
name: Catering # name: Catering
amount: 1000 # amount: 1000
created_at: now # type: 0
updated_at: now # created_at: now
# updated_at: now
# add a few mods to aircraft and fares # add a few mods to aircraft and fares
subfleet_fare: subfleet_fare:

View File

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Requests\CreateAirlineRequest; use App\Http\Requests\CreateAirlineRequest;
use App\Http\Requests\UpdateAirlineRequest; use App\Http\Requests\UpdateAirlineRequest;
use App\Models\Enums\ExpenseType; use App\Models\Enums\ExpenseType;
use App\Models\Expense;
use App\Repositories\AirlineRepository; use App\Repositories\AirlineRepository;
use App\Repositories\ExpenseRepository; use App\Repositories\ExpenseRepository;
use Flash; use Flash;
@@ -39,7 +40,9 @@ class ExpenseController extends BaseController
public function index(Request $request) public function index(Request $request)
{ {
$this->expenseRepo->pushCriteria(new RequestCriteria($request)); $this->expenseRepo->pushCriteria(new RequestCriteria($request));
$expenses = $this->expenseRepo->all(); $expenses = $this->expenseRepo->findWhere([
'ref_class' => Expense::class
]);
return view('admin.expenses.index', [ return view('admin.expenses.index', [
'expenses' => $expenses 'expenses' => $expenses
@@ -66,6 +69,7 @@ class ExpenseController extends BaseController
public function store(Request $request) public function store(Request $request)
{ {
$input = $request->all(); $input = $request->all();
$input['ref_class'] = Expense::class;
$this->expenseRepo->create($input); $this->expenseRepo->create($input);
Flash::success('Expense saved successfully.'); Flash::success('Expense saved successfully.');

View File

@@ -6,8 +6,8 @@ use App\Http\Requests\CreateSubfleetRequest;
use App\Http\Requests\UpdateSubfleetRequest; use App\Http\Requests\UpdateSubfleetRequest;
use App\Models\Airline; use App\Models\Airline;
use App\Models\Enums\FuelType; use App\Models\Enums\FuelType;
use App\Models\Expense;
use App\Models\Subfleet; use App\Models\Subfleet;
use App\Models\SubfleetExpense;
use App\Repositories\AircraftRepository; use App\Repositories\AircraftRepository;
use App\Repositories\FareRepository; use App\Repositories\FareRepository;
use App\Repositories\RankRepository; use App\Repositories\RankRepository;
@@ -338,17 +338,18 @@ class SubfleetController extends BaseController
* update specific rank data * update specific rank data
*/ */
if ($request->isMethod('post')) { if ($request->isMethod('post')) {
$expense = new SubfleetExpense($request->post()); $expense = new Expense($request->post());
$expense->subfleet_id = $subfleet->id; $expense->ref_class = Subfleet::class;
$expense->ref_class_id = $subfleet->id;
$expense->save(); $expense->save();
$subfleet->refresh(); $subfleet->refresh();
} elseif ($request->isMethod('put')) { } elseif ($request->isMethod('put')) {
$expense = SubfleetExpense::findOrFail($request->input('expense_id')); $expense = Expense::findOrFail($request->input('expense_id'));
$expense->{$request->name} = $request->value; $expense->{$request->name} = $request->value;
$expense->save(); $expense->save();
} // dissassociate fare from teh aircraft } // dissassociate fare from teh aircraft
elseif ($request->isMethod('delete')) { elseif ($request->isMethod('delete')) {
$expense = SubfleetExpense::findOrFail($request->input('expense_id')); $expense = Expense::findOrFail($request->input('expense_id'));
$expense->delete(); $expense->delete();
} }

View File

@@ -2,7 +2,7 @@
namespace App\Models; namespace App\Models;
use App\Models\Traits\ExpensableTrait; use App\Models\Traits\Expensable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
/** /**
@@ -12,7 +12,7 @@ use Illuminate\Notifications\Notifiable;
*/ */
class Airport extends BaseModel class Airport extends BaseModel
{ {
use ExpensableTrait; use Expensable;
use Notifiable; use Notifiable;
public $table = 'airports'; public $table = 'airports';

View File

@@ -18,6 +18,8 @@ class Expense extends BaseModel
'amount', 'amount',
'type', 'type',
'multiplier', 'multiplier',
'ref_class',
'ref_class_id',
'active', 'active',
]; ];

View File

@@ -3,6 +3,7 @@
namespace App\Models; namespace App\Models;
use App\Models\Enums\AircraftStatus; use App\Models\Enums\AircraftStatus;
use App\Models\Traits\Expensable;
/** /**
* Class Subfleet * Class Subfleet
@@ -10,6 +11,8 @@ use App\Models\Enums\AircraftStatus;
*/ */
class Subfleet extends BaseModel class Subfleet extends BaseModel
{ {
use Expensable;
public $table = 'subfleets'; public $table = 'subfleets';
public $fillable = [ public $fillable = [
@@ -88,14 +91,6 @@ class Subfleet extends BaseModel
return $this->belongsTo(Airline::class, 'airline_id'); return $this->belongsTo(Airline::class, 'airline_id');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function expenses()
{
return $this->hasMany(SubfleetExpense::class, 'subfleet_id');
}
public function fares() public function fares()
{ {
return $this->belongsToMany(Fare::class, 'subfleet_fare') return $this->belongsToMany(Fare::class, 'subfleet_fare')

View File

@@ -1,47 +0,0 @@
<?php
namespace App\Models;
/**
* Class SubfleetExpense
* @package App\Models
*/
class SubfleetExpense extends BaseModel
{
public $table = 'subfleet_expenses';
public $fillable = [
'subfleet_id',
'name',
'amount',
'type',
];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts = [
'amount' => 'float',
'type' => 'integer',
];
public static $rules = [
'name' => 'required',
'amount' => 'required|numeric',
];
/**
* Relationships
*/
/**
* Has a subfleet
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function subfleet()
{
return $this->belongsTo(Subfleet::class, 'subfleet_id');
}
}

View File

@@ -4,13 +4,12 @@ namespace App\Models\Traits;
use App\Models\Expense; use App\Models\Expense;
trait ExpensableTrait trait Expensable
{ {
/** /**
* Initialize a new journal when a new record is created * Initialize a new journal when a new record is created
*/ */
public static function bootExpensableTrait() public static function bootExpensable()
{ {
/*static::created(function ($model) { /*static::created(function ($model) {
$model->initJournal(config('phpvms.currency')); $model->initJournal(config('phpvms.currency'));
@@ -23,6 +22,7 @@ trait ExpensableTrait
*/ */
public function expenses() public function expenses()
{ {
return $this->morphToMany(Expense::class, 'expensable'); return $this->hasMany(Expense::class, 'ref_class_id')
->where('ref_class', __CLASS__);
} }
} }

View File

@@ -24,20 +24,39 @@ class ExpenseRepository extends BaseRepository implements CacheableInterface
* Get all of the expenses for a given type, and also * Get all of the expenses for a given type, and also
* include expenses for a given airline ID * include expenses for a given airline ID
* @param $type * @param $type
* @param null $airline_id
* @param null $ref_class
* @return Collection * @return Collection
*/ */
public function getAllForType($type, $airline_id=null) public function getAllForType($type, $airline_id=null, $ref_class=null)
{ {
$expenses = $this->findWhere([ $where = [
'type' => $type, 'type' => $type,
['airline_id', '=', null] ['airline_id', '=', null]
]); ];
if($ref_class) {
$where['ref_class'] = $ref_class;
} else {
$where[] = ['ref_class', '=', null];
}
$expenses = $this->findWhere($where);
if($airline_id) { if($airline_id) {
$airline_expenses = $this->findWhere([
$where = [
'type' => $type, 'type' => $type,
'airline_id' => $airline_id 'airline_id' => $airline_id
]); ];
if ($ref_class) {
$where['ref_class'] = $ref_class;
} else {
$where[] = ['ref_class', '=', null];
}
$airline_expenses = $this->findWhere($where);
$expenses = $expenses->concat($airline_expenses); $expenses = $expenses->concat($airline_expenses);
} }

View File

@@ -61,6 +61,7 @@ class JournalRepository extends BaseRepository implements CacheableInterface
'currency' => config('phpvms.currency'), 'currency' => config('phpvms.currency'),
'memo' => $memo, 'memo' => $memo,
'post_date' => $post_date ?? Carbon::now(), 'post_date' => $post_date ?? Carbon::now(),
'transaction_group' => $transaction_group,
]; ];
if($reference !== null) { if($reference !== null) {
@@ -68,11 +69,6 @@ class JournalRepository extends BaseRepository implements CacheableInterface
$attrs['ref_class_id'] = $reference->id; $attrs['ref_class_id'] = $reference->id;
} }
if($transaction_group) {
$transaction_group = str_replace(' ', '_', $transaction_group);
$attrs['transaction_group'] = $transaction_group;
}
try { try {
$transaction = $this->create($attrs); $transaction = $this->create($attrs);
} catch (ValidatorException $e) { } catch (ValidatorException $e) {

View File

@@ -7,7 +7,7 @@ use App\Models\Enums\ExpenseType;
use App\Models\Enums\PirepSource; use App\Models\Enums\PirepSource;
use App\Models\Expense; use App\Models\Expense;
use App\Models\Pirep; use App\Models\Pirep;
use App\Models\SubfleetExpense; use App\Models\Subfleet;
use App\Repositories\ExpenseRepository; use App\Repositories\ExpenseRepository;
use App\Repositories\JournalRepository; use App\Repositories\JournalRepository;
use App\Support\Math; use App\Support\Math;
@@ -178,8 +178,9 @@ class FinanceService extends BaseService
public function paySubfleetExpenses(Pirep $pirep) public function paySubfleetExpenses(Pirep $pirep)
{ {
$subfleet = $pirep->aircraft->subfleet; $subfleet = $pirep->aircraft->subfleet;
$subfleet_expenses = SubfleetExpense::where([ $subfleet_expenses = Expense::where([
'subfleet_id' => $subfleet->id, 'ref_class' => Subfleet::class,
'ref_class_id' => $subfleet->id,
])->get(); ])->get();
if(!$subfleet_expenses) { if(!$subfleet_expenses) {
@@ -344,8 +345,11 @@ class FinanceService extends BaseService
{ {
$event_expenses = []; $event_expenses = [];
$expenses = $this->expenseRepo $expenses = $this->expenseRepo->getAllForType(
->getAllForType(ExpenseType::FLIGHT, $pirep->airline_id); ExpenseType::FLIGHT,
$pirep->airline_id,
Expense::class
);
/** /**
* Go through the expenses and apply a mulitplier if present * Go through the expenses and apply a mulitplier if present

View File

@@ -56,6 +56,31 @@ if(!function_exists('list_to_assoc')) {
} }
} }
if(!function_exists('list_to_editable')) {
/**
* Convert a list (select box) into an editable list
* https://vitalets.github.io/x-editable/docs.html#select
* Takes a list of:
* [value => text, valueN => textN, ...]
* Return:
* [{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]
* @param array $list
* @return array
*/
function list_to_editable(array $list)
{
$editable = [];
foreach($list as $value => $key) {
$editable[] = [
'text' => $key,
'value' => $value,
];
}
return $editable;
}
}
if (!function_exists('skin_view')) { if (!function_exists('skin_view')) {
/** /**
* Render a skin * Render a skin

View File

@@ -14,7 +14,7 @@
<div class="content"> <div class="content">
@if(!filled($expenses)) @if(!filled($expenses))
<p class="text-center"> <p class="text-center">
You must add a subfleet before you can add an aircraft! There are no expenses
</p> </p>
@else @else
@include('admin.expenses.table') @include('admin.expenses.table')

View File

@@ -11,6 +11,7 @@
<thead> <thead>
<th>Name</th> <th>Name</th>
<th>Cost&nbsp;<span class="small">{!! currency(config('phpvms.currency')) !!}</span></th> <th>Cost&nbsp;<span class="small">{!! currency(config('phpvms.currency')) !!}</span></th>
<th>Type</th>
<th></th> <th></th>
</thead> </thead>
@endif @endif
@@ -19,16 +20,24 @@
<tr> <tr>
<td> <td>
<p> <p>
<a href="#" data-pk="{!! $expense->id !!}" <a class="text" href="#" data-pk="{!! $expense->id !!}"
data-name="name">{!! $expense->name !!}</a> data-name="name">{!! $expense->name !!}</a>
</p> </p>
</td> </td>
<td> <td>
<p> <p>
<a href="#" data-pk="{!! $expense->id !!}" <a class="text" href="#" data-pk="{!! $expense->id !!}"
data-name="amount">{!! $expense->amount !!}</a> data-name="amount">{!! $expense->amount !!}</a>
</p> </p>
</td> </td>
<td>
<p>
<a href="#"
class="dropdown"
data-pk="{!! $expense->id !!}"
data-name="type">{!! \App\Models\Enums\ExpenseType::label($expense->type) !!}</a>
</p>
</td>
<td align="right"> <td align="right">
{!! Form::open(['url' => url('/admin/subfleets/'.$subfleet->id.'/expenses'), {!! Form::open(['url' => url('/admin/subfleets/'.$subfleet->id.'/expenses'),
'method' => 'delete', 'class' => 'modify_expense form-inline']) !!} 'method' => 'delete', 'class' => 'modify_expense form-inline']) !!}
@@ -49,8 +58,9 @@
<div class="text-right"> <div class="text-right">
{!! Form::open(['url' => url('/admin/subfleets/'.$subfleet->id.'/expenses'), {!! Form::open(['url' => url('/admin/subfleets/'.$subfleet->id.'/expenses'),
'method' => 'post', 'class' => 'modify_expense form-inline']) !!} 'method' => 'post', 'class' => 'modify_expense form-inline']) !!}
{!! Form::input('text', 'name', null, ['class' => 'form-control input-sm']) !!} {!! Form::input('text', 'name', null, ['class' => 'form-control input-sm', 'placeholder' => 'Name']) !!}
{!! Form::number('cost', null, ['class' => 'form-control input-sm']) !!} {!! Form::number('cost', null, ['class' => 'form-control input-sm', 'placeholder' => 'Amount']) !!}
{!! Form::select('type', \App\Models\Enums\ExpenseType::select(), null, ['class' => 'select2']) !!}
{!! Form::button('<i class="fa fa-plus"></i> Add', ['type' => 'submit', {!! Form::button('<i class="fa fa-plus"></i> Add', ['type' => 'submit',
'class' => 'btn btn-success btn-small']) !!} 'class' => 'btn btn-success btn-small']) !!}
{!! Form::close() !!} {!! Form::close() !!}

View File

@@ -33,9 +33,7 @@ function setEditable() {
} }
}); });
$('#subfleet-expenses a').editable({ $('#subfleet-expenses a.text').editable({
type: 'text',
mode: 'inline',
emptytext: '0', emptytext: '0',
url: '{!! url('/admin/subfleets/'.$subfleet->id.'/expenses') !!}', url: '{!! url('/admin/subfleets/'.$subfleet->id.'/expenses') !!}',
title: 'Enter override value', title: 'Enter override value',
@@ -48,6 +46,23 @@ function setEditable() {
} }
} }
}); });
$('#subfleet-expenses a.dropdown').editable({
type: 'select',
emptytext: '0',
source: {!! json_encode(list_to_editable(\App\Models\Enums\ExpenseType::select())) !!},
url: '{!! url('/admin/subfleets/'.$subfleet->id.'/expenses') !!}',
title: 'Enter override value',
ajaxOptions: {'type': 'put'},
params: function (params) {
console.log(params);
return {
expense_id: params.pk,
name: params.name,
value: params.value
}
}
});
} }
$(document).ready(function() { $(document).ready(function() {

View File

@@ -98,8 +98,9 @@ class FinanceTest extends TestCase
]); ]);
# Add a subfleet expense # Add a subfleet expense
factory(App\Models\SubfleetExpense::class)->create([ factory(App\Models\Expense::class)->create([
'subfleet_id' => $subfleet['subfleet']->id, 'ref_class' => \App\Models\Subfleet::class,
'ref_class_id' => $subfleet['subfleet']->id,
'amount' => 200 'amount' => 200
]); ]);
@@ -570,7 +571,7 @@ class FinanceTest extends TestCase
$airline = factory(App\Models\Airline::class)->create(); $airline = factory(App\Models\Airline::class)->create();
$airline2 = factory(App\Models\Airline::class)->create(); $airline2 = factory(App\Models\Airline::class)->create();
factory(App\Models\Expense::class)->create([ $expense = factory(App\Models\Expense::class)->create([
'airline_id' => $airline->id 'airline_id' => $airline->id
]); ]);
@@ -582,8 +583,11 @@ class FinanceTest extends TestCase
'airline_id' => null 'airline_id' => null
]); ]);
$expenses = $this->expenseRepo $expenses = $this->expenseRepo->getAllForType(
->getAllForType(ExpenseType::FLIGHT, $airline->id); ExpenseType::FLIGHT,
$airline->id,
\App\Models\Expense::class
);
$this->assertCount(2, $expenses); $this->assertCount(2, $expenses);
@@ -632,11 +636,11 @@ class FinanceTest extends TestCase
# Check that all the different transaction types are there # Check that all the different transaction types are there
$transaction_types = [ $transaction_types = [
'expenses' => 1, 'Expenses' => 1,
'fares' => 3, 'Fares' => 3,
'ground_handling' => 1, 'Ground Handling' => 1,
'pilot_pay' => 2, # debit on the airline, credit to the pilot 'Pilot Pay' => 2, # debit on the airline, credit to the pilot
'subfleet_expense' => 1, 'Subfleet Expense' => 1,
]; ];
foreach($transaction_types as $type => $count) { foreach($transaction_types as $type => $count) {