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),
'type' => ExpenseType::FLIGHT,
'multiplier' => false,
'ref_class' => \App\Models\Expense::class,
'ref_class_id' => null,
'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\Schema\Blueprint;
/**
* Class CreateSubfleetTables
*/
class CreateSubfleetTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('subfleets', function (Blueprint $table) {
@@ -26,17 +23,6 @@ class CreateSubfleetTables extends Migration
$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) {
$table->unsignedInteger('subfleet_id');
$table->unsignedInteger('fare_id');
@@ -76,7 +62,6 @@ class CreateSubfleetTables extends Migration
public function down()
{
Schema::dropIfExists('subfleets');
Schema::dropIfExists('subfleet_expenses');
Schema::dropIfExists('subfleet_fare');
Schema::dropIfExists('subfleet_flight');
Schema::dropIfExists('subfleet_rank');

View File

@@ -16,15 +16,19 @@ class CreateExpensesTable extends Migration
Schema::create('expenses', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('airline_id')->nullable();
$table->string('name');
$table->unsignedInteger('amount');
$table->unsignedTinyInteger('type');
$table->boolean('multiplier')->nullable()->default(0);
$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
$table->nullableMorphs('expensable');
$table->string('ref_class')->nullable();
$table->string('ref_class_id', 36)->nullable();
$table->index(['ref_class', 'ref_class_id']);
$table->timestamps();
});
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
namespace App\Models;
use App\Models\Enums\AircraftStatus;
use App\Models\Traits\Expensable;
/**
* Class Subfleet
@@ -10,6 +11,8 @@ use App\Models\Enums\AircraftStatus;
*/
class Subfleet extends BaseModel
{
use Expensable;
public $table = 'subfleets';
public $fillable = [
@@ -88,14 +91,6 @@ class Subfleet extends BaseModel
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()
{
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;
trait ExpensableTrait
trait Expensable
{
/**
* Initialize a new journal when a new record is created
*/
public static function bootExpensableTrait()
public static function bootExpensable()
{
/*static::created(function ($model) {
$model->initJournal(config('phpvms.currency'));
@@ -23,6 +22,7 @@ trait ExpensableTrait
*/
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
* include expenses for a given airline ID
* @param $type
* @param null $airline_id
* @param null $ref_class
* @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,
['airline_id', '=', null]
]);
];
if($ref_class) {
$where['ref_class'] = $ref_class;
} else {
$where[] = ['ref_class', '=', null];
}
$expenses = $this->findWhere($where);
if($airline_id) {
$airline_expenses = $this->findWhere([
$where = [
'type' => $type,
'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);
}

View File

@@ -61,6 +61,7 @@ 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) {
@@ -68,11 +69,6 @@ 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) {

View File

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

View File

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

View File

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

View File

@@ -33,9 +33,7 @@ function setEditable() {
}
});
$('#subfleet-expenses a').editable({
type: 'text',
mode: 'inline',
$('#subfleet-expenses a.text').editable({
emptytext: '0',
url: '{!! url('/admin/subfleets/'.$subfleet->id.'/expenses') !!}',
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() {

View File

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