Working on invoice customizations

This commit is contained in:
Hillel Coren 2015-07-24 17:13:17 +03:00
parent 67a6d66bd0
commit 912fe5b7a3
19 changed files with 248 additions and 91 deletions

View File

@ -188,7 +188,7 @@ class AccountController extends BaseController
} elseif ($section == ACCOUNT_IMPORT_EXPORT) { } elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]); return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) { } elseif ($section == ACCOUNT_ADVANCED_SETTINGS) {
$account = Auth::user()->account; $account = Auth::user()->account->load('country');
$data = [ $data = [
'account' => $account, 'account' => $account,
'feature' => $subSection, 'feature' => $subSection,
@ -215,8 +215,8 @@ class AccountController extends BaseController
$invoice->account = json_decode($account->toJson()); $invoice->account = json_decode($account->toJson());
$invoice->amount = $invoice->balance = 100; $invoice->amount = $invoice->balance = 100;
$invoice->terms = $account->invoice_terms; $invoice->terms = trim($account->invoice_terms);
$invoice->invoice_footer = $account->invoice_footer; $invoice->invoice_footer = trim($account->invoice_footer);
$contact->email = 'contact@gmail.com'; $contact->email = 'contact@gmail.com';
$client->contacts = [$contact]; $client->contacts = [$contact];
@ -244,7 +244,7 @@ class AccountController extends BaseController
} }
if ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) { if ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
$data['customDesign'] = $account->custom_design ?: $design; $data['customDesign'] = $account->custom_design && !$design ? $account->custom_design : $design;
} }
} else if ($subSection == ACCOUNT_EMAIL_TEMPLATES) { } else if ($subSection == ACCOUNT_EMAIL_TEMPLATES) {
$data['invoiceEmail'] = $account->getEmailTemplate(ENTITY_INVOICE); $data['invoiceEmail'] = $account->getEmailTemplate(ENTITY_INVOICE);
@ -701,14 +701,22 @@ class AccountController extends BaseController
$image = Image::make($path); $image = Image::make($path);
$mimeType = $file->getMimeType(); $mimeType = $file->getMimeType();
if ($image->width() == 200 && $mimeType == 'image/jpeg') { if ($mimeType == 'image/jpeg') {
$file->move('logo/', $account->account_key . '.jpg'); $file->move('logo/', $account->account_key . '.jpg');
} else if ($mimeType == 'image/png') {
$file->move('logo/', $account->account_key . '.png');
if (!$account->utf8_invoices) {
$account->utf8_invoices = true;
$account->save();
}
} else { } else {
$image->resize(200, 120, function ($constraint) { $image->resize(200, 120, function ($constraint) {
$constraint->aspectRatio(); $constraint->aspectRatio();
}); });
Image::canvas($image->width(), $image->height(), '#FFFFFF')->insert($image)->save($account->getLogoPath()); Image::canvas($image->width(), $image->height(), '#FFFFFF')
->insert($image)->save('logo/'.$this->account_key.'.jpg');
} }
} }

View File

@ -44,6 +44,8 @@ class DashboardController extends BaseController
->where('accounts.id', '=', Auth::user()->account_id) ->where('accounts.id', '=', Auth::user()->account_id)
->where('clients.is_deleted', '=', false) ->where('clients.is_deleted', '=', false)
->where('invoices.is_deleted', '=', false) ->where('invoices.is_deleted', '=', false)
->where('invoices.is_quote', '=', false)
->where('invoices.is_recurring', '=', false)
->groupBy('accounts.id') ->groupBy('accounts.id')
->groupBy(DB::raw('CASE WHEN clients.currency_id IS NULL THEN CASE WHEN accounts.currency_id IS NULL THEN 1 ELSE accounts.currency_id END ELSE clients.currency_id END')) ->groupBy(DB::raw('CASE WHEN clients.currency_id IS NULL THEN CASE WHEN accounts.currency_id IS NULL THEN 1 ELSE accounts.currency_id END ELSE clients.currency_id END'))
->get(); ->get();

View File

@ -329,7 +329,6 @@ class InvoiceController extends BaseController
$data = array( $data = array(
'entityType' => $entityType, 'entityType' => $entityType,
'showBreadcrumbs' => $clone, 'showBreadcrumbs' => $clone,
'account' => $invoice->account,
'invoice' => $invoice, 'invoice' => $invoice,
'data' => false, 'data' => false,
'method' => $method, 'method' => $method,
@ -364,7 +363,6 @@ class InvoiceController extends BaseController
{ {
$client = null; $client = null;
$invoiceNumber = Auth::user()->account->getNextInvoiceNumber(); $invoiceNumber = Auth::user()->account->getNextInvoiceNumber();
$account = Account::with('country')->findOrFail(Auth::user()->account_id);
if ($clientPublicId) { if ($clientPublicId) {
$client = Client::scope($clientPublicId)->firstOrFail(); $client = Client::scope($clientPublicId)->firstOrFail();
@ -372,7 +370,6 @@ class InvoiceController extends BaseController
$data = array( $data = array(
'entityType' => ENTITY_INVOICE, 'entityType' => ENTITY_INVOICE,
'account' => $account,
'invoice' => null, 'invoice' => null,
'data' => Input::old('data'), 'data' => Input::old('data'),
'invoiceNumber' => $invoiceNumber, 'invoiceNumber' => $invoiceNumber,
@ -399,7 +396,7 @@ class InvoiceController extends BaseController
} }
return [ return [
'account' => Auth::user()->account, 'account' => Auth::user()->account->load('country'),
'products' => Product::scope()->orderBy('id')->get(array('product_key', 'notes', 'cost', 'qty')), 'products' => Product::scope()->orderBy('id')->get(array('product_key', 'notes', 'cost', 'qty')),
'countries' => Cache::get('countries'), 'countries' => Cache::get('countries'),
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(), 'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),

View File

@ -365,12 +365,15 @@ define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
define('NINJA_APP_URL', 'https://app.invoiceninja.com'); define('NINJA_APP_URL', 'https://app.invoiceninja.com');
define('NINJA_VERSION', '2.2.2'); define('NINJA_VERSION', '2.2.2');
define('NINJA_DATE', '2000-01-01'); define('NINJA_DATE', '2000-01-01');
define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'); define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com');
define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/'); define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/');
define('ZAPIER_URL', 'https://zapier.com/developer/invite/11276/85cf0ee4beae8e802c6c579eb4e351f1/'); define('ZAPIER_URL', 'https://zapier.com/developer/invite/11276/85cf0ee4beae8e802c6c579eb4e351f1/');
define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'); define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/');
define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html');
define('COUNT_FREE_DESIGNS', 4); define('COUNT_FREE_DESIGNS', 4);
define('COUNT_FREE_DESIGNS_SELF_HOST', 5); // include the custom design
define('PRODUCT_ONE_CLICK_INSTALL', 1); define('PRODUCT_ONE_CLICK_INSTALL', 1);
define('PRODUCT_INVOICE_DESIGNS', 2); define('PRODUCT_INVOICE_DESIGNS', 2);
define('PRODUCT_WHITE_LABEL', 3); define('PRODUCT_WHITE_LABEL', 3);
@ -436,7 +439,6 @@ function otrans($text)
} }
} }
/*
// Log all SQL queries to laravel.log // Log all SQL queries to laravel.log
Event::listen('illuminate.query', function($query, $bindings, $time, $name) Event::listen('illuminate.query', function($query, $bindings, $time, $name)
{ {
@ -461,7 +463,6 @@ Event::listen('illuminate.query', function($query, $bindings, $time, $name)
Log::info($query, $data); Log::info($query, $data);
}); });
*/
/* /*
if (Auth::check() && Auth::user()->id === 1) if (Auth::check() && Auth::user()->id === 1)

View File

@ -146,7 +146,8 @@ class Account extends Eloquent
public function getLogoPath() public function getLogoPath()
{ {
return 'logo/'.$this->account_key.'.jpg'; $fileName = 'logo/' . $this->account_key;
return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg';
} }
public function getLogoWidth() public function getLogoWidth()
@ -267,6 +268,9 @@ class Account extends Eloquent
'balance', 'balance',
'from', 'from',
'to', 'to',
'invoice_to',
'details',
'invoice_no',
]; ];
foreach ($fields as $field) { foreach ($fields as $field) {
@ -418,7 +422,7 @@ class Account extends Eloquent
Account::updating(function ($account) { Account::updating(function ($account) {
// Lithuanian requires UTF8 support // Lithuanian requires UTF8 support
if (!Utils::isPro()) { if (!Utils::isPro() && $account->language_id == 13) {
$account->utf8_invoices = ($account->language_id == 13) ? 1 : 0; $account->utf8_invoices = true;
} }
}); });

View File

@ -100,7 +100,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
public function maxInvoiceDesignId() public function maxInvoiceDesignId()
{ {
return $this->isPro() ? 11 : COUNT_FREE_DESIGNS; return $this->isPro() ? 11 : (Utils::isNinja() ? COUNT_FREE_DESIGNS : COUNT_FREE_DESIGNS_SELF_HOST);
} }
public function getDisplayName() public function getDisplayName()

View File

@ -33229,6 +33229,17 @@ function twoDigits(value) {
} }
return value; return value;
} }
function toSnakeCase(str) {
if (!str) return '';
return str.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
}
function getDescendantProp(obj, desc) {
var arr = desc.split(".");
while(arr.length && (obj = obj[arr.shift()]));
return obj;
}
var NINJA = NINJA || {}; var NINJA = NINJA || {};
NINJA.TEMPLATES = { NINJA.TEMPLATES = {
@ -33245,8 +33256,6 @@ NINJA.TEMPLATES = {
}; };
function GetPdfMake(invoice, javascript, callback) { function GetPdfMake(invoice, javascript, callback) {
//console.log("== GetPdfMake.. ");
//console.log(javascript);
javascript = NINJA.decodeJavascript(invoice, javascript); javascript = NINJA.decodeJavascript(invoice, javascript);
@ -33265,6 +33274,11 @@ function GetPdfMake(invoice, javascript, callback) {
return function (i, node) { return function (i, node) {
return i === 0 ? 0 : parseFloat(parts[1]); return i === 0 ? 0 : parseFloat(parts[1]);
}; };
} else if ((val+'').indexOf('$border') === 0) {
var parts = val.split(':');
return function (i, node) {
return parseFloat(parts[1]);
};
} else if ((val+'').indexOf('$padding') === 0) { } else if ((val+'').indexOf('$padding') === 0) {
var parts = val.split(':'); var parts = val.split(':');
return function (i, node) { return function (i, node) {
@ -33284,6 +33298,11 @@ function GetPdfMake(invoice, javascript, callback) {
//console.log(javascript); //console.log(javascript);
var dd = JSON.parse(javascript, jsonCallBack); var dd = JSON.parse(javascript, jsonCallBack);
if (!invoice.is_pro && dd.footer.hasOwnProperty('columns')) {
dd.footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130})
}
//console.log(JSON.stringify(dd)); //console.log(JSON.stringify(dd));
/* /*
@ -33309,6 +33328,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
var account = invoice.account; var account = invoice.account;
var blankImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII='; var blankImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
// search/replace variables
var json = { var json = {
'accountName': account.name || ' ', 'accountName': account.name || ' ',
'accountLogo': window.accountLogo || blankImage, 'accountLogo': window.accountLogo || blankImage,
@ -33326,14 +33346,19 @@ NINJA.decodeJavascript = function(invoice, javascript)
'balanceDue': formatMoney(invoice.balance_amount, invoice.client.currency_id), 'balanceDue': formatMoney(invoice.balance_amount, invoice.client.currency_id),
'balanceDueLabel': invoiceLabels.balance_due, 'balanceDueLabel': invoiceLabels.balance_due,
'invoiceFooter': account.invoice_footer || ' ', 'invoiceFooter': account.invoice_footer || ' ',
'invoiceNumber': invoice.invoice_number || ' ',
'entityType': invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice, 'entityType': invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice,
'entityTypeUpper': (invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(), 'entityTypeUpper': (invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(),
'yourInvoice': invoiceLabels.your_invoice, 'yourInvoice': invoiceLabels.your_invoice,
'yourInvoiceUpper': invoiceLabels.your_invoice.toUpperCase(), 'yourInvoiceUpper': invoiceLabels.your_invoice.toUpperCase(),
'invoiceIssuedTo': invoiceLabels.invoice_issued_to + ':', 'invoiceIssuedTo': invoiceLabels.invoice_issued_to + ':',
'invoiceTo': invoiceLabels.invoice_to + ':',
'details': invoiceLabels.details + ':',
'fromUpper': invoiceLabels.from.toUpperCase() + ':',
'toUpper': invoiceLabels.to.toUpperCase() + ':',
'fontSize': NINJA.fontSize, 'fontSize': NINJA.fontSize,
'fontSizeLarger': NINJA.fontSize + 1, 'fontSizeLarger': NINJA.fontSize + 1,
'fontSizeLargest': NINJA.fontSize + 2, 'fontSizeLargest': NINJA.fontSize + 2,
} }
for (var key in json) { for (var key in json) {
@ -33342,6 +33367,41 @@ NINJA.decodeJavascript = function(invoice, javascript)
javascript = javascript.replace(regExp, val); javascript = javascript.replace(regExp, val);
} }
// search/replace labels
var regExp = new RegExp('"\\$\\\w*?Label(UC)?"', 'g');
var matches = javascript.match(regExp);
if (matches) {
for (var i=0; i<matches.length; i++) {
var match = matches[i];
field = match.substring(2, match.indexOf('Label'));
field = toSnakeCase(field);
var label = invoiceLabels[field];
if (match.indexOf('UC') >= 0) {
if (!label) console.log('match: ' + field);
label = label.toUpperCase();
}
javascript = javascript.replace(match, '"'+label+'"');
}
}
// search/replace values
var regExp = new RegExp('"\\$\\\w*?Value"', 'g');
var matches = javascript.match(regExp);
if (matches) {
for (var i=0; i<matches.length; i++) {
var match = matches[i];
field = match.substring(2, match.indexOf('Value'));
field = toSnakeCase(field);
var value = getDescendantProp(invoice, field) || ' ';
if (field.indexOf('date') >= 0) {
value = moment(value, 'YYYY-MM-DD').format('MMM D YYYY');
}
javascript = javascript.replace(match, '"'+value+'"');
}
}
return javascript; return javascript;
} }
@ -33355,7 +33415,7 @@ NINJA.notesAndTerms = function(invoice)
} }
if (invoice.terms) { if (invoice.terms) {
data.push({text:invoiceLabels.terms, style: ['bold']}); data.push({text:invoiceLabels.terms, style: ['termsLabel']});
data.push({text:invoice.terms, style: ['terms']}); data.push({text:invoice.terms, style: ['terms']});
} }
@ -33405,8 +33465,7 @@ NINJA.invoiceLines = function(invoice) {
} }
// show at most one blank line // show at most one blank line
//if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) { if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
if ((!cost || cost == '0.00') && !notes && !productKey) {
continue; continue;
} }
@ -33490,7 +33549,6 @@ NINJA.subtotals = function(invoice, removeBalance)
]); ]);
} }
//return data;
return NINJA.prepareDataPairs(data, 'subtotals'); return NINJA.prepareDataPairs(data, 'subtotals');
} }
@ -33559,7 +33617,7 @@ NINJA.invoiceDetails = function(invoice) {
} }
data.push([ data.push([
{text: invoiceLabels.balance_due}, {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style: ['invoiceDetailBalanceDue']} {text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style: ['invoiceDetailBalanceDue']}
]) ])

View File

@ -14,8 +14,6 @@ NINJA.TEMPLATES = {
}; };
function GetPdfMake(invoice, javascript, callback) { function GetPdfMake(invoice, javascript, callback) {
//console.log("== GetPdfMake.. ");
//console.log(javascript);
javascript = NINJA.decodeJavascript(invoice, javascript); javascript = NINJA.decodeJavascript(invoice, javascript);
@ -34,6 +32,11 @@ function GetPdfMake(invoice, javascript, callback) {
return function (i, node) { return function (i, node) {
return i === 0 ? 0 : parseFloat(parts[1]); return i === 0 ? 0 : parseFloat(parts[1]);
}; };
} else if ((val+'').indexOf('$border') === 0) {
var parts = val.split(':');
return function (i, node) {
return parseFloat(parts[1]);
};
} else if ((val+'').indexOf('$padding') === 0) { } else if ((val+'').indexOf('$padding') === 0) {
var parts = val.split(':'); var parts = val.split(':');
return function (i, node) { return function (i, node) {
@ -53,6 +56,11 @@ function GetPdfMake(invoice, javascript, callback) {
//console.log(javascript); //console.log(javascript);
var dd = JSON.parse(javascript, jsonCallBack); var dd = JSON.parse(javascript, jsonCallBack);
if (!invoice.is_pro && dd.footer.hasOwnProperty('columns')) {
dd.footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130})
}
//console.log(JSON.stringify(dd)); //console.log(JSON.stringify(dd));
/* /*
@ -78,6 +86,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
var account = invoice.account; var account = invoice.account;
var blankImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII='; var blankImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
// search/replace variables
var json = { var json = {
'accountName': account.name || ' ', 'accountName': account.name || ' ',
'accountLogo': window.accountLogo || blankImage, 'accountLogo': window.accountLogo || blankImage,
@ -95,14 +104,19 @@ NINJA.decodeJavascript = function(invoice, javascript)
'balanceDue': formatMoney(invoice.balance_amount, invoice.client.currency_id), 'balanceDue': formatMoney(invoice.balance_amount, invoice.client.currency_id),
'balanceDueLabel': invoiceLabels.balance_due, 'balanceDueLabel': invoiceLabels.balance_due,
'invoiceFooter': account.invoice_footer || ' ', 'invoiceFooter': account.invoice_footer || ' ',
'invoiceNumber': invoice.invoice_number || ' ',
'entityType': invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice, 'entityType': invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice,
'entityTypeUpper': (invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(), 'entityTypeUpper': (invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(),
'yourInvoice': invoiceLabels.your_invoice, 'yourInvoice': invoiceLabels.your_invoice,
'yourInvoiceUpper': invoiceLabels.your_invoice.toUpperCase(), 'yourInvoiceUpper': invoiceLabels.your_invoice.toUpperCase(),
'invoiceIssuedTo': invoiceLabels.invoice_issued_to + ':', 'invoiceIssuedTo': invoiceLabels.invoice_issued_to + ':',
'invoiceTo': invoiceLabels.invoice_to + ':',
'details': invoiceLabels.details + ':',
'fromUpper': invoiceLabels.from.toUpperCase() + ':',
'toUpper': invoiceLabels.to.toUpperCase() + ':',
'fontSize': NINJA.fontSize, 'fontSize': NINJA.fontSize,
'fontSizeLarger': NINJA.fontSize + 1, 'fontSizeLarger': NINJA.fontSize + 1,
'fontSizeLargest': NINJA.fontSize + 2, 'fontSizeLargest': NINJA.fontSize + 2,
} }
for (var key in json) { for (var key in json) {
@ -111,6 +125,41 @@ NINJA.decodeJavascript = function(invoice, javascript)
javascript = javascript.replace(regExp, val); javascript = javascript.replace(regExp, val);
} }
// search/replace labels
var regExp = new RegExp('"\\$\\\w*?Label(UC)?"', 'g');
var matches = javascript.match(regExp);
if (matches) {
for (var i=0; i<matches.length; i++) {
var match = matches[i];
field = match.substring(2, match.indexOf('Label'));
field = toSnakeCase(field);
var label = invoiceLabels[field];
if (match.indexOf('UC') >= 0) {
if (!label) console.log('match: ' + field);
label = label.toUpperCase();
}
javascript = javascript.replace(match, '"'+label+'"');
}
}
// search/replace values
var regExp = new RegExp('"\\$\\\w*?Value"', 'g');
var matches = javascript.match(regExp);
if (matches) {
for (var i=0; i<matches.length; i++) {
var match = matches[i];
field = match.substring(2, match.indexOf('Value'));
field = toSnakeCase(field);
var value = getDescendantProp(invoice, field) || ' ';
if (field.indexOf('date') >= 0) {
value = moment(value, 'YYYY-MM-DD').format('MMM D YYYY');
}
javascript = javascript.replace(match, '"'+value+'"');
}
}
return javascript; return javascript;
} }
@ -124,7 +173,7 @@ NINJA.notesAndTerms = function(invoice)
} }
if (invoice.terms) { if (invoice.terms) {
data.push({text:invoiceLabels.terms, style: ['bold']}); data.push({text:invoiceLabels.terms, style: ['termsLabel']});
data.push({text:invoice.terms, style: ['terms']}); data.push({text:invoice.terms, style: ['terms']});
} }
@ -174,8 +223,7 @@ NINJA.invoiceLines = function(invoice) {
} }
// show at most one blank line // show at most one blank line
//if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) { if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
if ((!cost || cost == '0.00') && !notes && !productKey) {
continue; continue;
} }
@ -259,7 +307,6 @@ NINJA.subtotals = function(invoice, removeBalance)
]); ]);
} }
//return data;
return NINJA.prepareDataPairs(data, 'subtotals'); return NINJA.prepareDataPairs(data, 'subtotals');
} }
@ -328,7 +375,7 @@ NINJA.invoiceDetails = function(invoice) {
} }
data.push([ data.push([
{text: invoiceLabels.balance_due}, {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style: ['invoiceDetailBalanceDue']} {text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style: ['invoiceDetailBalanceDue']}
]) ])

View File

@ -1592,4 +1592,15 @@ function twoDigits(value) {
return '0' + value; return '0' + value;
} }
return value; return value;
}
function toSnakeCase(str) {
if (!str) return '';
return str.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
}
function getDescendantProp(obj, desc) {
var arr = desc.split(".");
while(arr.length && (obj = obj[arr.shift()]));
return obj;
} }

View File

@ -13,7 +13,7 @@
{"text":"$yourInvoiceUpper", "style": "yourInvoice"}, {"text":"$yourInvoiceUpper", "style": "yourInvoice"},
"$clientDetails" "$clientDetails"
], ],
"margin": [-32, 150, 0, 0] "margin": [-32, 120, 0, 0]
}, },
{ {
"canvas": [ "canvas": [
@ -22,21 +22,21 @@
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 225, "w": 225,
"h": 80, "h": "$invoiceDetailsHeight",
"r":0, "r":0,
"lineWidth": 1, "lineWidth": 1,
"color": "#36a399" "color": "$primaryColor:#36a498"
} }
], ],
"width":10, "width":10,
"margin":[-10,150,0,0] "margin":[-10,120,0,0]
}, },
{ {
"table": { "table": {
"body": "$invoiceDetails" "body": "$invoiceDetails"
}, },
"layout": "noBorders", "layout": "noBorders",
"margin": [0, 160, 0, 0] "margin": [0, 130, 0, 0]
} }
] ]
}, },
@ -93,8 +93,21 @@
{"canvas": [{ "type": "line", "x1": 0, "y1": 0, "x2": 150, "y2":0,"lineWidth": 60,"lineColor":"#2e2b2b"}],"width":100,"margin":[0,0,0,0]}, {"canvas": [{ "type": "line", "x1": 0, "y1": 0, "x2": 150, "y2":0,"lineWidth": 60,"lineColor":"#2e2b2b"}],"width":100,"margin":[0,0,0,0]},
{"canvas": [{ "type": "line", "x1": 149, "y1": 0, "x2": 600, "y2":0,"lineWidth": 200,"lineColor":"#2e2b2b"}],"width":10,"margin":[0,0,0,0]}, {"canvas": [{ "type": "line", "x1": 149, "y1": 0, "x2": 600, "y2":0,"lineWidth": 200,"lineColor":"#2e2b2b"}],"width":10,"margin":[0,0,0,0]},
{ {
"stack": "$accountDetails", "columns": [
"margin": [380, 16, 0, 0] {
"text": " ",
"width": 260
},
{
"stack": "$accountDetails",
"margin": [0, 16, 0, 0],
"width": 140
},
{
"stack": "$accountAddress",
"margin": [20, 16, 0, 0]
}
]
} }
], ],
"defaultStyle": { "defaultStyle": {
@ -103,22 +116,27 @@
}, },
"styles": { "styles": {
"primaryColor":{ "primaryColor":{
"color": "$primaryColor:#299CC2" "color": "$primaryColor:#36a498"
}, },
"accountName": { "accountName": {
"bold": true,
"margin": [4, 2, 4, 2], "margin": [4, 2, 4, 2],
"color": "$primaryColor:#299CC2" "color": "$primaryColor:#36a498"
}, },
"accountDetails": { "accountDetails": {
"margin": [4, 2, 4, 2], "margin": [4, 2, 4, 2],
"color": "#AAA9A9" "color": "#AAA9A9"
}, },
"accountAddress": {
"margin": [4, 2, 4, 2],
"color": "#AAA9A9"
},
"odd": { "odd": {
"fillColor": "#ebebeb", "fillColor": "#ebebeb",
"margin": [0,0,0,0] "margin": [0,0,0,0]
}, },
"productKey": { "productKey": {
"color": "$primaryColor:#299CC2" "color": "$primaryColor:#36a498"
}, },
"balanceDueLabel": { "balanceDueLabel": {
"fontSize": "$fontSizeLargest", "fontSize": "$fontSizeLargest",
@ -126,7 +144,7 @@
}, },
"balanceDue": { "balanceDue": {
"fontSize": "$fontSizeLargest", "fontSize": "$fontSizeLargest",
"color": "$primaryColor:#299CC2", "color": "$primaryColor:#36a498",
"bold": true "bold": true
}, },
"invoiceDetails": { "invoiceDetails": {
@ -146,18 +164,18 @@
"bold": true "bold": true
}, },
"productKey": { "productKey": {
"color": "$primaryColor:#299CC2", "color": "$primaryColor:#36a498",
"margin": [40,0,0,0], "margin": [40,0,0,0],
"bold": true "bold": true
}, },
"yourInvoice": { "yourInvoice": {
"bold": true, "bold": true,
"fontSize": 14, "fontSize": 14,
"color": "#36a399", "color": "$primaryColor:#36a498",
"margin": [0,0,0,8] "margin": [0,0,0,8]
}, },
"invoiceLineItemsTable": { "invoiceLineItemsTable": {
"margin": [0, 16, 0, 16] "margin": [0, 26, 0, 16]
}, },
"clientName": { "clientName": {
"bold": true "bold": true
@ -181,7 +199,7 @@
}, },
"termsLabel": { "termsLabel": {
"bold": true, "bold": true,
"margin": [0, 10, 0, 4] "margin": [0, 0, 0, 4]
} }
}, },
"pageMargins": [0, 80, 0, 40] "pageMargins": [0, 80, 0, 40]

View File

@ -94,20 +94,26 @@
"margin": [8, 4, 8, 4] "margin": [8, 4, 8, 4]
}, },
"footer": { "footer": {
"text": "$invoiceFooter", "columns": [
"margin": [40, -20, 40, 0], {
"alignment": "left" "text": "$invoiceFooter",
"alignment": "left",
"margin": [0, 0, 0, 12]
}
],
"margin": [40, -20, 40, 40]
}, },
"styles": { "styles": {
"entityTypeLabel": { "entityTypeLabel": {
"fontSize": "$fontSizeLargest", "fontSize": "$fontSizeLargest",
"color": "$primaryColor:#299CC2" "color": "$primaryColor:#37a3c6"
}, },
"primaryColor":{ "primaryColor":{
"color": "$primaryColor:#299CC2" "color": "$primaryColor:#37a3c6"
}, },
"accountName": { "accountName": {
"color": "$primaryColor:#299CC2", "color": "$primaryColor:#37a3c6",
"bold": true "bold": true
}, },
"accountDetails": { "accountDetails": {
@ -126,7 +132,7 @@
"fillColor": "#fbfbfb" "fillColor": "#fbfbfb"
}, },
"productKey": { "productKey": {
"color": "$primaryColor:#299CC2", "color": "$primaryColor:#37a3c6",
"bold": true "bold": true
}, },
"balanceDueLabel": { "balanceDueLabel": {
@ -134,7 +140,7 @@
}, },
"balanceDue": { "balanceDue": {
"fontSize": "$fontSizeLargest", "fontSize": "$fontSizeLargest",
"color": "$primaryColor:#299CC2" "color": "$primaryColor:#37a3c6"
}, },
"invoiceNumber": { "invoiceNumber": {
"bold": true "bold": true
@ -166,8 +172,8 @@
}, },
"termsLabel": { "termsLabel": {
"bold": true, "bold": true,
"margin": [0, 10, 0, 4] "margin": [0, 0, 0, 4]
} }
}, },
"pageMargins": [40, 40, 40, 40] "pageMargins": [40, 40, 40, 60]
} }

View File

@ -91,7 +91,7 @@
{ {
"canvas": [ "canvas": [
{ {
"type": "line", "x1": 0, "y1": 0, "x2": 600, "y2": 0,"lineWidth": 100,"lineColor":"#f26621" "type": "line", "x1": 0, "y1": 0, "x2": 600, "y2": 0,"lineWidth": 100,"lineColor":"$primaryColor:#f26621"
}] }]
,"width":10 ,"width":10
}, },
@ -119,7 +119,7 @@
], ],
"header": [ "header": [
{ {
"canvas": [{ "type": "line", "x1": 0, "y1": 0, "x2": 600, "y2": 0,"lineWidth": 200,"lineColor":"#f26621"}],"width":10 "canvas": [{ "type": "line", "x1": 0, "y1": 0, "x2": 600, "y2": 0,"lineWidth": 200,"lineColor":"$primaryColor:#f26621"}],"width":10
}, },
{ {
"columns": [ "columns": [
@ -206,7 +206,7 @@
}, },
"termsLabel": { "termsLabel": {
"bold": true, "bold": true,
"margin": [0, 10, 0, 4] "margin": [0, 0, 0, 4]
}, },
"invoiceNumberLabel": { "invoiceNumberLabel": {
"bold": true "bold": true

View File

@ -82,10 +82,16 @@
} }
], ],
"footer": { "footer": {
"text": "$invoiceFooter", "columns": [
"margin": [40, -40, 40, 0], {
"alignment": "left" "text": "$invoiceFooter",
}, "alignment": "left",
"margin": [0, 0, 0, 12]
}
],
"margin": [40, -20, 40, 40]
},
"defaultStyle": { "defaultStyle": {
"fontSize": "$fontSize", "fontSize": "$fontSize",
"margin": [8, 4, 8, 4] "margin": [8, 4, 8, 4]
@ -126,8 +132,8 @@
}, },
"termsLabel": { "termsLabel": {
"bold": true, "bold": true,
"margin": [0, 10, 0, 4] "margin": [0, 0, 0, 4]
} }
}, },
"pageMargins": [40, 40, 40, 40] "pageMargins": [40, 40, 40, 60]
} }

View File

@ -736,7 +736,8 @@ return array(
'header' => 'Header', 'header' => 'Header',
'footer' => 'Footer', 'footer' => 'Footer',
'custom' => 'Custom', 'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
); );

View File

@ -79,16 +79,16 @@
function onSelectChange() function onSelectChange()
{ {
var id = $('#invoice_design_id').val(); var id = $('#invoice_design_id').val();
if (parseInt(id)) { if (parseInt(id)) {
customDesign = JSON.parse(invoiceDesigns[id-1].javascript); var design = _.find(invoiceDesigns, function(design){ return design.id == id});
customDesign = JSON.parse(design.javascript);
} else { } else {
customDesign = origCustomDesign; customDesign = origCustomDesign;
} }
loadEditor(editorSection); loadEditor(editorSection);
refreshPDF(true); refreshPDF(true);
} }
function submitForm() function submitForm()
@ -104,9 +104,6 @@
var options = { var options = {
mode: 'form', mode: 'form',
modes: ['form', 'code'], modes: ['form', 'code'],
error: function (err) {
console.error(err.toString());
},
change: function() { change: function() {
saveEditor(); saveEditor();
} }
@ -127,7 +124,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
{!! Former::open()->addClass('warn-on-exit')->onchange('refreshPDF()') !!} {!! Former::open()->addClass('warn-on-exit') !!}
{!! Former::populateField('invoice_design_id', $account->invoice_design_id) !!} {!! Former::populateField('invoice_design_id', $account->invoice_design_id) !!}
<div style="display:none"> <div style="display:none">
@ -146,13 +143,16 @@
</ul> </ul>
</div> </div>
<div id="jsoneditor" style="width: 550px; height: 743px;"></div> <div id="jsoneditor" style="width: 550px; height: 743px;"></div>
<p>&nbsp;</p> <p>&nbsp;</p>
{!! Former::actions( <div>
Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->onchange('onSelectChange()')->raw(), {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->onchange('onSelectChange()')->raw() !!}
Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->large()->appendIcon(Icon::create('floppy-disk')) <div class="pull-right">
) !!} {!! Button::normal(trans('texts.documentation'))->asLinkTo(PDFMAKE_DOCS)->withAttributes(['target' => '_blank'])->appendIcon(Icon::create('info-sign')) !!}
{!! Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/company/advanced_settings/invoice_design'))->appendIcon(Icon::create('remove-circle')) !!}
{!! Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->appendIcon(Icon::create('floppy-disk')) !!}
</div>
</div>
@if (!Auth::user()->isPro()) @if (!Auth::user()->isPro())
<script> <script>

View File

@ -96,7 +96,7 @@
<div class="panel-body"> <div class="panel-body">
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS) @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!} {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else @else
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!} {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!}

View File

@ -13,7 +13,7 @@
<div class="pull-right"> <div class="pull-right">
{!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/knowledgebase/api-documentation/')->withAttributes(['target' => '_blank']) !!} {!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/knowledgebase/api-documentation/')->withAttributes(['target' => '_blank'])->appendIcon(Icon::create('info-sign')) !!}
@if (Utils::isNinja()) @if (Utils::isNinja())
{!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!} {!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!}
@endif @endif

View File

@ -132,7 +132,6 @@
Former::select('is_amount_discount')->addOption(trans('texts.discount_percent'), '0') Former::select('is_amount_discount')->addOption(trans('texts.discount_percent'), '0')
->addOption(trans('texts.discount_amount'), '1')->data_bind("value: is_amount_discount")->raw() ->addOption(trans('texts.discount_amount'), '1')->data_bind("value: is_amount_discount")->raw()
) !!} ) !!}
{{-- Former::select('currency_id')->addOption('', '')->fromQuery($currencies, 'name', 'id')->data_bind("value: currency_id") --}}
<div class="form-group" style="margin-bottom: 8px"> <div class="form-group" style="margin-bottom: 8px">
<label for="taxes" class="control-label col-lg-4 col-sm-4">{{ trans('texts.taxes') }}</label> <label for="taxes" class="control-label col-lg-4 col-sm-4">{{ trans('texts.taxes') }}</label>
@ -324,7 +323,7 @@
</div> </div>
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS) @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!} {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else @else
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!} {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!}
@ -588,7 +587,7 @@
} else { } else {
model.loadClient($.parseJSON(ko.toJSON(new ClientModel()))); model.loadClient($.parseJSON(ko.toJSON(new ClientModel())));
model.invoice().client().country = false; model.invoice().client().country = false;
} }
refreshPDF(true); refreshPDF(true);
}); });
@ -600,7 +599,7 @@
} }
$('#invoice_footer, #terms, #public_notes, #invoice_number, #invoice_date, #due_date, #po_number, #discount, #currency_id, #invoice_design_id, #recurring, #is_amount_discount, #partial').change(function() { $('#invoice_footer, #terms, #public_notes, #invoice_number, #invoice_date, #due_date, #po_number, #discount, #currency_id, #invoice_design_id, #recurring, #is_amount_discount, #partial').change(function() {
setTimeout(function() { setTimeout(function() {
refreshPDF(true); refreshPDF(true);
}, 1); }, 1);
}); });
@ -712,7 +711,7 @@
} }
function getPDFString(cb, force) { function getPDFString(cb, force) {
var invoice = createInvoiceModel(); var invoice = createInvoiceModel();
var design = getDesignJavascript(); var design = getDesignJavascript();
if (!design) return; if (!design) return;
generatePDF(invoice, design, force, cb); generatePDF(invoice, design, force, cb);
@ -918,7 +917,6 @@
var paymentTerms = parseInt(self.invoice().client().payment_terms()); var paymentTerms = parseInt(self.invoice().client().payment_terms());
if (paymentTerms && paymentTerms != 0 && !self.invoice().due_date()) if (paymentTerms && paymentTerms != 0 && !self.invoice().due_date())
{ {
console.log("here");
if (paymentTerms == -1) paymentTerms = 0; if (paymentTerms == -1) paymentTerms = 0;
var dueDate = $('#invoice_date').datepicker('getDate'); var dueDate = $('#invoice_date').datepicker('getDate');
dueDate.setDate(dueDate.getDate() + paymentTerms); dueDate.setDate(dueDate.getDate() + paymentTerms);
@ -1763,7 +1761,7 @@
// move the blank invoice line item to the end // move the blank invoice line item to the end
var blank = model.invoice().invoice_items.pop(); var blank = model.invoice().invoice_items.pop();
var tasks = {!! $tasks !!}; var tasks = {!! $tasks !!};
console.log(tasks);
for (var i=0; i<tasks.length; i++) { for (var i=0; i<tasks.length; i++) {
var task = tasks[i]; var task = tasks[i];
var item = model.invoice().addItem(); var item = model.invoice().addItem();

View File

@ -97,7 +97,7 @@
$('#theFrame').attr('src', string).show(); $('#theFrame').attr('src', string).show();
} else { } else {
if (isRefreshing) { if (isRefreshing) {
needsRefresh = true; //needsRefresh = true;
return; return;
} }
isRefreshing = true; isRefreshing = true;