diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index e67102df2b7a..91f7ac62690c 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -331,6 +331,9 @@ class AccountController extends BaseController $account->primary_color = Input::get('primary_color'); $account->secondary_color = Input::get('secondary_color'); $account->invoice_design_id = Input::get('invoice_design_id'); + if (Input::has('font_size')) { + $account->font_size = intval(Input::get('font_size')); + } $account->save(); Session::flash('message', trans('texts.updated_settings')); diff --git a/app/Http/routes.php b/app/Http/routes.php index 3e1fe71adde7..cb1508421c74 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -279,6 +279,7 @@ define('MAX_NUM_CLIENTS', 500); define('MAX_NUM_CLIENTS_PRO', 20000); define('MAX_NUM_USERS', 20); define('MAX_SUBDOMAIN_LENGTH', 30); +define('DEFAULT_FONT_SIZE', 9); define('INVOICE_STATUS_DRAFT', 1); define('INVOICE_STATUS_SENT', 2); diff --git a/database/migrations/2015_05_21_184104_add_font_size.php b/database/migrations/2015_05_21_184104_add_font_size.php new file mode 100644 index 000000000000..48656cde5906 --- /dev/null +++ b/database/migrations/2015_05_21_184104_add_font_size.php @@ -0,0 +1,34 @@ +smallInteger('font_size')->default(DEFAULT_FONT_SIZE); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('accounts', function($table) + { + $table->dropColumn('font_size'); + }); + } + +} diff --git a/public/js/built.js b/public/js/built.js index e6c51a38f5a4..eea1cb4bdd42 100644 --- a/public/js/built.js +++ b/public/js/built.js @@ -33068,14 +33068,56 @@ function roundToTwo(num, toString) { function truncate(str, length) { return (str && str.length > length) ? (str.substr(0, length-1) + '...') : str; } +var NINJA = NINJA || {}; + function GetPdfMake(invoice, javascript, callback) { var account = invoice.account; var baseDD = { - pageMargins: [40, 40, 40, 40] + pageMargins: [40, 40, 40, 40], + styles: { + bold: { + bold: true + }, + cost: { + alignment: 'right' + }, + quantity: { + alignment: 'right' + }, + tax: { + alignment: 'right' + }, + lineTotal: { + alignment: 'right' + }, + right: { + alignment: 'right' + }, + subtotals: { + alignment: 'right' + }, + termsLabel: { + bold: true, + margin: [0, 10, 0, 4] + } + }, + footer: function(){ + f = [{ text:invoice.invoice_footer?invoice.invoice_footer:"", margin: [40, 0]}] + if (!invoice.is_pro && logoImages.imageLogo1) { + f.push({ + image: logoImages.imageLogo1, + width: 150, + margin: [40,0] + }); + } + return f; + }, + }; - eval(javascript); - dd = _.extend(dd, baseDD); - + + eval(javascript); + dd = $.extend(true, baseDD, dd); + /* var fonts = { Roboto: { @@ -33093,7 +33135,8 @@ function GetPdfMake(invoice, javascript, callback) { }; return doc; } -function notesAndTerms(invoice) + +NINJA.notesAndTerms = function(invoice) { var text = []; if (invoice.public_notes) { @@ -33108,7 +33151,7 @@ function notesAndTerms(invoice) return text; } -function invoiceLines(invoice) { +NINJA.invoiceLines = function(invoice) { var grid = [ [ {text: invoiceLabels.item, style: 'tableHeader'}, @@ -33139,42 +33182,43 @@ function invoiceLines(invoice) { tax = parseFloat(item.tax_rate); } - // show at most one blank line - if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) { - continue; - } - shownItem = true; + // show at most one blank line + if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) { + continue; + } + shownItem = true; - // process date variables - if (invoice.is_recurring) { - notes = processVariables(notes); - productKey = processVariables(productKey); - } + // process date variables + if (invoice.is_recurring) { + notes = processVariables(notes); + productKey = processVariables(productKey); + } - var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); - if (tax) { - lineTotal += lineTotal * tax / 100; - } - if (lineTotal) { - total += lineTotal; - } - lineTotal = formatMoney(lineTotal, currencyId); + var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); + if (tax) { + lineTotal += lineTotal * tax / 100; + } + if (lineTotal) { + total += lineTotal; + } + lineTotal = formatMoney(lineTotal, currencyId); - rowStyle = i%2===0?'odd':'even'; + rowStyle = i%2===0?'odd':'even'; - row[0] = {style:["productKey", rowStyle], text:productKey}; - row[1] = {style:["notes", rowStyle], text:notes}; - row[2] = {style:["cost", rowStyle], text:cost}; - row[3] = {style:["quantity", rowStyle], text:qty}; - row[4] = {style:["tax", rowStyle], text:""+tax}; - row[5] = {style:["lineTotal", rowStyle], text:lineTotal}; + row[0] = {style:["productKey", rowStyle], text:productKey}; + row[1] = {style:["notes", rowStyle], text:notes}; + row[2] = {style:["cost", rowStyle], text:cost}; + row[3] = {style:["quantity", rowStyle], text:qty}; + row[4] = {style:["tax", rowStyle], text:""+tax}; + row[5] = {style:["lineTotal", rowStyle], text:lineTotal}; - grid.push(row); -} -return grid; + grid.push(row); + } + + return grid; } -function subtotals(invoice) +NINJA.subtotals = function(invoice) { if (!invoice) { return; @@ -33216,7 +33260,7 @@ function subtotals(invoice) return data; } -function accountDetails(account) { +NINJA.accountDetails = function(account) { var data = []; if(account.name) data.push({text:account.name, style:'accountName'}); if(account.id_number) data.push({text:account.id_number, style:'accountDetails'}); @@ -33226,7 +33270,7 @@ function accountDetails(account) { return data; } -function accountAddress(account) { +NINJA.accountAddress = function(account) { var address = ''; if (account.city || account.state || account.postal_code) { address = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim(); @@ -33239,7 +33283,7 @@ function accountAddress(account) { return data; } -function invoiceDetails(invoice) { +NINJA.invoiceDetails = function(invoice) { var data = [ [ invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number, @@ -33258,7 +33302,7 @@ function invoiceDetails(invoice) { return data; } -function clientDetails(invoice) { +NINJA.clientDetails = function(invoice) { var client = invoice.client; if (!client) { return; @@ -33282,16 +33326,23 @@ function clientDetails(invoice) { if (!field) { continue; } - data.push(field); - } + data.push([field]); + } + if (!data.length) { + data.push(['']); + } return data; } -function primaryColor( defaultColor) { - return NINJA.primaryColor?NINJA.primaryColor:defaultColor; +NINJA.getPrimaryColor = function(defaultColor) { + return NINJA.primaryColor ? NINJA.primaryColor : defaultColor; } -function secondaryColor( defaultColor) { - return NINJA.primaryColor?NINJA.secondaryColor:defaultColor; +NINJA.getSecondaryColor = function(defaultColor) { + return NINJA.primaryColor ? NINJA.secondaryColor : defaultColor; +} + +NINJA.getEntityLabel = function(invoice) { + return invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice; } \ No newline at end of file diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index 54f7dd91b6f5..a82f5eeb1546 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -1,11 +1,53 @@ +var NINJA = NINJA || {}; + function GetPdfMake(invoice, javascript, callback) { var account = invoice.account; var baseDD = { - pageMargins: [40, 40, 40, 40] + pageMargins: [40, 40, 40, 40], + styles: { + bold: { + bold: true + }, + cost: { + alignment: 'right' + }, + quantity: { + alignment: 'right' + }, + tax: { + alignment: 'right' + }, + lineTotal: { + alignment: 'right' + }, + right: { + alignment: 'right' + }, + subtotals: { + alignment: 'right' + }, + termsLabel: { + bold: true, + margin: [0, 10, 0, 4] + } + }, + footer: function(){ + f = [{ text:invoice.invoice_footer?invoice.invoice_footer:"", margin: [40, 0]}] + if (!invoice.is_pro && logoImages.imageLogo1) { + f.push({ + image: logoImages.imageLogo1, + width: 150, + margin: [40,0] + }); + } + return f; + }, + }; - eval(javascript); - dd = _.extend(dd, baseDD); - + + eval(javascript); + dd = $.extend(true, baseDD, dd); + /* var fonts = { Roboto: { @@ -23,7 +65,8 @@ function GetPdfMake(invoice, javascript, callback) { }; return doc; } -function notesAndTerms(invoice) + +NINJA.notesAndTerms = function(invoice) { var text = []; if (invoice.public_notes) { @@ -38,7 +81,7 @@ function notesAndTerms(invoice) return text; } -function invoiceLines(invoice) { +NINJA.invoiceLines = function(invoice) { var grid = [ [ {text: invoiceLabels.item, style: 'tableHeader'}, @@ -69,42 +112,43 @@ function invoiceLines(invoice) { tax = parseFloat(item.tax_rate); } - // show at most one blank line - if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) { - continue; - } - shownItem = true; + // show at most one blank line + if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) { + continue; + } + shownItem = true; - // process date variables - if (invoice.is_recurring) { - notes = processVariables(notes); - productKey = processVariables(productKey); - } + // process date variables + if (invoice.is_recurring) { + notes = processVariables(notes); + productKey = processVariables(productKey); + } - var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); - if (tax) { - lineTotal += lineTotal * tax / 100; - } - if (lineTotal) { - total += lineTotal; - } - lineTotal = formatMoney(lineTotal, currencyId); + var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); + if (tax) { + lineTotal += lineTotal * tax / 100; + } + if (lineTotal) { + total += lineTotal; + } + lineTotal = formatMoney(lineTotal, currencyId); - rowStyle = i%2===0?'odd':'even'; + rowStyle = i%2===0?'odd':'even'; - row[0] = {style:["productKey", rowStyle], text:productKey}; - row[1] = {style:["notes", rowStyle], text:notes}; - row[2] = {style:["cost", rowStyle], text:cost}; - row[3] = {style:["quantity", rowStyle], text:qty}; - row[4] = {style:["tax", rowStyle], text:""+tax}; - row[5] = {style:["lineTotal", rowStyle], text:lineTotal}; + row[0] = {style:["productKey", rowStyle], text:productKey}; + row[1] = {style:["notes", rowStyle], text:notes}; + row[2] = {style:["cost", rowStyle], text:cost}; + row[3] = {style:["quantity", rowStyle], text:qty}; + row[4] = {style:["tax", rowStyle], text:""+tax}; + row[5] = {style:["lineTotal", rowStyle], text:lineTotal}; - grid.push(row); -} -return grid; + grid.push(row); + } + + return grid; } -function subtotals(invoice) +NINJA.subtotals = function(invoice) { if (!invoice) { return; @@ -146,7 +190,7 @@ function subtotals(invoice) return data; } -function accountDetails(account) { +NINJA.accountDetails = function(account) { var data = []; if(account.name) data.push({text:account.name, style:'accountName'}); if(account.id_number) data.push({text:account.id_number, style:'accountDetails'}); @@ -156,7 +200,7 @@ function accountDetails(account) { return data; } -function accountAddress(account) { +NINJA.accountAddress = function(account) { var address = ''; if (account.city || account.state || account.postal_code) { address = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim(); @@ -169,7 +213,7 @@ function accountAddress(account) { return data; } -function invoiceDetails(invoice) { +NINJA.invoiceDetails = function(invoice) { var data = [ [ invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number, @@ -188,7 +232,7 @@ function invoiceDetails(invoice) { return data; } -function clientDetails(invoice) { +NINJA.clientDetails = function(invoice) { var client = invoice.client; if (!client) { return; @@ -212,16 +256,23 @@ function clientDetails(invoice) { if (!field) { continue; } - data.push(field); - } + data.push([field]); + } + if (!data.length) { + data.push(['']); + } return data; } -function primaryColor( defaultColor) { - return NINJA.primaryColor?NINJA.primaryColor:defaultColor; +NINJA.getPrimaryColor = function(defaultColor) { + return NINJA.primaryColor ? NINJA.primaryColor : defaultColor; } -function secondaryColor( defaultColor) { - return NINJA.primaryColor?NINJA.secondaryColor:defaultColor; +NINJA.getSecondaryColor = function(defaultColor) { + return NINJA.primaryColor ? NINJA.secondaryColor : defaultColor; +} + +NINJA.getEntityLabel = function(invoice) { + return invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice; } \ No newline at end of file diff --git a/public/js/templates/clean.js b/public/js/templates/clean.js index dd0ffd830792..4858454b9529 100644 --- a/public/js/templates/clean.js +++ b/public/js/templates/clean.js @@ -1,187 +1,151 @@ //pdfmake var dd = { - content: [ + content: [ { - columns: [ + columns: [ [ - invoice.image? - { - image: invoice.image, - fit: [150, 80] - }:"" + invoice.image? + { + image: invoice.image, + fit: [150, 80] + }:"" ], { - stack: accountDetails(account) + stack: NINJA.accountDetails(account) }, { - stack: accountAddress(account) + stack: NINJA.accountAddress(account) } - ] + ] }, { - text:(invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(), - margin: [8, 70, 8, 16], - style: 'primaryColor', - fontSize: 11 + text:(NINJA.getEntityLabel(invoice)).toUpperCase(), + margin: [8, 70, 8, 16], + style: 'primaryColor', + fontSize: NINJA.fontSize + 2 }, { - table: { - headerRows: 1, - widths: ['auto', 'auto', '*'], - body: [[ - { - table: { - body: invoiceDetails(invoice), - }, - layout: 'noBorders', - }, - clientDetails(invoice), - '' - ]] - }, - layout: { - hLineWidth: function (i, node) { - return (i === 0 || i === node.table.body.length) ? .5 : 0; + table: { + headerRows: 1, + widths: ['auto', 'auto', '*'], + body: [ + [ + { + table: { + body: NINJA.invoiceDetails(invoice), + }, + layout: 'noBorders', + }, + { + table: { + body: NINJA.clientDetails(invoice), + }, + layout: 'noBorders', + }, + '' + ] + ] }, - vLineWidth: function (i, node) { - return 0; - }, - hLineColor: function (i, node) { - return '#D8D8D8'; - }, - paddingLeft: function(i, node) { return 8; }, - paddingRight: function(i, node) { return 8; }, - paddingTop: function(i, node) { return 4; }, - paddingBottom: function(i, node) { return 4; } - } - }, - '\n', - { - table: { - headerRows: 1, - widths: ['15%', '*', 'auto', 'auto', 'auto', 'auto'], - body:invoiceLines(invoice), - }, - layout: { - hLineWidth: function (i, node) { - return i === 0 ? 0 : .5; - }, - vLineWidth: function (i, node) { - return 0; - }, - hLineColor: function (i, node) { - return '#D8D8D8'; - }, - paddingLeft: function(i, node) { return 8; }, - paddingRight: function(i, node) { return 8; }, - paddingTop: function(i, node) { return 8; }, - paddingBottom: function(i, node) { return 8; } - }, - }, - '\n', - { - columns: [ - notesAndTerms(invoice), - { - style: 'subtotals', - table: { - widths: ['*', '*'], - body: subtotals(invoice), - }, - layout: { + layout: { hLineWidth: function (i, node) { - return 0; + return (i === 0 || i === node.table.body.length) ? .5 : 0; }, vLineWidth: function (i, node) { - return 0; + return 0; + }, + hLineColor: function (i, node) { + return '#D8D8D8'; }, paddingLeft: function(i, node) { return 8; }, paddingRight: function(i, node) { return 8; }, paddingTop: function(i, node) { return 4; }, - paddingBottom: function(i, node) { return 4; } - }, + paddingBottom: function(i, node) { return 4; } } - ] }, - ], + '\n', + { + table: { + headerRows: 1, + widths: ['15%', '*', 'auto', 'auto', 'auto', 'auto'], + body: NINJA.invoiceLines(invoice), + }, + layout: { + hLineWidth: function (i, node) { + return i === 0 ? 0 : .5; + }, + vLineWidth: function (i, node) { + return 0; + }, + hLineColor: function (i, node) { + return '#D8D8D8'; + }, + paddingLeft: function(i, node) { return 8; }, + paddingRight: function(i, node) { return 8; }, + paddingTop: function(i, node) { return 8; }, + paddingBottom: function(i, node) { return 8; } + }, + }, + '\n', + { + columns: [ + NINJA.notesAndTerms(invoice), + { + style: 'subtotals', + table: { + widths: ['*', '*'], + body: NINJA.subtotals(invoice), + }, + layout: { + hLineWidth: function (i, node) { + return 0; + }, + vLineWidth: function (i, node) { + return 0; + }, + paddingLeft: function(i, node) { return 8; }, + paddingRight: function(i, node) { return 8; }, + paddingTop: function(i, node) { return 4; }, + paddingBottom: function(i, node) { return 4; } + }, + } + ] + }, + ], - footer: function(){ - f = [{ text:invoice.invoice_footer?invoice.invoice_footer:"", margin: [40, 0]}] - if (!invoice.is_pro && logoImages.imageLogo1) { - f.push({ - image: logoImages.imageLogo1, - width: 150, - margin: [40,0] - }); - } - return f; - }, - - defaultStyle: { - //font: 'Roboto', - fontSize: 9, - margin: [8, 4, 8, 4] - }, - styles: { - primaryColor:{ - color: primaryColor('#299CC2') + defaultStyle: { + fontSize: NINJA.fontSize, + margin: [8, 4, 8, 4] }, - accountName: { - margin: [4, 2, 4, 2], - color:primaryColor('#299CC2') + styles: { + primaryColor:{ + color: NINJA.getPrimaryColor('#299CC2') + }, + accountName: { + margin: [4, 2, 4, 2], + color: NINJA.getPrimaryColor('#299CC2') + }, + accountDetails: { + margin: [4, 2, 4, 2], + color: '#AAA9A9' + }, + even: { + }, + odd: { + fillColor:'#F4F4F4' + }, + productKey: { + color: NINJA.getPrimaryColor('#299CC2') + }, + tableHeader: { + bold: true + }, + balanceDueLabel: { + fontSize: NINJA.fontSize + 2 + }, + balanceDueValue: { + fontSize: NINJA.fontSize + 2, + color: NINJA.getPrimaryColor('#299CC2') + }, }, - accountDetails: { - margin: [4, 2, 4, 2], - color: '#AAA9A9' - }, - bold: { - bold: true - }, - even: { - }, - odd: { - fillColor:'#F4F4F4' - }, - productKey: { - color:primaryColor('#299CC2') - }, - cost: { - alignment: 'right' - }, - quantity: { - alignment: 'right' - }, - tax: { - alignment: 'right' - }, - lineTotal: { - alignment: 'right' - }, - right: { - alignment: 'right' - }, - subtotals: { - alignment: 'right' - }, - tableHeader: { - bold: true - }, - balanceDueLabel: { - fontSize: 11 - }, - balanceDueValue: { - fontSize: 11, - color:primaryColor('#299CC2') - }, - notes: { - }, - terms: { - - }, - termsLabel: { - bold: true, - fontSize: 10, - margin: [0, 10, 0, 4] - } - } + pageMargins: [40, 40, 40, 40], }; \ No newline at end of file diff --git a/resources/views/accounts/invoice_design.blade.php b/resources/views/accounts/invoice_design.blade.php index 20d530ecf8c5..255676e1eb8a 100644 --- a/resources/views/accounts/invoice_design.blade.php +++ b/resources/views/accounts/invoice_design.blade.php @@ -40,6 +40,7 @@ NINJA.primaryColor = $('#primary_color').val(); NINJA.secondaryColor = $('#secondary_color').val(); + NINJA.fontSize = parseInt($('#font_size').val()); doc = generatePDF(invoice, getDesignJavascript(), true); doc.getDataUrl(cb); @@ -80,15 +81,18 @@ @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS) - {!! 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 {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!} @endif - + @if (Auth::user()->account->utf8_invoices) + {!! Former::text('font_size')->type('number')->min('0')->step('1')->style('width:120px') !!} + @endif {!! Former::text('primary_color') !!} {!! Former::text('secondary_color') !!} + diff --git a/resources/views/script.blade.php b/resources/views/script.blade.php index 47c1c9fa3f5b..4f60ac5626b4 100644 --- a/resources/views/script.blade.php +++ b/resources/views/script.blade.php @@ -5,17 +5,21 @@ for (var i=0; iaccount->font_size ?: DEFAULT_FONT_SIZE }}; @endif + NINJA.parseFloat = function(str) { if (!str) return ''; str = (str+'').replace(/[^0-9\.\-]/g, ''); return window.parseFloat(str); } + function formatMoney(value, currency_id, hide_symbol) { value = NINJA.parseFloat(value); if (!currency_id) currency_id = {{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY) }};