Fixes and tests for the journal and journaled transactions #130
This commit is contained in:
9
app/Database/factories/JournalFactory.php
Normal file
9
app/Database/factories/JournalFactory.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Faker\Generator as Faker;
|
||||
|
||||
$factory->define(App\Models\Journal::class, function (Faker $faker) {
|
||||
return [
|
||||
'currency' => 'USD',
|
||||
];
|
||||
});
|
||||
17
app/Database/factories/JournalTransactionsFactory.php
Normal file
17
app/Database/factories/JournalTransactionsFactory.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
use Faker\Generator as Faker;
|
||||
|
||||
$factory->define(App\Models\JournalTransactions::class, function (Faker $faker) {
|
||||
return [
|
||||
'transaction_group' => \Ramsey\Uuid\Uuid::uuid4()->toString(),
|
||||
'journal_id' => function () {
|
||||
return factory(App\Models\Journal::class)->create()->id;
|
||||
},
|
||||
'credit' => $faker->numberBetween(100, 10000),
|
||||
'debit' => $faker->numberBetween(100, 10000),
|
||||
'currency' => 'USD',
|
||||
'memo' => $faker->sentence(6),
|
||||
'post_date' => \Carbon\Carbon::now(),
|
||||
];
|
||||
});
|
||||
@@ -17,8 +17,8 @@ class CreateJournalTransactionsTable extends Migration
|
||||
$table->char('id', 36)->unique();
|
||||
$table->char('transaction_group', 36)->nullable();
|
||||
$table->integer('journal_id');
|
||||
$table->unsignedBigInteger('debit')->nullable();
|
||||
$table->unsignedBigInteger('credit')->nullable();
|
||||
$table->unsignedBigInteger('debit')->nullable();
|
||||
$table->char('currency', 5);
|
||||
$table->text('memo')->nullable();
|
||||
$table->char('ref_class', 32)->nullable();
|
||||
|
||||
@@ -17,9 +17,9 @@ class CreateJournalsTable extends Migration
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('ledger_id')->nullable();
|
||||
$table->bigInteger('balance')->default(0);
|
||||
$table->char('currency', 5);
|
||||
$table->char('morphed_type', 32);
|
||||
$table->integer('morphed_id');
|
||||
$table->string('currency', 5);
|
||||
$table->string('morphed_type', 32)->nullable();
|
||||
$table->unsignedInteger('morphed_id')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ class JournalTransaction extends BaseModel
|
||||
public $fillable = [
|
||||
'transaction_group',
|
||||
'journal_id',
|
||||
'debit',
|
||||
'credit',
|
||||
'debit',
|
||||
'currency',
|
||||
'memo',
|
||||
'tags',
|
||||
@@ -35,8 +35,8 @@ class JournalTransaction extends BaseModel
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'debit' => 'integer',
|
||||
'credits' => 'integer',
|
||||
'debit' => 'integer',
|
||||
'post_date' => 'datetime',
|
||||
'tags' => 'array',
|
||||
];
|
||||
|
||||
@@ -11,7 +11,10 @@ use Prettus\Repository\Contracts\CacheableInterface;
|
||||
use Prettus\Repository\Traits\CacheableRepository;
|
||||
use Prettus\Validator\Exceptions\ValidatorException;
|
||||
|
||||
|
||||
/**
|
||||
* Class JournalRepository
|
||||
* @package App\Repositories
|
||||
*/
|
||||
class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
{
|
||||
use CacheableRepository;
|
||||
@@ -39,7 +42,7 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
* @throws ValidatorException
|
||||
*/
|
||||
public function post(
|
||||
Journal $journal,
|
||||
Journal &$journal,
|
||||
Money $credit = null,
|
||||
Money $debit = null,
|
||||
$reference = null,
|
||||
@@ -52,7 +55,7 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
'journal_id' => $journal->id,
|
||||
'credit' => $credit ? $credit->getAmount():null,
|
||||
'debit' => $debit ? $debit->getAmount():null,
|
||||
'currency_code' => config('phpvms.currency'),
|
||||
'currency' => config('phpvms.currency'),
|
||||
'memo' => $memo,
|
||||
'post_date' => $post_date ?: Carbon::now(),
|
||||
'transaction_group' => $transaction_group,
|
||||
@@ -70,17 +73,16 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
}
|
||||
|
||||
# Adjust the balance on the journal
|
||||
$balance = new Money($journal->balance);
|
||||
if($credit) {
|
||||
$balance = $balance->add($credit);
|
||||
$journal->balance->add($credit);
|
||||
}
|
||||
|
||||
if($debit) {
|
||||
$balance = $balance->subtract($debit);
|
||||
$journal->balance->subtract($debit);
|
||||
}
|
||||
|
||||
$journal->balance = $balance->getAmount();
|
||||
$journal->save();
|
||||
$journal->refresh();
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
@@ -92,14 +94,14 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getBalance(Journal $journal, Carbon $date=null)
|
||||
public function getBalance(Journal $journal=null, Carbon $date=null)
|
||||
{
|
||||
if(!$date) {
|
||||
$date = Carbon::now();
|
||||
}
|
||||
|
||||
$credit = $this->getCreditBalanceOn($journal, $date);
|
||||
$debit = $this->getDebitBalanceOn($journal, $date);
|
||||
$credit = $this->getCreditBalanceBetween($date, $journal);
|
||||
$debit = $this->getDebitBalanceBetween($date, $journal);
|
||||
|
||||
return $credit->subtract($debit);
|
||||
}
|
||||
@@ -112,12 +114,27 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getCreditBalanceOn(Journal $journal, Carbon $date)
|
||||
{
|
||||
$balance = $this->findWhere([
|
||||
'journal_id' => $journal->id,
|
||||
public function getCreditBalanceBetween(
|
||||
Carbon $date,
|
||||
Journal $journal=null,
|
||||
Carbon $start_date=null
|
||||
): Money {
|
||||
|
||||
$where = [
|
||||
['post_date', '<=', $date]
|
||||
], ['id', 'credit'])->sum('credit') ?: 0;
|
||||
];
|
||||
|
||||
if($journal) {
|
||||
$where['journal_id'] = $journal->id;
|
||||
}
|
||||
|
||||
if ($start_date) {
|
||||
$where[] = ['post_date', '>=', $start_date];
|
||||
}
|
||||
|
||||
$balance = $this
|
||||
->findWhere($where, ['id', 'credit'])
|
||||
->sum('credit') ?: 0;
|
||||
|
||||
return new Money($balance);
|
||||
}
|
||||
@@ -129,13 +146,49 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getDebitBalanceOn(Journal $journal, Carbon $date): Money
|
||||
{
|
||||
$balance = $this->findWhere([
|
||||
'journal_id' => $journal->id,
|
||||
public function getDebitBalanceBetween(
|
||||
Carbon $date,
|
||||
Journal $journal=null,
|
||||
Carbon $start_date=null
|
||||
): Money {
|
||||
|
||||
$where = [
|
||||
['post_date', '<=', $date]
|
||||
], ['id', 'debit'])->sum('debit') ?: 0;
|
||||
];
|
||||
|
||||
if ($journal) {
|
||||
$where['journal_id'] = $journal->id;
|
||||
}
|
||||
|
||||
if($start_date) {
|
||||
$where[] = ['post_date', '>=', $start_date];
|
||||
}
|
||||
|
||||
$balance = $this
|
||||
->findWhere($where, ['id', 'debit'])
|
||||
->sum('debit') ?: 0;
|
||||
|
||||
return new Money($balance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all transactions for a given object
|
||||
* @param $object
|
||||
* @return array
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getAllForObject($object)
|
||||
{
|
||||
$transactions = $this->findWhere([
|
||||
'ref_class' => \get_class($object),
|
||||
'ref_class_id' => $object->id,
|
||||
]);
|
||||
|
||||
return [
|
||||
'credits' => new Money($transactions->sum('credit')),
|
||||
'debits' => new Money($transactions->sum('debit')),
|
||||
'transactions' => $transactions,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,6 @@ class FinanceService extends BaseService
|
||||
$pilot_rate = $this->getPayRateForPirep($pirep) / 60;
|
||||
$payment = round($pirep->flight_time * $pilot_rate, 2);
|
||||
|
||||
return new Money($payment);
|
||||
return Money::createFromAmount($payment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,20 @@ class Money
|
||||
return new MoneyBase($amount, static::currency());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from a dollar amount
|
||||
* @param $amount
|
||||
* @return Money
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function createFromAmount($amount)
|
||||
{
|
||||
return new Money(
|
||||
static::convertToSubunit($amount)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a whole unit into it's subunit, e,g: dollar to cents
|
||||
* @param $amount
|
||||
@@ -65,7 +79,6 @@ class Money
|
||||
*/
|
||||
public function __construct($amount)
|
||||
{
|
||||
$amount = static::convertToSubunit($amount);
|
||||
$this->money = static::create($amount);
|
||||
}
|
||||
|
||||
@@ -78,9 +91,17 @@ class Money
|
||||
return $this->money->getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of getAmount()
|
||||
*/
|
||||
public function toAmount()
|
||||
{
|
||||
return $this->getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value in whole amounts, e.g: 100.00
|
||||
* vs returning in all cents
|
||||
* instead of returning in the smallest denomination
|
||||
* @return float
|
||||
*/
|
||||
public function getValue()
|
||||
@@ -88,6 +109,14 @@ class Money
|
||||
return $this->money->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of getValue()
|
||||
*/
|
||||
public function toValue()
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MoneyBase
|
||||
*/
|
||||
@@ -116,10 +145,16 @@ class Money
|
||||
/**
|
||||
* Add an amount
|
||||
* @param $amount
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function add($amount)
|
||||
{
|
||||
$this->money = $this->money->add($amount);
|
||||
if(!($amount instanceof self)) {
|
||||
$amount = static::createFromAmount($amount);
|
||||
}
|
||||
|
||||
$this->money = $this->money->add($amount->money);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,11 +178,16 @@ class Money
|
||||
* Subtract an amount
|
||||
* @param $amount
|
||||
* @return Money
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function subtract($amount)
|
||||
{
|
||||
$this->money = $this->money->subtract($amount);
|
||||
if (!($amount instanceof self)) {
|
||||
$amount = static::createFromAmount($amount);
|
||||
}
|
||||
|
||||
$this->money = $this->money->subtract($amount->money);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -155,12 +195,17 @@ class Money
|
||||
* Multiply by an amount
|
||||
* @param $amount
|
||||
* @return Money
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \OutOfBoundsException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function multiply($amount)
|
||||
{
|
||||
$this->money = $this->money->multiply($amount);
|
||||
if (!($amount instanceof self)) {
|
||||
$amount = static::createFromAmount($amount);
|
||||
}
|
||||
|
||||
$this->money = $this->money->multiply($amount->money);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
use App\Repositories\JournalRepository;
|
||||
use App\Services\FareService;
|
||||
use App\Services\FinanceService;
|
||||
use App\Services\FleetService;
|
||||
use App\Support\Math;
|
||||
use App\Support\Money;
|
||||
|
||||
class FinanceTest extends TestCase
|
||||
{
|
||||
@@ -373,7 +375,7 @@ class FinanceTest extends TestCase
|
||||
]);
|
||||
|
||||
$payment = $this->financeSvc->getPilotPilotPay($pirep_acars);
|
||||
$this->assertEquals($payment->getValue(), 100);
|
||||
$this->assertEquals(100, $payment->getValue());
|
||||
|
||||
$pirep_acars = factory(App\Models\Pirep::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
@@ -385,4 +387,58 @@ class FinanceTest extends TestCase
|
||||
$payment = $this->financeSvc->getPilotPilotPay($pirep_acars);
|
||||
$this->assertEquals($payment->getValue(), 150);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Prettus\Validator\Exceptions\ValidatorException
|
||||
*/
|
||||
public function testJournalOperations()
|
||||
{
|
||||
$journalRepo = app(JournalRepository::class);
|
||||
|
||||
$user = factory(App\Models\User::class)->create();
|
||||
$journal = factory(App\Models\Journal::class)->create();
|
||||
|
||||
$journalRepo->post(
|
||||
$journal,
|
||||
Money::createFromAmount(100),
|
||||
null,
|
||||
$user
|
||||
);
|
||||
|
||||
$balance = $journalRepo->getBalance($journal);
|
||||
$this->assertEquals(100, $balance->getValue());
|
||||
|
||||
# add another transaction
|
||||
|
||||
$journalRepo->post(
|
||||
$journal,
|
||||
Money::createFromAmount(25),
|
||||
null,
|
||||
$user
|
||||
);
|
||||
|
||||
$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,
|
||||
Money::createFromAmount(25),
|
||||
$user
|
||||
);
|
||||
|
||||
$this->assertEquals(100, $journal->balance->getValue());
|
||||
|
||||
# find all transactions
|
||||
$transactions = $journalRepo->getAllForObject($user);
|
||||
|
||||
$this->assertCount(3, $transactions['transactions']);
|
||||
$this->assertEquals(125, $transactions['credits']->getValue());
|
||||
$this->assertEquals(25, $transactions['debits']->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user