diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 182cb4ab6e2f..4922c68163ec 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -157,6 +157,8 @@ class AccountController extends BaseController return self::showLocalization(); } elseif ($section == ACCOUNT_PAYMENTS) { return self::showOnlinePayments(); + } elseif ($section == ACCOUNT_INVOICE_SETTINGS) { + return self::showInvoiceSettings(); } elseif ($section == ACCOUNT_IMPORT_EXPORT) { return View::make('accounts.import_export', ['title' => trans('texts.import_export')]); } elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) { @@ -177,6 +179,29 @@ class AccountController extends BaseController } } + private function showInvoiceSettings() + { + $account = Auth::user()->account; + $recurringHours = []; + + for ($i=0; $i<24; $i++) { + if ($account->military_time) { + $format = 'H:i'; + } else { + $format = 'g:i a'; + } + $recurringHours[$i] = date($format, strtotime("{$i}:00")); + } + + $data = [ + 'account' => Account::with('users')->findOrFail(Auth::user()->account_id), + 'title' => trans("texts.invoice_settings"), + 'section' => ACCOUNT_INVOICE_SETTINGS, + 'recurringHours' => $recurringHours + ]; + return View::make("accounts.invoice_settings", $data); + } + private function showCompanyDetails() { $data = [ @@ -487,18 +512,36 @@ class AccountController extends BaseController $account->custom_invoice_text_label1 = trim(Input::get('custom_invoice_text_label1')); $account->custom_invoice_text_label2 = trim(Input::get('custom_invoice_text_label2')); - $account->invoice_number_prefix = Input::get('invoice_number_prefix'); $account->invoice_number_counter = Input::get('invoice_number_counter'); $account->quote_number_prefix = Input::get('quote_number_prefix'); $account->share_counter = Input::get('share_counter') ? true : false; $account->pdf_email_attachment = Input::get('pdf_email_attachment') ? true : false; - $account->auto_wrap = Input::get('auto_wrap') ? true : false; + + if (Input::has('recurring_hour')) { + $account->recurring_hour = Input::get('recurring_hour'); + } if (!$account->share_counter) { $account->quote_number_counter = Input::get('quote_number_counter'); } + if (Input::get('invoice_number_type') == 'prefix') { + $account->invoice_number_prefix = trim(Input::get('invoice_number_prefix')); + $account->invoice_number_pattern = null; + } else { + $account->invoice_number_pattern = trim(Input::get('invoice_number_pattern')); + $account->invoice_number_prefix = null; + } + + if (Input::get('quote_number_type') == 'prefix') { + $account->quote_number_prefix = trim(Input::get('quote_number_prefix')); + $account->quote_number_pattern = null; + } else { + $account->quote_number_pattern = trim(Input::get('quote_number_pattern')); + $account->quote_number_prefix = null; + } + if (!$account->share_counter && $account->invoice_number_prefix == $account->quote_number_prefix) { Session::flash('error', trans('texts.invalid_counter')); return Redirect::to('settings/' . ACCOUNT_INVOICE_SETTINGS)->withInput(); diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 306a59fcda8b..6b168751cb59 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -258,6 +258,13 @@ class ClientController extends BaseController $client->payment_terms = Input::get('payment_terms') ?: 0; $client->website = trim(Input::get('website')); + if (Input::has('invoice_number_counter')) { + $client->invoice_number_counter = (int) Input::get('invoice_number_counter'); + } + if (Input::has('quote_number_counter')) { + $client->invoice_number_counter = (int) Input::get('quote_number_counter'); + } + $client->save(); $data = json_decode(Input::get('data')); diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 2dd1eef286f6..2c6735c4c215 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -59,16 +59,6 @@ class InvoiceApiController extends Controller $data = Input::all(); $error = null; - // check if the invoice number is set and unique - if (!isset($data['invoice_number']) && !isset($data['id'])) { - $data['invoice_number'] = Auth::user()->account->getNextInvoiceNumber(); - } else if (isset($data['invoice_number'])) { - $invoice = Invoice::scope()->where('invoice_number', '=', $data['invoice_number'])->first(); - if ($invoice) { - $error = trans('validation.unique', ['attribute' => 'texts.invoice_number']); - } - } - if (isset($data['email'])) { $client = Client::scope()->whereHas('contacts', function($query) use ($data) { $query->where('email', '=', $data['email']); @@ -95,6 +85,16 @@ class InvoiceApiController extends Controller $client = Client::scope($data['client_id'])->first(); } + // check if the invoice number is set and unique + if (!isset($data['invoice_number']) && !isset($data['id'])) { + $data['invoice_number'] = Auth::user()->account->getNextInvoiceNumber(false, '', $client); + } else if (isset($data['invoice_number'])) { + $invoice = Invoice::scope()->where('invoice_number', '=', $data['invoice_number'])->first(); + if ($invoice) { + $error = trans('validation.unique', ['attribute' => 'texts.invoice_number']); + } + } + if (!$error) { if (!isset($data['client_id']) && !isset($data['email'])) { $error = trans('validation.', ['attribute' => 'client_id or email']); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 6b6194bc50ec..450ac9b244ed 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -235,6 +235,7 @@ class InvoiceController extends BaseController public function edit($publicId, $clone = false) { + $account = Auth::user()->account; $invoice = Invoice::scope($publicId)->withTrashed()->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')->firstOrFail(); $entityType = $invoice->getEntityType(); @@ -247,7 +248,7 @@ class InvoiceController extends BaseController if ($clone) { $invoice->id = null; - $invoice->invoice_number = Auth::user()->account->getNextInvoiceNumber($invoice->is_quote); + $invoice->invoice_number = $account->getNextInvoiceNumber($invoice->is_quote, '', $invoice->client); $invoice->balance = $invoice->amount; $invoice->invoice_status_id = 0; $invoice->invoice_date = date_create()->format('Y-m-d'); @@ -349,7 +350,7 @@ class InvoiceController extends BaseController public function create($clientPublicId = 0, $isRecurring = false) { $client = null; - $invoiceNumber = $isRecurring ? microtime(true) : Auth::user()->account->getNextInvoiceNumber(); + $invoiceNumber = $isRecurring ? microtime(true) : Auth::user()->account->getDefaultInvoiceNumber(); if ($clientPublicId) { $client = Client::scope($clientPublicId)->firstOrFail(); diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 81445cc089db..46d885e51ace 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -82,7 +82,7 @@ class QuoteController extends BaseController } $client = null; - $invoiceNumber = Auth::user()->account->getNextInvoiceNumber(true); + $invoiceNumber = Auth::user()->account->getDefaultInvoiceNumber(true); $account = Account::with('country')->findOrFail(Auth::user()->account_id); if ($clientPublicId) { diff --git a/app/Http/routes.php b/app/Http/routes.php index f0635392e060..9d11b35a3826 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -408,6 +408,7 @@ if (!defined('CONTACT_EMAIL')) { define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'); define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'); define('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/single/browser/v1/'); + define('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php'); define('REFERRAL_PROGRAM_URL', false); define('COUNT_FREE_DESIGNS', 4); diff --git a/app/Models/Account.php b/app/Models/Account.php index 5bbc4bb48bc7..438a7f3292e0 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -243,9 +243,88 @@ class Account extends Eloquent return $height; } - public function getNextInvoiceNumber($isQuote = false, $prefix = '') + public function hasNumberPattern($isQuote) { - $counter = $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter; + return $isQuote ? ($this->quote_number_pattern ? true : false) : ($this->invoice_number_pattern ? true : false); + } + + public function getNumberPattern($isQuote, $client = null) + { + $pattern = $isQuote ? $this->quote_number_pattern : $this->invoice_number_pattern; + + if (!$pattern) { + return false; + } + + $search = ['{$year}']; + $replace = [date('Y')]; + + $search[] = '{$counter}'; + $replace[] = str_pad($this->getCounter($isQuote), 4, '0', STR_PAD_LEFT); + + $matches = false; + preg_match('/{\$date:(.*?)}/', $pattern, $matches); + if (count($matches) > 1) { + $format = $matches[1]; + $search[] = $matches[0]; + $replace[] = str_replace($format, date($format), $matches[1]); + } + + $pattern = str_replace($search, $replace, $pattern); + + if ($client) { + $pattern = $this->getClientInvoiceNumber($pattern, $isQuote, $client); + } + + return $pattern; + } + + private function getClientInvoiceNumber($pattern, $isQuote, $client) + { + if (!$client) { + return $pattern; + } + + $search = [ + //'{$clientId}', + '{$userId}', + '{$custom1}', + '{$custom2}', + ]; + + $replace = [ + //str_pad($client->public_id, 3, '0', STR_PAD_LEFT), + str_pad($client->user->public_id, 2, '0', STR_PAD_LEFT), + $client->custom_value1, + $client->custom_value2, + ]; + + return str_replace($search, $replace, $pattern); + } + + // if we're using a pattern we don't know the next number until a client + // is selected, to support this the default value is blank + public function getDefaultInvoiceNumber($isQuote = false, $prefix = '') + { + if ($this->getNumberPattern($isQuote)) { + return false; + } + + return $this->getNextInvoiceNumber($isQuote = false, $prefix = ''); + } + + public function getCounter($isQuote) + { + return $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter; + } + + public function getNextInvoiceNumber($isQuote = false, $prefix = '', $client = null) + { + if ($this->hasNumberPattern($isQuote)) { + return $this->getNumberPattern($isQuote, $client); + } + + $counter = $this->getCounter($isQuote); $prefix .= $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix; $counterOffset = 0; @@ -271,14 +350,14 @@ class Account extends Eloquent return $number; } - public function incrementCounter($isQuote = false) + public function incrementCounter($invoice) { - if ($isQuote && !$this->share_counter) { + if ($invoice->is_quote && !$this->share_counter) { $this->quote_number_counter += 1; } else { $this->invoice_number_counter += 1; } - + $this->save(); } diff --git a/app/Models/Client.php b/app/Models/Client.php index 7fa606bd7664..6c672ae1e3dd 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -25,6 +25,11 @@ class Client extends EntityModel return $this->belongsTo('App\Models\Account'); } + public function user() + { + return $this->belongsTo('App\Models\User'); + } + public function invoices() { return $this->hasMany('App\Models\Invoice'); @@ -168,6 +173,11 @@ class Client extends EntityModel return $this->account->currency_id ?: DEFAULT_CURRENCY; } + + public function getCounter($isQuote) + { + return $isQuote ? $this->quote_number_counter : $this->invoice_number_counter; + } } /* diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 1a56abd2df1d..38a5c0c48fde 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -15,6 +15,16 @@ class Invoice extends EntityModel 'auto_bill' => 'boolean', ]; + public static $patternFields = [ + 'counter', + 'custom1', + 'custom2', + 'userId', + //'clientId', // need to update after saving + 'year', + 'date:', + ]; + public function account() { return $this->belongsTo('App\Models\Account'); @@ -223,7 +233,7 @@ class Invoice extends EntityModel } $startDate = $this->getOriginal('last_sent_date') ?: $this->getOriginal('start_date'); - $startDate .= ' ' . DEFAULT_SEND_RECURRING_HOUR . ':00:00'; + $startDate .= ' ' . $this->account->recurring_hour . ':00:00'; $startDate = $this->account->getDateTime($startDate); $endDate = $this->end_date ? $this->account->getDateTime($this->getOriginal('end_date')) : null; $timezone = $this->account->getTimezone(); @@ -249,7 +259,7 @@ class Invoice extends EntityModel public function getNextSendDate() { if ($this->start_date && !$this->last_sent_date) { - $startDate = $this->getOriginal('start_date') . ' ' . DEFAULT_SEND_RECURRING_HOUR . ':00:00'; + $startDate = $this->getOriginal('start_date') . ' ' . $this->account->recurring_hour . ':00:00'; return $this->account->getDateTime($startDate); } @@ -432,7 +442,7 @@ class Invoice extends EntityModel Invoice::creating(function ($invoice) { if (!$invoice->is_recurring) { - $invoice->account->incrementCounter($invoice->is_quote); + $invoice->account->incrementCounter($invoice); } }); diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index dcebc1858f1a..ff218a9d6d32 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -478,7 +478,7 @@ class InvoiceRepository } $clone->invoice_number = $account->invoice_number_prefix.$invoiceNumber; } else { - $clone->invoice_number = $account->getNextInvoiceNumber(); + $clone->invoice_number = $account->getNextInvoiceNumber($invoice->is_quote, '', $invoice->client); } foreach ([ @@ -631,7 +631,7 @@ class InvoiceRepository $invoice = Invoice::createNew($recurInvoice); $invoice->client_id = $recurInvoice->client_id; $invoice->recurring_invoice_id = $recurInvoice->id; - $invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber(false, 'R'); + $invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber(false, 'R', $recurInvoice->client); $invoice->amount = $recurInvoice->amount; $invoice->balance = $recurInvoice->amount; $invoice->invoice_date = date_create()->format('Y-m-d'); diff --git a/database/migrations/2015_10_21_185724_add_invoice_number_pattern.php b/database/migrations/2015_10_21_185724_add_invoice_number_pattern.php new file mode 100644 index 000000000000..20201a23a452 --- /dev/null +++ b/database/migrations/2015_10_21_185724_add_invoice_number_pattern.php @@ -0,0 +1,42 @@ +string('invoice_number_pattern')->nullable(); + $table->string('quote_number_pattern')->nullable(); + }); + + Schema::table('clients', function ($table) { + $table->integer('invoice_number_counter')->default(1)->nullable(); + $table->integer('quote_number_counter')->default(1)->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('accounts', function ($table) { + $table->dropColumn('invoice_number_pattern'); + $table->dropColumn('quote_number_pattern'); + }); + + Schema::table('accounts', function ($table) { + $table->dropColumn('invoice_number_counter'); + $table->dropColumn('quote_number_counter'); + }); + } +} diff --git a/public/js/built.js b/public/js/built.js index df28ee641dd5..0f1f8f26835d 100644 --- a/public/js/built.js +++ b/public/js/built.js @@ -32060,6 +32060,7 @@ NINJA.clientDetails = function(invoice) { if (!client) { return; } + var account = invoice.account; var contact = client.contacts[0]; var clientName = client.name || (contact.first_name || contact.last_name ? (contact.first_name + ' ' + contact.last_name) : contact.email); var clientEmail = client.contacts[0].email == clientName ? '' : client.contacts[0].email; @@ -32070,6 +32071,11 @@ NINJA.clientDetails = function(invoice) { cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap); } + // if a custom field is used in the invoice/quote number then we'll hide it from the PDF + var pattern = invoice.is_quote ? account.quote_number_pattern : account.invoice_number_pattern; + var custom1InPattern = (pattern && pattern.indexOf('{$custom1}') >= 0); + var custom2InPattern = (pattern && pattern.indexOf('{$custom2}') >= 0); + data = [ {text:clientName || ' ', style: ['clientName']}, {text:client.id_number}, @@ -32079,8 +32085,8 @@ NINJA.clientDetails = function(invoice) { {text:cityStatePostal}, {text:client.country ? client.country.name : ''}, {text:clientEmail}, - {text: invoice.client.custom_value1 ? invoice.account.custom_client_label1 + ' ' + invoice.client.custom_value1 : false}, - {text: invoice.client.custom_value2 ? invoice.account.custom_client_label2 + ' ' + invoice.client.custom_value2 : false} + {text: client.custom_value1 && !custom1InPattern ? account.custom_client_label1 + ' ' + client.custom_value1 : false}, + {text: client.custom_value2 && !custom2InPattern ? account.custom_client_label2 + ' ' + client.custom_value2 : false} ]; return NINJA.prepareDataList(data, 'clientDetails'); diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index 2980f26fa97e..ea7329ebb0c5 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -487,6 +487,7 @@ NINJA.clientDetails = function(invoice) { if (!client) { return; } + var account = invoice.account; var contact = client.contacts[0]; var clientName = client.name || (contact.first_name || contact.last_name ? (contact.first_name + ' ' + contact.last_name) : contact.email); var clientEmail = client.contacts[0].email == clientName ? '' : client.contacts[0].email; @@ -497,6 +498,11 @@ NINJA.clientDetails = function(invoice) { cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap); } + // if a custom field is used in the invoice/quote number then we'll hide it from the PDF + var pattern = invoice.is_quote ? account.quote_number_pattern : account.invoice_number_pattern; + var custom1InPattern = (pattern && pattern.indexOf('{$custom1}') >= 0); + var custom2InPattern = (pattern && pattern.indexOf('{$custom2}') >= 0); + data = [ {text:clientName || ' ', style: ['clientName']}, {text:client.id_number}, @@ -506,8 +512,8 @@ NINJA.clientDetails = function(invoice) { {text:cityStatePostal}, {text:client.country ? client.country.name : ''}, {text:clientEmail}, - {text: invoice.client.custom_value1 ? invoice.account.custom_client_label1 + ' ' + invoice.client.custom_value1 : false}, - {text: invoice.client.custom_value2 ? invoice.account.custom_client_label2 + ' ' + invoice.client.custom_value2 : false} + {text: client.custom_value1 && !custom1InPattern ? account.custom_client_label1 + ' ' + client.custom_value1 : false}, + {text: client.custom_value2 && !custom2InPattern ? account.custom_client_label2 + ' ' + client.custom_value2 : false} ]; return NINJA.prepareDataList(data, 'clientDetails'); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 01fbbc7ee2bf..f79c55b76571 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -840,6 +840,16 @@ return array( 'archived_tax_rate' => 'Successfully archived the tax rate', 'default_tax_rate_id' => 'Default Tax Rate', 'tax_rate' => 'Tax Rate', + '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_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', ); diff --git a/resources/views/accounts/invoice_settings.blade.php b/resources/views/accounts/invoice_settings.blade.php index cfd929fbd334..5f05ff4445e0 100644 --- a/resources/views/accounts/invoice_settings.blade.php +++ b/resources/views/accounts/invoice_settings.blade.php @@ -10,7 +10,7 @@ .input-group-addon div.checkbox { display: inline; } - .tab-content span.input-group-addon { + .tab-content .pad-checkbox span.input-group-addon { padding-right: 30px; } @@ -20,7 +20,7 @@ @parent @include('accounts.nav', ['selected' => ACCOUNT_INVOICE_SETTINGS, 'advanced' => true]) - {!! Former::open()->addClass('warn-on-exit') !!} + {!! Former::open()->rules(['iframe_url' => 'url'])->addClass('warn-on-exit') !!} {{ Former::populate($account) }} {{ Former::populateField('custom_invoice_taxes1', intval($account->custom_invoice_taxes1)) }} {{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }} @@ -34,25 +34,28 @@
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!} - @if (Utils::isNinja()) - {!! Former::inline_radios('custom_invoice_link') - ->onchange('onCustomLinkChange()') - ->radios([ - trans('texts.subdomain') => ['value' => 'subdomain', 'name' => 'custom_link'], - trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'], - ])->check($account->iframe_url ? 'website' : 'subdomain') !!} - {{ Former::setOption('capitalize_translations', false) }} - {!! Former::text('subdomain') - ->placeholder(trans('texts.www')) - ->onchange('onSubdomainChange()') - ->addGroupClass('subdomain') - ->label(' ') !!} - {!! Former::text('iframe_url') - ->placeholder('http://www.example.com/invoice') - ->appendIcon('question-sign') - ->addGroupClass('iframe_url') - ->label(' ') !!} - @endif + + {{-- Former::select('recurring_hour')->options($recurringHours) --}} + + {!! Former::inline_radios('custom_invoice_link') + ->onchange('onCustomLinkChange()') + ->radios([ + trans('texts.subdomain') => ['value' => 'subdomain', 'name' => 'custom_link'], + trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'], + ])->check($account->iframe_url ? 'website' : 'subdomain') !!} + {{ Former::setOption('capitalize_translations', false) }} + + {!! Former::text('subdomain') + ->placeholder(trans('texts.www')) + ->onchange('onSubdomainChange()') + ->addGroupClass('subdomain') + ->label(' ') !!} + + {!! Former::text('iframe_url') + ->placeholder('http://www.example.com/invoice') + ->appendIcon('question-sign') + ->addGroupClass('iframe_url') + ->label(' ') !!}
@@ -61,6 +64,7 @@

{!! trans('texts.invoice_quote_number') !!}

+
@@ -131,10 +173,16 @@
- {!! Former::text('custom_invoice_label1')->label(trans('texts.field_label')) - ->append(Former::checkbox('custom_invoice_taxes1')->raw() . trans('texts.charge_taxes')) !!} - {!! Former::text('custom_invoice_label2')->label(trans('texts.field_label')) - ->append(Former::checkbox('custom_invoice_taxes2')->raw() . trans('texts.charge_taxes')) !!} + {!! Former::text('custom_invoice_label1') + ->label(trans('texts.field_label')) + ->addGroupClass('pad-checkbox') + ->append(Former::checkbox('custom_invoice_taxes1') + ->raw() . trans('texts.charge_taxes')) !!} + {!! Former::text('custom_invoice_label2') + ->label(trans('texts.field_label')) + ->addGroupClass('pad-checkbox') + ->append(Former::checkbox('custom_invoice_taxes2') + ->raw() . trans('texts.charge_taxes')) !!}
@@ -179,6 +227,40 @@ + + {!! Former::close() !!} @@ -210,13 +292,41 @@ } } + function onInvoiceNumberTypeChange() { + var val = $('input[name=invoice_number_type]:checked').val() + if (val == 'prefix') { + $('.invoice-prefix').show(); + $('.invoice-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(); + } + } + $('.iframe_url .input-group-addon').click(function() { $('#iframeHelpModal').modal('show'); }); + $('.number-pattern .input-group-addon').click(function() { + $('#patternHelpModal').modal('show'); + }); + $(function() { setQuoteNumberEnabled(); onCustomLinkChange(); + onInvoiceNumberTypeChange(); + onQuoteNumberTypeChange(); $('#subdomain').change(function() { $('#iframe_url').val(''); @@ -224,7 +334,7 @@ $('#iframe_url').change(function() { $('#subdomain').val(''); }); - }); + }); diff --git a/resources/views/accounts/localization.blade.php b/resources/views/accounts/localization.blade.php index b11e2dfffd34..1ad5de55ff17 100644 --- a/resources/views/accounts/localization.blade.php +++ b/resources/views/accounts/localization.blade.php @@ -5,6 +5,7 @@ {!! Former::open_for_files()->addClass('warn-on-exit') !!} {{ Former::populate($account) }} + {{ Former::populateField('military_time', intval($account->military_time)) }} @include('accounts.nav', ['selected' => ACCOUNT_LOCALIZATION]) diff --git a/resources/views/accounts/user_details.blade.php b/resources/views/accounts/user_details.blade.php index dc86f0ce4c16..071b95cb9f65 100644 --- a/resources/views/accounts/user_details.blade.php +++ b/resources/views/accounts/user_details.blade.php @@ -8,7 +8,6 @@ )) !!} {{ Former::populate($account) }} - {{ Former::populateField('military_time', intval($account->military_time)) }} {{ Former::populateField('first_name', $user->first_name) }} {{ Former::populateField('last_name', $user->last_name) }} {{ Former::populateField('email', $user->email) }} diff --git a/resources/views/clients/edit.blade.php b/resources/views/clients/edit.blade.php index 1f2fd752d149..1891cc6f7cb0 100644 --- a/resources/views/clients/edit.blade.php +++ b/resources/views/clients/edit.blade.php @@ -1,3 +1,4 @@ + @extends('header') @@ -34,7 +35,7 @@ {!! Former::text('website') !!} {!! Former::text('work_phone') !!} - @if (Auth::user()->isPro()) + @if (Auth::user()->isPro()) @if ($customLabel1) {!! Former::text('custom_value1')->label($customLabel1) !!} @endif @@ -115,6 +116,7 @@ {!! Former::select('industry_id')->addOption('','') ->fromQuery($industries, 'name', 'id') !!} {!! Former::textarea('private_notes') !!} + diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 0e48a6ebce2d..8cf488bedbb1 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -3,8 +3,7 @@ @section('head') @parent - - + @stop @section('content') @@ -515,13 +514,107 @@ + @include('invoices.knockout') + diff --git a/resources/views/invoices/knockout.blade.php b/resources/views/invoices/knockout.blade.php new file mode 100644 index 000000000000..e97c986f28fe --- /dev/null +++ b/resources/views/invoices/knockout.blade.php @@ -0,0 +1,797 @@ + \ No newline at end of file diff --git a/resources/views/list.blade.php b/resources/views/list.blade.php index 1683c7176f1b..adc38aa54d03 100644 --- a/resources/views/list.blade.php +++ b/resources/views/list.blade.php @@ -167,10 +167,13 @@ }); function setBulkActionsEnabled() { - var checked = $('tbody :checkbox:checked').length > 0; - $('button.archive, button.invoice').prop('disabled', !checked); - - + var buttonLabel = "{{ trans('texts.archive') }}"; + var count = $('tbody :checkbox:checked').length; + $('button.archive, button.invoice').prop('disabled', !count); + if (count) { + buttonLabel += ' (' + count + ')'; + } + $('button.archive').not('.dropdown-toggle').text(buttonLabel); } });