Implement events for cron expenses; add processing of daily/monthly expenses #136
This commit is contained in:
@@ -3,13 +3,30 @@
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class BaseCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Splice the logger and replace the handler to direct
|
||||
* everything to stdout as well as the log
|
||||
*/
|
||||
public function redirectLoggingToStdout()
|
||||
{
|
||||
$logger = app(\Illuminate\Log\Logger::class);
|
||||
$handlers = $logger->getHandlers();
|
||||
|
||||
try {
|
||||
$handlers[] = new StreamHandler('php://stdout');
|
||||
$logger->setHandlers($handlers);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Couldn\'t splice the logger');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Streaming file read
|
||||
* Streaming file reader
|
||||
* @param $filename
|
||||
* @return \Generator
|
||||
*/
|
||||
@@ -33,6 +50,8 @@ class BaseCommand extends Command
|
||||
* @param $cmd
|
||||
* @param bool $return
|
||||
* @return string
|
||||
* @throws \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @throws \Symfony\Component\Process\Exception\LogicException
|
||||
*/
|
||||
public function runCommand($cmd, $return=false, $verbose=true)
|
||||
{
|
||||
@@ -46,18 +65,12 @@ class BaseCommand extends Command
|
||||
|
||||
$val = '';
|
||||
$process = new Process($cmd);
|
||||
$process->run(function ($type, $buffer) use ($return, $val) {
|
||||
$process->run(function ($type, $buffer) use ($return, &$val) {
|
||||
if ($return) {
|
||||
$val .= $buffer;
|
||||
} else {
|
||||
echo $buffer;
|
||||
}
|
||||
|
||||
/*if (Process::ERR === $type) {
|
||||
echo $buffer;
|
||||
} else {
|
||||
echo $buffer;
|
||||
}*/
|
||||
});
|
||||
|
||||
return $val;
|
||||
|
||||
24
app/Console/Cron/Monthly.php
Normal file
24
app/Console/Cron/Monthly.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Cron;
|
||||
|
||||
use App\Console\BaseCommand;
|
||||
use App\Events\CronMonthly;
|
||||
|
||||
/**
|
||||
* This just calls the CronMonthly event, so all of the
|
||||
* listeners, etc can just be called to run those tasks
|
||||
* @package App\Console\Cron
|
||||
*/
|
||||
class Monthly extends BaseCommand
|
||||
{
|
||||
protected $signature = 'cron:monthly';
|
||||
protected $description = 'Run the monthly cron tasks';
|
||||
protected $schedule;
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$this->redirectLoggingToStdout();
|
||||
event(new CronMonthly());
|
||||
}
|
||||
}
|
||||
24
app/Console/Cron/Nightly.php
Normal file
24
app/Console/Cron/Nightly.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Cron;
|
||||
|
||||
use App\Console\BaseCommand;
|
||||
use App\Events\CronNightly;
|
||||
|
||||
/**
|
||||
* This just calls the CronNightly event, so all of the
|
||||
* listeners, etc can just be called to run those tasks
|
||||
* @package App\Console\Cron
|
||||
*/
|
||||
class Nightly extends BaseCommand
|
||||
{
|
||||
protected $signature = 'cron:nightly';
|
||||
protected $description = 'Run the nightly cron tasks';
|
||||
protected $schedule;
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$this->redirectLoggingToStdout();
|
||||
event(new CronNightly());
|
||||
}
|
||||
}
|
||||
24
app/Console/Cron/Weekly.php
Normal file
24
app/Console/Cron/Weekly.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Cron;
|
||||
|
||||
use App\Console\BaseCommand;
|
||||
use App\Events\CronMonthly;
|
||||
|
||||
/**
|
||||
* This just calls the CronWeekly event, so all of the
|
||||
* listeners, etc can just be called to run those tasks
|
||||
* @package App\Console\Cron
|
||||
*/
|
||||
class Weekly extends BaseCommand
|
||||
{
|
||||
protected $signature = 'cron:monthly';
|
||||
protected $description = 'Run the monthly cron tasks';
|
||||
protected $schedule;
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$this->redirectLoggingToStdout();
|
||||
event(new CronMonthly());
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Console\Cron\Monthly;
|
||||
use App\Console\Cron\Nightly;
|
||||
use App\Console\Cron\Weekly;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
@@ -25,24 +28,24 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// $schedule->command('inspire')
|
||||
// ->hourly();
|
||||
$schedule->command(Nightly::class)->dailyAt('01:00');
|
||||
$schedule->command(Weekly::class)->weeklyOn(0);
|
||||
$schedule->command(Monthly::class)->monthlyOn(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Closure based commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
protected function commands(): void
|
||||
{
|
||||
require app_path('Routes/console.php');
|
||||
$this->load(__DIR__ . '/Commands');
|
||||
$this->load(__DIR__ . '/Cron');
|
||||
}
|
||||
}
|
||||
|
||||
23
app/Console/Logger.php
Normal file
23
app/Console/Logger.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
|
||||
/**
|
||||
* Just a simple custom logger that dumps to the console
|
||||
* @package App\Console
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
public function __invoke(array $config)
|
||||
{
|
||||
$logger = new \Monolog\Logger('console');
|
||||
try {
|
||||
$logger->pushHandler(new StreamHandler('php://stdout'));
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
return $logger;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ class CreateJournalTransactionsTable extends Migration
|
||||
$table->string('ref_class', 50)->nullable();
|
||||
$table->string('ref_class_id', 36)->nullable();
|
||||
$table->timestamps();
|
||||
$table->dateTime('post_date');
|
||||
$table->date('post_date');
|
||||
|
||||
$table->primary('id');
|
||||
$table->index('journal_id');
|
||||
|
||||
@@ -216,6 +216,22 @@ expenses:
|
||||
ref_class_id: 1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
- name: Catering Staff
|
||||
amount: 1000
|
||||
type: 1
|
||||
active: 1
|
||||
ref_class: App\Models\Subfleet
|
||||
ref_class_id: 1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
- name: Maintenance
|
||||
amount: 1000
|
||||
type: 1
|
||||
active: 1
|
||||
ref_class: App\Models\Aircraft
|
||||
ref_class_id: 1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
|
||||
fares:
|
||||
- id: 1
|
||||
@@ -448,7 +464,7 @@ journals:
|
||||
id: 1
|
||||
ledger_id: null
|
||||
type: 0
|
||||
balance: 23710000
|
||||
balance: 7870000
|
||||
currency: USD
|
||||
morphed_type: App\Models\Airline
|
||||
morphed_id: 1
|
||||
@@ -458,7 +474,7 @@ journals:
|
||||
id: 2
|
||||
ledger_id: null
|
||||
type: 1
|
||||
balance: 45000
|
||||
balance: 15000
|
||||
currency: USD
|
||||
morphed_type: App\Models\User
|
||||
morphed_id: 1
|
||||
|
||||
21
app/Events/CronMonthly.php
Normal file
21
app/Events/CronMonthly.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* This event is dispatched when the monthly cron is run
|
||||
* It happens after all of the default nightly tasks
|
||||
* @package App\Events
|
||||
*/
|
||||
class CronMonthly
|
||||
{
|
||||
use Dispatchable, SerializesModels;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
21
app/Events/CronNightly.php
Normal file
21
app/Events/CronNightly.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* This event is dispatched when the daily cron is run
|
||||
* It happens after all of the default nightly tasks
|
||||
* @package App\Events
|
||||
*/
|
||||
class CronNightly
|
||||
{
|
||||
use Dispatchable, SerializesModels;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
21
app/Events/CronWeekly.php
Normal file
21
app/Events/CronWeekly.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* This event is dispatched when the weekly cron is run
|
||||
* It happens after all of the default nightly tasks
|
||||
* @package App\Events
|
||||
*/
|
||||
class CronWeekly
|
||||
{
|
||||
use Dispatchable, SerializesModels;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
33
app/Listeners/Cron/Monthly/ApplyExpenses.php
Normal file
33
app/Listeners/Cron/Monthly/ApplyExpenses.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners\Cron\Monthly;
|
||||
|
||||
use App\Events\CronMonthly;
|
||||
use App\Models\Enums\ExpenseType;
|
||||
use App\Services\Finance\RecurringFinanceService;
|
||||
|
||||
/**
|
||||
* Go through and apply any finances that are daily
|
||||
* @package App\Listeners\Cron\Nightly
|
||||
*/
|
||||
class ApplyExpenses
|
||||
{
|
||||
private $dfSvc;
|
||||
|
||||
public function __construct(RecurringFinanceService $dfSvc)
|
||||
{
|
||||
$this->dfSvc = $dfSvc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all of the expenses for a month
|
||||
* @param CronMonthly $event
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \Prettus\Validator\Exceptions\ValidatorException
|
||||
*/
|
||||
public function handle(CronMonthly $event)
|
||||
{
|
||||
$this->dfSvc->processExpenses(ExpenseType::MONTHLY);
|
||||
}
|
||||
}
|
||||
33
app/Listeners/Cron/Nightly/ApplyExpenses.php
Normal file
33
app/Listeners/Cron/Nightly/ApplyExpenses.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners\Cron\Nightly;
|
||||
|
||||
use App\Events\CronNightly;
|
||||
use App\Models\Enums\ExpenseType;
|
||||
use App\Services\Finance\RecurringFinanceService;
|
||||
|
||||
/**
|
||||
* Go through and apply any finances that are daily
|
||||
* @package App\Listeners\Cron\Nightly
|
||||
*/
|
||||
class ApplyExpenses
|
||||
{
|
||||
private $dfSvc;
|
||||
|
||||
public function __construct(RecurringFinanceService $dfSvc)
|
||||
{
|
||||
$this->dfSvc = $dfSvc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all of the expenses for a day
|
||||
* @param CronNightly $event
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \Prettus\Validator\Exceptions\ValidatorException
|
||||
*/
|
||||
public function handle(CronNightly $event): void
|
||||
{
|
||||
$this->dfSvc->processExpenses(ExpenseType::DAILY);
|
||||
}
|
||||
}
|
||||
57
app/Listeners/Cron/Nightly/RecalculateBalances.php
Normal file
57
app/Listeners/Cron/Nightly/RecalculateBalances.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners\Cron\Nightly;
|
||||
|
||||
use App\Events\CronNightly;
|
||||
use App\Models\Journal;
|
||||
use App\Models\JournalTransaction;
|
||||
use App\Repositories\JournalRepository;
|
||||
use App\Support\Money;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* This recalculates the balances on all of the journals
|
||||
* @package App\Listeners\Cron
|
||||
*/
|
||||
class RecalculateBalances
|
||||
{
|
||||
private $journalRepo;
|
||||
|
||||
/**
|
||||
* Nightly constructor.
|
||||
* @param JournalRepository $journalRepo
|
||||
*/
|
||||
public function __construct(JournalRepository $journalRepo)
|
||||
{
|
||||
$this->journalRepo = $journalRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate all the balances for the different ledgers
|
||||
* @param CronNightly $event
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function handle(CronNightly $event): void
|
||||
{
|
||||
Log::info('Recalculating balances');
|
||||
|
||||
$journals = Journal::all();
|
||||
foreach ($journals as $journal) {
|
||||
|
||||
$where = ['journal_id' => $journal->id];
|
||||
$credits = Money::create(JournalTransaction::where($where)->sum('credit') ?: 0);
|
||||
$debits = Money::create(JournalTransaction::where($where)->sum('debit') ?: 0);
|
||||
$balance = $credits->subtract($debits);
|
||||
|
||||
Log::info('Adjusting balance on ' .
|
||||
$journal->morphed_type . ':' . $journal->morphed_id
|
||||
. ' from ' . $journal->balance . ' to ' . $balance);
|
||||
|
||||
$journal->balance = $balance->getAmount();
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
Log::info('Done calculating balances');
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,11 @@ namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class Expense
|
||||
* @property int airline_id
|
||||
* @property float amount
|
||||
* @property string name
|
||||
* @property string ref_class
|
||||
* @property string ref_class_id
|
||||
* @package App\Models
|
||||
*/
|
||||
class Expense extends BaseModel
|
||||
@@ -33,6 +36,27 @@ class Expense extends BaseModel
|
||||
'charge_to_user' => 'bool',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the referring object
|
||||
*/
|
||||
public function getReference()
|
||||
{
|
||||
if (!$this->ref_class || !$this->ref_class_id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if($this->ref_class === __CLASS__) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
try {
|
||||
$klass = new $this->ref_class;
|
||||
return $klass->find($this->ref_class_id);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Foreign Keys
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@ use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Class Journal
|
||||
* @package Scottlaurent\Accounting
|
||||
* @property mixed id
|
||||
* @property Money $balance
|
||||
* @property string $currency
|
||||
* @property Carbon $updated_at
|
||||
|
||||
@@ -41,6 +41,13 @@ class JournalTransaction extends BaseModel
|
||||
'tags' => 'array',
|
||||
];
|
||||
|
||||
protected $dateFormat = 'Y-m-d';
|
||||
protected $dates = [
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'post_date',
|
||||
];
|
||||
|
||||
/**
|
||||
* Callbacks
|
||||
* @throws \UnexpectedValueException
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Events\CronMonthly;
|
||||
use App\Events\CronNightly;
|
||||
use App\Events\CronWeekly;
|
||||
use App\Events\Expenses;
|
||||
use App\Listeners\Cron\Nightly\RecalculateBalances;
|
||||
use App\Listeners\ExpenseListener;
|
||||
use App\Listeners\FinanceEvents;
|
||||
use App\Listeners\NotificationEvents;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use App\Listeners\ExpenseListener;
|
||||
use App\Events\Expenses;
|
||||
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
@@ -20,6 +24,19 @@ class EventServiceProvider extends ServiceProvider
|
||||
Expenses::class => [
|
||||
ExpenseListener::class
|
||||
],
|
||||
|
||||
# Cron hooks
|
||||
CronNightly::class => [
|
||||
\App\Listeners\Cron\Nightly\ApplyExpenses::class,
|
||||
RecalculateBalances::class,
|
||||
],
|
||||
|
||||
CronWeekly::class => [
|
||||
],
|
||||
|
||||
CronMonthly::class => [
|
||||
\App\Listeners\Cron\Monthly\ApplyExpenses::class
|
||||
],
|
||||
];
|
||||
|
||||
protected $subscribe = [
|
||||
|
||||
@@ -27,6 +27,20 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
return JournalTransaction::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Y-m-d string for the post date
|
||||
* @param Carbon $date
|
||||
* @return string
|
||||
*/
|
||||
public function formatPostDate(Carbon $date=null)
|
||||
{
|
||||
if(!$date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $date->setTimezone('UTC')->toDateString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Post a new transaction to a journal, and also adjust the balance
|
||||
* on the transaction itself. A cron will run to reconcile the journal
|
||||
@@ -55,22 +69,26 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
) {
|
||||
|
||||
# tags can be passed in a list
|
||||
if($tags && \is_array($tags)) {
|
||||
if ($tags && \is_array($tags)) {
|
||||
$tags = implode(',', $tags);
|
||||
}
|
||||
|
||||
if(!$post_date) {
|
||||
$post_date = Carbon::now('UTC');
|
||||
}
|
||||
|
||||
$attrs = [
|
||||
'journal_id' => $journal->id,
|
||||
'credit' => $credit ? $credit->getAmount():null,
|
||||
'debit' => $debit ? $debit->getAmount():null,
|
||||
'currency' => config('phpvms.currency'),
|
||||
'memo' => $memo,
|
||||
'post_date' => $post_date ?? Carbon::now(),
|
||||
'journal_id' => $journal->id,
|
||||
'credit' => $credit ? $credit->getAmount() : null,
|
||||
'debit' => $debit ? $debit->getAmount() : null,
|
||||
'currency' => config('phpvms.currency'),
|
||||
'memo' => $memo,
|
||||
'post_date' => $post_date,
|
||||
'transaction_group' => $transaction_group,
|
||||
'tags' => $tags
|
||||
'tags' => $tags
|
||||
];
|
||||
|
||||
if($reference !== null) {
|
||||
if ($reference !== null) {
|
||||
$attrs['ref_class'] = \get_class($reference);
|
||||
$attrs['ref_class_id'] = $reference->id;
|
||||
}
|
||||
@@ -112,6 +130,8 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
* @param Carbon|null $start_date
|
||||
* @param null $transaction_group
|
||||
* @return Money
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getCreditBalanceBetween(
|
||||
Carbon $date,
|
||||
@@ -124,7 +144,7 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
['post_date', '<=', $date]
|
||||
];
|
||||
|
||||
if($journal) {
|
||||
if ($journal) {
|
||||
$where['journal_id'] = $journal->id;
|
||||
}
|
||||
|
||||
@@ -152,10 +172,13 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
*/
|
||||
public function getDebitBalanceBetween(
|
||||
Carbon $date,
|
||||
Journal $journal=null,
|
||||
Carbon $start_date=null,
|
||||
$transaction_group=null
|
||||
): Money {
|
||||
Journal $journal = null,
|
||||
Carbon $start_date = null,
|
||||
$transaction_group = null
|
||||
): Money
|
||||
{
|
||||
|
||||
$date = $this->formatPostDate($date);
|
||||
|
||||
$where = [
|
||||
['post_date', '<=', $date]
|
||||
@@ -165,11 +188,12 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
$where['journal_id'] = $journal->id;
|
||||
}
|
||||
|
||||
if($start_date) {
|
||||
if ($start_date) {
|
||||
$start_date = $this->formatPostDate($start_date);
|
||||
$where[] = ['post_date', '>=', $start_date];
|
||||
}
|
||||
|
||||
if($transaction_group) {
|
||||
if ($transaction_group) {
|
||||
$where['transaction_group'] = $transaction_group;
|
||||
}
|
||||
|
||||
@@ -184,25 +208,31 @@ class JournalRepository extends BaseRepository implements CacheableInterface
|
||||
* Return all transactions for a given object
|
||||
* @param $object
|
||||
* @param null $journal
|
||||
* @param Carbon|null $date
|
||||
* @return array
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getAllForObject($object, $journal=null)
|
||||
public function getAllForObject($object, $journal = null, Carbon $date = null)
|
||||
{
|
||||
$where = [
|
||||
'ref_class' => \get_class($object),
|
||||
'ref_class_id' => $object->id,
|
||||
];
|
||||
|
||||
if($journal) {
|
||||
if ($journal) {
|
||||
$where['journal_id'] = $journal->id;
|
||||
}
|
||||
|
||||
if ($date) {
|
||||
$date = $this->formatPostDate($date);
|
||||
$where[] = ['post_date', '=', $date];
|
||||
}
|
||||
|
||||
$transactions = $this->whereOrder($where, [
|
||||
'credit' => 'desc',
|
||||
'debit' => 'desc'
|
||||
])->get();
|
||||
'credit' => 'desc',
|
||||
'debit' => 'desc'
|
||||
])->get();
|
||||
|
||||
return [
|
||||
'credits' => new Money($transactions->sum('credit')),
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<?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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
141
app/Services/Finance/RecurringFinanceService.php
Normal file
141
app/Services/Finance/RecurringFinanceService.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Finance;
|
||||
|
||||
use App\Models\Airline;
|
||||
use App\Models\Enums\ExpenseType;
|
||||
use App\Models\Expense;
|
||||
use App\Models\JournalTransaction;
|
||||
use App\Repositories\JournalRepository;
|
||||
use App\Services\BaseService;
|
||||
use App\Support\Money;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Process all of the daily expenses and charge them
|
||||
* @package App\Services\Finance
|
||||
*/
|
||||
class RecurringFinanceService extends BaseService
|
||||
{
|
||||
private $journalRepo;
|
||||
|
||||
public function __construct(JournalRepository $journalRepo)
|
||||
{
|
||||
$this->journalRepo = $journalRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the journal to charge to, otherwise, it's charged
|
||||
* to every airline journal
|
||||
* @param Expense $expense
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function findJournals(Expense $expense)
|
||||
{
|
||||
if($expense->airline_id) {
|
||||
$airline = Airline::find($expense->airline_id)->first(['id', 'icao']);
|
||||
Log::info('Charging to ' . $airline->icao);
|
||||
yield $airline->journal;
|
||||
} else {
|
||||
$airlines = Airline::all(['id', 'icao']);
|
||||
foreach($airlines as $airline) {
|
||||
Log::info('Charging to ' . $airline->icao);
|
||||
yield $airline->journal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the transaction group from the expense
|
||||
* @param Expense $expense
|
||||
* @return array
|
||||
*/
|
||||
protected function getMemoAndGroup(Expense $expense): array
|
||||
{
|
||||
$klass = 'Expense';
|
||||
if ($expense->ref_class) {
|
||||
$ref = explode('\\', $expense->ref_class);
|
||||
$klass = end($ref);
|
||||
$obj = $expense->getReference();
|
||||
}
|
||||
|
||||
if ($klass === 'Airport') {
|
||||
$memo = "Airport Expense: {$expense->name} ({$expense->ref_class_id})";
|
||||
$transaction_group = "Airport: {$expense->ref_class_id}";
|
||||
} elseif ($klass === 'Subfleet') {
|
||||
$memo = "Subfleet Expense: {$expense->name}";
|
||||
$transaction_group = "Subfleet: {$expense->name}";
|
||||
} elseif ($klass === 'Aircraft') {
|
||||
$memo = "Aircraft Expense: {$expense->name} ({$obj->name})";
|
||||
$transaction_group = "Aircraft: {$expense->name} ({$obj->name}-{$obj->registration})";
|
||||
} else {
|
||||
$memo = "Expense: {$expense->name}";
|
||||
$transaction_group = "Expense: {$expense->name}";
|
||||
}
|
||||
|
||||
return [$memo, $transaction_group];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run all of the daily expense/financials
|
||||
* @param int $type
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \Prettus\Validator\Exceptions\ValidatorException
|
||||
*/
|
||||
public function processExpenses($type=ExpenseType::DAILY): void
|
||||
{
|
||||
$expenses = Expense::where(['type' => $type])->get();
|
||||
|
||||
$tag = 'expense_recurring';
|
||||
if($type === ExpenseType::DAILY) {
|
||||
$tag = 'expenses_daily';
|
||||
} elseif ($type === ExpenseType::MONTHLY) {
|
||||
$tag === 'expenses_monthly';
|
||||
}
|
||||
|
||||
/**
|
||||
* @var $expenses Expense[]
|
||||
*/
|
||||
foreach ($expenses as $expense) {
|
||||
|
||||
# Apply the expenses to the appropriate journals
|
||||
$journals = $this->findJournals($expense);
|
||||
foreach($journals as $journal) {
|
||||
|
||||
$amount = $expense->amount;
|
||||
|
||||
# Has this expense already been charged? Check
|
||||
# against this specific journal, on today
|
||||
$w = [
|
||||
'journal_id' => $journal->id,
|
||||
'ref_class' => Expense::class,
|
||||
'ref_class_id' => $expense->id,
|
||||
'post_date' => \Carbon::now('UTC')->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$found = JournalTransaction::where($w)->count(['id']);
|
||||
if($found > 0) {
|
||||
Log::info('Expense "'.$expense->name.'" already charged for today, skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
[$memo, $ta_group] = $this->getMemoAndGroup($expense);
|
||||
|
||||
$this->journalRepo->post(
|
||||
$journal,
|
||||
null,
|
||||
Money::createFromAmount($amount),
|
||||
$expense,
|
||||
$memo,
|
||||
null,
|
||||
$ta_group,
|
||||
$tag
|
||||
);
|
||||
|
||||
Log::info('Expense memo: "'.$memo.'"; group: "'. $ta_group. '" charged!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
composer.lock
generated
72
composer.lock
generated
@@ -876,16 +876,16 @@
|
||||
},
|
||||
{
|
||||
"name": "google/apiclient-services",
|
||||
"version": "v0.49",
|
||||
"version": "v0.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/google/google-api-php-client-services.git",
|
||||
"reference": "7552d7d1bb92e933fc93088014c8c2555c0feab6"
|
||||
"reference": "3e792254734e4444dd8a594e927926f28de2e609"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/google/google-api-php-client-services/zipball/7552d7d1bb92e933fc93088014c8c2555c0feab6",
|
||||
"reference": "7552d7d1bb92e933fc93088014c8c2555c0feab6",
|
||||
"url": "https://api.github.com/repos/google/google-api-php-client-services/zipball/3e792254734e4444dd8a594e927926f28de2e609",
|
||||
"reference": "3e792254734e4444dd8a594e927926f28de2e609",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -909,7 +909,7 @@
|
||||
"keywords": [
|
||||
"google"
|
||||
],
|
||||
"time": "2018-03-04T00:24:05+00:00"
|
||||
"time": "2018-03-10T00:23:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "google/auth",
|
||||
@@ -1567,16 +1567,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v5.6.11",
|
||||
"version": "v5.6.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "195ba6a67bdad2a23105c7ab410cd43e0f20bb73"
|
||||
"reference": "82d8165d1ea86bdd81ddfa1db9343fa19e7d1450"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/195ba6a67bdad2a23105c7ab410cd43e0f20bb73",
|
||||
"reference": "195ba6a67bdad2a23105c7ab410cd43e0f20bb73",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/82d8165d1ea86bdd81ddfa1db9343fa19e7d1450",
|
||||
"reference": "82d8165d1ea86bdd81ddfa1db9343fa19e7d1450",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1585,7 +1585,7 @@
|
||||
"erusev/parsedown": "~1.7",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"league/flysystem": "~1.0",
|
||||
"league/flysystem": "^1.0.8",
|
||||
"monolog/monolog": "~1.12",
|
||||
"nesbot/carbon": "^1.24.1",
|
||||
"php": "^7.1.3",
|
||||
@@ -1701,7 +1701,7 @@
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"time": "2018-03-09T16:53:27+00:00"
|
||||
"time": "2018-03-14T17:29:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravelcollective/html",
|
||||
@@ -2162,16 +2162,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "1.24.1",
|
||||
"version": "1.24.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
||||
"reference": "ef12cbd8ba5ce624f0a6a95e0850c4cc13e71e41"
|
||||
"reference": "bba6c6e410c6b4317e37a9474aeaa753808c3875"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/ef12cbd8ba5ce624f0a6a95e0850c4cc13e71e41",
|
||||
"reference": "ef12cbd8ba5ce624f0a6a95e0850c4cc13e71e41",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bba6c6e410c6b4317e37a9474aeaa753808c3875",
|
||||
"reference": "bba6c6e410c6b4317e37a9474aeaa753808c3875",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2211,7 +2211,7 @@
|
||||
"datetime",
|
||||
"time"
|
||||
],
|
||||
"time": "2018-03-09T15:49:34+00:00"
|
||||
"time": "2018-03-10T10:10:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
@@ -2362,7 +2362,7 @@
|
||||
"lib/random.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -2588,16 +2588,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/version",
|
||||
"version": "v0.2.5",
|
||||
"version": "v0.2.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/antonioribeiro/version.git",
|
||||
"reference": "ed34e37904067bdb15857d3f2bcc11683f37eb84"
|
||||
"reference": "73439723bf8f32534e0a79a07a914971b729ec1e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/antonioribeiro/version/zipball/ed34e37904067bdb15857d3f2bcc11683f37eb84",
|
||||
"reference": "ed34e37904067bdb15857d3f2bcc11683f37eb84",
|
||||
"url": "https://api.github.com/repos/antonioribeiro/version/zipball/73439723bf8f32534e0a79a07a914971b729ec1e",
|
||||
"reference": "73439723bf8f32534e0a79a07a914971b729ec1e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2645,7 +2645,7 @@
|
||||
"version",
|
||||
"versioning"
|
||||
],
|
||||
"time": "2018-02-10T17:40:28+00:00"
|
||||
"time": "2018-03-13T00:04:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/yaml",
|
||||
@@ -2795,7 +2795,7 @@
|
||||
"Prettus\\Validator\\": "src/Prettus/Validator/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Anderson Andrade",
|
||||
@@ -2884,7 +2884,7 @@
|
||||
"Psr\\Container\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -3439,7 +3439,7 @@
|
||||
"lib/swift_required.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -5387,7 +5387,7 @@
|
||||
"Faker\\": "src/Faker/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -5739,7 +5739,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -5786,7 +5786,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -5843,7 +5843,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -6606,7 +6606,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -6658,7 +6658,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -6726,7 +6726,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -6776,7 +6776,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -6821,7 +6821,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -6866,7 +6866,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
@@ -7055,7 +7055,7 @@
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"notification-url": "http://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
|
||||
@@ -28,7 +28,16 @@ return [
|
||||
'channels' => [
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => ['daily'],
|
||||
'channels' => [
|
||||
'daily',
|
||||
# PHP_SAPI === 'cli' ? 'console' : 'daily',
|
||||
],
|
||||
],
|
||||
'console' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => [
|
||||
'stdout',
|
||||
],
|
||||
],
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
@@ -48,6 +57,10 @@ return [
|
||||
'emoji' => ':boom:',
|
||||
'level' => 'critical',
|
||||
],
|
||||
'stdout' => [
|
||||
'driver' => 'custom',
|
||||
'via' => \App\Console\Logger::class,
|
||||
],
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => 'debug',
|
||||
|
||||
Reference in New Issue
Block a user