diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 3f8917b65427..db2a65e6eca1 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -1301,4 +1301,37 @@ class AccountController extends BaseController return Redirect::to("/settings/$section/", 301); } + + public function previewEmail(\App\Services\TemplateService $templateService) + { + $template = Input::get('template'); + $invoice = Invoice::scope() + ->invoices() + ->withTrashed() + ->first(); + + if ( ! $invoice) { + return trans('texts.create_invoice_for_sample'); + } + + $account = Auth::user()->account; + + // replace the variables with sample data + $data = [ + 'account' => $account, + 'invoice' => $invoice, + 'invitation' => $invoice->invitations->first(), + 'client' => $invoice->client, + 'amount' => $invoice->amount + ]; + + // create the email view + $view = 'emails.' . $account->getTemplateView(ENTITY_INVOICE) . '_html'; + $data = array_merge($data, [ + 'body' => $templateService->processVariables($template, $data), + 'entityType' => ENTITY_INVOICE, + ]); + + return Response::view($view, $data); + } } diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 568cb4d45f5b..a602ae7ecb85 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -266,18 +266,7 @@ class AppController extends BaseController Cache::flush(); Session::flush(); Artisan::call('migrate', array('--force' => true)); - foreach ([ - 'PaymentLibraries', - 'Fonts', - 'Banks', - 'InvoiceStatus', - 'Currencies', - 'DateFormats', - 'InvoiceDesigns', - 'PaymentTerms', - ] as $seeder) { - Artisan::call('db:seed', array('--force' => true, '--class' => "{$seeder}Seeder")); - } + Artisan::call('db:seed', array('--force' => true, '--class' => "UpdateSeeder")); Event::fire(new UserSettingsChanged()); Session::flash('message', trans('texts.processed_updates')); } catch (Exception $e) { diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php index adbe2d4c2ae6..ec28cdb77d1d 100644 --- a/app/Http/Requests/ClientRequest.php +++ b/app/Http/Requests/ClientRequest.php @@ -9,7 +9,7 @@ class ClientRequest extends EntityRequest { $client = parent::entity(); // eager load the contacts - if ($client && ! count($client->contacts)) { + if ($client && ! $client->relationLoaded('contacts')) { $client->load('contacts'); } diff --git a/app/Http/Requests/ExpenseRequest.php b/app/Http/Requests/ExpenseRequest.php index d5e2c793c371..ae2e83b6d12f 100644 --- a/app/Http/Requests/ExpenseRequest.php +++ b/app/Http/Requests/ExpenseRequest.php @@ -8,11 +8,11 @@ class ExpenseRequest extends EntityRequest { { $expense = parent::entity(); - // eager load the contacts - if ($expense && ! count($expense->documents)) { + // eager load the documents + if ($expense && ! $expense->relationLoaded('documents')) { $expense->load('documents'); } - + return $expense; } } \ No newline at end of file diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php index bf24c38839a9..5e2d93139003 100644 --- a/app/Http/Requests/InvoiceRequest.php +++ b/app/Http/Requests/InvoiceRequest.php @@ -8,8 +8,8 @@ class InvoiceRequest extends EntityRequest { { $invoice = parent::entity(); - // eager load the contacts - if ($invoice && ! count($invoice->invoice_items)) { + // eager load the invoice items + if ($invoice && ! $invoice->relationLoaded('invoice_items')) { $invoice->load('invoice_items'); } diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php index a06e5ad808e6..8f96e7c55025 100644 --- a/app/Http/Requests/VendorRequest.php +++ b/app/Http/Requests/VendorRequest.php @@ -9,7 +9,7 @@ class VendorRequest extends EntityRequest { $vendor = parent::entity(); // eager load the contacts - if ($vendor && ! count($vendor->vendor_contacts)) { + if ($vendor && ! $vendor->relationLoaded('vendor_contacts')) { $vendor->load('vendor_contacts'); } diff --git a/app/Http/routes.php b/app/Http/routes.php index a70b38cc707b..79a818a1aaa5 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -208,6 +208,7 @@ Route::group([ Route::resource('tax_rates', 'TaxRateController'); Route::post('tax_rates/bulk', 'TaxRateController@bulk'); + Route::get('settings/email_preview', 'AccountController@previewEmail'); Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy'); Route::get('settings/data_visualizations', 'ReportController@d3'); Route::get('settings/charts_and_reports', 'ReportController@showReports'); diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 7c1b11dfd84b..4699603d4d4c 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -341,6 +341,7 @@ class Utils $currency = self::getFromCache($currencyId, 'currencies'); $thousand = $currency->thousand_separator; $decimal = $currency->decimal_separator; + $precision = $currency->precision; $code = $currency->code; $swapSymbol = false; @@ -355,7 +356,7 @@ class Utils } } - $value = number_format($value, $currency->precision, $decimal, $thousand); + $value = number_format($value, $precision, $decimal, $thousand); $symbol = $currency->symbol; if ($showCode || !$symbol) { diff --git a/app/Models/Account.php b/app/Models/Account.php index c8ece3b4da95..92e2a623ff7b 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -212,7 +212,9 @@ class Account extends Eloquent public function isGatewayConfigured($gatewayId = 0) { - $this->load('account_gateways'); + if ( ! $this->relationLoaded('account_gateways')) { + $this->load('account_gateways'); + } if ($gatewayId) { return $this->getGatewayConfig($gatewayId) != false; @@ -241,7 +243,7 @@ class Account extends Eloquent return $this->name; } - $this->load('users'); + //$this->load('users'); $user = $this->users()->first(); return $user->getDisplayName(); @@ -1167,6 +1169,11 @@ class Account extends Eloquent return str_replace('/>', ' />', $template); } + public function getTemplateView($view = '') + { + return $this->getEmailDesignId() == EMAIL_DESIGN_PLAIN ? $view : 'design' . $this->getEmailDesignId(); + } + public function getEmailFooter() { if ($this->email_footer) { diff --git a/app/Models/Client.php b/app/Models/Client.php index 85cb543a6b03..4b26f40df0a7 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -256,13 +256,17 @@ class Client extends EntityModel public function getGatewayToken() { - $this->account->load('account_gateways'); + $account = $this->account; + + if ( ! $account->relationLoaded('account_gateways')) { + $account->load('account_gateways'); + } - if (!count($this->account->account_gateways)) { + if (!count($account->account_gateways)) { return false; } - $accountGateway = $this->account->getGatewayConfig(GATEWAY_STRIPE); + $accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE); if (!$accountGateway) { return false; diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index deb65b75ab30..cd08e33a855d 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -228,6 +228,12 @@ class Invoice extends EntityModel implements BalanceAffecting return $this->hasMany('App\Models\Expense','invoice_id','id')->withTrashed(); } + public function scopeInvoices($query) + { + return $query->where('is_quote', '=', false) + ->where('is_recurring', '=', false); + } + public function markInvitationsSent($notify = false) { foreach ($this->invitations as $invitation) { diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index 4b176628ebf0..fbebd0c5fdfd 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -1,17 +1,13 @@ templateService = $templateService; + } + public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false) { $invoice->load('invitations', 'client.language', 'account'); @@ -144,7 +145,7 @@ class ContactMailer extends Mailer } $data = [ - 'body' => $this->processVariables($body, $variables), + 'body' => $this->templateService->processVariables($body, $variables), 'link' => $invitation->getLink(), 'entityType' => $invoice->getEntityType(), 'invoiceId' => $invoice->id, @@ -160,14 +161,9 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($subject, $variables); + $subject = $this->templateService->processVariables($subject, $variables); $fromEmail = $user->email; - - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = ENTITY_INVOICE; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView(ENTITY_INVOICE); $response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data); @@ -230,7 +226,7 @@ class ContactMailer extends Mailer ]; $data = [ - 'body' => $this->processVariables($emailTemplate, $variables), + 'body' => $this->templateService->processVariables($emailTemplate, $variables), 'link' => $invitation->getLink(), 'invoice' => $invoice, 'client' => $client, @@ -244,14 +240,10 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($emailSubject, $variables); + $subject = $this->templateService->processVariables($emailSubject, $variables); $data['invoice_id'] = $payment->invoice->id; - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = 'payment_confirmation'; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView('payment_confirmation'); if ($user->email && $contact->email) { $this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data); @@ -281,75 +273,4 @@ class ContactMailer extends Mailer $this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); } - - private function processVariables($template, $data) - { - $account = $data['account']; - $client = $data['client']; - $invitation = $data['invitation']; - $invoice = $invitation->invoice; - $passwordHTML = isset($data['password'])?'

'.trans('texts.password').': '.$data['password'].'

':false; - $documentsHTML = ''; - - if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){ - $documentsHTML .= trans('texts.email_documents_header').'

'; - } - - $variables = [ - '$footer' => $account->getEmailFooter(), - '$client' => $client->getDisplayName(), - '$account' => $account->getDisplayName(), - '$dueDate' => $account->formatDate($invoice->due_date), - '$invoiceDate' => $account->formatDate($invoice->invoice_date), - '$contact' => $invitation->contact->getDisplayName(), - '$firstName' => $invitation->contact->first_name, - '$amount' => $account->formatMoney($data['amount'], $client), - '$invoice' => $invoice->invoice_number, - '$quote' => $invoice->invoice_number, - '$link' => $invitation->getLink(), - '$password' => $passwordHTML, - '$viewLink' => $invitation->getLink().'$password', - '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', - '$paymentLink' => $invitation->getLink('payment').'$password', - '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', - '$customClient1' => $account->custom_client_label1, - '$customClient2' => $account->custom_client_label2, - '$customInvoice1' => $account->custom_invoice_text_label1, - '$customInvoice2' => $account->custom_invoice_text_label2, - '$documents' => $documentsHTML, - ]; - - // Add variables for available payment types - foreach (Gateway::$paymentTypes as $type) { - $camelType = Gateway::getPaymentTypeName($type); - $type = Utils::toSnakeCase($camelType); - $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; - $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); - } - - $includesPasswordPlaceholder = strpos($template, '$password') !== false; - - $str = str_replace(array_keys($variables), array_values($variables), $template); - - if(!$includesPasswordPlaceholder && $passwordHTML){ - $pos = strrpos($str, '$password'); - if($pos !== false) - { - $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); - } - } - $str = str_replace('$password', '', $str); - $str = autolink($str, 100); - - return $str; - } } diff --git a/app/Ninja/Presenters/InvoicePresenter.php b/app/Ninja/Presenters/InvoicePresenter.php index ebb3297d5c1a..36a2ce2acbd6 100644 --- a/app/Ninja/Presenters/InvoicePresenter.php +++ b/app/Ninja/Presenters/InvoicePresenter.php @@ -18,7 +18,7 @@ class InvoicePresenter extends Presenter { public function balanceDueLabel() { - if ($this->entity->partial) { + if ($this->entity->partial > 0) { return 'partial_due'; } elseif ($this->entity->is_quote) { return 'total'; diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index e8fe2d041843..3b3c36d3ecc3 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -167,6 +167,7 @@ class AccountRepository ENTITY_QUOTE, ENTITY_TASK, ENTITY_EXPENSE, + ENTITY_VENDOR, ENTITY_RECURRING_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT @@ -183,15 +184,22 @@ class AccountRepository ]; } - $features[] = ['dashboard', '/dashboard']; - $features[] = ['customize_design', '/settings/customize_design']; - $features[] = ['new_tax_rate', '/tax_rates/create']; - $features[] = ['new_product', '/products/create']; - $features[] = ['new_user', '/users/create']; - $features[] = ['custom_fields', '/settings/invoice_settings']; + $features = array_merge($features, [ + ['dashboard', '/dashboard'], + ['customize_design', '/settings/customize_design'], + ['new_tax_rate', '/tax_rates/create'], + ['new_product', '/products/create'], + ['new_user', '/users/create'], + ['custom_fields', '/settings/invoice_settings'], + ['invoice_number', '/settings/invoice_settings'], + ]); $settings = array_merge(Account::$basicSettings, Account::$advancedSettings); + if ( ! Utils::isNinjaProd()) { + $settings[] = ACCOUNT_SYSTEM_SETTINGS; + } + foreach ($settings as $setting) { $features[] = [ $setting, @@ -332,7 +340,7 @@ class AccountRepository $client->public_id = $account->id; $client->user_id = $ninjaAccount->users()->first()->id; $client->currency_id = 1; - foreach (['name', 'address1', 'address2', 'city', 'state', 'postal_code', 'country_id', 'work_phone', 'language_id'] as $field) { + foreach (['name', 'address1', 'address2', 'city', 'state', 'postal_code', 'country_id', 'work_phone', 'language_id', 'vat_number'] as $field) { $client->$field = $account->$field; } $ninjaAccount->clients()->save($client); diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 8d8c3ed527d4..83f08cd97a59 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -105,9 +105,9 @@ class ClientRepository extends BaseRepository // If the primary is set ensure it's listed first usort($contacts, function ($left, $right) { - return (isset($right['is_primary']) ? $right['is_primary'] : 0) - (isset($left['is_primary']) ? $left['is_primary'] : 0); + return (isset($right['is_primary']) ? $right['is_primary'] : 1) - (isset($left['is_primary']) ? $left['is_primary'] : 0); }); - + foreach ($contacts as $contact) { $contact = $client->addContact($contact, $first); $contactIds[] = $contact->public_id; diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php new file mode 100644 index 000000000000..5a41c705352d --- /dev/null +++ b/app/Services/TemplateService.php @@ -0,0 +1,80 @@ +invoice; + $passwordHTML = isset($data['password'])?'

'.trans('texts.password').': '.$data['password'].'

':false; + $documentsHTML = ''; + + if ($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()) { + $documentsHTML .= trans('texts.email_documents_header').'

'; + } + + $variables = [ + '$footer' => $account->getEmailFooter(), + '$client' => $client->getDisplayName(), + '$account' => $account->getDisplayName(), + '$dueDate' => $account->formatDate($invoice->due_date), + '$invoiceDate' => $account->formatDate($invoice->invoice_date), + '$contact' => $invitation->contact->getDisplayName(), + '$firstName' => $invitation->contact->first_name, + '$amount' => $account->formatMoney($data['amount'], $client), + '$invoice' => $invoice->invoice_number, + '$quote' => $invoice->invoice_number, + '$link' => $invitation->getLink(), + '$password' => $passwordHTML, + '$viewLink' => $invitation->getLink().'$password', + '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', + '$paymentLink' => $invitation->getLink('payment').'$password', + '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', + '$customClient1' => $account->custom_client_label1, + '$customClient2' => $account->custom_client_label2, + '$customInvoice1' => $account->custom_invoice_text_label1, + '$customInvoice2' => $account->custom_invoice_text_label2, + '$documents' => $documentsHTML, + ]; + + // Add variables for available payment types + foreach (Gateway::$paymentTypes as $type) { + $camelType = Gateway::getPaymentTypeName($type); + $type = Utils::toSnakeCase($camelType); + $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; + $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); + } + + $includesPasswordPlaceholder = strpos($template, '$password') !== false; + + $str = str_replace(array_keys($variables), array_values($variables), $template); + + if (!$includesPasswordPlaceholder && $passwordHTML) { + $pos = strrpos($str, '$password'); + if ($pos !== false) + { + $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); + } + } + $str = str_replace('$password', '', $str); + $str = autolink($str, 100); + + return $str; + } +} \ No newline at end of file diff --git a/database/seeds/CurrenciesSeeder.php b/database/seeds/CurrenciesSeeder.php index b3da84dad01d..3b44d55d8d7f 100644 --- a/database/seeds/CurrenciesSeeder.php +++ b/database/seeds/CurrenciesSeeder.php @@ -8,6 +8,7 @@ class CurrenciesSeeder extends Seeder { Eloquent::unguard(); + // http://www.localeplanet.com/icu/currency.html $currencies = [ ['name' => 'US Dollar', 'code' => 'USD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Pound Sterling', 'code' => 'GBP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], @@ -56,6 +57,7 @@ class CurrenciesSeeder extends Seeder ['name' => 'Japanese Yen', 'code' => 'JPY', 'symbol' => '¥', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Maldivian Rufiyaa', 'code' => 'MVR', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Costa Rican Colón', 'code' => 'CRC', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], + ['name' => 'Pakistani Rupee', 'code' => 'PKR', 'symbol' => 'Rs ', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'], ]; foreach ($currencies as $currency) { diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 8791f30e71fb..d7f38d3e1e53 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -23,5 +23,6 @@ class DatabaseSeeder extends Seeder $this->call('DateFormatsSeeder'); $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); + $this->call('LanguageSeeder'); } } diff --git a/database/seeds/LanguageSeeder.php b/database/seeds/LanguageSeeder.php new file mode 100644 index 000000000000..c82b2630771c --- /dev/null +++ b/database/seeds/LanguageSeeder.php @@ -0,0 +1,41 @@ + 'English', 'locale' => 'en'], + ['name' => 'Italian', 'locale' => 'it'], + ['name' => 'German', 'locale' => 'de'], + ['name' => 'French', 'locale' => 'fr'], + ['name' => 'Brazilian Portuguese', 'locale' => 'pt_BR'], + ['name' => 'Dutch', 'locale' => 'nl'], + ['name' => 'Spanish', 'locale' => 'es'], + ['name' => 'Norwegian', 'locale' => 'nb_NO'], + ['name' => 'Danish', 'locale' => 'da'], + ['name' => 'Japanese', 'locale' => 'ja'], + ['name' => 'Swedish', 'locale' => 'sv'], + ['name' => 'Spanish - Spain', 'locale' => 'es_ES'], + ['name' => 'French - Canada', 'locale' => 'fr_CA'], + ['name' => 'Lithuanian', 'locale' => 'lt'], + ['name' => 'Polish', 'locale' => 'pl'], + ]; + + foreach ($languages as $language) { + $record = Language::whereLocale($language['locale'])->first(); + if ($record) { + $record->name = $language['name']; + $record->save(); + } else { + Language::create($language); + } + } + + Eloquent::reguard(); + } +} diff --git a/database/seeds/UpdateSeeder.php b/database/seeds/UpdateSeeder.php index e4d43f78e512..d445811de506 100644 --- a/database/seeds/UpdateSeeder.php +++ b/database/seeds/UpdateSeeder.php @@ -19,5 +19,6 @@ class UpdateSeeder extends Seeder $this->call('DateFormatsSeeder'); $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); + $this->call('LanguageSeeder'); } } diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php index 1ec659bf389e..dbaba660b75c 100644 --- a/database/seeds/UserTableSeeder.php +++ b/database/seeds/UserTableSeeder.php @@ -1,9 +1,13 @@ 'Test Account', + 'name' => $faker->name, + 'address1' => $faker->streetAddress, + 'address2' => $faker->secondaryAddress, + 'city' => $faker->city, + 'state' => $faker->state, + 'postal_code' => $faker->postcode, + 'country_id' => Country::all()->random()->id, 'account_key' => str_random(RANDOM_KEY_LENGTH), + 'invoice_terms' => $faker->text($faker->numberBetween(50, 300)), + 'work_phone' => $faker->phoneNumber, + 'work_email' => $faker->safeEmail, + 'invoice_design_id' => min(InvoiceDesign::all()->random()->id, 10), + 'header_font_id' => min(Font::all()->random()->id, 17), + 'body_font_id' => min(Font::all()->random()->id, 17), + 'primary_color' => $faker->hexcolor, 'timezone_id' => 1, 'company_id' => $company->id, ]); @@ -30,6 +48,8 @@ class UserTableSeeder extends Seeder 'password' => Hash::make(TEST_PASSWORD), 'registered' => true, 'confirmed' => true, + 'notify_sent' => false, + 'notify_paid' => false, ]); Affiliate::create([ diff --git a/public/built.js b/public/built.js index faffe4a2c348..2e1938ba78b6 100644 --- a/public/built.js +++ b/public/built.js @@ -31196,7 +31196,7 @@ NINJA.decodeJavascript = function(invoice, javascript) field = toSnakeCase(field); var value = getDescendantProp(invoice, field); if (match.indexOf('?') < 0 || value) { - if (invoice.partial && field == 'balance_due') { + if (invoice.partial > 0 && field == 'balance_due') { field = 'partial_due'; } else if (invoice.is_quote) { field = field.replace('invoice', 'quote'); @@ -31584,7 +31584,7 @@ NINJA.invoiceDetails = function(invoice) { ], [ {text: (invoice.is_quote ? invoiceLabels.valid_until : invoiceLabels.due_date)}, - {text: invoice.due_date} + {text: invoice.is_recurring ? false : invoice.due_date} ] ]; diff --git a/public/css/built.css b/public/css/built.css index 484aeebab8c2..3c7c53828f1c 100644 --- a/public/css/built.css +++ b/public/css/built.css @@ -2539,6 +2539,10 @@ ul.dropdown-menu, box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } +.twitter-typeahead .tt-menu { + overflow-x: hidden; +} + .panel-default, canvas { border: 1px solid; diff --git a/public/css/style.css b/public/css/style.css index 0ac30b8a4694..801141c03cc9 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -410,6 +410,10 @@ ul.dropdown-menu, box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } +.twitter-typeahead .tt-menu { + overflow-x: hidden; +} + .panel-default, canvas { border: 1px solid; diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index db8e16f79442..ca728d4066c3 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -192,7 +192,7 @@ NINJA.decodeJavascript = function(invoice, javascript) field = toSnakeCase(field); var value = getDescendantProp(invoice, field); if (match.indexOf('?') < 0 || value) { - if (invoice.partial && field == 'balance_due') { + if (invoice.partial > 0 && field == 'balance_due') { field = 'partial_due'; } else if (invoice.is_quote) { field = field.replace('invoice', 'quote'); @@ -580,7 +580,7 @@ NINJA.invoiceDetails = function(invoice) { ], [ {text: (invoice.is_quote ? invoiceLabels.valid_until : invoiceLabels.due_date)}, - {text: invoice.due_date} + {text: invoice.is_recurring ? false : invoice.due_date} ] ]; diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f101c79564e7..11be9fb1d5d7 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1176,6 +1176,8 @@ $LANG = array( 'page_size' => 'Page Size', 'live_preview_disabled' => 'Live preview has been disabled to support selected font', 'invoice_number_padding' => 'Padding', + 'preview' => 'Preview', + 'list_vendors' => 'List Vendors', ); diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index cf9917deb005..cfb7b6eb28ee 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -94,9 +94,9 @@ @endif @if (Utils::isNinjaProd()) - {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} - @else - {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} + {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} + @elseif (!$account->hasFeature(FEATURE_WHITE_LABEL)) + {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} @endif @endif diff --git a/resources/views/accounts/template.blade.php b/resources/views/accounts/template.blade.php index 0d4294af5b6f..68279299f607 100644 --- a/resources/views/accounts/template.blade.php +++ b/resources/views/accounts/template.blade.php @@ -62,11 +62,14 @@
+

 

-
-

 

+

@include('partials/quill_toolbar', ['name' => $field])
+
+ {!! Button::primary(trans('texts.preview'))->withAttributes(['onclick' => 'serverPreview("'.$field.'")'])->small() !!} +
diff --git a/resources/views/accounts/templates_and_reminders.blade.php b/resources/views/accounts/templates_and_reminders.blade.php index 067433a21763..87453e80dae6 100644 --- a/resources/views/accounts/templates_and_reminders.blade.php +++ b/resources/views/accounts/templates_and_reminders.blade.php @@ -25,11 +25,16 @@ {!! Former::vertical_open()->addClass('warn-on-exit') !!} - {!! Former::populate($account) !!} @foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) @foreach (['subject', 'template'] as $field) - {!! Former::populateField("email_{$field}_{$type}", $templates[$type][$field]) !!} + {{ Former::populateField("email_{$field}_{$type}", $templates[$type][$field]) }} + @endforeach + @endforeach + + @foreach ([REMINDER1, REMINDER2, REMINDER3] as $type) + @foreach (['enable', 'num_days', 'direction', 'field'] as $field) + {{ Former::populateField("{$field}_{$type}", $account->{"{$field}_{$type}"}) }} @endforeach @endforeach @@ -80,6 +85,26 @@ + + +