mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
1bef3b0791
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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');
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -1,17 +1,13 @@
|
||||
<?php namespace App\Ninja\Mailers;
|
||||
|
||||
use Form;
|
||||
use HTML;
|
||||
use Utils;
|
||||
use Event;
|
||||
use URL;
|
||||
use Auth;
|
||||
|
||||
use App\Services\TemplateService;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Gateway;
|
||||
|
||||
use App\Events\InvoiceWasEmailed;
|
||||
use App\Events\QuoteWasEmailed;
|
||||
|
||||
@ -36,6 +32,11 @@ class ContactMailer extends Mailer
|
||||
'paymentButton',
|
||||
];
|
||||
|
||||
public function __construct(TemplateService $templateService)
|
||||
{
|
||||
$this->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'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false;
|
||||
$documentsHTML = '';
|
||||
|
||||
if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){
|
||||
$documentsHTML .= trans('texts.email_documents_header').'<ul>';
|
||||
foreach($invoice->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
foreach($invoice->expenses as $expense){
|
||||
foreach($expense->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
}
|
||||
$documentsHTML .= '</ul>';
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
80
app/Services/TemplateService.php
Normal file
80
app/Services/TemplateService.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php namespace App\Services;
|
||||
|
||||
use Form;
|
||||
use HTML;
|
||||
use Utils;
|
||||
use App\Models\Gateway;
|
||||
|
||||
class TemplateService
|
||||
{
|
||||
public function processVariables($template, $data)
|
||||
{
|
||||
$account = $data['account'];
|
||||
$client = $data['client'];
|
||||
$invitation = $data['invitation'];
|
||||
$invoice = $invitation->invoice;
|
||||
$passwordHTML = isset($data['password'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false;
|
||||
$documentsHTML = '';
|
||||
|
||||
if ($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()) {
|
||||
$documentsHTML .= trans('texts.email_documents_header').'<ul>';
|
||||
foreach($invoice->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
foreach($invoice->expenses as $expense){
|
||||
foreach($expense->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
}
|
||||
$documentsHTML .= '</ul>';
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -23,5 +23,6 @@ class DatabaseSeeder extends Seeder
|
||||
$this->call('DateFormatsSeeder');
|
||||
$this->call('InvoiceDesignsSeeder');
|
||||
$this->call('PaymentTermsSeeder');
|
||||
$this->call('LanguageSeeder');
|
||||
}
|
||||
}
|
||||
|
41
database/seeds/LanguageSeeder.php
Normal file
41
database/seeds/LanguageSeeder.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Language;
|
||||
|
||||
class LanguageSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
Eloquent::unguard();
|
||||
|
||||
$languages = [
|
||||
['name' => '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();
|
||||
}
|
||||
}
|
@ -19,5 +19,6 @@ class UpdateSeeder extends Seeder
|
||||
$this->call('DateFormatsSeeder');
|
||||
$this->call('InvoiceDesignsSeeder');
|
||||
$this->call('PaymentTermsSeeder');
|
||||
$this->call('LanguageSeeder');
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Font;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\Affiliate;
|
||||
use App\Models\Country;
|
||||
use App\Models\InvoiceDesign;
|
||||
use Faker\Factory;
|
||||
|
||||
class UserTableSeeder extends Seeder
|
||||
{
|
||||
@ -14,11 +18,25 @@ class UserTableSeeder extends Seeder
|
||||
|
||||
Eloquent::unguard();
|
||||
|
||||
$faker = Faker\Factory::create();
|
||||
$company = Company::create();
|
||||
|
||||
$account = Account::create([
|
||||
//'name' => '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([
|
||||
|
@ -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}
|
||||
]
|
||||
];
|
||||
|
||||
|
4
public/css/built.css
vendored
4
public/css/built.css
vendored
@ -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;
|
||||
|
4
public/css/style.css
vendored
4
public/css/style.css
vendored
@ -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;
|
||||
|
@ -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}
|
||||
]
|
||||
];
|
||||
|
||||
|
@ -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',
|
||||
|
||||
);
|
||||
|
||||
|
@ -94,9 +94,9 @@
|
||||
</div>
|
||||
@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
|
||||
</div>
|
||||
|
@ -62,11 +62,14 @@
|
||||
<div id="{{ $field }}_template_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p> <p/>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p> <p/>
|
||||
<div class="col-md-10">
|
||||
@include('partials/quill_toolbar', ['name' => $field])
|
||||
</div>
|
||||
<div class="col-md-2" style="padding-top:10px">
|
||||
{!! Button::primary(trans('texts.preview'))->withAttributes(['onclick' => 'serverPreview("'.$field.'")'])->small() !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" id="templatePreviewModal" tabindex="-1" role="dialog" aria-labelledby="templatePreviewModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="width:800px">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="templatePreviewModalLabel">{{ trans('texts.preview') }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<iframe id="server-preview" frameborder="1" width="100%" height="500px"/></iframe>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer" style="margin-top: 0px">
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ trans('texts.close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" id="templateHelpModal" tabindex="-1" role="dialog" aria-labelledby="templateHelpModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="min-width:150px">
|
||||
<div class="modal-content">
|
||||
@ -158,6 +183,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
function serverPreview(field) {
|
||||
console.log(field);
|
||||
$('#templatePreviewModal').modal('show');
|
||||
var template = $('#email_template_' + field).val();
|
||||
var url = '{{ URL::to('settings/email_preview') }}?template=' + template;
|
||||
$('#server-preview').attr('src', url).load(function() {
|
||||
// disable links in the preview
|
||||
$('iframe').contents().find('a').each(function(index) {
|
||||
$(this).on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(function() {
|
||||
for (var i=0; i<entityTypes.length; i++) {
|
||||
var entityType = entityTypes[i];
|
||||
|
@ -12,30 +12,30 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-collapse: collapse;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="{{ $account->primary_color ?: '#2E2B2B' }}" width="580" align="center" class="header"
|
||||
style="border-bottom-width: 6px; border-bottom-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-bottom-style: solid;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="#F4F5F5" width="600" align="center"
|
||||
class="header" style="border-top-width: 6px; border-top-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-top-style: solid;">
|
||||
<tr>
|
||||
<td class="logo" width="205" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<td class="logo" width="208" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
@include('emails.partials.account_logo')
|
||||
</td>
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 3px 0 0; padding: 0;">
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 0; padding: 2px 0 0;">
|
||||
@if ($invoice->due_date)
|
||||
<span style="font-size: 11px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.due_by', ['date' => $account->formatDate($invoice->due_date)])) }}
|
||||
</span><br />
|
||||
@endif
|
||||
<span style="font-size: 19px; color: #FFFFFF;">
|
||||
<span style="font-size: 18px;">
|
||||
{{ trans("texts.{$entityType}") }} {{ $invoice->invoice_number }}
|
||||
</span>
|
||||
</p>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p style="margin: 0; padding: 0;">
|
||||
<span style="font-size: 12px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.' . $invoice->present()->balanceDueLabel)) }}:
|
||||
<td style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="right" style="line-height: 14px; margin: 0; padding: 0;">
|
||||
<span style="font-size: 15px; color: #231F20;">
|
||||
{{ trans('texts.' . $invoice->present()->balanceDueLabel) }}:
|
||||
</span><br />
|
||||
<span class="total" style="font-size: 27px; color: #FFFFFF; margin-top: 5px;display: block;">
|
||||
<span class="total" style="font-size: 26px; display: block;margin-top: 5px;">
|
||||
{{ $account->formatMoney($invoice->getRequestedAmount(), $client) }}
|
||||
</span>
|
||||
</p>
|
||||
|
@ -12,30 +12,30 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-collapse: collapse;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="#F4F5F5" width="580" align="center"
|
||||
class="header" style="border-top-width: 6px; border-top-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-top-style: solid;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="{{ $account->primary_color ?: '#2E2B2B' }}" width="600" align="center" class="header"
|
||||
style="border-bottom-width: 6px; border-bottom-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-bottom-style: solid;">
|
||||
<tr>
|
||||
<td class="logo" width="208" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<td class="logo" width="205" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
@include('emails.partials.account_logo')
|
||||
</td>
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 0; padding: 2px 0 0;">
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 3px 0 0; padding: 0;">
|
||||
@if ($invoice->due_date)
|
||||
<span style="font-size: 11px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.due_by', ['date' => $account->formatDate($invoice->due_date)])) }}
|
||||
</span><br />
|
||||
@endif
|
||||
<span style="font-size: 18px;">
|
||||
<span style="font-size: 19px; color: #FFFFFF;">
|
||||
{{ trans("texts.{$entityType}") }} {{ $invoice->invoice_number }}
|
||||
</span>
|
||||
</p>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="right" style="line-height: 14px; margin: 0; padding: 0;">
|
||||
<span style="font-size: 15px; color: #231F20;">
|
||||
{{ trans('texts.' . $invoice->present()->balanceDueLabel) }}:
|
||||
<td style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p style="margin: 0; padding: 0;">
|
||||
<span style="font-size: 12px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.' . $invoice->present()->balanceDueLabel)) }}:
|
||||
</span><br />
|
||||
<span class="total" style="font-size: 26px; display: block;margin-top: 5px;">
|
||||
<span class="total" style="font-size: 27px; color: #FFFFFF; margin-top: 5px;display: block;">
|
||||
{{ $account->formatMoney($invoice->getRequestedAmount(), $client) }}
|
||||
</span>
|
||||
</p>
|
@ -3,7 +3,7 @@
|
||||
<a href="{{ $account->website }}" style="color: #19BB40; text-decoration: underline;">
|
||||
@endif
|
||||
|
||||
<img src="{{ $message->embed($account->getLogoURL()) }}" style="max-height:50px; max-width:140px; margin-left: 33px;" />
|
||||
<img src="{{ isset($message) ? $message->embed($account->getLogoURL()) : $account->getLogoURL() }}" style="max-height:50px; max-width:140px; margin-left: 33px;" />
|
||||
|
||||
@if ($account->website)
|
||||
</a>
|
||||
|
@ -93,14 +93,14 @@
|
||||
<div class="container main-container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 logo">
|
||||
<div class="col-md-6 logo">
|
||||
@if ($account->hasLogo())
|
||||
{!! HTML::image($account->getLogoURL()) !!}
|
||||
@else
|
||||
<h2>{{ $account->name}}</h2>
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-3 address-details">
|
||||
<div class="col-md-3 address-details">
|
||||
@if ($account->address1)
|
||||
{{ $account->address1 }}<br/>
|
||||
@endif
|
||||
|
@ -73,7 +73,12 @@
|
||||
<div class="form-group">
|
||||
<label for="client" class="control-label col-lg-4 col-sm-4">{{ trans('texts.client') }}</label>
|
||||
<div class="col-lg-8 col-sm-8">
|
||||
<h4><div data-bind="text: getClientDisplayName(ko.toJS(client()))"></div></h4>
|
||||
<h4>
|
||||
<span data-bind="text: getClientDisplayName(ko.toJS(client()))"></span>
|
||||
@if ($invoice->client->is_deleted)
|
||||
<div class="label label-danger">{{ trans('texts.deleted') }}</div>
|
||||
@endif
|
||||
</h4>
|
||||
|
||||
@can('view', $invoice->client)
|
||||
@can('edit', $invoice->client)
|
||||
@ -109,6 +114,7 @@
|
||||
<input type="checkbox" value="1" data-bind="checked: send_invoice, attr: {id: $index() + '_check', name: 'client[contacts][' + $index() + '][send_invoice]'}">
|
||||
<span data-bind="html: email.display"></span>
|
||||
</label>
|
||||
@if ( ! $invoice->is_deleted && ! $invoice->client->is_deleted)
|
||||
<span data-bind="visible: !$root.invoice().is_recurring()">
|
||||
<span data-bind="html: $data.view_as_recipient"></span>
|
||||
@if (Utils::isConfirmed())
|
||||
@ -119,6 +125,7 @@
|
||||
style: {color: $data.info_color}"></span>
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -66,6 +66,7 @@
|
||||
}
|
||||
|
||||
var currency = currencyMap[currencyId];
|
||||
var precision = currency.precision;
|
||||
var thousand = currency.thousand_separator;
|
||||
var decimal = currency.decimal_separator;
|
||||
var code = currency.code;
|
||||
@ -82,7 +83,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
value = accounting.formatMoney(value, '', 2, thousand, decimal);
|
||||
value = accounting.formatMoney(value, '', precision, thousand, decimal);
|
||||
var symbol = currency.symbol;
|
||||
|
||||
if (hideSymbol) {
|
||||
|
@ -96,7 +96,7 @@ class OnlinePaymentCest
|
||||
$I->click('table.invoice-table tbody tr:nth-child(1) .tt-selectable');
|
||||
$I->checkOption('#auto_bill');
|
||||
$I->executeJS('preparePdfData(\'email\')');
|
||||
$I->wait(2);
|
||||
$I->wait(3);
|
||||
$I->see("$0.00");
|
||||
|
||||
}
|
||||
|
@ -84,7 +84,8 @@ class TaxRatesCest
|
||||
// check total is right after saving
|
||||
$I->see("\${$total}");
|
||||
$I->amOnPage('/invoices');
|
||||
|
||||
$I->wait(2);
|
||||
|
||||
// check total is right in list view
|
||||
$I->see("\${$total}");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user