mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
New cron job for bills.
This commit is contained in:
parent
e5a08d2cf1
commit
f2849c8058
@ -28,6 +28,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Support\Cronjobs\AutoBudgetCronjob;
|
||||
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
||||
use FireflyIII\Support\Cronjobs\BillWarningCronjob;
|
||||
use Illuminate\Console\Command;
|
||||
use InvalidArgumentException;
|
||||
use Log;
|
||||
@ -66,7 +67,7 @@ class Cron extends Command
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->error(sprintf('"%s" is not a valid date', $this->option('date')));
|
||||
}
|
||||
$force = (bool)$this->option('force');
|
||||
$force = (bool) $this->option('force');
|
||||
|
||||
/*
|
||||
* Fire recurring transaction cron job.
|
||||
@ -90,6 +91,17 @@ class Cron extends Command
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
/*
|
||||
* Fire bill warning cron job
|
||||
*/
|
||||
try {
|
||||
$this->billWarningCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->info('More feedback on the cron jobs can be found in the log files.');
|
||||
|
||||
return 0;
|
||||
@ -150,4 +162,32 @@ class Cron extends Command
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
*/
|
||||
private function billWarningCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$autoBudget = new BillWarningCronjob;
|
||||
$autoBudget->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$autoBudget->setDate($date);
|
||||
}
|
||||
|
||||
$autoBudget->fire();
|
||||
|
||||
if ($autoBudget->jobErrored) {
|
||||
$this->error(sprintf('Error in "bill warnings" cron: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobFired) {
|
||||
$this->error(sprintf('"Send bill warnings" cron fired: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobSucceeded) {
|
||||
$this->error(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
54
app/Events/WarnUserAboutBill.php
Normal file
54
app/Events/WarnUserAboutBill.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* DestroyedTransactionGroup.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class WarnUserAboutBill.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class WarnUserAboutBill extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public Bill $bill;
|
||||
public string $field;
|
||||
public int $diff;
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param string $field
|
||||
* @param int $diff
|
||||
*/
|
||||
public function __construct(Bill $bill, string $field, int $diff)
|
||||
{
|
||||
$this->bill = $bill;
|
||||
$this->field = $field;
|
||||
$this->diff = $diff;
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ class BillFactory
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__), $data);
|
||||
$factory = app(TransactionCurrencyFactory::class);
|
||||
$currency = $factory->find((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null)) ??
|
||||
$currency = $factory->find((int) ($data['currency_id'] ?? null), (string) ($data['currency_code'] ?? null)) ??
|
||||
app('amount')->getDefaultCurrencyByUser($this->user);
|
||||
|
||||
try {
|
||||
@ -82,7 +82,7 @@ class BillFactory
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $data)) {
|
||||
$this->updateNote($bill, (string)$data['notes']);
|
||||
$this->updateNote($bill, (string) $data['notes']);
|
||||
}
|
||||
$objectGroupTitle = $data['object_group_title'] ?? '';
|
||||
if ('' !== $objectGroupTitle) {
|
||||
@ -93,7 +93,7 @@ class BillFactory
|
||||
}
|
||||
}
|
||||
// try also with ID:
|
||||
$objectGroupId = (int)($data['object_group_id'] ?? 0);
|
||||
$objectGroupId = (int) ($data['object_group_id'] ?? 0);
|
||||
if (0 !== $objectGroupId) {
|
||||
$objectGroup = $this->findObjectGroupById($objectGroupId);
|
||||
if (null !== $objectGroup) {
|
||||
@ -113,8 +113,8 @@ class BillFactory
|
||||
*/
|
||||
public function find(?int $billId, ?string $billName): ?Bill
|
||||
{
|
||||
$billId = (int)$billId;
|
||||
$billName = (string)$billName;
|
||||
$billId = (int) $billId;
|
||||
$billName = (string) $billName;
|
||||
$bill = null;
|
||||
// first find by ID:
|
||||
if ($billId > 0) {
|
||||
|
41
app/Handlers/Events/BillEventHandler.php
Normal file
41
app/Handlers/Events/BillEventHandler.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\WarnUserAboutBill;
|
||||
use FireflyIII\Mail\BillWarningMail;
|
||||
use Log;
|
||||
use Mail;
|
||||
|
||||
/**
|
||||
* Class BillEventHandler
|
||||
*/
|
||||
class BillEventHandler
|
||||
{
|
||||
/**
|
||||
* @param WarnUserAboutBill $event
|
||||
* @return void
|
||||
*/
|
||||
public function warnAboutBill(WarnUserAboutBill $event): void
|
||||
{
|
||||
$bill = $event->bill;
|
||||
$field = $event->field;
|
||||
$diff = $event->diff;
|
||||
$user = $bill->user;
|
||||
$address = $user->email;
|
||||
$ipAddress = request()?->ip();
|
||||
|
||||
// see if user has alternative email address:
|
||||
$pref = app('preferences')->getForUser($user, 'remote_guard_alt_email');
|
||||
if (null !== $pref) {
|
||||
$address = $pref->data;
|
||||
}
|
||||
|
||||
// send message:
|
||||
Mail::to($address)->send(new BillWarningMail($bill, $field, $diff, $ipAddress));
|
||||
|
||||
|
||||
Log::debug('warnAboutBill');
|
||||
}
|
||||
|
||||
}
|
@ -102,6 +102,7 @@ class CreateController extends Controller
|
||||
public function store(BillStoreRequest $request): RedirectResponse
|
||||
{
|
||||
$billData = $request->getBillData();
|
||||
|
||||
$billData['active'] = true;
|
||||
try {
|
||||
$bill = $this->repository->store($billData);
|
||||
|
@ -53,7 +53,7 @@ class EditController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', (string)trans('firefly.bills'));
|
||||
app('view')->share('title', (string) trans('firefly.bills'));
|
||||
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
@ -78,10 +78,10 @@ class EditController extends Controller
|
||||
$billPeriods = config('firefly.bill_periods');
|
||||
|
||||
foreach ($billPeriods as $current) {
|
||||
$periods[$current] = (string)trans('firefly.' . $current);
|
||||
$periods[$current] = (string) trans('firefly.' . $current);
|
||||
}
|
||||
|
||||
$subTitle = (string)trans('firefly.edit_bill', ['name' => $bill->name]);
|
||||
$subTitle = (string) trans('firefly.edit_bill', ['name' => $bill->name]);
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (true !== session('bills.edit.fromUpdate')) {
|
||||
@ -89,8 +89,8 @@ class EditController extends Controller
|
||||
}
|
||||
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
$bill->amount_min = round((float)$bill->amount_min, $currency->decimal_places);
|
||||
$bill->amount_max = round((float)$bill->amount_max, $currency->decimal_places);
|
||||
$bill->amount_min = round((float) $bill->amount_min, $currency->decimal_places);
|
||||
$bill->amount_max = round((float) $bill->amount_max, $currency->decimal_places);
|
||||
$rules = $this->repository->getRulesForBill($bill);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
|
||||
@ -98,9 +98,11 @@ class EditController extends Controller
|
||||
$hasOldInput = null !== $request->old('_token');
|
||||
|
||||
$preFilled = [
|
||||
'bill_end_date' => $bill->end_date,
|
||||
'extension_date' => $bill->extension_date,
|
||||
'notes' => $this->repository->getNoteText($bill),
|
||||
'transaction_currency_id' => $bill->transaction_currency_id,
|
||||
'active' => $hasOldInput ? (bool)$request->old('active') : $bill->active,
|
||||
'active' => $hasOldInput ? (bool) $request->old('active') : $bill->active,
|
||||
'object_group' => $bill->objectGroups->first() ? $bill->objectGroups->first()->title : '',
|
||||
];
|
||||
|
||||
@ -123,7 +125,7 @@ class EditController extends Controller
|
||||
$billData = $request->getBillData();
|
||||
$bill = $this->repository->update($bill, $billData);
|
||||
|
||||
$request->session()->flash('success', (string)trans('firefly.updated_bill', ['name' => $bill->name]));
|
||||
$request->session()->flash('success', (string) trans('firefly.updated_bill', ['name' => $bill->name]));
|
||||
app('preferences')->mark();
|
||||
|
||||
/** @var array $files */
|
||||
@ -132,7 +134,7 @@ class EditController extends Controller
|
||||
$this->attachments->saveAttachmentsForModel($bill, $files);
|
||||
}
|
||||
if (null !== $files && auth()->user()->hasRole('demo')) {
|
||||
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
|
||||
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
|
||||
}
|
||||
|
||||
// flash messages
|
||||
@ -141,7 +143,7 @@ class EditController extends Controller
|
||||
}
|
||||
$redirect = redirect($this->getPreviousUri('bills.edit.uri'));
|
||||
|
||||
if (1 === (int)$request->get('return_to_edit')) {
|
||||
if (1 === (int) $request->get('return_to_edit')) {
|
||||
|
||||
$request->session()->put('bills.edit.fromUpdate', true);
|
||||
|
||||
|
@ -136,8 +136,9 @@ class IndexController extends Controller
|
||||
// summarise per currency / per group.
|
||||
$sums = $this->getSums($bills);
|
||||
$totals = $this->getTotals($sums);
|
||||
$today = now()->startOfDay();
|
||||
|
||||
return view('bills.index', compact('bills', 'sums', 'total', 'totals'));
|
||||
return view('bills.index', compact('bills', 'sums', 'total', 'totals','today'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,6 +47,8 @@ class BillStoreRequest extends FormRequest
|
||||
'currency_code' => '',
|
||||
'amount_max' => $this->string('amount_max'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'end_date' => $this->getCarbonDate('bill_end_date'),
|
||||
'extension_date' => $this->getCarbonDate('extension_date'),
|
||||
'repeat_freq' => $this->string('repeat_freq'),
|
||||
'skip' => $this->integer('skip'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
@ -68,6 +70,8 @@ class BillStoreRequest extends FormRequest
|
||||
'amount_max' => 'required|numeric|gt:0|max:1000000000',
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'bill_end_date' => 'nullable|date',
|
||||
'extension_date' => 'nullable|date',
|
||||
'repeat_freq' => sprintf('required|in:%s', join(',', config('firefly.bill_periods'))),
|
||||
'skip' => 'required|integer|gte:0|lte:31',
|
||||
'active' => 'boolean',
|
||||
|
@ -48,6 +48,8 @@ class BillUpdateRequest extends FormRequest
|
||||
'currency_code' => '',
|
||||
'amount_max' => $this->string('amount_max'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'end_date' => $this->getCarbonDate('bill_end_date'),
|
||||
'extension_date' => $this->getCarbonDate('extension_date'),
|
||||
'repeat_freq' => $this->string('repeat_freq'),
|
||||
'skip' => $this->integer('skip'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
@ -72,6 +74,8 @@ class BillUpdateRequest extends FormRequest
|
||||
'amount_max' => 'required|numeric|gt:0|max:1000000000',
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'bill_end_date' => 'nullable|date',
|
||||
'extension_date' => 'nullable|date',
|
||||
'repeat_freq' => sprintf('required|in:%s', join(',', config('firefly.bill_periods'))),
|
||||
'skip' => 'required|integer|gte:0|lte:31',
|
||||
'active' => 'boolean',
|
||||
|
@ -130,7 +130,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
||||
// find budget limit:
|
||||
$budgetLimit = $this->findBudgetLimit($autoBudget->budget, $start, $end);
|
||||
|
||||
if (null === $budgetLimit && AutoBudget::AUTO_BUDGET_RESET === (int)$autoBudget->auto_budget_type) {
|
||||
if (null === $budgetLimit && AutoBudget::AUTO_BUDGET_RESET === (int) $autoBudget->auto_budget_type) {
|
||||
// that's easy: create one.
|
||||
// do nothing else.
|
||||
$this->createBudgetLimit($autoBudget, $start, $end);
|
||||
@ -139,7 +139,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $budgetLimit && AutoBudget::AUTO_BUDGET_ROLLOVER === (int)$autoBudget->auto_budget_type) {
|
||||
if (null === $budgetLimit && AutoBudget::AUTO_BUDGET_ROLLOVER === (int) $autoBudget->auto_budget_type) {
|
||||
// budget limit exists already,
|
||||
$this->createRollover($autoBudget);
|
||||
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||
@ -277,7 +277,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
||||
$repository = app(OperationsRepositoryInterface::class);
|
||||
$repository->setUser($autoBudget->budget->user);
|
||||
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
|
||||
$currencyId = (int)$autoBudget->transaction_currency_id;
|
||||
$currencyId = (int) $autoBudget->transaction_currency_id;
|
||||
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
||||
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
||||
|
||||
|
@ -430,7 +430,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
'type' => strtolower($recurrence->transactionType->type),
|
||||
'date' => $date,
|
||||
'user' => $recurrence->user_id,
|
||||
'currency_id' => (int)$transaction->transaction_currency_id,
|
||||
'currency_id' => (int) $transaction->transaction_currency_id,
|
||||
'currency_code' => null,
|
||||
'description' => $transactions->first()->description,
|
||||
'amount' => $transaction->amount,
|
||||
@ -447,9 +447,9 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
'foreign_amount' => $transaction->foreign_amount,
|
||||
'reconciled' => false,
|
||||
'identifier' => $index,
|
||||
'recurrence_id' => (int)$recurrence->id,
|
||||
'recurrence_id' => (int) $recurrence->id,
|
||||
'order' => $index,
|
||||
'notes' => (string)trans('firefly.created_from_recurrence', ['id' => $recurrence->id, 'title' => $recurrence->title]),
|
||||
'notes' => (string) trans('firefly.created_from_recurrence', ['id' => $recurrence->id, 'title' => $recurrence->title]),
|
||||
'tags' => $this->repository->getTags($transaction),
|
||||
'piggy_bank_id' => $this->repository->getPiggyBank($transaction),
|
||||
'piggy_bank_name' => null,
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Jobs;
|
||||
|
||||
use Log;
|
||||
use FireflyIII\Models\WebhookMessage;
|
||||
use FireflyIII\Services\Webhook\WebhookSenderInterface;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@ -32,6 +31,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class SendWebhookMessage
|
||||
|
153
app/Jobs/WarnAboutBills.php
Normal file
153
app/Jobs/WarnAboutBills.php
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\RequestedReportOnJournals;
|
||||
use FireflyIII\Events\WarnUserAboutBill;
|
||||
use FireflyIII\Models\Bill;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class WarnAboutBills
|
||||
*/
|
||||
class WarnAboutBills implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private Carbon $date;
|
||||
private bool $force;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Carbon|null $date
|
||||
*/
|
||||
public function __construct(?Carbon $date)
|
||||
{
|
||||
if (null !== $date) {
|
||||
$newDate = clone $date;
|
||||
$newDate->startOfDay();
|
||||
$this->date = $newDate;
|
||||
}
|
||||
if (null === $date) {
|
||||
$newDate = new Carbon;
|
||||
$newDate->startOfDay();
|
||||
$this->date = $newDate;
|
||||
}
|
||||
$this->force = false;
|
||||
|
||||
Log::debug(sprintf('Created new WarnAboutBills("%s")', $this->date->format('Y-m-d')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y')));
|
||||
$bills = Bill::all();
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
Log::debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name));
|
||||
if ($this->hasDateFields($bill)) {
|
||||
if ($this->needsWarning($bill, 'end_date')) {
|
||||
$this->sendWarning($bill, 'end_date');
|
||||
}
|
||||
if ($this->needsWarning($bill, 'extension_date')) {
|
||||
$this->sendWarning($bill, 'extension_date');
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::debug('Done with handle()');
|
||||
|
||||
// clear cache:
|
||||
app('preferences')->mark();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @return bool
|
||||
*/
|
||||
private function hasDateFields(Bill $bill): bool
|
||||
{
|
||||
if (false === $bill->active) {
|
||||
Log::debug('Bill is not active.');
|
||||
return false;
|
||||
}
|
||||
if (null === $bill->end_date && null === $bill->extension_date) {
|
||||
Log::debug('Bill has no date fields.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param string $field
|
||||
* @return bool
|
||||
*/
|
||||
private function needsWarning(Bill $bill, string $field): bool
|
||||
{
|
||||
if (null === $bill->$field) {
|
||||
return false;
|
||||
}
|
||||
$diff = $this->getDiff($bill, $field);
|
||||
$list = config('firefly.bill_reminder_periods');
|
||||
Log::debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->$field->format('Y-m-d'), $diff));
|
||||
if (in_array($diff, $list, true)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param string $field
|
||||
* @return int
|
||||
*/
|
||||
private function getDiff(Bill $bill, string $field): int
|
||||
{
|
||||
$today = clone $this->date;
|
||||
$carbon = clone $bill->$field;
|
||||
return $today->diffInDays($carbon, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param string $field
|
||||
* @return void
|
||||
*/
|
||||
private function sendWarning(Bill $bill, string $field): void
|
||||
{
|
||||
$diff = $this->getDiff($bill, $field);
|
||||
Log::debug('Will now send warning!');
|
||||
event(new WarnUserAboutBill($bill, $field, $diff));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*/
|
||||
public function setDate(Carbon $date): void
|
||||
{
|
||||
$newDate = clone $date;
|
||||
$newDate->startOfDay();
|
||||
$this->date = $newDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
*/
|
||||
public function setForce(bool $force): void
|
||||
{
|
||||
$this->force = $force;
|
||||
}
|
||||
}
|
@ -37,10 +37,8 @@ class AdminTestMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/** @var string Email address of admin */
|
||||
public $email;
|
||||
/** @var string IP address of admin */
|
||||
public $ipAddress;
|
||||
public string $email;
|
||||
public string $ipAddress;
|
||||
|
||||
/**
|
||||
* ConfirmEmailChangeMail constructor.
|
||||
|
52
app/Mail/BillWarningMail.php
Normal file
52
app/Mail/BillWarningMail.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Mail;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class BillWarningMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public Bill $bill;
|
||||
public string $field;
|
||||
public int $diff;
|
||||
public string $ipAddress;
|
||||
|
||||
/**
|
||||
* ConfirmEmailChangeMail constructor.
|
||||
*
|
||||
* @param Bill $bill
|
||||
* @param string $field
|
||||
* @param int $diff
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(Bill $bill, string $field, int $diff, string $ipAddress)
|
||||
{
|
||||
$this->bill = $bill;
|
||||
$this->field = $field;
|
||||
$this->diff = $diff;
|
||||
$this->ipAddress = $ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build(): self
|
||||
{
|
||||
$subject = (string) trans(sprintf('email.bill_warning_subject_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]);
|
||||
if (0 === $this->diff) {
|
||||
$subject = (string) trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]);
|
||||
}
|
||||
|
||||
return $this
|
||||
->view('emails.bill-warning-html')
|
||||
->text('emails.bill-warning-text')
|
||||
->subject($subject);
|
||||
}
|
||||
}
|
@ -37,14 +37,10 @@ class ConfirmEmailChangeMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/** @var string IP address of user */
|
||||
public $ipAddress;
|
||||
/** @var string New email address */
|
||||
public $newEmail;
|
||||
/** @var string Old email address */
|
||||
public $oldEmail;
|
||||
/** @var string Confirmation link */
|
||||
public $uri;
|
||||
public string $ipAddress;
|
||||
public string $newEmail;
|
||||
public string $oldEmail;
|
||||
public string $uri;
|
||||
|
||||
/**
|
||||
* ConfirmEmailChangeMail constructor.
|
||||
@ -70,6 +66,6 @@ class ConfirmEmailChangeMail extends Mailable
|
||||
public function build(): self
|
||||
{
|
||||
return $this->view('emails.confirm-email-change-html')->text('emails.confirm-email-change-text')
|
||||
->subject((string)trans('email.email_change_subject'));
|
||||
->subject((string) trans('email.email_change_subject'));
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Events\UpdatedAccount;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Events\WarnUserAboutBill;
|
||||
use FireflyIII\Mail\OAuthTokenCreatedMail;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankRepetition;
|
||||
@ -134,6 +135,11 @@ class EventServiceProvider extends ServiceProvider
|
||||
UpdatedAccount::class => [
|
||||
'FireflyIII\Handlers\Events\UpdatedAccountEventHandler@recalculateCredit',
|
||||
],
|
||||
|
||||
// bill related events:
|
||||
WarnUserAboutBill::class => [
|
||||
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -166,6 +166,12 @@ class BillUpdateService
|
||||
if (array_key_exists('active', $data)) {
|
||||
$bill->active = $data['active'];
|
||||
}
|
||||
if(array_key_exists('end_date', $data)) {
|
||||
$bill->end_date = $data['end_date'];
|
||||
}
|
||||
if(array_key_exists('extension_date', $data)) {
|
||||
$bill->extension_date = $data['extension_date'];
|
||||
}
|
||||
|
||||
$bill->match = 'EMPTY';
|
||||
$bill->automatch = true;
|
||||
|
102
app/Support/Cronjobs/BillWarningCronjob.php
Normal file
102
app/Support/Cronjobs/BillWarningCronjob.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* RecurringCronjob.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Cronjobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Jobs\WarnAboutBills;
|
||||
use FireflyIII\Models\Configuration;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class BillWarningCronjob
|
||||
*/
|
||||
class BillWarningCronjob extends AbstractCronjob
|
||||
{
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function fire(): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
/** @var Configuration $config */
|
||||
$config = app('fireflyconfig')->get('last_bw_job', 0);
|
||||
$lastTime = (int)$config->data;
|
||||
$diff = time() - $lastTime;
|
||||
$diffForHumans = Carbon::now()->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
||||
|
||||
if (0 === $lastTime) {
|
||||
Log::info('The bill warning cron-job has never fired before.');
|
||||
}
|
||||
// less than half a day ago:
|
||||
if ($lastTime > 0 && $diff <= 43200) {
|
||||
Log::info(sprintf('It has been %s since the bill warning cron-job has fired.', $diffForHumans));
|
||||
if (false === $this->force) {
|
||||
Log::info('The cron-job will not fire now.');
|
||||
$this->message = sprintf('It has been %s since the bill warning cron-job has fired. It will not fire now.', $diffForHumans);
|
||||
$this->jobFired = false;
|
||||
$this->jobErrored = false;
|
||||
$this->jobSucceeded = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// fire job regardless.
|
||||
if (true === $this->force) {
|
||||
Log::info('Execution of the bill warning cron-job has been FORCED.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($lastTime > 0 && $diff > 43200) {
|
||||
Log::info(sprintf('It has been %s since the bill warning cron-job has fired. It will fire now!', $diffForHumans));
|
||||
}
|
||||
|
||||
$this->fireWarnings();
|
||||
|
||||
app('preferences')->mark();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function fireWarnings(): void
|
||||
{
|
||||
Log::info(sprintf('Will now fire bill warning job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
|
||||
/** @var WarnAboutBills $job */
|
||||
$job = app(WarnAboutBills::class);
|
||||
$job->setDate($this->date);
|
||||
$job->setForce($this->force);
|
||||
$job->handle();
|
||||
|
||||
// get stuff from job:
|
||||
$this->jobFired = true;
|
||||
$this->jobErrored = false;
|
||||
$this->jobSucceeded = true;
|
||||
$this->message = 'Bill warning cron job fired successfully.';
|
||||
|
||||
app('fireflyconfig')->set('last_bw_job', (int)$this->date->format('U'));
|
||||
Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U')));
|
||||
Log::info('Done with bill warning cron job task.');
|
||||
}
|
||||
}
|
@ -68,6 +68,7 @@ class General extends AbstractExtension
|
||||
$this->getMetaField(),
|
||||
$this->hasRole(),
|
||||
$this->getRootSearchOperator(),
|
||||
$this->carbonize()
|
||||
];
|
||||
}
|
||||
|
||||
@ -395,6 +396,19 @@ class General extends AbstractExtension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFunction
|
||||
*/
|
||||
protected function carbonize(): TwigFunction
|
||||
{
|
||||
return new TwigFunction(
|
||||
'carbonize',
|
||||
static function (string $date): Carbon {
|
||||
return new Carbon($date);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return true if the user is of role X.
|
||||
*
|
||||
|
@ -221,6 +221,7 @@ return [
|
||||
TransactionJournal::class,
|
||||
Recurrence::class,
|
||||
],
|
||||
'bill_reminder_periods' => [90, 30, 14, 7, 0],
|
||||
'valid_view_ranges' => ['1D', '1W', '1M', '3M', '6M', '1Y',],
|
||||
'allowedMimes' => [
|
||||
/* plain files */
|
||||
@ -708,7 +709,7 @@ return [
|
||||
'import_hash', 'import_hash_v2', 'external_id', 'original_source',
|
||||
|
||||
// recurring transactions
|
||||
'recurrence_total', 'recurrence_count'
|
||||
'recurrence_total', 'recurrence_count',
|
||||
],
|
||||
'webhooks' => [
|
||||
'max_attempts' => env('WEBHOOK_MAX_ATTEMPTS', 3),
|
||||
|
@ -102,4 +102,19 @@ return [
|
||||
// report new journals
|
||||
'new_journals_subject' => 'Firefly III has created a new transaction|Firefly III has created :count new transactions',
|
||||
'new_journals_header' => 'Firefly III has created a transaction for you. You can find it in your Firefly III installation:|Firefly III has created :count transactions for you. You can find them in your Firefly III installation:',
|
||||
|
||||
// bill warning
|
||||
'bill_warning_subject_end_date' => 'Your bill ":name" is due to end in :diff days',
|
||||
'bill_warning_subject_now_end_date' => 'Your bill ":name" is due to end TODAY',
|
||||
'bill_warning_subject_extension_date' => 'Your bill ":name" is due to be extended or cancelled in :diff days',
|
||||
'bill_warning_subject_now_extension_date' => 'Your bill ":name" is due to be extended or cancelled TODAY',
|
||||
'bill_warning_end_date_text' => 'Your bill ":name" is due to end on :date. This moment will pass in about :diff days.',
|
||||
'bill_warning_extension_date_text' => 'Your bill ":name" is due to be extended or cancelled on :date. This moment will pass in about :diff days.',
|
||||
'bill_warning_end_date_text_zero' => 'Your bill ":name" is due to end on :date. This moment will pass TODAY!',
|
||||
'bill_warning_extension_date_text_zero' => 'Your bill ":name" is due to be extended or cancelled on :date. This moment will pass TODAY!',
|
||||
'bill_warning_please_action' => 'Please take the appropriate action.',
|
||||
'bill_warning_end_date_html' => 'Your bill <strong>":name"</strong> is due to end on :date. This moment will pass in about <strong>:diff days</strong>.',
|
||||
'bill_warning_extension_date_html' => 'Your bill <strong>":name"</strong> is due to be extended or cancelled on :date. This moment will pass in about <strong>:diff days</strong>.',
|
||||
'bill_warning_end_date_html_zero' => 'Your bill <strong>":name"</strong> is due to end on :date. This moment will pass <strong>TODAY!</strong>',
|
||||
'bill_warning_extension_date_html_zero' => 'Your bill <strong>":name"</strong> is due to be extended or cancelled on :date. This moment will pass <strong>TODAY!</strong>',
|
||||
];
|
||||
|
@ -1309,6 +1309,10 @@ return [
|
||||
'running_again_loss' => 'Previously linked transactions to this bill may lose their connection, if they (no longer) match the rule(s).',
|
||||
'bill_related_rules' => 'Rules related to this bill',
|
||||
'repeats' => 'Repeats',
|
||||
'bill_end_date_help' => 'Optional field. The bill is expected to end on this date.',
|
||||
'bill_extension_date_help' => 'Optional field. The bill must be extended (or cancelled) on or before this date.',
|
||||
'bill_end_index_line' => 'This bill ends on :date',
|
||||
'bill_extension_index_line' => 'This bill must be extended or cancelled on :date',
|
||||
'connected_journals' => 'Connected transactions',
|
||||
'auto_match_on' => 'Automatically matched by Firefly III',
|
||||
'auto_match_off' => 'Not automatically matched by Firefly III',
|
||||
|
@ -181,6 +181,7 @@ return [
|
||||
'login_name' => 'Login',
|
||||
'is_owner' => 'Is admin?',
|
||||
'url' => 'URL',
|
||||
'bill_end_date' => 'End date',
|
||||
|
||||
// import
|
||||
'apply_rules' => 'Apply rules',
|
||||
|
@ -22,6 +22,7 @@
|
||||
{{ ExpandedForm.amountNoCurrency('amount_max') }}
|
||||
{{ ExpandedForm.date('date',phpdate('Y-m-d')) }}
|
||||
{{ ExpandedForm.select('repeat_freq',periods,'monthly') }}
|
||||
{{ ExpandedForm.integer('skip',0) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -32,9 +33,12 @@
|
||||
<h3 class="box-title">{{ 'optionalFields'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
{{ ExpandedForm.date('bill_end_date',null, {'helpText': trans('firefly.bill_end_date_help')}) }}
|
||||
{{ ExpandedForm.date('extension_date',null,{'helpText': trans('firefly.bill_extension_date_help')} ) }}
|
||||
|
||||
|
||||
{{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }}
|
||||
{{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }}
|
||||
{{ ExpandedForm.integer('skip',0) }}
|
||||
{{ ExpandedForm.objectGroup() }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,6 +27,8 @@
|
||||
{{ ExpandedForm.amountNoCurrency('amount_max') }}
|
||||
{{ ExpandedForm.date('date',bill.date.format('Y-m-d')) }}
|
||||
{{ ExpandedForm.select('repeat_freq',periods) }}
|
||||
{{ ExpandedForm.integer('skip') }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -38,9 +40,11 @@
|
||||
<h3 class="box-title">{{ 'optionalFields'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
{{ ExpandedForm.date('bill_end_date',null, {'helpText': trans('firefly.bill_end_date_help')}) }}
|
||||
{{ ExpandedForm.date('extension_date',null,{'helpText': trans('firefly.bill_extension_date_help')} ) }}
|
||||
|
||||
{{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }}
|
||||
{{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }}
|
||||
{{ ExpandedForm.integer('skip') }}
|
||||
{{ ExpandedForm.objectGroup() }}
|
||||
{# only correct way to do active checkbox #}
|
||||
{{ ExpandedForm.checkbox('active', 1) }}
|
||||
|
25
resources/views/emails/bill-warning-html.twig
Normal file
25
resources/views/emails/bill-warning-html.twig
Normal file
@ -0,0 +1,25 @@
|
||||
{% include 'emails.header-html' %}
|
||||
|
||||
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
|
||||
|
||||
{% if field == 'end_date' and diff != 0 %}
|
||||
{{ trans('email.bill_warning_end_date_html', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
|
||||
{% endif %}
|
||||
{% if field == 'extension_date' and diff != 0 %}
|
||||
{{ trans('email.bill_warning_extension_date_html', {name: bill.name|escape, date: bill.extension_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
|
||||
{% endif %}
|
||||
{% if field == 'end_date' and diff == 0 %}
|
||||
{{ trans('email.bill_warning_end_date_html_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
|
||||
{% endif %}
|
||||
{% if field == 'extension_date' and diff == 0 %}
|
||||
{{ trans('email.bill_warning_extension_date_html_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
|
||||
{% endif %}
|
||||
|
||||
</p>
|
||||
|
||||
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
|
||||
Please take the appropriate action.
|
||||
</p>
|
||||
|
||||
|
||||
{% include 'emails.footer-html' %}
|
17
resources/views/emails/bill-warning-text.twig
Normal file
17
resources/views/emails/bill-warning-text.twig
Normal file
@ -0,0 +1,17 @@
|
||||
{% include 'emails.header-text' %}
|
||||
{% if field == 'end_date' and diff != 0 %}
|
||||
{{ trans('email.bill_warning_end_date_text', {name: bill.name, date: bill.end_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
|
||||
{% endif %}
|
||||
{% if field == 'extension_date' and diff != 0 %}
|
||||
{{ trans('email.bill_warning_extension_date_text', {name: bill.name|escape, date: bill.extension_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
|
||||
{% endif %}
|
||||
{% if field == 'end_date' and diff == 0 %}
|
||||
{{ trans('email.bill_warning_end_date_text_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
|
||||
{% endif %}
|
||||
{% if field == 'extension_date' and diff == 0 %}
|
||||
{{ trans('email.bill_warning_extension_date_text_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
|
||||
{% endif %}
|
||||
|
||||
{{ trans('email.bill_warning_please_action') }}
|
||||
|
||||
{% include 'emails.footer-text' %}
|
@ -16,20 +16,25 @@
|
||||
</thead>
|
||||
{% for objectGroupOrder, objectGroup in bills %}
|
||||
{% if objectGroup.bills|length > 0 %}
|
||||
<tbody class="bill-connected-list" {% if objectGroupOrder != 0 %}data-title="{{ objectGroup.object_group_title }}" {% else %}data-title=""{% endif %}>
|
||||
<tbody class="bill-connected-list"
|
||||
{% if objectGroupOrder != 0 %}data-title="{{ objectGroup.object_group_title }}"
|
||||
{% else %}data-title=""{% endif %}>
|
||||
<tr>
|
||||
<td class="hidden-sm hidden-xs"> </td>
|
||||
<td class="hidden-sm hidden-xs"> </td>
|
||||
<td colspan="6"><small>{{ objectGroup.object_group_title }}</small></td>
|
||||
</tr>
|
||||
{% for entry in objectGroup.bills %}
|
||||
<tr class="bill-sortable" data-id="{{ entry.id }}" data-name="{{ entry.name }}" data-order="{{ entry.order }}" data-position="{{ loop.index0 }}">
|
||||
<tr class="bill-sortable" data-id="{{ entry.id }}" data-name="{{ entry.name }}"
|
||||
data-order="{{ entry.order }}" data-position="{{ loop.index0 }}">
|
||||
<td class="hidden-sm hidden-xs">
|
||||
<span class="fa fa-fw fa-bars bill-handle"></span>
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs">
|
||||
<div class="btn-group btn-group-xs edit_tr_buttons"><a href="{{ route('bills.edit',entry.id) }}" class="btn btn-default btn-xs"><span
|
||||
class="fa fa-fw fa-pencil"></span></a><a href="{{ route('bills.delete',entry.id) }}" class="btn btn-danger btn-xs"><span
|
||||
<div class="btn-group btn-group-xs edit_tr_buttons"><a href="{{ route('bills.edit',entry.id) }}"
|
||||
class="btn btn-default btn-xs"><span
|
||||
class="fa fa-fw fa-pencil"></span></a><a
|
||||
href="{{ route('bills.delete',entry.id) }}" class="btn btn-danger btn-xs"><span
|
||||
class="fa fa-fw fa-trash-o"></span></a></div>
|
||||
</td>
|
||||
<td>
|
||||
@ -126,6 +131,26 @@
|
||||
{% if entry.skip > 0 %}
|
||||
{{ 'skips_over'|_ }} {{ entry.skip }}
|
||||
{% endif %}
|
||||
{% if entry.end_date %}
|
||||
<br>
|
||||
{% if carbonize(entry.end_date).lte(today) %}
|
||||
<span class="text-danger">
|
||||
{{ trans('firefly.bill_end_index_line', {date: carbonize(entry.end_date).isoFormat(monthAndDayFormat) }) }}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ trans('firefly.bill_end_index_line', {date: carbonize(entry.end_date).isoFormat(monthAndDayFormat) }) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if entry.extension_date %}
|
||||
<br>
|
||||
{% if carbonize(entry.extension_date).lte(today) %}
|
||||
<span class="text-danger">
|
||||
{{ trans('firefly.bill_extension_index_line', {date: carbonize(entry.extension_date).isoFormat(monthAndDayFormat) }) }}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ trans('firefly.bill_extension_index_line', {date: carbonize(entry.extension_date).isoFormat(monthAndDayFormat) }) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -150,7 +175,8 @@
|
||||
<td class="hidden-sm hidden-xs"> </td> <!-- handle -->
|
||||
<td class="hidden-sm hidden-xs"> </td> <!-- buttons -->
|
||||
<td colspan="2" style="text-align: right;"> <!-- title -->
|
||||
<small>{{ ('per_period_sum_'~sum.period)|_ }} ({{ sum.currency_name }}) ({{ 'active_bills_only'|_ }})</small>
|
||||
<small>{{ ('per_period_sum_'~sum.period)|_ }} ({{ sum.currency_name }})
|
||||
({{ 'active_bills_only'|_ }})</small>
|
||||
</td>
|
||||
<td style="text-align: right;"> <!-- amount -->
|
||||
{{ formatAmountBySymbol(sum.per_period, sum.currency_symbol, sum.currency_decimal_places) }}
|
||||
@ -190,7 +216,8 @@
|
||||
<td class="hidden-sm hidden-xs"> </td> <!-- handle -->
|
||||
<td class="hidden-sm hidden-xs"> </td> <!-- buttons -->
|
||||
<td colspan="2" style="text-align: right;"> <!-- title -->
|
||||
<small>{{ ('per_period_sum_'~sum.period)|_ }} ({{ sum.currency_name }}) ({{ 'active_bills_only_total'|_ }})</small>
|
||||
<small>{{ ('per_period_sum_'~sum.period)|_ }} ({{ sum.currency_name }})
|
||||
({{ 'active_bills_only_total'|_ }})</small>
|
||||
</td>
|
||||
<td style="text-align: right;"> <!-- amount -->
|
||||
{{ formatAmountBySymbol(sum.per_period, sum.currency_symbol, sum.currency_decimal_places) }}
|
||||
|
Loading…
Reference in New Issue
Block a user