mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 08:37:32 -05: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>
 | 
			
		||||
                <div class="col-md-8">
 | 
			
		||||
                    {!! Former::plaintext('')
 | 
			
		||||
            <div class="well" style="padding-bottom:20px">
 | 
			
		||||
                <div class="row">
 | 
			
		||||
                    <div class="col-md-6">
 | 
			
		||||
                        {!! Former::plaintext('schedule')
 | 
			
		||||
                                ->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>
 | 
			
		||||
                    <div class="col-md-6">
 | 
			
		||||
 | 
			
		||||
                        {!! Former::checkbox('enable_' . $field)
 | 
			
		||||
                                ->text('enable')
 | 
			
		||||
                                ->label('send_email')
 | 
			
		||||
                                ->value(1) !!}
 | 
			
		||||
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <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