mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Separate counter for credit invoices #1451
This commit is contained in:
parent
e217718918
commit
4d8fba0d70
@ -915,6 +915,9 @@ class AccountController extends BaseController
|
||||
$account->client_number_prefix = trim(Input::get('client_number_prefix'));
|
||||
$account->client_number_pattern = trim(Input::get('client_number_pattern'));
|
||||
$account->client_number_counter = Input::get('client_number_counter');
|
||||
$account->credit_number_counter = Input::get('credit_number_counter');
|
||||
$account->credit_number_prefix = trim(Input::get('credit_number_prefix'));
|
||||
$account->credit_number_pattern = trim(Input::get('credit_number_pattern'));
|
||||
$account->reset_counter_frequency_id = Input::get('reset_counter_frequency_id');
|
||||
$account->reset_counter_date = $account->reset_counter_frequency_id ? Utils::toSqlDate(Input::get('reset_counter_date')) : null;
|
||||
|
||||
|
@ -57,6 +57,7 @@ class PurgeAccountData extends Job
|
||||
|
||||
$account->invoice_number_counter = 1;
|
||||
$account->quote_number_counter = 1;
|
||||
$account->credit_number_counter = 1;
|
||||
$account->client_number_counter = $account->client_number_counter > 0 ? 1 : 0;
|
||||
$account->save();
|
||||
|
||||
|
@ -171,6 +171,9 @@ class Account extends Eloquent
|
||||
'custom_contact_label2',
|
||||
'domain_id',
|
||||
'analytics_key',
|
||||
'credit_number_counter',
|
||||
'credit_number_prefix',
|
||||
'credit_number_pattern',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@ use App\Events\InvoiceInvitationWasEmailed;
|
||||
use App\Events\QuoteInvitationWasEmailed;
|
||||
use App\Libraries\CurlUtils;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Traits\ChargesFees;
|
||||
use App\Models\Traits\HasRecurrence;
|
||||
use DateTime;
|
||||
@ -1405,8 +1406,13 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
}
|
||||
|
||||
Invoice::creating(function ($invoice) {
|
||||
if (! $invoice->is_recurring && $invoice->amount >= 0) {
|
||||
$invoice->account->incrementCounter($invoice);
|
||||
if (! $invoice->is_recurring) {
|
||||
$account = $invoice->account;
|
||||
if ($invoice->amount >= 0) {
|
||||
$account->incrementCounter($invoice);
|
||||
} elseif ($account->credit_number_counter > 0) {
|
||||
$account->incrementCounter(new Credit());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -67,6 +67,11 @@ trait GeneratesNumbers
|
||||
$this->client_number_counter += $counterOffset - 1;
|
||||
$this->save();
|
||||
}
|
||||
} elseif ($entity->isEntityType(ENTITY_CREDIT)) {
|
||||
if ($this->creditNumbersEnabled()) {
|
||||
$this->credit_number_counter += $counterOffset - 1;
|
||||
$this->save();
|
||||
}
|
||||
} elseif ($entity->isType(INVOICE_TYPE_QUOTE)) {
|
||||
if (! $this->share_counter) {
|
||||
$this->quote_number_counter += $counterOffset - 1;
|
||||
@ -227,6 +232,8 @@ trait GeneratesNumbers
|
||||
{
|
||||
if ($entityType == ENTITY_CLIENT) {
|
||||
return $this->client_number_counter;
|
||||
} elseif ($entityType == ENTITY_CREDIT) {
|
||||
return $this->credit_number_counter;
|
||||
} elseif ($entityType == ENTITY_QUOTE && ! $this->share_counter) {
|
||||
return $this->quote_number_counter;
|
||||
} else {
|
||||
@ -254,11 +261,17 @@ trait GeneratesNumbers
|
||||
public function incrementCounter($entity)
|
||||
{
|
||||
if ($entity->isEntityType(ENTITY_CLIENT)) {
|
||||
if ($this->client_number_counter) {
|
||||
if ($this->client_number_counter > 0) {
|
||||
$this->client_number_counter += 1;
|
||||
}
|
||||
$this->save();
|
||||
return;
|
||||
} elseif ($entity->isEntityType(ENTITY_CREDIT)) {
|
||||
if ($this->credit_number_counter > 0) {
|
||||
$this->credit_number_counter += 1;
|
||||
}
|
||||
$this->save();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->usesClientInvoiceCounter()) {
|
||||
@ -295,6 +308,11 @@ trait GeneratesNumbers
|
||||
return $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->client_number_counter > 0;
|
||||
}
|
||||
|
||||
public function creditNumbersEnabled()
|
||||
{
|
||||
return $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->credit_number_counter > 0;
|
||||
}
|
||||
|
||||
public function checkCounterReset()
|
||||
{
|
||||
if (! $this->reset_counter_frequency_id || ! $this->reset_counter_date) {
|
||||
|
@ -16,6 +16,12 @@ class UpdateDarkMode extends Migration
|
||||
$table->boolean('dark_mode')->default(true)->change();
|
||||
});
|
||||
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->integer('credit_number_counter')->default(0)->nullable();
|
||||
$table->text('credit_number_prefix')->nullable();
|
||||
$table->text('credit_number_pattern')->nullable();
|
||||
});
|
||||
|
||||
DB::statement('update users set dark_mode = 1;');
|
||||
|
||||
// update invoice_item_type_id for task invoice items
|
||||
@ -82,5 +88,11 @@ class UpdateDarkMode extends Migration
|
||||
Schema::table('expenses', function ($table) {
|
||||
$table->dropColumn('recurring_expense_id');
|
||||
});
|
||||
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->dropColumn('credit_number_counter');
|
||||
$table->dropColumn('credit_number_prefix');
|
||||
$table->dropColumn('credit_number_pattern');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -2290,6 +2290,12 @@ $LANG = array(
|
||||
'view_recurring_expense' => 'View Recurring Expense',
|
||||
'other' => 'Other',
|
||||
'import_failed' => 'Import Failed',
|
||||
'recurring_prefix' => 'Recurring Prefix',
|
||||
'options' => 'Options',
|
||||
'credit_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the credit number for negative invoices.',
|
||||
'next_credit_number' => 'The next credit number is :number.',
|
||||
'padding_help' => 'The number of zero\'s to pad the number.',
|
||||
'client_number_help'
|
||||
|
||||
);
|
||||
|
||||
|
@ -45,10 +45,10 @@
|
||||
<a href="#client_number" aria-controls="client_number" role="tab" data-toggle="tab">{{ trans('texts.client_number') }}</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#recurring_invoice_number" aria-controls="recurring_invoice_number" role="tab" data-toggle="tab">{{ trans('texts.recurring_invoice_number') }}</a>
|
||||
<a href="#credit_number" aria-controls="credit_number" role="tab" data-toggle="tab">{{ trans('texts.credit_number') }}</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#reset_counter" aria-controls="reset_counter" role="tab" data-toggle="tab">{{ trans('texts.reset_counter') }}</a>
|
||||
<a href="#options" aria-controls="options" role="tab" data-toggle="tab">{{ trans('texts.options') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -56,7 +56,7 @@
|
||||
<div role="tabpanel" class="tab-pane active" id="invoice_number">
|
||||
<div class="panel-body">
|
||||
{!! Former::inline_radios('invoice_number_type')
|
||||
->onchange('onInvoiceNumberTypeChange()')
|
||||
->onchange("onNumberTypeChange('invoice')")
|
||||
->label(trans('texts.type'))
|
||||
->radios([
|
||||
trans('texts.prefix') => ['value' => 'prefix', 'name' => 'invoice_number_type'],
|
||||
@ -71,7 +71,6 @@
|
||||
->addGroupClass('invoice-pattern')
|
||||
->label(trans('texts.pattern'))
|
||||
->addGroupClass('number-pattern') !!}
|
||||
{!! Former::text('invoice_number_padding') !!}
|
||||
{!! Former::text('invoice_number_counter')
|
||||
->label(trans('texts.counter'))
|
||||
->help(trans('texts.invoice_number_help') . ' ' .
|
||||
@ -81,7 +80,7 @@
|
||||
<div role="tabpanel" class="tab-pane" id="quote_number">
|
||||
<div class="panel-body">
|
||||
{!! Former::inline_radios('quote_number_type')
|
||||
->onchange('onQuoteNumberTypeChange()')
|
||||
->onchange("onNumberTypeChange('quote')")
|
||||
->label(trans('texts.type'))
|
||||
->radios([
|
||||
trans('texts.prefix') => ['value' => 'prefix', 'name' => 'quote_number_type'],
|
||||
@ -119,7 +118,7 @@
|
||||
<div id="clientNumberDiv" style="display:none">
|
||||
|
||||
{!! Former::inline_radios('client_number_type')
|
||||
->onchange('onClientNumberTypeChange()')
|
||||
->onchange("onNumberTypeChange('client')")
|
||||
->label(trans('texts.type'))
|
||||
->radios([
|
||||
trans('texts.prefix') => ['value' => 'prefix', 'name' => 'client_number_type'],
|
||||
@ -143,21 +142,55 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="recurring_invoice_number">
|
||||
<div role="tabpanel" class="tab-pane" id="credit_number">
|
||||
<div class="panel-body">
|
||||
|
||||
{!! Former::checkbox('credit_number_enabled')
|
||||
->label('credit_number')
|
||||
->onchange('onCreditNumberEnabled()')
|
||||
->text('enable')
|
||||
->value(1)
|
||||
->check($account->credit_number_counter > 0) !!}
|
||||
|
||||
<div id="creditNumberDiv" style="display:none">
|
||||
|
||||
{!! Former::inline_radios('credit_number_type')
|
||||
->onchange("onNumberTypeChange('credit')")
|
||||
->label(trans('texts.type'))
|
||||
->radios([
|
||||
trans('texts.prefix') => ['value' => 'prefix', 'name' => 'credit_number_type'],
|
||||
trans('texts.pattern') => ['value' => 'pattern', 'name' => 'credit_number_type'],
|
||||
])->check($account->credit_number_pattern ? 'pattern' : 'prefix') !!}
|
||||
|
||||
{!! Former::text('credit_number_prefix')
|
||||
->addGroupClass('credit-prefix')
|
||||
->label(trans('texts.prefix')) !!}
|
||||
{!! Former::text('credit_number_pattern')
|
||||
->appendIcon('question-sign')
|
||||
->addGroupClass('credit-pattern')
|
||||
->addGroupClass('credit-number-pattern')
|
||||
->label(trans('texts.pattern')) !!}
|
||||
{!! Former::text('credit_number_counter')
|
||||
->label(trans('texts.counter'))
|
||||
->addGroupClass('pad-checkbox')
|
||||
->help(trans('texts.credit_number_help') . ' ' .
|
||||
trans('texts.next_credit_number', ['number' => $account->getNextNumber(new \App\Models\Credit()) ?: '0001'])) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="options">
|
||||
<div class="panel-body">
|
||||
|
||||
{!! Former::text('invoice_number_padding')
|
||||
->help('padding_help') !!}
|
||||
|
||||
{!! Former::text('recurring_invoice_number_prefix')
|
||||
->label(trans('texts.prefix'))
|
||||
->label(trans('texts.recurring_prefix'))
|
||||
->help(trans('texts.recurring_invoice_number_prefix_help')) !!}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="reset_counter">
|
||||
<div class="panel-body">
|
||||
|
||||
{!! Former::select('reset_counter_frequency_id')
|
||||
->onchange('onResetFrequencyChange()')
|
||||
->label('frequency')
|
||||
->label('reset_counter')
|
||||
->addOption(trans('texts.never'), '')
|
||||
->options(\App\Models\Frequency::selectOptions())
|
||||
->help('reset_counter_help') !!}
|
||||
@ -393,36 +426,14 @@
|
||||
$('#quote_number_counter').val(disabled ? '' : '{!! $account->quote_number_counter !!}');
|
||||
}
|
||||
|
||||
function onInvoiceNumberTypeChange() {
|
||||
var val = $('input[name=invoice_number_type]:checked').val()
|
||||
function onNumberTypeChange(entityType) {
|
||||
var val = $('input[name=' + entityType + '_number_type]:checked').val();
|
||||
if (val == 'prefix') {
|
||||
$('.invoice-prefix').show();
|
||||
$('.invoice-pattern').hide();
|
||||
$('.' + entityType + '-prefix').show();
|
||||
$('.' + entityType + '-pattern').hide();
|
||||
} else {
|
||||
$('.invoice-prefix').hide();
|
||||
$('.invoice-pattern').show();
|
||||
}
|
||||
}
|
||||
|
||||
function onQuoteNumberTypeChange() {
|
||||
var val = $('input[name=quote_number_type]:checked').val()
|
||||
if (val == 'prefix') {
|
||||
$('.quote-prefix').show();
|
||||
$('.quote-pattern').hide();
|
||||
} else {
|
||||
$('.quote-prefix').hide();
|
||||
$('.quote-pattern').show();
|
||||
}
|
||||
}
|
||||
|
||||
function onClientNumberTypeChange() {
|
||||
var val = $('input[name=client_number_type]:checked').val()
|
||||
if (val == 'prefix') {
|
||||
$('.client-prefix').show();
|
||||
$('.client-pattern').hide();
|
||||
} else {
|
||||
$('.client-prefix').hide();
|
||||
$('.client-pattern').show();
|
||||
$('.' + entityType + '-prefix').hide();
|
||||
$('.' + entityType + '-pattern').show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,6 +448,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
function onCreditNumberEnabled() {
|
||||
var enabled = $('#credit_number_enabled').is(':checked');
|
||||
if (enabled) {
|
||||
$('#creditNumberDiv').show();
|
||||
$('#credit_number_counter').val({{ $account->credit_number_counter ?: 1 }});
|
||||
} else {
|
||||
$('#creditNumberDiv').hide();
|
||||
$('#credit_number_counter').val(0);
|
||||
}
|
||||
}
|
||||
|
||||
function onResetFrequencyChange() {
|
||||
var val = $('#reset_counter_frequency_id').val();
|
||||
if (val) {
|
||||
@ -456,12 +478,20 @@
|
||||
$('#patternHelpModal').modal('show');
|
||||
});
|
||||
|
||||
$('.credit-number-pattern .input-group-addon').click(function() {
|
||||
$('.hide-client').hide();
|
||||
$('#patternHelpModal').modal('show');
|
||||
});
|
||||
|
||||
|
||||
$(function() {
|
||||
setQuoteNumberEnabled();
|
||||
onInvoiceNumberTypeChange();
|
||||
onQuoteNumberTypeChange();
|
||||
onClientNumberTypeChange();
|
||||
onNumberTypeChange('invoice');
|
||||
onNumberTypeChange('quote');
|
||||
onNumberTypeChange('client');
|
||||
onNumberTypeChange('credit');
|
||||
onClientNumberEnabled();
|
||||
onCreditNumberEnabled();
|
||||
onResetFrequencyChange();
|
||||
|
||||
$('#reset_counter_date').datepicker('update', '{{ Utils::fromSqlDate($account->reset_counter_date) ?: 'new Date()' }}');
|
||||
|
@ -159,7 +159,7 @@
|
||||
{!! Former::text('invoice_date')->data_bind("datePicker: invoice_date, valueUpdate: 'afterkeydown'")->label(trans("texts.{$entityType}_date"))
|
||||
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('invoice_date') !!}
|
||||
{!! Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'")->label($account->getLabel($invoice->getDueDateLabel()))
|
||||
->placeholder($invoice->exists || $invoice->isQuote() ? ' ' : $account->present()->dueDatePlaceholder())
|
||||
->placeholder($invoice->id || $invoice->isQuote() ? ' ' : $account->present()->dueDatePlaceholder())
|
||||
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('due_date') !!}
|
||||
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onkeyup('onPartialChange()')
|
||||
->addGroupClass('partial')!!}
|
||||
@ -482,7 +482,7 @@
|
||||
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!}
|
||||
@endif
|
||||
|
||||
@if ( $invoice->exists && $invoice->id && ! $invoice->is_recurring)
|
||||
@if ( $invoice->id && ! $invoice->is_recurring)
|
||||
{!! Button::primary(trans('texts.download_pdf'))
|
||||
->withAttributes(['onclick' => 'onDownloadClick()', 'id' => 'downloadPdfButton'])
|
||||
->appendIcon(Icon::create('download-alt')) !!}
|
||||
@ -1155,7 +1155,20 @@
|
||||
return invoice;
|
||||
}
|
||||
|
||||
var origInvoiceNumber = false;
|
||||
function getPDFString(cb, force) {
|
||||
@if (! $invoice->id && $account->credit_number_counter > 0)
|
||||
var total = model.invoice().totals.rawTotal();
|
||||
var invoiceNumber = model.invoice().invoice_number();
|
||||
var creditNumber = "{{ $account->getNextNumber(new \App\Models\Credit()) }}";
|
||||
if (total < 0 && invoiceNumber != creditNumber) {
|
||||
origInvoiceNumber = invoiceNumber;
|
||||
model.invoice().invoice_number(creditNumber);
|
||||
} else if (total >= 0 && invoiceNumber == creditNumber && origInvoiceNumber) {
|
||||
model.invoice().invoice_number(origInvoiceNumber);
|
||||
}
|
||||
@endif
|
||||
|
||||
@if ( ! $account->live_preview)
|
||||
return;
|
||||
@endif
|
||||
|
@ -35,7 +35,7 @@ function ViewModel(data) {
|
||||
dueDate = moment(dueDate).format("{{ $account->getMomentDateFormat() }}");
|
||||
$('#due_date').attr('placeholder', dueDate);
|
||||
} else {
|
||||
$('#due_date').attr('placeholder', "{{ $invoice->exists || $invoice->isQuote() ? ' ' : $account->present()->dueDatePlaceholder() }}");
|
||||
$('#due_date').attr('placeholder', "{{ $invoice->id || $invoice->isQuote() ? ' ' : $account->present()->dueDatePlaceholder() }}");
|
||||
}
|
||||
@endif
|
||||
}
|
||||
@ -1007,7 +1007,7 @@ ko.bindingHandlers.productTypeahead = {
|
||||
};
|
||||
|
||||
function checkInvoiceNumber() {
|
||||
var url = '{{ url('check_invoice_number') }}/{{ $invoice->exists ? $invoice->public_id : '' }}?invoice_number=' + encodeURIComponent($('#invoice_number').val());
|
||||
var url = '{{ url('check_invoice_number') }}/{{ $invoice->id ? $invoice->public_id : '' }}?invoice_number=' + encodeURIComponent($('#invoice_number').val());
|
||||
$.get(url, function(data) {
|
||||
var isValid = data == '{{ RESULT_SUCCESS }}' ? true : false;
|
||||
if (isValid) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user