mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Automatic late fees #1505
This commit is contained in:
parent
f5f161865a
commit
2bef0bdb66
@ -63,8 +63,30 @@ class SendReminders extends Command
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
$accounts = $this->accountRepo->findWithFees();
|
||||
$this->info(count($accounts) . ' accounts found with fees');
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoices = $this->invoiceRepo->findNeedingReminding($account, false);
|
||||
$this->info($account->name . ': ' . count($invoices) . ' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($reminder = $account->getInvoiceReminder($invoice, false)) {
|
||||
$this->info('Charge fee: ' . $invoice->id);
|
||||
$number = preg_replace('/[^0-9]/', '', $reminder);
|
||||
$amount = $account->account_email_settings->{"late_fee{$number}_amount"};
|
||||
$percent = $account->account_email_settings->{"late_fee{$number}_percent"};
|
||||
$this->invoiceRepo->setLateFee($invoice, $amount, $percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$accounts = $this->accountRepo->findWithReminders();
|
||||
$this->info(count($accounts) . ' accounts found');
|
||||
$this->info(count($accounts) . ' accounts found with reminders');
|
||||
|
||||
/** @var \App\Models\Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
@ -78,7 +100,7 @@ class SendReminders extends Command
|
||||
/** @var Invoice $invoice */
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($reminder = $account->getInvoiceReminder($invoice)) {
|
||||
$this->info('Send to ' . $invoice->id);
|
||||
$this->info('Send email: ' . $invoice->id);
|
||||
$this->mailer->sendInvoice($invoice, $reminder);
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ if (! defined('APP_NAME')) {
|
||||
define('INVOICE_ITEM_TYPE_TASK', 2);
|
||||
define('INVOICE_ITEM_TYPE_PENDING_GATEWAY_FEE', 3);
|
||||
define('INVOICE_ITEM_TYPE_PAID_GATEWAY_FEE', 4);
|
||||
define('INVOICE_ITEM_TYPE_LATE_FEE', 5);
|
||||
|
||||
define('PERSON_CONTACT', 'contact');
|
||||
define('PERSON_USER', 'user');
|
||||
|
@ -823,6 +823,10 @@ class AccountController extends BaseController
|
||||
$account->{"num_days_{$type}"} = Input::get("num_days_{$type}");
|
||||
$account->{"field_{$type}"} = Input::get("field_{$type}");
|
||||
$account->{"direction_{$type}"} = Input::get("field_{$type}") == REMINDER_FIELD_INVOICE_DATE ? REMINDER_DIRECTION_AFTER : Input::get("direction_{$type}");
|
||||
|
||||
$number = preg_replace('/[^0-9]/', '', $type);
|
||||
$account->account_email_settings->{"late_fee{$number}_amount"} = Input::get("late_fee{$number}_amount");
|
||||
$account->account_email_settings->{"late_fee{$number}_percent"} = Input::get("late_fee{$number}_percent");
|
||||
}
|
||||
|
||||
$account->save();
|
||||
|
@ -27,6 +27,12 @@ class AccountEmailSettings extends Eloquent
|
||||
'email_template_reminder1',
|
||||
'email_template_reminder2',
|
||||
'email_template_reminder3',
|
||||
'late_fee1_amount',
|
||||
'late_fee1_percent',
|
||||
'late_fee2_amount',
|
||||
'late_fee2_percent',
|
||||
'late_fee3_amount',
|
||||
'late_fee3_percent',
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -125,9 +125,9 @@ trait SendsEmails
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getReminderDate($reminder)
|
||||
public function getReminderDate($reminder, $filterEnabled = true)
|
||||
{
|
||||
if (! $this->{"enable_reminder{$reminder}"}) {
|
||||
if ($filterEnabled && ! $this->{"enable_reminder{$reminder}"}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -142,10 +142,10 @@ trait SendsEmails
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getInvoiceReminder($invoice)
|
||||
public function getInvoiceReminder($invoice, $filterEnabled = true)
|
||||
{
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
if ($date = $this->getReminderDate($i)) {
|
||||
if ($date = $this->getReminderDate($i, $filterEnabled)) {
|
||||
$field = $this->{"field_reminder{$i}"} == REMINDER_FIELD_DUE_DATE ? 'due_date' : 'invoice_date';
|
||||
if ($invoice->$field == $date) {
|
||||
return "reminder{$i}";
|
||||
|
@ -656,6 +656,18 @@ class AccountRepository
|
||||
return Account::whereRaw('enable_reminder1 = 1 OR enable_reminder2 = 1 OR enable_reminder3 = 1')->get();
|
||||
}
|
||||
|
||||
public function findWithFees()
|
||||
{
|
||||
return Account::whereHas('account_email_settings', function($query) {
|
||||
$query->where('late_fee1_amount', '>', 0)
|
||||
->orWhere('late_fee1_percent', '>', 0)
|
||||
->orWhere('late_fee2_amount', '>', 0)
|
||||
->orWhere('late_fee2_percent', '>', 0)
|
||||
->orWhere('late_fee3_amount', '>', 0)
|
||||
->orWhere('late_fee3_percent', '>', 0);
|
||||
})->get();
|
||||
}
|
||||
|
||||
public function createTokens($user, $name)
|
||||
{
|
||||
$name = trim($name) ?: 'TOKEN';
|
||||
|
@ -1091,19 +1091,24 @@ class InvoiceRepository extends BaseRepository
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function findNeedingReminding(Account $account)
|
||||
public function findNeedingReminding(Account $account, $filterEnabled = true)
|
||||
{
|
||||
$dates = [];
|
||||
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
if ($date = $account->getReminderDate($i)) {
|
||||
if ($date = $account->getReminderDate($i, $filterEnabled)) {
|
||||
$field = $account->{"field_reminder{$i}"} == REMINDER_FIELD_DUE_DATE ? 'due_date' : 'invoice_date';
|
||||
$dates[] = "$field = '$date'";
|
||||
}
|
||||
}
|
||||
|
||||
if (! count($dates)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql = implode(' OR ', $dates);
|
||||
$invoices = Invoice::invoiceType(INVOICE_TYPE_STANDARD)
|
||||
->with('invoice_items')
|
||||
->whereAccountId($account->id)
|
||||
->where('balance', '>', 0)
|
||||
->where('is_recurring', '=', false)
|
||||
@ -1132,6 +1137,32 @@ class InvoiceRepository extends BaseRepository
|
||||
}
|
||||
}
|
||||
|
||||
public function setLateFee($invoice, $amount, $percent)
|
||||
{
|
||||
if ($amount <= 0 && $percent <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$account = $invoice->account;
|
||||
|
||||
$data = $invoice->toArray();
|
||||
$fee = $amount;
|
||||
|
||||
if ($invoice->amount > 0) {
|
||||
$fee += round($invoice->amount * $percent / 100, 2);
|
||||
}
|
||||
|
||||
$item = [];
|
||||
$item['product_key'] = trans('texts.fee');
|
||||
$item['notes'] = trans('texts.late_fee_added', ['date' => $account->formatDate('now')]);
|
||||
$item['qty'] = 1;
|
||||
$item['cost'] = $fee;
|
||||
$item['invoice_item_type_id'] = INVOICE_ITEM_TYPE_LATE_FEE;
|
||||
$data['invoice_items'][] = $item;
|
||||
|
||||
$this->save($data, $invoice);
|
||||
}
|
||||
|
||||
public function setGatewayFee($invoice, $gatewayTypeId)
|
||||
{
|
||||
$account = $invoice->account;
|
||||
|
@ -2303,7 +2303,11 @@ $LANG = array(
|
||||
'ofx_version' => 'OFX Version',
|
||||
'gateway_help_23' => ':link to get your Stripe API keys.',
|
||||
'error_app_key_not_set' => 'Error: the APP_KEY value is not set in the .env file.',
|
||||
'error_app_key_set_to_default' => 'Error: the APP_KEY value is set to the default in the .env file.'
|
||||
'error_app_key_set_to_default' => 'Error: the APP_KEY value is set to the default in the .env file.',
|
||||
'charge_late_fee' => 'Charge Late Fee',
|
||||
'late_fee_amount' => 'Late Fee Amount',
|
||||
'late_fee_percent' => 'Late Fee Percent',
|
||||
'late_fee_added' => 'Late fee added on :date',
|
||||
|
||||
);
|
||||
|
||||
|
@ -3,36 +3,56 @@
|
||||
@if (isset($isReminder) && $isReminder)
|
||||
|
||||
{!! Former::populateField('enable_' . $field, intval($account->{'enable_' . $field})) !!}
|
||||
{!! Former::populateField('late_fee' . $number . '_amount', $account->account_email_settings->{"late_fee{$number}_amount"}) !!}
|
||||
{!! Former::populateField('late_fee' . $number . '_percent', $account->account_email_settings->{"late_fee{$number}_percent"}) !!}
|
||||
|
||||
<div class="row well" style="padding-bottom:20px">
|
||||
<div class="col-md-4" style="padding-top:10px">
|
||||
{!! Former::checkbox('enable_' . $field)
|
||||
->text(trans('texts.send_automatically'))->label('')
|
||||
->value(1) !!}
|
||||
<div class="well" style="padding-bottom:20px">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::plaintext('schedule')
|
||||
->value(
|
||||
Former::input('num_days_' . $field)
|
||||
->style('float:left;width:20%')
|
||||
->raw() .
|
||||
Former::select('direction_' . $field)
|
||||
->addOption(trans('texts.days_before'), REMINDER_DIRECTION_BEFORE)
|
||||
->addOption(trans('texts.days_after'), REMINDER_DIRECTION_AFTER)
|
||||
->style('float:left;width:40%')
|
||||
->raw() .
|
||||
'<div id="days_after_'. $field .'" style="float:left;width:40%;display:none;padding-top:8px;padding-left:16px;font-size:16px;">' . trans('texts.days_after') . '</div>' .
|
||||
Former::select('field_' . $field)
|
||||
->addOption(trans('texts.field_due_date'), REMINDER_FIELD_DUE_DATE)
|
||||
->addOption(trans('texts.field_invoice_date'), REMINDER_FIELD_INVOICE_DATE)
|
||||
->style('float:left;width:40%')
|
||||
->raw()
|
||||
) !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
|
||||
{!! Former::checkbox('enable_' . $field)
|
||||
->text('enable')
|
||||
->label('send_email')
|
||||
->value(1) !!}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{!! Former::plaintext('')
|
||||
->value(
|
||||
Former::input('num_days_' . $field)
|
||||
->addClass('enable-' . $field)
|
||||
->style('float:left;width:20%')
|
||||
->raw() .
|
||||
Former::select('direction_' . $field)
|
||||
->addOption(trans('texts.days_before'), REMINDER_DIRECTION_BEFORE)
|
||||
->addOption(trans('texts.days_after'), REMINDER_DIRECTION_AFTER)
|
||||
->addClass('enable-' . $field)
|
||||
->style('float:left;width:40%')
|
||||
->raw() .
|
||||
'<div id="days_after_'. $field .'" style="float:left;width:40%;display:none;padding-top:8px;padding-left:16px;font-size:16px;">' . trans('texts.days_after') . '</div>' .
|
||||
Former::select('field_' . $field)
|
||||
->addOption(trans('texts.field_due_date'), REMINDER_FIELD_DUE_DATE)
|
||||
->addOption(trans('texts.field_invoice_date'), REMINDER_FIELD_INVOICE_DATE)
|
||||
->addClass('enable-' . $field)
|
||||
->style('float:left;width:40%')
|
||||
->raw()
|
||||
) !!}
|
||||
<div class="row" style="padding-top:30px">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('late_fee' . $number . '_amount')
|
||||
->label('late_fee_amount')
|
||||
->type('number')
|
||||
->step('any') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('late_fee' . $number . '_percent')
|
||||
->label('late_fee_percent')
|
||||
->type('number')
|
||||
->step('any')
|
||||
->append('%') !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
@endif
|
||||
<div class="row">
|
||||
|
@ -75,9 +75,9 @@
|
||||
<li role="presentation"><a href="#reminder3" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.third_reminder') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
@include('accounts.template', ['field' => 'reminder1', 'isReminder' => true, 'active' => true])
|
||||
@include('accounts.template', ['field' => 'reminder2', 'isReminder' => true])
|
||||
@include('accounts.template', ['field' => 'reminder3', 'isReminder' => true])
|
||||
@include('accounts.template', ['field' => 'reminder1', 'number' => 1, 'isReminder' => true, 'active' => true])
|
||||
@include('accounts.template', ['field' => 'reminder2', 'number' => 2, 'isReminder' => true])
|
||||
@include('accounts.template', ['field' => 'reminder3', 'number' => 3, 'isReminder' => true])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -192,23 +192,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
for (var i=1; i<=3; i++) {
|
||||
$('#enable_reminder' + i).bind('click', {id: i}, function(event) {
|
||||
enableReminder(event.data.id)
|
||||
});
|
||||
enableReminder(i);
|
||||
}
|
||||
|
||||
$('.show-when-ready').show();
|
||||
|
||||
refreshPreview();
|
||||
});
|
||||
|
||||
function enableReminder(id) {
|
||||
var checked = $('#enable_reminder' + id).is(':checked');
|
||||
$('.enable-reminder' + id).attr('disabled', !checked)
|
||||
}
|
||||
|
||||
function setDirectionShown(field) {
|
||||
var val = $('#field_' + field).val();
|
||||
if (val == {{ REMINDER_FIELD_INVOICE_DATE }}) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user