Move the model callbacks into Observables; reduce caching since it held balances incorrectly

This commit is contained in:
Nabeel Shahzad 2018-03-18 20:37:35 -05:00
parent 6b002f24a8
commit 36ea12e135
25 changed files with 336 additions and 266 deletions

View File

@ -1,5 +1,5 @@
dnsmasq: /usr/local/sbin/dnsmasq --keep-in-foreground
php-fpm: /usr/local/sbin/php-fpm --nodaemonize
nginx: /usr/local/bin/nginx -g 'daemon off;'
mysql: /usr/local/bin/mysqld
#mysql: /usr/local/bin/mysqld
#mailhog: /usr/local/bin/mailhog

View File

@ -4,7 +4,6 @@ namespace App\Models;
use App\Models\Enums\AircraftStatus;
use App\Models\Traits\ExpensableTrait;
use App\Support\ICAO;
/**
* @property mixed subfleet_id
@ -58,27 +57,23 @@ class Aircraft extends BaseModel
];
/**
* Callbacks
* See if this aircraft is active
* @return bool
*/
protected static function boot()
{
parent::boot();
static::creating(function (Aircraft $model) {
if (!empty($model->icao)) {
$model->icao = strtoupper(trim($model->icao));
}
if(empty($model->hex_code)) {
$model->hex_code = ICAO::createHexCode();
}
});
}
public function getActiveAttribute()
public function getActiveAttribute(): bool
{
return $this->status === AircraftStatus::ACTIVE;
}
/**
* Capitalize the ICAO when set
* @param $icao
*/
public function setIcaoAttribute($icao): void
{
$this->attributes['icao'] = strtoupper($icao);
}
/**
* foreign keys
*/

View File

@ -7,6 +7,12 @@ use App\Models\Traits\JournalTrait;
/**
* Class Airline
* @property string code
* @property string icao
* @property string iata
* @property string name
* @property string logo
* @property string country
* @property Journal journal
* @package App\Models
*/
@ -63,21 +69,21 @@ class Airline extends BaseModel
return $this->icao;
}
protected static function boot()
/**
* Capitalize the IATA code when set
* @param $iata
*/
public function setIataAttribute($iata)
{
parent::boot();
$this->attributes['iata'] = strtoupper($iata);
}
/**
* IATA and ICAO should be in all caps
*/
static::creating(function (Airline $model) {
if (!empty($model->iata)) {
$model->iata = strtoupper($model->iata);
}
if (!empty($model->icao)) {
$model->icao = strtoupper($model->icao);
}
});
/**
* Capitalize the ICAO when set
* @param $icao
*/
public function setIcaoAttribute($icao): void
{
$this->attributes['icao'] = strtoupper($icao);
}
}

View File

@ -3,17 +3,18 @@
namespace App\Models;
use App\Models\Traits\ExpensableTrait;
use Illuminate\Notifications\Notifiable;
/**
* Class Airport
* @property string id
* @property string iata
* @property string icao
* @property float ground_handling_cost
* @package App\Models
*/
class Airport extends BaseModel
{
use ExpensableTrait;
use Notifiable;
public $table = 'airports';
public $timestamps = false;
@ -59,32 +60,6 @@ class Airport extends BaseModel
'ground_handling_cost' => 'nullable|numeric',
];
/**
* Callbacks
*/
public static function boot()
{
parent::boot();
static::creating(function ($model) {
if(filled($model->iata)) {
$model->iata = strtoupper(trim($model->iata));
}
$model->icao = strtoupper(trim($model->icao));
$model->id = $model->icao;
});
static::updating(function($model) {
if (filled($model->iata)) {
$model->iata = strtoupper(trim($model->iata));
}
$model->icao = strtoupper(trim($model->icao));
$model->id = $model->icao;
});
}
/**
* @param $icao
*/
@ -98,13 +73,12 @@ class Airport extends BaseModel
/**
* @param $iata
*/
public function setIataAttribute($iata)
public function setIataAttribute($iata): void
{
$iata = strtoupper($iata);
$this->attributes['iata'] = $iata;
}
/**
* Return full name like:
* KJFK - John F Kennedy
@ -121,14 +95,14 @@ class Airport extends BaseModel
*/
public function getTzAttribute(): string
{
return $this->timezone;
return $this->attributes['timezone'];
}
/**
* Shorthand for setting the timezone
* @param $value
*/
public function setTzAttribute($value)
public function setTzAttribute($value): void
{
$this->attributes['timezone'] = $value;
}

View File

@ -40,9 +40,9 @@ class Fare extends BaseModel
* any foreign keys
*/
public function subfleets() {
public function subfleets()
{
return $this->belongsToMany(Subfleet::class, 'subfleet_fare')
->withPivot('price', 'cost', 'capacity');
}
}

View File

@ -4,10 +4,15 @@ namespace App\Models;
use App\Models\Traits\HashIdTrait;
use App\Support\Units\Distance;
use App\Support\Units\Time;
use PhpUnitsOfMeasure\Exception\NonNumericValue;
use PhpUnitsOfMeasure\Exception\NonStringUnitName;
/**
* @property Airline airline
* @property mixed flight_number
* @property mixed route_code
* @property mixed route_leg
*/
class Flight extends BaseModel
{
use HashIdTrait;
@ -64,7 +69,7 @@ class Flight extends BaseModel
/**
* Get the flight ident, e.,g JBU1900
*/
public function getIdentAttribute()
public function getIdentAttribute(): string
{
$flight_id = $this->airline->code;
$flight_id .= $this->flight_number;
@ -104,7 +109,7 @@ class Flight extends BaseModel
* Set the distance unit, convert to our internal default unit
* @param $value
*/
public function setDistanceAttribute($value)
public function setDistanceAttribute($value): void
{
if($value instanceof Distance) {
$this->attributes['distance'] = $value->toUnit(
@ -115,30 +120,6 @@ class Flight extends BaseModel
}
}
/**
* @return Time
*/
/*public function getFlightTimeAttribute()
{
if (!array_key_exists('flight_time', $this->attributes)) {
return null;
}
return new Time($this->attributes['flight_time']);
}*/
/**
* @param $value
*/
/*public function setFlightTimeAttribute($value)
{
if ($value instanceof Time) {
$this->attributes['flight_time'] = $value->getMinutes();
} else {
$this->attributes['flight_time'] = $value;
}
}*/
/**
* Relationship
*/

View File

@ -3,8 +3,7 @@
namespace App\Models;
/**
* Class Flight
*
* Class FlightFields
* @package App\Models
*/
class FlightFields extends BaseModel
@ -17,8 +16,6 @@ class FlightFields extends BaseModel
'value',
];
protected $casts = [];
public static $rules = [];
/**
@ -29,5 +26,4 @@ class FlightFields extends BaseModel
{
return $this->belongsTo(Flight::class, 'flight_id');
}
}

View File

@ -10,7 +10,6 @@ use GeoJson\Geometry\Point;
/**
* Return different points/features in GeoJSON format
* https://tools.ietf.org/html/rfc7946
*
* @package App\Models
*/
class GeoJson

View File

@ -34,10 +34,8 @@ class Journal extends BaseModel
'morphed_id',
];
/**
* @var array
*/
protected $dates = [
'created_at',
'deleted_at',
'updated_at'
];
@ -50,20 +48,6 @@ class Journal extends BaseModel
return $this->morphTo();
}
/**
* @internal Journal $journal
* @throws \UnexpectedValueException
* @throws \InvalidArgumentException
*/
protected static function boot()
{
static::created(function (Journal $journal) {
$journal->resetCurrentBalances();
});
parent::boot();
}
/**
* Relationship
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@ -7,6 +7,7 @@
namespace App\Models;
/**
* @property string id UUID type
* @property string currency
* @property string memo
* @property string transaction_group
@ -15,6 +16,7 @@ namespace App\Models;
* @property integer debit
* @property string ref_class
* @property integer ref_class_id
* @property Journal journal
*/
class JournalTransaction extends BaseModel
{
@ -48,59 +50,6 @@ class JournalTransaction extends BaseModel
'post_date',
];
/**
* Callbacks
* @throws \UnexpectedValueException
* @throws \InvalidArgumentException
*/
protected static function boot()
{
static::creating(function ($transaction) {
if(!$transaction->id) {
$transaction->id = \Ramsey\Uuid\Uuid::uuid4()->toString();
}
});
/**
* Adjust the balance according to credits and debits
*/
static::saved(function ($transaction) {
//$transaction->journal->resetCurrentBalances();
$journal = $transaction->journal;
if($transaction['credit']) {
$balance = $journal->balance->toAmount();
$journal->balance = $balance + $transaction['credit'];
}
if($transaction['debit']) {
$balance = $journal->balance->toAmount();
$journal->balance = $balance - $transaction['debit'];
}
$journal->save();
});
/**
* Deleting a transaction reverses the credits and debits
*/
static::deleted(function ($transaction) {
$journal = $transaction->journal;
if ($transaction['credit']) {
$balance = $journal->balance->toAmount();
$journal->balance = $balance - $transaction['credit'];
}
if ($transaction['debit']) {
$balance = $journal->balance->toAmount();
$journal->balance = $balance + $transaction['debit'];
}
$journal->save();
});
parent::boot();
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@ -24,17 +24,12 @@ class Navdata extends BaseModel
'freq' => 'float',
];
protected static function boot()
/**
* Make sure the ID is in all caps
* @param $id
*/
public function setIdAttribute($id): void
{
parent::boot();
/**
* Make sure the ID is all caps
*/
static::creating(function (Navdata $model) {
if (!empty($model->id)) {
$model->id = strtoupper($model->id);
}
});
$this->attributes['id'] = strtoupper($id);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Models\Observers;
use App\Models\Aircraft;
use App\Support\ICAO;
/**
* Class AircraftObserver
* @package App\Models\Observers
*/
class AircraftObserver
{
public function creating(Aircraft $aircraft): void
{
if (empty($aircraft->hex_code)) {
$aircraft->hex_code = ICAO::createHexCode();
}
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Models\Observers;
use App\Models\Airport;
/**
* Make sure that the fields are properly capitalized
* @package App\Models\Observers
*/
class AirportObserver
{
/**
* @param Airport $airport
*/
public function creating(Airport $airport): void
{
if (filled($airport->iata)) {
$airport->iata = strtoupper(trim($airport->iata));
}
$airport->icao = strtoupper(trim($airport->icao));
$airport->id = $airport->icao;
}
/**
* @param Airport $airport
*/
public function updating(Airport $airport): void
{
if (filled($airport->iata)) {
$airport->iata = strtoupper(trim($airport->iata));
}
$airport->icao = strtoupper(trim($airport->icao));
$airport->id = $airport->icao;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Models\Observers;
use App\Models\Journal;
/**
* Class JournalObserver
* @package App\Models\Observers
*/
class JournalObserver
{
/**
* @param Journal $journal
* @throws \UnexpectedValueException
* @throws \InvalidArgumentException
*/
public function creating(Journal $journal): void
{
$journal->balance = 0;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Models\Observers;
use App\Models\JournalTransaction;
class JournalTransactionObserver
{
/**
* Set the ID to a UUID
* @param JournalTransaction $transaction
*/
public function creating(JournalTransaction $transaction): void
{
if (!$transaction->id) {
$transaction->id = \Ramsey\Uuid\Uuid::uuid4()->toString();
}
}
/**
* After transaction is saved, adjust the journal balance
* @param JournalTransaction $transaction
*/
public function saved(JournalTransaction $transaction): void
{
$journal = $transaction->journal;
if ($transaction['credit']) {
$balance = $journal->balance->toAmount();
$journal->balance = (int) $balance + $transaction->credit;
}
if ($transaction['debit']) {
$balance = $journal->balance->toAmount();
#$journal->balance = $journal->balance->subtract($transaction['debit']);
$journal->balance = (int) $balance - $transaction->debit;
}
$journal->save();
}
/**
* After transaction is deleted, adjust the balance on the journal
* @param JournalTransaction $transaction
*/
public function deleted(JournalTransaction $transaction): void
{
$journal = $transaction->journal;
if ($transaction['credit']) {
$balance = $journal->balance->toAmount();
$journal->balance = $balance - $transaction['credit'];
}
if ($transaction['debit']) {
$balance = $journal->balance->toAmount();
$journal->balance = $balance + $transaction['debit'];
}
$journal->save();
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Models\Observers;
use App\Models\PirepField;
/**
* Class PirepFieldObserver
* @package App\Models\Observers
*/
class PirepFieldObserver
{
/**
* @param PirepField $model
*/
public function creating(PirepField $model): void
{
$model->slug = str_slug($model->name);
}
/**
* @param PirepField $model
*/
public function updating(PirepField $model): void
{
$model->slug = str_slug($model->name);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Models\Observers;
use App\Models\Setting;
class SettingObserver
{
/**
* @param Setting $model
*/
public function creating(Setting $model): void
{
if (!empty($model->id)) {
$model->id = Setting::formatKey($model->id);
}
}
}

View File

@ -102,7 +102,7 @@ class Pirep extends BaseModel
* Get the flight ident, e.,g JBU1900
* @return string
*/
public function getIdentAttribute()
public function getIdentAttribute(): string
{
$flight_id = $this->airline->code;
$flight_id .= $this->flight_number;
@ -196,7 +196,7 @@ class Pirep extends BaseModel
* Look up the flight, based on the PIREP flight info
* @return Flight|null
*/
public function getFlightAttribute()
public function getFlightAttribute(): ?Flight
{
$where = [
'airline_id' => $this->airline_id,

View File

@ -4,6 +4,8 @@ namespace App\Models;
/**
* Class PirepField
* @property string name
* @property string slug
* @package App\Models
*/
class PirepField extends BaseModel
@ -25,33 +27,11 @@ class PirepField extends BaseModel
'name' => 'required',
];
/**
* Create/update the field slug
*/
protected static function boot()
{
parent::boot();
/**
* On creation
*/
static::creating(function (PirepField $model) {
$model->slug = str_slug($model->name);
});
/**
* When updating
*/
static::updating(function(PirepField $model) {
$model->slug = str_slug($model->name);
});
}
/**
* When setting the name attribute, also set the slug
* @param $name
*/
public function setNameAttribute($name)
public function setNameAttribute($name): void
{
$this->attributes['name'] = $name;
$this->attributes['slug'] = str_slug($name);

View File

@ -25,22 +25,27 @@ class Rank extends BaseModel
];
protected $casts = [
'hours' => 'integer',
'base_pay_rate' => 'float',
'auto_approve_acars' => 'bool',
'hours' => 'integer',
'base_pay_rate' => 'float',
'auto_approve_acars' => 'bool',
'auto_approve_manual' => 'bool',
'auto_promote' => 'bool',
'auto_promote' => 'bool',
];
public static $rules = [
'name' => 'required',
'name' => 'required',
'hours' => 'required|integer',
'acars_base_pay_rate' => 'nullable|numeric',
'manual_base_pay_rate' => 'nullable|numeric',
];
public function subfleets() {
/*
* Relationships
*/
public function subfleets()
{
return $this->belongsToMany(Subfleet::class, 'subfleet_rank')
->withPivot('acars_pay', 'manual_pay');
->withPivot('acars_pay', 'manual_pay');
}
}

View File

@ -2,6 +2,18 @@
namespace App\Models;
/**
* Class Setting
* @property string id
* @property string name
* @property string key
* @property string value
* @property string group
* @property string type
* @property string options
* @property string description
* @package App\Models
*/
class Setting extends BaseModel
{
public $table = 'settings';
@ -33,29 +45,21 @@ class Setting extends BaseModel
}
/**
* Callbacks
* Force formatting the key
* @param $id
*/
protected static function boot()
public function setIdAttribute($id): void
{
parent::boot();
static::creating(function (Setting $model) {
if (!empty($model->id)) {
$model->id = Setting::formatKey($model->id);
}
});
$id = strtolower($id);
$this->attributes['id'] = self::formatKey($id);
}
/**
* Override the casting mechanism
* @param string $key
* @return mixed|string
* Set the key to lowercase
* @param $key
*/
/*protected function getCastType($key)
public function setKeyAttribute($key): void
{
if ($key === 'value' && !empty($this->type)) {
return $this->type;
} else {
return parent::getCastType($key);
}
}*/
$this->attributes['key'] = strtolower($key);
}
}

View File

@ -1,6 +1,19 @@
<?php
namespace App\Providers;
use App\Models\Aircraft;
use App\Models\Airport;
use App\Models\Journal;
use App\Models\JournalTransaction;
use App\Models\Observers\AircraftObserver;
use App\Models\Observers\AirportObserver;
use App\Models\Observers\JournalObserver;
use App\Models\Observers\JournalTransactionObserver;
use App\Models\Observers\PirepFieldObserver;
use App\Models\Observers\SettingObserver;
use App\Models\PirepField;
use App\Models\Setting;
use App\Repositories\SettingRepository;
use App\Services\ModuleService;
use Illuminate\Support\Facades\Schema;
@ -15,6 +28,14 @@ class AppServiceProvider extends ServiceProvider
$this->app->bind('setting', SettingRepository::class);
View::share('moduleSvc', app(ModuleService::class));
// Model observers
Aircraft::observe(AircraftObserver::class);
Airport::observe(AirportObserver::class);
Journal::observe(JournalObserver::class);
JournalTransaction::observe(JournalTransactionObserver::class);
PirepField::observe(PirepFieldObserver::class);
Setting::observe(SettingObserver::class);
}
/**

View File

@ -135,6 +135,8 @@ class JournalRepository extends BaseRepository implements CacheableInterface
*/
public function getBalance(Journal $journal=null, Carbon $date=null)
{
$journal->refresh();
if(!$date) {
$date = Carbon::now();
}
@ -160,27 +162,26 @@ class JournalRepository extends BaseRepository implements CacheableInterface
Journal $journal=null,
Carbon $start_date=null,
$transaction_group=null
): Money {
$where = [
['post_date', '<=', $date]
];
): Money
{
$where = [];
if ($journal) {
$where['journal_id'] = $journal->id;
}
if ($start_date) {
$where[] = ['post_date', '>=', $start_date];
}
if ($transaction_group) {
$where['transaction_group'] = $transaction_group;
}
$balance = $this
->findWhere($where, ['id', 'credit'])
->sum('credit') ?: 0;
$query = JournalTransaction::where($where);
$query = $query->whereDate('post_date', '<=', $date->toDateString());
if ($start_date) {
$query = $query->whereDate('post_date', '>=', $start_date->toDateString());
}
$balance = $query->sum('credit') ?: 0;
return new Money($balance);
}
@ -191,6 +192,8 @@ class JournalRepository extends BaseRepository implements CacheableInterface
* @param Carbon|null $start_date
* @param null $transaction_group
* @return Money
* @throws \UnexpectedValueException
* @throws \InvalidArgumentException
*/
public function getDebitBalanceBetween(
Carbon $date,
@ -199,29 +202,24 @@ class JournalRepository extends BaseRepository implements CacheableInterface
$transaction_group = null
): Money
{
$date = $this->formatPostDate($date);
$where = [
['post_date', '<=', $date]
];
$where = [];
if ($journal) {
$where['journal_id'] = $journal->id;
}
if ($start_date) {
$start_date = $this->formatPostDate($start_date);
$where[] = ['post_date', '>=', $start_date];
}
if ($transaction_group) {
$where['transaction_group'] = $transaction_group;
}
$balance = $this
->findWhere($where, ['id', 'debit'])
->sum('debit') ?: 0;
$query = JournalTransaction::where($where);
$query = $query->whereDate('post_date', '<=', $date->toDateString());
if ($start_date) {
$query = $query->whereDate('post_date', '>=', $start_date->toDateString());
}
$balance = $query->sum('debit') ?: 0;
return new Money($balance);
}

10
composer.lock generated
View File

@ -2588,16 +2588,16 @@
},
{
"name": "pragmarx/version",
"version": "v0.2.6",
"version": "v0.2.7",
"source": {
"type": "git",
"url": "https://github.com/antonioribeiro/version.git",
"reference": "73439723bf8f32534e0a79a07a914971b729ec1e"
"reference": "e48e1d193f55b0e0b8cd2980825485f572ff728f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/antonioribeiro/version/zipball/73439723bf8f32534e0a79a07a914971b729ec1e",
"reference": "73439723bf8f32534e0a79a07a914971b729ec1e",
"url": "https://api.github.com/repos/antonioribeiro/version/zipball/e48e1d193f55b0e0b8cd2980825485f572ff728f",
"reference": "e48e1d193f55b0e0b8cd2980825485f572ff728f",
"shasum": ""
},
"require": {
@ -2645,7 +2645,7 @@
"version",
"versioning"
],
"time": "2018-03-13T00:04:06+00:00"
"time": "2018-03-17T00:01:54+00:00"
},
{
"name": "pragmarx/yaml",

View File

@ -479,7 +479,7 @@ class FinanceTest extends TestCase
/**
* @throws \Prettus\Validator\Exceptions\ValidatorException
*/
public function testJournalOperations()
public function testJournalOperations(): void
{
$journalRepo = app(JournalRepository::class);
@ -495,6 +495,7 @@ class FinanceTest extends TestCase
$balance = $journalRepo->getBalance($journal);
$this->assertEquals(100, $balance->getValue());
$this->assertEquals(100, $journal->balance->getValue());
# add another transaction
@ -507,12 +508,9 @@ class FinanceTest extends TestCase
$balance = $journalRepo->getBalance($journal);
$this->assertEquals(125, $balance->getValue());
# Get the total balance
$this->assertEquals(125, $journal->balance->getValue());
# debit an amount
$journalRepo->post(
$journal,
null,
@ -522,7 +520,6 @@ class FinanceTest extends TestCase
$balance = $journalRepo->getBalance($journal);
$this->assertEquals(100, $balance->getValue());
$this->assertEquals(100, $journal->balance->getValue());
# find all transactions