Refactor expenses; move finance service classes; add daily/monthly skeletons #130 #136

This commit is contained in:
Nabeel Shahzad 2018-03-05 22:49:42 -06:00
parent db10ebf807
commit 9d3953f3ac
14 changed files with 164 additions and 155 deletions

View File

@ -6,9 +6,7 @@ use Faker\Generator as Faker;
$factory->define(App\Models\Expense::class, function (Faker $faker) {
return [
'id' => null,
'airline_id' => function () {
return factory(App\Models\Airline::class)->create()->id;
},
'airline_id' => null,
'name' => $faker->text(20),
'amount' => $faker->randomFloat(2, 100, 1000),
'type' => ExpenseType::FLIGHT,

View File

@ -1,7 +1,7 @@
<?php
use App\Models\Enums\NavaidType;
use Faker\Generator as Faker;
use \App\Models\Enums\NavaidType;
$factory->define(App\Models\Navdata::class, function (Faker $faker) {
return [

View File

@ -1,7 +1,7 @@
<?php
use Faker\Generator as Faker;
use App\Models\Enums\UserState;
use Faker\Generator as Faker;
$factory->define(App\Models\User::class, function (Faker $faker)
{

View File

@ -1,8 +1,8 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateExpensesTable extends Migration
{

View File

@ -251,15 +251,6 @@ subfleets:
type: 772-36ER-GE90-115B
ground_handling_multiplier: 150
#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

@ -7,7 +7,7 @@ use App\Models\Journal;
use App\Models\JournalTransaction;
use App\Repositories\AirlineRepository;
use App\Repositories\JournalRepository;
use App\Services\FinanceService;
use App\Services\Finance\PirepFinanceService;
use App\Support\Dates;
use App\Support\Money;
use Illuminate\Http\Request;
@ -23,12 +23,12 @@ class FinanceController extends BaseController
$journalRepo;
/**
* @param FinanceService $financeSvc
* @param PirepFinanceService $financeSvc
* @param JournalRepository $journalRepo
*/
public function __construct(
AirlineRepository $airlineRepo,
FinanceService $financeSvc,
PirepFinanceService $financeSvc,
JournalRepository $journalRepo
) {
$this->airlineRepo = $airlineRepo;

View File

@ -26,7 +26,7 @@ use App\Models\PirepComment;
use App\Repositories\AcarsRepository;
use App\Repositories\JournalRepository;
use App\Repositories\PirepRepository;
use App\Services\FinanceService;
use App\Services\Finance\PirepFinanceService;
use App\Services\GeoService;
use App\Services\PIREPService;
use App\Services\UserService;
@ -47,7 +47,7 @@ class PirepController extends RestController
/**
* PirepController constructor.
* @param AcarsRepository $acarsRepo
* @param FinanceService $financeSvc
* @param PirepFinanceService $financeSvc
* @param GeoService $geoSvc
* @param JournalRepository $journalRepo
* @param PirepRepository $pirepRepo
@ -56,7 +56,7 @@ class PirepController extends RestController
*/
public function __construct(
AcarsRepository $acarsRepo,
FinanceService $financeSvc,
PirepFinanceService $financeSvc,
GeoService $geoSvc,
JournalRepository $journalRepo,
PirepRepository $pirepRepo,

View File

@ -4,7 +4,7 @@ namespace App\Listeners;
use App\Events\PirepAccepted;
use App\Events\PirepRejected;
use App\Services\FinanceService;
use App\Services\Finance\PirepFinanceService;
/**
* Subscribe for events that we do some financial processing for
@ -16,7 +16,7 @@ class FinanceEvents
private $financeSvc;
public function __construct(
FinanceService $financeSvc
PirepFinanceService $financeSvc
) {
$this->financeSvc = $financeSvc;
}

View File

@ -37,8 +37,6 @@ class ExpenseRepository extends BaseRepository implements CacheableInterface
if($ref_class) {
$where['ref_class'] = $ref_class;
} else {
$where[] = ['ref_class', '=', null];
}
$expenses = $this->findWhere($where);
@ -52,12 +50,9 @@ class ExpenseRepository extends BaseRepository implements CacheableInterface
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

@ -0,0 +1,20 @@
<?php
namespace App\Services\Finance;
use App\Services\BaseService;
/**
* Class DailyFinanceService
* @package App\Services\Finance
*/
class DailyFinanceService extends BaseService
{
/**
* Run all of the daily expense/financials
*/
public function processFinances()
{
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Services\Finance;
use App\Services\BaseService;
/**
* Class MonthlyFinanceService
* @package App\Services\Finance
*/
class MonthlyFinanceService extends BaseService
{
/**
* Run all of the daily expense/financials
*/
public function processFinances()
{
}
}

View File

@ -1,15 +1,17 @@
<?php
namespace App\Services;
namespace App\Services\Finance;
use App\Events\Expenses as ExpensesEvent;
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\BaseService;
use App\Services\FareService;
use App\Services\PIREPService;
use App\Support\Math;
use App\Support\Money;
use Log;
@ -19,7 +21,7 @@ use Log;
* @package App\Services
*
*/
class FinanceService extends BaseService
class PirepFinanceService extends BaseService
{
private $expenseRepo,
$fareSvc,
@ -45,27 +47,6 @@ class FinanceService extends BaseService
$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)
@ -92,7 +73,7 @@ class FinanceService extends BaseService
# Now start and pay from scratch
$this->payFaresForPirep($pirep);
$this->payExpensesForPirep($pirep);
$this->paySubfleetExpenses($pirep);
$this->payExpensesEventsForPirep($pirep);
$this->payGroundHandlingForPirep($pirep);
$this->payPilotForPirep($pirep);
@ -147,16 +128,34 @@ class FinanceService extends BaseService
* @param Pirep $pirep
* @throws \UnexpectedValueException
* @throws \InvalidArgumentException
* @throws \Prettus\Validator\Exceptions\ValidatorException
*/
public function payExpensesForPirep(Pirep $pirep): void
{
$expenses = $this->getExpenses($pirep);
/** @var \App\Models\Expense $expense */
foreach ($expenses as $expense) {
$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,
@ -165,42 +164,56 @@ class FinanceService extends BaseService
$pirep,
'Expense: ' . $expense->name,
null,
'Expenses'
$transaction_group
);
}
});
}
/**
* Pay out the expenses for the subfleet
* 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 paySubfleetExpenses(Pirep $pirep)
public function payExpensesEventsForPirep(Pirep $pirep): void
{
$subfleet = $pirep->aircraft->subfleet;
$subfleet_expenses = Expense::where([
'ref_class' => Subfleet::class,
'ref_class_id' => $subfleet->id,
])->get();
if(!$subfleet_expenses) {
/**
* Throw an event and collect any expenses returned from it
*/
$gathered_expenses = event(new ExpensesEvent($pirep));
if (!\is_array($gathered_expenses)) {
return;
}
foreach ($subfleet_expenses as $expense) {
foreach ($gathered_expenses as $event_expense) {
if (!\is_array($event_expense)) {
continue;
}
Log::info('Finance: PIREP: '.$pirep->id
.'; subfleet expense: "'.$expense->name.'", cost: "'.$expense->amount);
foreach ($event_expense as $expense) {
# Make sure it's of type expense Model
if (!($expense instanceof Expense)) {
continue;
}
$this->journalRepo->post(
$pirep->airline->journal,
null,
Money::createFromAmount($expense->amount),
$pirep,
'Subfleet ('.$subfleet->type.'): '.$expense->name,
null,
'Subfleet Expense'
);
# 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'
);
}
}
}
@ -325,7 +338,7 @@ class FinanceService extends BaseService
if(filled($pirep->aircraft->subfleet->ground_handling_multiplier)) {
// force into percent mode
$multiplier = $pirep->aircraft->subfleet->ground_handling_multiplier.'%';
return $this->applyAmountOrPercent(
return Math::applyAmountOrPercent(
$pirep->arr_airport->ground_handling_cost,
$multiplier
);
@ -334,68 +347,6 @@ class FinanceService extends BaseService
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,
Expense::class
);
/**
* 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
return $expense;
});
/**
* Throw an event and collect any expenses returned from it
*/
$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
@ -435,7 +386,7 @@ class FinanceService extends BaseService
}
Log::debug('pilot pay: base rate=' . $base_rate . ', override=' . $override_rate);
return $this->applyAmountOrPercent(
return Math::applyAmountOrPercent(
$base_rate,
$override_rate
);

View File

@ -8,6 +8,26 @@ namespace App\Support;
*/
class Math
{
/**
* 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 static 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 static::addPercent($base_rate, $override_rate);
}
/**
* Add/subtract a percentage to a number
@ -25,8 +45,6 @@ class Math
$percent = (float) $percent;
}
return $number + ($number * ($percent/100));
}
}

View File

@ -5,7 +5,7 @@ use App\Repositories\ExpenseRepository;
use App\Services\PIREPService;
use App\Repositories\JournalRepository;
use App\Services\FareService;
use App\Services\FinanceService;
use App\Services\Finance\PirepFinanceService;
use App\Services\FleetService;
use App\Support\Math;
use App\Support\Money;
@ -28,7 +28,7 @@ class FinanceTest extends TestCase
$this->expenseRepo = app(ExpenseRepository::class);
$this->fareSvc = app(FareService::class);
$this->financeSvc = app(FinanceService::class);
$this->financeSvc = app(PirepFinanceService::class);
$this->fleetSvc = app(FleetService::class);
$this->pirepSvc = app(PIREPService::class);
}
@ -80,7 +80,6 @@ class FinanceTest extends TestCase
* 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([
'price' => 100,
'cost' => 50,
@ -571,7 +570,7 @@ class FinanceTest extends TestCase
$airline = factory(App\Models\Airline::class)->create();
$airline2 = factory(App\Models\Airline::class)->create();
$expense = factory(App\Models\Expense::class)->create([
factory(App\Models\Expense::class)->create([
'airline_id' => $airline->id
]);
@ -599,6 +598,23 @@ class FinanceTest extends TestCase
$found = $expenses->where('airline_id', $airline2->id);
$this->assertCount(0, $found);
/*
* Test the subfleet class
*/
factory(App\Models\Expense::class)->create([
'airline_id' => null,
'ref_class' => \App\Models\Subfleet::class,
]);
$expenses = $this->expenseRepo->getAllForType(
ExpenseType::FLIGHT,
$airline->id,
\App\Models\Subfleet::class
);
$this->assertCount(1, $expenses);
}
/**
@ -610,8 +626,7 @@ class FinanceTest extends TestCase
$journalRepo = app(JournalRepository::class);
[$user, $pirep, $fares] = $this->createFullPirep();
$journal = $user->airline->initJournal(config('phpvms.currency'));
$user->airline->initJournal(config('phpvms.currency'));
# Override the fares
$fare_counts = [];
@ -635,12 +650,13 @@ class FinanceTest extends TestCase
$this->assertEquals(1840, $transactions['debits']->getValue());
# Check that all the different transaction types are there
# test by the different groups that exist
$transaction_types = [
'Expenses' => 1,
'Expense' => 1,
'Subfleet' => 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) {