Working on product fields

This commit is contained in:
Hillel Coren 2017-10-29 09:48:23 +02:00
parent e8888e38d0
commit 97fd40c8d3
8 changed files with 130 additions and 94 deletions

View File

@ -331,6 +331,8 @@ trait PresentsInvoice
'unit_cost', 'unit_cost',
'tax1', 'tax1',
'tax2', 'tax2',
'custom_value1',
'custom_value2',
]; ];
foreach ($fields as $field) { foreach ($fields as $field) {

View File

@ -104,7 +104,7 @@ You can disable the feature by adding ``GOOGLE_MAPS_ENABLED=false`` to the .env
Time Tracking App Time Tracking App
""""""""""""""""" """""""""""""""""
You can create a Windows, MacOS or Linux desktop wrapper for the time tracking app by installing `Nativefier <https://github.com/jiahaog/nativefier>`_ and then running: You can create a Windows, macOS or Linux desktop wrapper for the time tracking app by installing `Nativefier <https://github.com/jiahaog/nativefier>`_ and then running:
.. code-block:: shell .. code-block:: shell

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -24,7 +24,7 @@ function GetPdfMake(invoice, javascript, callback) {
if (item.table && item.table.body == '$invoiceLineItems') { if (item.table && item.table.body == '$invoiceLineItems') {
itemsTable = JSON.stringify(item); itemsTable = JSON.stringify(item);
itemsTable = itemsTable.replace('$invoiceLineItems', '$taskLineItems'); itemsTable = itemsTable.replace('$invoiceLineItems', '$taskLineItems');
//itemsTable = itemsTable.replace('$invoiceLineItemColumns', '$taskLineItemColumns'); itemsTable = itemsTable.replace('$invoiceLineItemColumns', '$taskLineItemColumns');
break; break;
} }
} }
@ -223,7 +223,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
'invoiceLineItems': invoice.is_statement ? NINJA.statementLines(invoice) : NINJA.invoiceLines(invoice), 'invoiceLineItems': invoice.is_statement ? NINJA.statementLines(invoice) : NINJA.invoiceLines(invoice),
'invoiceLineItemColumns': invoice.is_statement ? NINJA.statementColumns(invoice) : NINJA.invoiceColumns(invoice), 'invoiceLineItemColumns': invoice.is_statement ? NINJA.statementColumns(invoice) : NINJA.invoiceColumns(invoice),
'taskLineItems': NINJA.invoiceLines(invoice, true), 'taskLineItems': NINJA.invoiceLines(invoice, true),
//'taskLineItemColumns': NINJA.invoiceColumns(invoice), 'taskLineItemColumns': NINJA.invoiceColumns(invoice, true),
'invoiceDocuments' : NINJA.invoiceDocuments(invoice), 'invoiceDocuments' : NINJA.invoiceDocuments(invoice),
'quantityWidth': NINJA.quantityWidth(invoice), 'quantityWidth': NINJA.quantityWidth(invoice),
'taxWidth': NINJA.taxWidth(invoice), 'taxWidth': NINJA.taxWidth(invoice),
@ -399,33 +399,32 @@ NINJA.statementLines = function(invoice)
return NINJA.prepareDataTable(grid, 'invoiceItems'); return NINJA.prepareDataTable(grid, 'invoiceItems');
} }
NINJA.invoiceColumns = function(invoice) NINJA.invoiceColumns = function(invoice, isTasks)
{ {
var account = invoice.account; var account = invoice.account;
var columns = []; var columns = [];
var fields = NINJA.productFields(invoice, isTasks);
var hasDescription = fields.indexOf('product.description') >= 0;
if (invoice.has_product_key) { for (var i=0; i<fields.length; i++) {
columns.push("15%"); var field = fields[i];
} if (field == 'product.custom_value1') {
columns.push("*")
if (invoice.has_custom_item_value1) { if (invoice.has_custom_item_value1) {
columns.push("10%"); columns.push(hasDescription ? '10%' : '*');
} }
} else if (field == 'product.custom_value2') {
if (invoice.has_custom_item_value2) { if (invoice.has_custom_item_value2) {
columns.push("10%"); columns.push(hasDescription ? '10%' : '*');
} }
} else if (field == 'product.tax') {
var count = 3; if (invoice.has_taxes) {
if (account.hide_quantity == '1') { columns.push(hasDescription ? '15%' : '*');
count -= 2;
} }
if (account.show_item_taxes == '1') { } else if (field == 'product.description') {
count++; columns.push('*');
} else {
columns.push(hasDescription ? '15%' : '*');
} }
for (var i=0; i<count; i++) {
columns.push("14%");
} }
return columns; return columns;
@ -456,45 +455,66 @@ NINJA.taxWidth = function(invoice)
return invoice.account.show_item_taxes == '1' ? '"14%", ' : ''; return invoice.account.show_item_taxes == '1' ? '"14%", ' : '';
} }
NINJA.productFields = function(invoice, isTasks) {
var account = invoice.account;
var fields = JSON.parse(account.invoice_fields);
if (fields) {
if (isTasks && fields.task_fields) {
fields = fields.task_fields;
} else if (! isTasks && fields.product_fields) {
fields = fields.product_fields;
}
}
if (! fields) {
fields = [
isTasks ? 'product.service' : 'product.item',
'product.description',
'product.custom_value1',
'product.custom_value2',
isTasks ? 'product.rate' : 'product.unit_cost',
isTasks ? 'product.hours' : 'product.quantity',
'product.tax',
'product.line_total',
];
}
return fields;
}
NINJA.invoiceLines = function(invoice, isSecondTable) { NINJA.invoiceLines = function(invoice, isSecondTable) {
var account = invoice.account; var account = invoice.account;
var total = 0; var total = 0;
var shownItem = false; var shownItem = false;
var hideQuantity = invoice.account.hide_quantity == '1';
var showItemTaxes = invoice.account.show_item_taxes == '1';
var isTasks = isSecondTable || (invoice.hasTasks && !invoice.hasStandard); var isTasks = isSecondTable || (invoice.hasTasks && !invoice.hasStandard);
var grid = [[]]; var grid = [[]];
var styles = ['tableHeader']; var styles = ['tableHeader'];
if (isSecondTable) { if (isSecondTable) {
styles.push('secondTableHeader'); styles.push('secondTableHeader');
} }
if (invoice.has_product_key) { var fields = NINJA.productFields(invoice, isTasks);
grid[0].push({text: isTasks ? invoiceLabels.service : invoiceLabels.item, style: styles.concat('itemTableHeader')}); var hasDescription = fields.indexOf('product.description') >= 0;
for (var i=0; i<fields.length; i++) {
var field = fields[i].split('.')[1]; // split to remove 'product.'
if (field == 'custom_value1' && ! invoice.has_custom_item_value1) {
continue;
} else if (field == 'custom_value2' && ! invoice.has_custom_item_value1) {
continue;
} else if (field == 'tax' && ! invoice.has_item_taxes) {
continue;
} else if (field == 'product_key' && ! invoice.has_product_key) {
continue;
} }
grid[0].push({text: invoiceLabels.description, style: styles.concat('descriptionTableHeader')}); grid[0].push({text: invoiceLabels[field], style: styles.concat(snakeToCamel(field) + 'TableHeader')});
if (invoice.has_custom_item_value1) {
grid[0].push({text: account.custom_invoice_item_label1, style: styles.concat('custom1TableHeader')});
}
if (invoice.has_custom_item_value2) {
grid[0].push({text: account.custom_invoice_item_label2, style: styles.concat('custom2TableHeader')});
} }
if (!hideQuantity) { for (var i=0; i<invoice.invoice_items.length; i++) {
grid[0].push({text: isTasks ? invoiceLabels.rate : invoiceLabels.unit_cost, style: styles.concat('costTableHeader')});
grid[0].push({text: isTasks ? invoiceLabels.hours : invoiceLabels.quantity, style: styles.concat('qtyTableHeader')});
}
if (showItemTaxes) {
grid[0].push({text: invoiceLabels.tax, style: styles.concat('taxTableHeader')});
}
grid[0].push({text: invoiceLabels.line_total, style: styles.concat('lineTotalTableHeader')});
for (var i = 0; i < invoice.invoice_items.length; i++) {
var row = []; var row = [];
var item = invoice.invoice_items[i]; var item = invoice.invoice_items[i];
var cost = NINJA.parseFloat(item.cost) ? formatMoneyInvoice(item.cost, invoice, null, getPrecision(item.cost)) : ' '; var cost = NINJA.parseFloat(item.cost) ? formatMoneyInvoice(item.cost, invoice, null, getPrecision(item.cost)) : ' ';
@ -516,14 +536,12 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
} }
} }
if (showItemTaxes) {
if (parseFloat(item.tax_rate1) != 0) { if (parseFloat(item.tax_rate1) != 0) {
tax1 = parseFloat(item.tax_rate1); tax1 = parseFloat(item.tax_rate1);
} }
if (parseFloat(item.tax_rate2) != 0) { if (parseFloat(item.tax_rate2) != 0) {
tax2 = parseFloat(item.tax_rate2); tax2 = parseFloat(item.tax_rate2);
} }
}
// show at most one blank line // show at most one blank line
if (shownItem && !notes && !productKey && !item.cost) { if (shownItem && !notes && !productKey && !item.cost) {
@ -558,34 +576,48 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
} }
rowStyle = (grid.length % 2 == 0) ? 'even' : 'odd'; rowStyle = (grid.length % 2 == 0) ? 'even' : 'odd';
if (invoice.has_product_key) { for (var j=0; j<fields.length; j++) {
row.push({style:["productKey", rowStyle], text:productKey || ' '}); // product key can be blank when selecting from a datalist var field = fields[j].split('.')[1]; // split to remove 'product.'
var value = item[field];
var styles = [snakeToCamel(field), rowStyle];
if (field == 'custom_value1' && ! invoice.has_custom_item_value1) {
continue;
} else if (field == 'custom_value2' && ! invoice.has_custom_item_value1) {
continue;
} else if (field == 'tax' && ! invoice.has_item_taxes) {
continue;
} else if (field == 'product_key' && ! invoice.has_product_key) {
continue;
} }
row.push({style:["notes", rowStyle], stack:[{text:notes || ' '}]});
if (invoice.has_custom_item_value1) { if (field == 'item' || field == 'service') {
row.push({style:["customValue1", rowStyle], text:custom_value1 || ' '}); value = item.product_key;
} styles.push('productKey');
if (invoice.has_custom_item_value2) { } else if (field == 'description') {
row.push({style:["customValue2", rowStyle], text:custom_value2 || ' '}); value = item.notes;
} } else if (field == 'unit_cost') {
if (!hideQuantity) { value = item.cost;
row.push({style:["cost", rowStyle], text:cost}); styles.push('cost');
row.push({style:["quantity", rowStyle], text:formatAmount(qty, invoice.client.currency_id, getPrecision(qty)) || ' '}); } else if (field == 'quantity') {
} value = formatAmount(item.qty, invoice.client.currency_id, getPrecision(item.qty));
if (showItemTaxes) { } else if (field == 'tax') {
var str = ' '; value = ' ';
if (item.tax_name1) { if (item.tax_name1) {
str += tax1.toString() + '%'; value += tax1.toString() + '%';
} }
if (item.tax_name2) { if (item.tax_name2) {
if (item.tax_name1) { if (item.tax_name1) {
str += ' '; value += ' ';
} }
str += tax2.toString() + '%'; value += tax2.toString() + '%';
} }
row.push({style:["tax", rowStyle], text:str}); } else if (field == 'line_total') {
value = lineTotal;
}
row.push({text:value || ' ', style:styles});
} }
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
grid.push(row); grid.push(row);
} }

View File

@ -751,6 +751,7 @@ function calculateAmounts(invoice) {
var taxAmount1 = roundToTwo(lineTotal * taxRate1 / 100); var taxAmount1 = roundToTwo(lineTotal * taxRate1 / 100);
if (taxAmount1 != 0 || taxName1) { if (taxAmount1 != 0 || taxName1) {
hasTaxes = true;
var key = taxName1 + taxRate1; var key = taxName1 + taxRate1;
if (taxes.hasOwnProperty(key)) { if (taxes.hasOwnProperty(key)) {
taxes[key].amount += taxAmount1; taxes[key].amount += taxAmount1;
@ -761,6 +762,7 @@ function calculateAmounts(invoice) {
var taxAmount2 = roundToTwo(lineTotal * taxRate2 / 100); var taxAmount2 = roundToTwo(lineTotal * taxRate2 / 100);
if (taxAmount2 != 0 || taxName2) { if (taxAmount2 != 0 || taxName2) {
hasTaxes = true;
var key = taxName2 + taxRate2; var key = taxName2 + taxRate2;
if (taxes.hasOwnProperty(key)) { if (taxes.hasOwnProperty(key)) {
taxes[key].amount += taxAmount2; taxes[key].amount += taxAmount2;
@ -768,11 +770,9 @@ function calculateAmounts(invoice) {
taxes[key] = {name: taxName2, rate:taxRate2, amount:taxAmount2}; taxes[key] = {name: taxName2, rate:taxRate2, amount:taxAmount2};
} }
} }
}
if (item.tax_name1 || item.tax_name2) { invoice.has_item_taxes = hasTaxes;
hasTaxes = true;
}
}
invoice.subtotal_amount = total; invoice.subtotal_amount = total;
var discount = 0; var discount = 0;

View File

@ -2510,6 +2510,8 @@ $LANG = array(
'partial_due_date' => 'Partial Due Date', 'partial_due_date' => 'Partial Due Date',
'task_fields' => 'Task Fields', 'task_fields' => 'Task Fields',
'product_fields_help' => 'Drag and drop fields to change their order', 'product_fields_help' => 'Drag and drop fields to change their order',
'custom_value1' => 'Custom Value',
'custom_value2' => 'Custom Value',
); );

View File

@ -38,7 +38,7 @@
} }
function openTimeTracker() { function openTimeTracker() {
var width = 1000; var width = 1060;
var height = 700; var height = 700;
var left = (screen.width/2)-(width/4); var left = (screen.width/2)-(width/4);
var top = (screen.height/2)-(height/1.5); var top = (screen.height/2)-(height/1.5);