mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Support both (Unit Cost/Quantity) and (Rate/Hour) #506
This commit is contained in:
parent
973ceec719
commit
e871916116
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -15,6 +15,24 @@ NINJA.TEMPLATES = {
|
|||||||
|
|
||||||
function GetPdfMake(invoice, javascript, callback) {
|
function GetPdfMake(invoice, javascript, callback) {
|
||||||
|
|
||||||
|
// check if we need to add a second table for tasks
|
||||||
|
var itemsTable = false;
|
||||||
|
if (invoice.hasSecondTable) {
|
||||||
|
var json = JSON.parse(javascript);
|
||||||
|
for (var i=0; i<json.content.length; i++) {
|
||||||
|
var item = json.content[i];
|
||||||
|
if (item.style == 'invoiceLineItemsTable') {
|
||||||
|
itemsTable = JSON.stringify(item);
|
||||||
|
itemsTable = itemsTable.replace('$invoiceLineItems', '$taskLineItems');
|
||||||
|
//itemsTable = itemsTable.replace('$invoiceLineItemColumns', '$taskLineItemColumns');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemsTable = JSON.parse(itemsTable);
|
||||||
|
json.content.splice(i+1, 0, itemsTable);
|
||||||
|
javascript = JSON.stringify(json);
|
||||||
|
}
|
||||||
|
|
||||||
javascript = NINJA.decodeJavascript(invoice, javascript);
|
javascript = NINJA.decodeJavascript(invoice, javascript);
|
||||||
|
|
||||||
function jsonCallBack(key, val) {
|
function jsonCallBack(key, val) {
|
||||||
@ -201,6 +219,8 @@ NINJA.decodeJavascript = function(invoice, javascript)
|
|||||||
'invoiceDetailsHeight': (NINJA.invoiceDetails(invoice).length * 16) + 16,
|
'invoiceDetailsHeight': (NINJA.invoiceDetails(invoice).length * 16) + 16,
|
||||||
'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),
|
||||||
|
//'taskLineItemColumns': NINJA.invoiceColumns(invoice),
|
||||||
'invoiceDocuments' : isEdge ? [] : NINJA.invoiceDocuments(invoice),
|
'invoiceDocuments' : isEdge ? [] : NINJA.invoiceDocuments(invoice),
|
||||||
'quantityWidth': NINJA.quantityWidth(invoice),
|
'quantityWidth': NINJA.quantityWidth(invoice),
|
||||||
'taxWidth': NINJA.taxWidth(invoice),
|
'taxWidth': NINJA.taxWidth(invoice),
|
||||||
@ -433,12 +453,13 @@ NINJA.taxWidth = function(invoice)
|
|||||||
return invoice.account.show_item_taxes == '1' ? '"14%", ' : '';
|
return invoice.account.show_item_taxes == '1' ? '"14%", ' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
NINJA.invoiceLines = function(invoice) {
|
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 hideQuantity = invoice.account.hide_quantity == '1';
|
||||||
var showItemTaxes = invoice.account.show_item_taxes == '1';
|
var showItemTaxes = invoice.account.show_item_taxes == '1';
|
||||||
|
var isTasks = isSecondTable || (invoice.hasTasks && !invoice.hasStandard);
|
||||||
|
|
||||||
var grid = [[]];
|
var grid = [[]];
|
||||||
|
|
||||||
@ -456,8 +477,8 @@ NINJA.invoiceLines = function(invoice) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hideQuantity) {
|
if (!hideQuantity) {
|
||||||
grid[0].push({text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']});
|
grid[0].push({text: isTasks ? invoiceLabels.rate : invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']});
|
||||||
grid[0].push({text: invoiceLabels.quantity, style: ['tableHeader', 'qtyTableHeader']});
|
grid[0].push({text: isTasks ? invoiceLabels.hours : invoiceLabels.quantity, style: ['tableHeader', 'qtyTableHeader']});
|
||||||
}
|
}
|
||||||
if (showItemTaxes) {
|
if (showItemTaxes) {
|
||||||
grid[0].push({text: invoiceLabels.tax, style: ['tableHeader', 'taxTableHeader']});
|
grid[0].push({text: invoiceLabels.tax, style: ['tableHeader', 'taxTableHeader']});
|
||||||
@ -477,6 +498,17 @@ NINJA.invoiceLines = function(invoice) {
|
|||||||
var custom_value1 = item.custom_value1;
|
var custom_value1 = item.custom_value1;
|
||||||
var custom_value2 = item.custom_value2;
|
var custom_value2 = item.custom_value2;
|
||||||
|
|
||||||
|
console.log('isTasks: %s', isTasks);
|
||||||
|
if (isTasks) {
|
||||||
|
if (item.invoice_item_type_id != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (item.invoice_item_type_id == 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (showItemTaxes) {
|
if (showItemTaxes) {
|
||||||
if (item.tax_name1) {
|
if (item.tax_name1) {
|
||||||
tax1 = parseFloat(item.tax_rate1);
|
tax1 = parseFloat(item.tax_rate1);
|
||||||
|
@ -660,6 +660,9 @@ function calculateAmounts(invoice) {
|
|||||||
invoice.has_product_key = true;
|
invoice.has_product_key = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasStandard;
|
||||||
|
var hasTask;
|
||||||
|
|
||||||
// sum line item
|
// sum line item
|
||||||
for (var i=0; i<invoice.invoice_items.length; i++) {
|
for (var i=0; i<invoice.invoice_items.length; i++) {
|
||||||
var item = invoice.invoice_items[i];
|
var item = invoice.invoice_items[i];
|
||||||
@ -668,8 +671,20 @@ function calculateAmounts(invoice) {
|
|||||||
if (lineTotal) {
|
if (lineTotal) {
|
||||||
total += lineTotal;
|
total += lineTotal;
|
||||||
}
|
}
|
||||||
|
if (!item.notes && !item.product_key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item.invoice_item_type_id == 2) {
|
||||||
|
hasTask = true;
|
||||||
|
} else {
|
||||||
|
hasStandard = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invoice.hasTasks = hasTask;
|
||||||
|
invoice.hasStandard = hasStandard;
|
||||||
|
invoice.hasSecondTable = hasTask && hasStandard;
|
||||||
|
|
||||||
for (var i=0; i<invoice.invoice_items.length; i++) {
|
for (var i=0; i<invoice.invoice_items.length; i++) {
|
||||||
var item = invoice.invoice_items[i];
|
var item = invoice.invoice_items[i];
|
||||||
var taxRate1 = 0;
|
var taxRate1 = 0;
|
||||||
|
@ -2241,7 +2241,7 @@ $LANG = array(
|
|||||||
'resume_task' => 'Resume Task',
|
'resume_task' => 'Resume Task',
|
||||||
'resumed_task' => 'Successfully resumed task',
|
'resumed_task' => 'Successfully resumed task',
|
||||||
'quote_design' => 'Quote Design',
|
'quote_design' => 'Quote Design',
|
||||||
'default_design' => 'Default Design',
|
'default_design' => 'Standard Design',
|
||||||
'custom_design1' => 'Custom Design 1',
|
'custom_design1' => 'Custom Design 1',
|
||||||
'custom_design2' => 'Custom Design 2',
|
'custom_design2' => 'Custom Design 2',
|
||||||
'custom_design3' => 'Custom Design 3',
|
'custom_design3' => 'Custom Design 3',
|
||||||
|
@ -249,93 +249,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive" style="padding-top:4px">
|
<div class="table-responsivex" style="padding-top:4px;">
|
||||||
<table class="table invoice-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="min-width:32px;" class="hide-border"></th>
|
|
||||||
<th style="min-width:120px;width:25%">{{ $invoiceLabels['item'] }}</th>
|
|
||||||
<th style="width:100%">{{ $invoiceLabels['description'] }}</th>
|
|
||||||
@if ($account->showCustomField('custom_invoice_item_label1'))
|
|
||||||
<th style="min-width:120px">{{ $account->custom_invoice_item_label1 }}</th>
|
|
||||||
@endif
|
|
||||||
@if ($account->showCustomField('custom_invoice_item_label2'))
|
|
||||||
<th style="min-width:120px">{{ $account->custom_invoice_item_label2 }}</th>
|
|
||||||
@endif
|
|
||||||
<th style="min-width:120px" data-bind="text: costLabel">{{ $invoiceLabels['unit_cost'] }}</th>
|
|
||||||
<th style="{{ $account->hide_quantity ? 'display:none' : 'min-width:120px' }}" data-bind="text: qtyLabel">{{ $invoiceLabels['quantity'] }}</th>
|
|
||||||
<th style="min-width:{{ $account->enable_second_tax_rate ? 180 : 120 }}px;display:none;" data-bind="visible: $root.invoice_item_taxes.show">{{ trans('texts.tax') }}</th>
|
|
||||||
<th style="min-width:120px;">{{ trans('texts.line_total') }}</th>
|
|
||||||
<th style="min-width:32px;" class="hide-border"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody data-bind="sortable: { data: invoice_items, afterMove: onDragged }">
|
|
||||||
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }" class="sortable-row">
|
|
||||||
<td class="hide-border td-icon">
|
|
||||||
<i style="display:none" data-bind="visible: actionsVisible() &&
|
|
||||||
$parent.invoice_items().length > 1" class="fa fa-sort"></i>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div id="scrollable-dropdown-menu">
|
|
||||||
<input id="product_key" type="text" data-bind="productTypeahead: product_key, items: $root.products, key: 'product_key', valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][product_key]'}" class="form-control invoice-item handled"/>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<textarea data-bind="value: notes, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][notes]'}"
|
|
||||||
rows="1" cols="60" style="resize: vertical;height:42px" class="form-control word-wrap"></textarea>
|
|
||||||
<input type="text" data-bind="value: task_public_id, attr: {name: 'invoice_items[' + $index() + '][task_public_id]'}" style="display: none"/>
|
|
||||||
<input type="text" data-bind="value: expense_public_id, attr: {name: 'invoice_items[' + $index() + '][expense_public_id]'}" style="display: none"/>
|
|
||||||
<input type="text" data-bind="value: invoice_item_type_id, attr: {name: 'invoice_items[' + $index() + '][invoice_item_type_id]'}" style="display: none"/>
|
|
||||||
</td>
|
|
||||||
@if ($account->showCustomField('custom_invoice_item_label1'))
|
|
||||||
<td>
|
|
||||||
<input data-bind="value: custom_value1, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][custom_value1]'}" class="form-control invoice-item"/>
|
|
||||||
</td>
|
|
||||||
@endif
|
|
||||||
@if ($account->showCustomField('custom_invoice_item_label2'))
|
|
||||||
<td>
|
|
||||||
<input data-bind="value: custom_value2, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][custom_value2]'}" class="form-control invoice-item"/>
|
|
||||||
</td>
|
|
||||||
@endif
|
|
||||||
<td>
|
|
||||||
<input data-bind="value: prettyCost, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][cost]'}"
|
|
||||||
style="text-align: right" class="form-control invoice-item"/>
|
|
||||||
</td>
|
|
||||||
<td style="{{ $account->hide_quantity ? 'display:none' : '' }}">
|
|
||||||
<input data-bind="value: prettyQty, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][qty]'}"
|
|
||||||
style="text-align: right" class="form-control invoice-item" name="quantity"/>
|
|
||||||
</td>
|
|
||||||
<td style="display:none;" data-bind="visible: $root.invoice_item_taxes.show">
|
|
||||||
{!! Former::select('')
|
|
||||||
->addOption('', '')
|
|
||||||
->options($taxRateOptions)
|
|
||||||
->data_bind('value: tax1, event:{change:onTax1Change}')
|
|
||||||
->addClass($account->enable_second_tax_rate ? 'tax-select' : '')
|
|
||||||
->raw() !!}
|
|
||||||
<input type="text" data-bind="value: tax_name1, attr: {name: 'invoice_items[' + $index() + '][tax_name1]'}" style="display:none">
|
|
||||||
<input type="text" data-bind="value: tax_rate1, attr: {name: 'invoice_items[' + $index() + '][tax_rate1]'}" style="display:none">
|
|
||||||
<div data-bind="visible: $root.invoice().account.enable_second_tax_rate == '1'">
|
|
||||||
{!! Former::select('')
|
|
||||||
->addOption('', '')
|
|
||||||
->options($taxRateOptions)
|
|
||||||
->data_bind('value: tax2, event:{change:onTax2Change}')
|
|
||||||
->addClass('tax-select')
|
|
||||||
->raw() !!}
|
|
||||||
</div>
|
|
||||||
<input type="text" data-bind="value: tax_name2, attr: {name: 'invoice_items[' + $index() + '][tax_name2]'}" style="display:none">
|
|
||||||
<input type="text" data-bind="value: tax_rate2, attr: {name: 'invoice_items[' + $index() + '][tax_rate2]'}" style="display:none">
|
|
||||||
</td>
|
|
||||||
<td style="text-align:right;padding-top:9px !important" nowrap>
|
|
||||||
<div class="line-total" data-bind="text: totals.total"></div>
|
|
||||||
</td>
|
|
||||||
<td style="cursor:pointer" class="hide-border td-icon">
|
|
||||||
<i style="padding-left:2px" data-bind="click: $parent.removeItem, visible: actionsVisible() &&
|
|
||||||
$index() < ($parent.invoice_items().length - 1) &&
|
|
||||||
$parent.invoice_items().length > 1" class="fa fa-minus-circle redlink" title="Remove item"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
|
@include('invoices.edit_table', ['isTasks' => false])
|
||||||
|
@include('invoices.edit_table', ['isTasks' => true])
|
||||||
|
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
@ -936,7 +853,7 @@
|
|||||||
item.notes(task.description);
|
item.notes(task.description);
|
||||||
item.qty(task.duration);
|
item.qty(task.duration);
|
||||||
item.task_public_id(task.publicId);
|
item.task_public_id(task.publicId);
|
||||||
item.invoice_item_type_id = {{ INVOICE_ITEM_TYPE_TASK }};
|
item.invoice_item_type_id({{ INVOICE_ITEM_TYPE_TASK }});
|
||||||
}
|
}
|
||||||
model.invoice().invoice_items.push(blank);
|
model.invoice().invoice_items.push(blank);
|
||||||
model.invoice().has_tasks(true);
|
model.invoice().has_tasks(true);
|
||||||
@ -1233,10 +1150,6 @@
|
|||||||
invoice.imageHeight = {{ $account->getLogoHeight() }};
|
invoice.imageHeight = {{ $account->getLogoHeight() }};
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
//invoiceLabels.item = invoice.has_tasks ? invoiceLabels.date : invoiceLabels.item_orig;
|
|
||||||
invoiceLabels.quantity = invoice.has_tasks ? invoiceLabels.hours : invoiceLabels.quantity_orig;
|
|
||||||
invoiceLabels.unit_cost = invoice.has_tasks ? invoiceLabels.rate : invoiceLabels.unit_cost_orig;
|
|
||||||
|
|
||||||
return invoice;
|
return invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1607,17 +1520,26 @@
|
|||||||
|
|
||||||
function onItemChange(silent)
|
function onItemChange(silent)
|
||||||
{
|
{
|
||||||
var hasEmpty = false;
|
var hasEmptyStandard = false;
|
||||||
|
var hasEmptyTask = false;
|
||||||
for(var i=0; i<model.invoice().invoice_items().length; i++) {
|
for(var i=0; i<model.invoice().invoice_items().length; i++) {
|
||||||
var item = model.invoice().invoice_items()[i];
|
var item = model.invoice().invoice_items()[i];
|
||||||
if (item.isEmpty()) {
|
if (item.isEmpty()) {
|
||||||
hasEmpty = true;
|
if (item.invoice_item_type_id() == {{ INVOICE_ITEM_TYPE_TASK }}) {
|
||||||
|
hasEmptyTask = true;
|
||||||
|
} else {
|
||||||
|
hasEmptyStandard = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasEmpty) {
|
if (!hasEmptyStandard) {
|
||||||
model.invoice().addItem();
|
model.invoice().addItem();
|
||||||
}
|
}
|
||||||
|
if (!hasEmptyTask) {
|
||||||
|
item = model.invoice().addItem();
|
||||||
|
item.invoice_item_type_id({{ INVOICE_ITEM_TYPE_TASK }});
|
||||||
|
}
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
NINJA.formIsChanged = true;
|
NINJA.formIsChanged = true;
|
||||||
|
85
resources/views/invoices/edit_table.blade.php
Normal file
85
resources/views/invoices/edit_table.blade.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<table class="table invoice-table" {!! $isTasks ? 'style="display:none;margin-top:24px;" data-bind="visible: $root.hasTasks"' : '' !!}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="min-width:32px;" class="hide-border"></th>
|
||||||
|
<th style="min-width:120px;width:25%">{{ $invoiceLabels['item'] }}</th>
|
||||||
|
<th style="width:100%">{{ $invoiceLabels['description'] }}</th>
|
||||||
|
@if ($account->showCustomField('custom_invoice_item_label1'))
|
||||||
|
<th style="min-width:120px">{{ $account->custom_invoice_item_label1 }}</th>
|
||||||
|
@endif
|
||||||
|
@if ($account->showCustomField('custom_invoice_item_label2'))
|
||||||
|
<th style="min-width:120px">{{ $account->custom_invoice_item_label2 }}</th>
|
||||||
|
@endif
|
||||||
|
<th style="min-width:120px">{{ $invoiceLabels[$isTasks ? 'rate' : 'unit_cost'] }}</th>
|
||||||
|
<th style="{{ $account->hide_quantity ? 'display:none' : 'min-width:120px' }}">{{ $invoiceLabels[$isTasks ? 'hours' : 'quantity'] }}</th>
|
||||||
|
<th style="min-width:{{ $account->enable_second_tax_rate ? 180 : 120 }}px;display:none;" data-bind="visible: $root.invoice_item_taxes.show">{{ trans('texts.tax') }}</th>
|
||||||
|
<th style="min-width:120px;">{{ trans('texts.line_total') }}</th>
|
||||||
|
<th style="min-width:32px;" class="hide-border"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody data-bind="sortable: { data: {{ $isTasks ? 'invoice_items_with_tasks' : 'invoice_items_without_tasks' }}, afterMove: onDragged }">
|
||||||
|
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }" class="sortable-row">
|
||||||
|
<td class="hide-border td-icon">
|
||||||
|
<i style="display:none" data-bind="visible: actionsVisible() &&
|
||||||
|
$parent.invoice_items().length > 1" class="fa fa-sort"></i>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div id="scrollable-dropdown-menu">
|
||||||
|
<input id="product_key" type="text" data-bind="productTypeahead: product_key, items: $root.products, key: 'product_key', valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][product_key]'}" class="form-control invoice-item handled"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<textarea data-bind="value: notes, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][notes]'}"
|
||||||
|
rows="1" cols="60" style="resize: vertical;height:42px" class="form-control word-wrap"></textarea>
|
||||||
|
<input type="text" data-bind="value: task_public_id, attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][task_public_id]'}" style="display: none"/>
|
||||||
|
<input type="text" data-bind="value: expense_public_id, attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][expense_public_id]'}" style="display: none"/>
|
||||||
|
<input type="text" data-bind="value: invoice_item_type_id, attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][invoice_item_type_id]'}" style="display: none"/>
|
||||||
|
</td>
|
||||||
|
@if ($account->showCustomField('custom_invoice_item_label1'))
|
||||||
|
<td>
|
||||||
|
<input data-bind="value: custom_value1, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][custom_value1]'}" class="form-control invoice-item"/>
|
||||||
|
</td>
|
||||||
|
@endif
|
||||||
|
@if ($account->showCustomField('custom_invoice_item_label2'))
|
||||||
|
<td>
|
||||||
|
<input data-bind="value: custom_value2, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][custom_value2]'}" class="form-control invoice-item"/>
|
||||||
|
</td>
|
||||||
|
@endif
|
||||||
|
<td>
|
||||||
|
<input data-bind="value: prettyCost, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][cost]'}"
|
||||||
|
style="text-align: right" class="form-control invoice-item"/>
|
||||||
|
</td>
|
||||||
|
<td style="{{ $account->hide_quantity ? 'display:none' : '' }}">
|
||||||
|
<input data-bind="value: prettyQty, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][qty]'}"
|
||||||
|
style="text-align: right" class="form-control invoice-item" name="quantity"/>
|
||||||
|
</td>
|
||||||
|
<td style="display:none;" data-bind="visible: $root.invoice_item_taxes.show">
|
||||||
|
{!! Former::select('')
|
||||||
|
->addOption('', '')
|
||||||
|
->options($taxRateOptions)
|
||||||
|
->data_bind('value: tax1, event:{change:onTax1Change}')
|
||||||
|
->addClass($account->enable_second_tax_rate ? 'tax-select' : '')
|
||||||
|
->raw() !!}
|
||||||
|
<input type="text" data-bind="value: tax_name1, attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][tax_name1]'}" style="display:none">
|
||||||
|
<input type="text" data-bind="value: tax_rate1, attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][tax_rate1]'}" style="display:none">
|
||||||
|
<div data-bind="visible: $root.invoice().account.enable_second_tax_rate == '1'">
|
||||||
|
{!! Former::select('')
|
||||||
|
->addOption('', '')
|
||||||
|
->options($taxRateOptions)
|
||||||
|
->data_bind('value: tax2, event:{change:onTax2Change}')
|
||||||
|
->addClass('tax-select')
|
||||||
|
->raw() !!}
|
||||||
|
</div>
|
||||||
|
<input type="text" data-bind="value: tax_name2, attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][tax_name2]'}" style="display:none">
|
||||||
|
<input type="text" data-bind="value: tax_rate2, attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][tax_rate2]'}" style="display:none">
|
||||||
|
</td>
|
||||||
|
<td style="text-align:right;padding-top:9px !important" nowrap>
|
||||||
|
<div class="line-total" data-bind="text: totals.total"></div>
|
||||||
|
</td>
|
||||||
|
<td style="cursor:pointer" class="hide-border td-icon">
|
||||||
|
<i style="padding-left:2px" data-bind="click: $parent.removeItem, visible: actionsVisible() &&
|
||||||
|
$index() < ($parent.invoice_items().length - 1) &&
|
||||||
|
$parent.invoice_items().length > 1" class="fa fa-minus-circle redlink" title="Remove item"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
@ -165,6 +165,17 @@ function ViewModel(data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.hasTasks = ko.computed(function() {
|
||||||
|
invoice = self.invoice();
|
||||||
|
for (var i=0; i<invoice.invoice_items().length; ++i) {
|
||||||
|
var item = invoice.invoice_items()[i];
|
||||||
|
if (! item.isEmpty() && item.invoice_item_type_id() == {{ INVOICE_ITEM_TYPE_TASK }}) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function InvoiceModel(data) {
|
function InvoiceModel(data) {
|
||||||
@ -288,14 +299,6 @@ function InvoiceModel(data) {
|
|||||||
self.addItem();
|
self.addItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.qtyLabel = ko.computed(function() {
|
|
||||||
return self.has_tasks() ? invoiceLabels['hours'] : invoiceLabels['quantity'];
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
self.costLabel = ko.computed(function() {
|
|
||||||
return self.has_tasks() ? invoiceLabels['rate'] : invoiceLabels['unit_cost'];
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.tax1 = ko.computed({
|
this.tax1 = ko.computed({
|
||||||
read: function () {
|
read: function () {
|
||||||
return self.tax_rate1IsInclusive() + ' ' + self.tax_rate1() + ' ' + self.tax_name1();
|
return self.tax_rate1IsInclusive() + ' ' + self.tax_rate1() + ' ' + self.tax_name1();
|
||||||
@ -560,6 +563,18 @@ function InvoiceModel(data) {
|
|||||||
}
|
}
|
||||||
self.applyInclusivTax(taxRate);
|
self.applyInclusivTax(taxRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.invoice_items_with_tasks = ko.computed(function() {
|
||||||
|
return ko.utils.arrayFilter(self.invoice_items(), function(item) {
|
||||||
|
return item.invoice_item_type_id() == {{ INVOICE_ITEM_TYPE_TASK }};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.invoice_items_without_tasks = ko.computed(function() {
|
||||||
|
return ko.utils.arrayFilter(self.invoice_items(), function(item) {
|
||||||
|
return item.invoice_item_type_id() != {{ INVOICE_ITEM_TYPE_TASK }};
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function ClientModel(data) {
|
function ClientModel(data) {
|
||||||
|
@ -103,13 +103,6 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
var invoiceLabels = {!! json_encode($account->getInvoiceLabels()) !!};
|
var invoiceLabels = {!! json_encode($account->getInvoiceLabels()) !!};
|
||||||
|
|
||||||
if (window.invoice) {
|
|
||||||
//invoiceLabels.item = invoice.has_tasks ? invoiceLabels.date : invoiceLabels.item_orig;
|
|
||||||
invoiceLabels.quantity = invoice.has_tasks ? invoiceLabels.hours : invoiceLabels.quantity_orig;
|
|
||||||
invoiceLabels.unit_cost = invoice.has_tasks ? invoiceLabels.rate : invoiceLabels.unit_cost_orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isRefreshing = false;
|
var isRefreshing = false;
|
||||||
var needsRefresh = false;
|
var needsRefresh = false;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user