mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 12:42:36 +01:00
Added per client invoice counter #1344
This commit is contained in:
parent
d292fc2786
commit
cc63385e14
@ -49,6 +49,8 @@ class Client extends EntityModel
|
||||
'language_id',
|
||||
'payment_terms',
|
||||
'website',
|
||||
'invoice_number_counter',
|
||||
'quote_number_counter',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -64,9 +64,11 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
*/
|
||||
public static $patternFields = [
|
||||
'counter',
|
||||
'custom1',
|
||||
'custom2',
|
||||
'idNumber',
|
||||
'clientInvoiceCounter',
|
||||
'clientQuoteCounter',
|
||||
'clientCustom1',
|
||||
'clientCustom2',
|
||||
'clientIdNumber',
|
||||
'userId',
|
||||
'year',
|
||||
'date:',
|
||||
|
@ -125,7 +125,7 @@ trait GeneratesNumbers
|
||||
{
|
||||
$pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
||||
|
||||
return strstr($pattern, '$custom') || strstr($pattern, '$idNumber');
|
||||
return strstr($pattern, '$client') !== false || strstr($pattern, '$idNumber') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,16 +161,12 @@ trait GeneratesNumbers
|
||||
if (count($matches) > 1) {
|
||||
$format = $matches[1];
|
||||
$search[] = $matches[0];
|
||||
//$date = date_create()->format($format);
|
||||
$date = Carbon::now(session(SESSION_TIMEZONE, DEFAULT_TIMEZONE))->format($format);
|
||||
$replace[] = str_replace($format, $date, $matches[1]);
|
||||
}
|
||||
|
||||
$pattern = str_replace($search, $replace, $pattern);
|
||||
|
||||
if ($entity->client_id) {
|
||||
$pattern = $this->getClientInvoiceNumber($pattern, $entity);
|
||||
}
|
||||
$pattern = $this->getClientInvoiceNumber($pattern, $entity);
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
@ -183,7 +179,7 @@ trait GeneratesNumbers
|
||||
*/
|
||||
private function getClientInvoiceNumber($pattern, $invoice)
|
||||
{
|
||||
if (! $invoice->client) {
|
||||
if (! $invoice->client_id) {
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
@ -191,12 +187,22 @@ trait GeneratesNumbers
|
||||
'{$custom1}',
|
||||
'{$custom2}',
|
||||
'{$idNumber}',
|
||||
'{$clientCustom1}',
|
||||
'{$clientCustom2}',
|
||||
'{$clientIdNumber}',
|
||||
'{$clientInvoiceCounter}',
|
||||
'{$clientQuoteCounter}',
|
||||
];
|
||||
|
||||
$replace = [
|
||||
$invoice->client->custom_value1,
|
||||
$invoice->client->custom_value2,
|
||||
$invoice->client->id_number,
|
||||
$invoice->client->custom_value1, // backwards compatibility
|
||||
$invoice->client->custom_value2,
|
||||
$invoice->client->id_number,
|
||||
str_pad($invoice->client->invoice_number_counter, $this->invoice_number_padding, '0', STR_PAD_LEFT),
|
||||
str_pad($invoice->client->quote_number_counter, $this->invoice_number_padding, '0', STR_PAD_LEFT),
|
||||
];
|
||||
|
||||
return str_replace($search, $replace, $pattern);
|
||||
@ -225,7 +231,9 @@ trait GeneratesNumbers
|
||||
*/
|
||||
public function previewNextInvoiceNumber($entityType = ENTITY_INVOICE)
|
||||
{
|
||||
$invoice = $this->createInvoice($entityType);
|
||||
$client = \App\Models\Client::scope()->first();
|
||||
|
||||
$invoice = $this->createInvoice($entityType, $client ? $client->id : 0);
|
||||
|
||||
return $this->getNextNumber($invoice);
|
||||
}
|
||||
@ -239,13 +247,41 @@ trait GeneratesNumbers
|
||||
if ($this->client_number_counter) {
|
||||
$this->client_number_counter += 1;
|
||||
}
|
||||
} elseif ($entity->isType(INVOICE_TYPE_QUOTE) && ! $this->share_counter) {
|
||||
$this->quote_number_counter += 1;
|
||||
} else {
|
||||
$this->invoice_number_counter += 1;
|
||||
$this->save();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->save();
|
||||
if ($this->usesClientInvoiceCounter()) {
|
||||
$entity->client->invoice_number_counter += 1;
|
||||
$entity->client->save();
|
||||
} elseif ($this->usesClientQuoteCounter()) {
|
||||
$entity->client->quote_number_counter += 1;
|
||||
$entity->client->save();
|
||||
}
|
||||
|
||||
if ($this->usesInvoiceCounter()) {
|
||||
if ($entity->isType(INVOICE_TYPE_QUOTE) && ! $this->share_counter) {
|
||||
$this->quote_number_counter += 1;
|
||||
} else {
|
||||
$this->invoice_number_counter += 1;
|
||||
}
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function usesInvoiceCounter()
|
||||
{
|
||||
return strpos($this->invoice_number_pattern, '{$counter}') !== false;
|
||||
}
|
||||
|
||||
public function usesClientInvoiceCounter()
|
||||
{
|
||||
return strpos($this->invoice_number_pattern, '{$clientInvoiceCounter}') !== false;
|
||||
}
|
||||
|
||||
public function usesClientQuoteCounter()
|
||||
{
|
||||
return strpos($this->invoice_number_pattern, '{$clientQuoteCounter}') !== false;
|
||||
}
|
||||
|
||||
public function clientNumbersEnabled()
|
||||
|
@ -131,6 +131,8 @@ class ClientTransformer extends EntityTransformer
|
||||
'currency_id' => (int) $client->currency_id,
|
||||
'custom_value1' => $client->custom_value1,
|
||||
'custom_value2' => $client->custom_value2,
|
||||
'invoice_number_counter' => (int) $client->invoice_number_counter,
|
||||
'quote_number_counter' => (int) $client->quote_number_counter,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,15 @@ class AppServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
Validator::extend('has_counter', function ($attribute, $value, $parameters) {
|
||||
return ! $value || strstr($value, '{$counter}');
|
||||
if (! $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strstr($value, '{$counter}') !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((strstr($value, '{$idNumber}') !== false || strstr($value, '{$clientIdNumber}') != false) && (strstr($value, '{$clientInvoiceCounter}') || strstr($value, '{$clientQuoteCounter}')));
|
||||
});
|
||||
|
||||
Validator::extend('valid_invoice_items', function ($attribute, $value, $parameters) {
|
||||
|
@ -19,6 +19,11 @@ class AddGatewayFeeLocation extends Migration
|
||||
$table->date('reset_counter_date')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('clients', function ($table) {
|
||||
$table->integer('invoice_number_counter')->default(1)->nullable();
|
||||
$table->integer('quote_number_counter')->default(1)->nullable();
|
||||
});
|
||||
|
||||
// update invoice_item_type_id for task invoice items
|
||||
DB::statement('update invoice_items
|
||||
left join invoices on invoices.id = invoice_items.invoice_id
|
||||
@ -37,5 +42,10 @@ class AddGatewayFeeLocation extends Migration
|
||||
$table->dropColumn('gateway_fee_enabled');
|
||||
$table->dropColumn('reset_counter_date');
|
||||
});
|
||||
|
||||
Schema::table('clients', function ($table) {
|
||||
$table->dropColumn('invoice_number_counter');
|
||||
$table->dropColumn('quote_number_counter');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1151,3 +1151,10 @@ function firstJSONError(json) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
|
||||
function pad(n, width, z) {
|
||||
z = z || '0';
|
||||
n = n + '';
|
||||
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
|
||||
}
|
||||
|
@ -732,7 +732,7 @@ $LANG = array(
|
||||
'recurring_hour' => 'Recurring Hour',
|
||||
'pattern' => 'Pattern',
|
||||
'pattern_help_title' => 'Pattern Help',
|
||||
'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern',
|
||||
'pattern_help_1' => 'Create custom numbers by specifying a pattern',
|
||||
'pattern_help_2' => 'Available variables:',
|
||||
'pattern_help_3' => 'For example, :example would be converted to :value',
|
||||
'see_options' => 'See options',
|
||||
|
@ -73,7 +73,7 @@ return array(
|
||||
"has_credit" => "The client does not have enough credit.",
|
||||
"notmasked" => "The values are masked",
|
||||
"less_than" => "The :attribute must be less than :value",
|
||||
"has_counter" => "The value must contain {\$counter}",
|
||||
"has_counter" => "To enusre all invoice numbers are unique the pattern needs to contain either {\$counter} or {\$clientIdNumber} and {\$clientInvoiceCounter}",
|
||||
"valid_contacts" => "The contact must have either an email or name",
|
||||
"valid_invoice_items" => "The invoice exceeds the maximum amount",
|
||||
"valid_subdomain" => "The subdomain is restricted",
|
||||
|
@ -132,7 +132,7 @@
|
||||
{!! Former::text('client_number_pattern')
|
||||
->appendIcon('question-sign')
|
||||
->addGroupClass('client-pattern')
|
||||
->addGroupClass('number-pattern')
|
||||
->addGroupClass('client-number-pattern')
|
||||
->label(trans('texts.pattern')) !!}
|
||||
{!! Former::text('client_number_counter')
|
||||
->label(trans('texts.counter'))
|
||||
@ -352,12 +352,14 @@
|
||||
@foreach (\App\Models\Invoice::$patternFields as $field)
|
||||
@if ($field == 'date:')
|
||||
<li>{$date:format} - {!! link_to(PHP_DATE_FORMATS, trans('texts.see_options'), ['target' => '_blank']) !!}</li>
|
||||
@elseif (strpos($field, 'client') !== false)
|
||||
<li class="hide-client">{${{ $field }}}</li>
|
||||
@else
|
||||
<li>{${{ $field }}}</li>
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
<p>{{ trans('texts.pattern_help_3', [
|
||||
<p class="hide-client">{{ trans('texts.pattern_help_3', [
|
||||
'example' => '{$year}-{$counter}',
|
||||
'value' => date('Y') . '-0001'
|
||||
]) }}</p>
|
||||
@ -439,6 +441,12 @@
|
||||
}
|
||||
|
||||
$('.number-pattern .input-group-addon').click(function() {
|
||||
$('.hide-client').show();
|
||||
$('#patternHelpModal').modal('show');
|
||||
});
|
||||
|
||||
$('.client-number-pattern .input-group-addon').click(function() {
|
||||
$('.hide-client').hide();
|
||||
$('#patternHelpModal').modal('show');
|
||||
});
|
||||
|
||||
|
@ -24,8 +24,12 @@
|
||||
@if ($client)
|
||||
{!! Former::populate($client) !!}
|
||||
{!! Former::hidden('public_id') !!}
|
||||
@elseif ($account->client_number_counter)
|
||||
{!! Former::populateField('id_number', $account->getNextNumber()) !!}
|
||||
@else
|
||||
{!! Former::populateField('invoice_number_counter', 1) !!}
|
||||
{!! Former::populateField('quote_number_counter', 1) !!}
|
||||
@if ($account->client_number_counter)
|
||||
{!! Former::populateField('id_number', $account->getNextNumber()) !!}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
@ -52,8 +56,16 @@
|
||||
{!! Former::text('custom_value2')->label($customLabel2) !!}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if ($account->usesClientInvoiceCounter())
|
||||
{!! Former::text('invoice_number_counter')->label('invoice_counter') !!}
|
||||
@endif
|
||||
|
||||
@if ($account->usesClientQuoteCounter())
|
||||
{!! Former::text('quote_number_counter')->label('quote_counter') !!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default" style="min-height: 500px">
|
||||
<div class="panel-heading">
|
||||
|
@ -1622,11 +1622,17 @@
|
||||
}
|
||||
|
||||
function setInvoiceNumber(client) {
|
||||
@if ($invoice->id || !$account->hasClientNumberPattern($invoice))
|
||||
@if ($invoice->id || !$account->hasClientNumberPattern($invoice))
|
||||
return;
|
||||
@endif
|
||||
var number = '{{ $account->applyNumberPattern($invoice) }}';
|
||||
number = number.replace('{$custom1}', client.custom_value1 ? client.custom_value1 : '');
|
||||
number = number.replace('{$clientCustom1}', client.custom_value1 ? client.custom_value1 : '');
|
||||
number = number.replace('{$clientCustom2}', client.custom_value2 ? client.custom_value1 : '');
|
||||
number = number.replace('{$clientIdNumber}', client.id_number ? client.id_number : '');
|
||||
number = number.replace('{$clientInvoiceCounter}', pad(client.invoice_number_counter, {{ $account->invoice_number_padding }}));
|
||||
number = number.replace('{$clientQuoteCounter}', pad(client.quote_number_counter, {{ $account->invoice_number_padding }}));
|
||||
// backwards compatibility
|
||||
number = number.replace('{$custom1}', client.custom_value1 ? client.custom_value1 : '');
|
||||
number = number.replace('{$custom2}', client.custom_value2 ? client.custom_value1 : '');
|
||||
number = number.replace('{$idNumber}', client.id_number ? client.id_number : '');
|
||||
model.invoice().invoice_number(number);
|
||||
|
Loading…
Reference in New Issue
Block a user