line item discounts

This commit is contained in:
Hillel Coren 2017-12-21 16:30:18 +02:00
parent f37559a239
commit 15b5860ffe
6 changed files with 150 additions and 137 deletions

View File

@ -40,6 +40,7 @@ class InvoiceItem extends EntityModel
'tax_name2',
'tax_rate2',
'invoice_item_type_id',
'discount',
];
/**

View File

@ -186,6 +186,7 @@ trait PresentsInvoice
'product.custom_value2',
'product.unit_cost',
'product.quantity',
'product.discount',
'product.tax',
'product.line_total',
],
@ -196,6 +197,7 @@ trait PresentsInvoice
'product.custom_value2',
'product.rate',
'product.hours',
'product.discount',
'product.tax',
'product.line_total',
],
@ -378,9 +380,9 @@ trait PresentsInvoice
return null;
}
public function hideQuantity() {
public function hasInvoiceField($type, $field) {
$fields = $this->getInvoiceFields();
return ! isset($fields['product_fields']['product.quantity']);
return isset($fields[$type . '_fields'][$field]);
}
}

View File

@ -51,6 +51,10 @@ class AddRemember2faToken extends Migration
Schema::table('accounts', function ($table) {
$table->boolean('convert_products')->default(false);
});
Schema::table('invoice_items', function ($table) {
$table->float('discount');
});
}
/**
@ -82,5 +86,9 @@ class AddRemember2faToken extends Migration
Schema::table('accounts', function ($table) {
$table->dropColumn('convert_products');
});
Schema::table('invoice_items', function ($table) {
$table->dropColumn('discount');
});
}
}

View File

@ -24,6 +24,25 @@
padding: 20px;
}
.subtotals-table {
min-width: 340px;
}
.subtotals-table tr {
border-bottom: solid #CCCCCC 1px;
}
.subtotals-table td {
padding-top: 20px;
padding-bottom: 12px;
}
.subtotals-table input {
float: right;
text-align: right;
max-width: 150px;
}
</style>
@stop
@ -284,7 +303,6 @@
<div class="table-responsive" style="padding-top:4px;">
<table class="table invoice-table">
@include('invoices.edit_table', ['isTasks' => false])
@ -292,12 +310,100 @@
@include('invoices.edit_table', ['isTasks' => true])
@endif
<tfoot>
{{ Former::setOption('TwitterBootstrap3.labelWidths.large', 4) }}
{{ Former::setOption('TwitterBootstrap3.labelWidths.small', 4) }}
<table class="pull-right subtotals-table" style="margin-right:40px; margin-top:0px;">
<tr>
<td class="hide-border"/>
<td class="hide-border" colspan="{{ 2 + ($account->showCustomField('custom_invoice_item_label1') ? 1 : 0) + ($account->showCustomField('custom_invoice_item_label2') ? 1 : 0) }}" rowspan="10" style="vertical-align:top">
<br/>
<div role="tabpanel">
<td colspan="2">{{ trans('texts.subtotal') }}</td>
<td style="text-align: right"><span data-bind="text: totals.subtotal"/></td>
</tr>
<tr style="display:none" data-bind="visible: discount() != 0">
<td colspan="2">{{ trans('texts.discount') }}</td>
<td style="text-align: right"><span data-bind="text: totals.discounted"/></td>
</tr>
@if ($account->showCustomField('custom_invoice_label1', $invoice) && $invoice->custom_taxes1)
<tr>
<td colspan="2">{{ $account->custom_invoice_label1 ?: trans('texts.surcharge') }}</td>
<td><input name="custom_value1" class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if ($account->showCustomField('custom_invoice_label2', $invoice) && $invoice->custom_taxes2)
<tr>
<td colspan="2">{{ $account->custom_invoice_label2 ?: trans('texts.surcharge') }}</td>
<td><input name="custom_value2" class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
<tr style="display:none" data-bind="visible: $root.invoice_item_taxes.show &amp;&amp; totals.hasItemTaxes">
<td>{{ trans('texts.tax') }}&nbsp;&nbsp;</td>
<td style="min-width:120px"><span data-bind="html: totals.itemTaxRates"/></td>
<td style="text-align: right"><span data-bind="html: totals.itemTaxAmounts"/></td>
</tr>
<tr style="display:none" data-bind="visible: $root.invoice_taxes.show">
<td>{{ trans('texts.tax') }}&nbsp;&nbsp;</td>
<td style="min-width:120px">
{!! Former::select('')
->id('taxRateSelect1')
->addOption('', '')
->options($taxRateOptions)
->addClass($account->enable_second_tax_rate ? 'tax-select' : '')
->data_bind('value: tax1, event:{change:onTax1Change}')
->raw() !!}
<input type="text" name="tax_name1" data-bind="value: tax_name1" style="display:none">
<input type="text" name="tax_rate1" data-bind="value: tax_rate1" style="display:none">
<div data-bind="visible: $root.invoice().account.enable_second_tax_rate == '1'">
{!! Former::select('')
->addOption('', '')
->options($taxRateOptions)
->addClass('tax-select')
->data_bind('value: tax2, event:{change:onTax2Change}')
->raw() !!}
</div>
<input type="text" name="tax_name2" data-bind="value: tax_name2" style="display:none">
<input type="text" name="tax_rate2" data-bind="value: tax_rate2" style="display:none">
</td>
<td style="text-align: right"><span data-bind="text: totals.taxAmount"/></td>
</tr>
@if ($account->showCustomField('custom_invoice_label1', $invoice) && !$invoice->custom_taxes1)
<tr>
<td colspan="2">{{ $account->custom_invoice_label1 ?: trans('texts.surcharge') }}</td>
<td><input name="custom_value1" class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if ($account->showCustomField('custom_invoice_label2', $invoice) && !$invoice->custom_taxes2)
<tr>
<td colspan="2">{{ $account->custom_invoice_label2 ?: trans('texts.surcharge') }}</td>
<td><input name="custom_value2" class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if (!$account->hide_paid_to_date)
<tr>
<td colspan="2">{{ trans('texts.paid_to_date') }}</td>
<td style="text-align: right" data-bind="text: totals.paidToDate"></td>
</tr>
@endif
<tr data-bind="style: { 'font-weight': partial() ? 'normal' : 'bold', 'font-size': partial() ? '1em' : '1.05em' }">
<td class="hide-border" data-bind="css: {'hide-border': !partial()}" colspan="2">{{ $entityType == ENTITY_INVOICE ? $invoiceLabels['balance_due'] : trans('texts.total') }}</td>
<td class="hide-border" data-bind="css: {'hide-border': !partial()}" style="text-align: right"><span data-bind="text: totals.total"></span></td>
</tr>
<tr style="font-size:1.05em; display:none; font-weight:bold" data-bind="visible: partial">
<td class="hide-border" colspan="2">{{ $invoiceLabels['partial_due'] }}</td>
<td class="hide-border" style="text-align: right"><span data-bind="text: totals.partial"></span></td>
</tr>
</table>
<div role="tabpanel" class="pull-left" style="margin-left:40px; margin-top:30px;">
<ul class="nav nav-tabs" role="tablist" style="border: none">
<li role="presentation" class="active"><a href="#public_notes" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.public_notes') }}</a></li>
@ -385,120 +491,8 @@
</div>
</div>
{{ Former::setOption('TwitterBootstrap3.labelWidths.large', 4) }}
{{ Former::setOption('TwitterBootstrap3.labelWidths.small', 4) }}
</td>
<td class="hide-border" style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ trans('texts.subtotal') }}</td>
<td style="text-align: right"><span data-bind="text: totals.subtotal"/></td>
</tr>
<tr style="display:none" data-bind="visible: discount() != 0">
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ trans('texts.discount') }}</td>
<td style="text-align: right"><span data-bind="text: totals.discounted"/></td>
</tr>
@if ($account->showCustomField('custom_invoice_label1', $invoice) && $invoice->custom_taxes1)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ $account->custom_invoice_label1 ?: trans('texts.surcharge') }}</td>
<td style="text-align: right;padding-right: 28px" colspan="2"><input name="custom_value1" class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if ($account->showCustomField('custom_invoice_label2', $invoice) && $invoice->custom_taxes2)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ $account->custom_invoice_label2 ?: trans('texts.surcharge') }}</td>
<td style="text-align: right;padding-right: 28px" colspan="2"><input name="custom_value2" class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
<tr style="display:none" data-bind="visible: $root.invoice_item_taxes.show &amp;&amp; totals.hasItemTaxes">
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td>{{ trans('texts.tax') }}</td>
<td style="min-width:120px"><span data-bind="html: totals.itemTaxRates"/></td>
<td style="text-align: right"><span data-bind="html: totals.itemTaxAmounts"/></td>
</tr>
<tr style="display:none" data-bind="visible: $root.invoice_taxes.show">
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td>{{ trans('texts.tax') }}</td>
<td style="min-width:120px">
{!! Former::select('')
->id('taxRateSelect1')
->addOption('', '')
->options($taxRateOptions)
->addClass($account->enable_second_tax_rate ? 'tax-select' : '')
->data_bind('value: tax1, event:{change:onTax1Change}')
->raw() !!}
<input type="text" name="tax_name1" data-bind="value: tax_name1" style="display:none">
<input type="text" name="tax_rate1" data-bind="value: tax_rate1" style="display:none">
<div data-bind="visible: $root.invoice().account.enable_second_tax_rate == '1'">
{!! Former::select('')
->addOption('', '')
->options($taxRateOptions)
->addClass('tax-select')
->data_bind('value: tax2, event:{change:onTax2Change}')
->raw() !!}
</div>
<input type="text" name="tax_name2" data-bind="value: tax_name2" style="display:none">
<input type="text" name="tax_rate2" data-bind="value: tax_rate2" style="display:none">
</td>
<td style="text-align: right"><span data-bind="text: totals.taxAmount"/></td>
</tr>
@if ($account->showCustomField('custom_invoice_label1', $invoice) && !$invoice->custom_taxes1)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ $account->custom_invoice_label1 ?: trans('texts.surcharge') }}</td>
<td style="text-align: right;padding-right: 28px" colspan="2"><input name="custom_value1" class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if ($account->showCustomField('custom_invoice_label2', $invoice) && !$invoice->custom_taxes2)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ $account->custom_invoice_label2 ?: trans('texts.surcharge') }}</td>
<td style="text-align: right;padding-right: 28px" colspan="2"><input name="custom_value2" class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if (!$account->hide_paid_to_date)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ trans('texts.paid_to_date') }}</td>
<td style="text-align: right" data-bind="text: totals.paidToDate"></td>
</tr>
@endif
<tr data-bind="style: { 'font-weight': partial() ? 'normal' : 'bold', 'font-size': partial() ? '1em' : '1.05em' }">
<td class="hide-border" colspan="3"/>
<td class="hide-border" style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
<td class="hide-border" data-bind="css: {'hide-border': !partial()}" colspan="2">{{ $entityType == ENTITY_INVOICE ? $invoiceLabels['balance_due'] : trans('texts.total') }}</td>
<td class="hide-border" data-bind="css: {'hide-border': !partial()}" style="text-align: right"><span data-bind="text: totals.total"></span></td>
</tr>
<tr style="font-size:1.05em; display:none; font-weight:bold" data-bind="visible: partial">
<td class="hide-border" colspan="3"/>
<td class="hide-border" style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
<td class="hide-border" colspan="2">{{ $invoiceLabels['partial_due'] }}</td>
<td class="hide-border" style="text-align: right"><span data-bind="text: totals.partial"></span></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>

View File

@ -1,3 +1,4 @@
<table class="table invoice-table">
<thead {!! $isTasks ? 'style="display:none;" data-bind="visible: $root.hasTasks"' : ($invoice->has_tasks || ! empty($tasks) ? 'data-bind="visible: $root.hasItems"' : '') !!}>
@if ($isTasks)
<tr data-bind="visible: $root.hasItems">
@ -15,7 +16,8 @@
<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="min-width:120px">{{ $invoiceLabels[$isTasks ? 'hours' : 'quantity'] }}</th>
<th style="min-width:120px;display:{{ $account->hasInvoiceField($isTasks ? 'task' : 'product', $isTasks ? 'product.hours' : 'product.quantity') ? 'table-cell' : 'none' }}">{{ $invoiceLabels[$isTasks ? 'hours' : 'quantity'] }}</th>
<th style="min-width:120px;display:{{ $account->hasInvoiceField($isTasks ? 'task' : 'product', 'product.discount') ? 'table-cell' : 'none' }}">{{ $invoiceLabels['discount'] }}</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>
@ -54,10 +56,14 @@
<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>
<td style="display:{{ $account->hasInvoiceField($isTasks ? 'task' : 'product', $isTasks ? 'product.hours' : 'product.quantity') ? 'table-cell' : '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:{{ $account->hasInvoiceField($isTasks ? 'task' : 'product', 'product.discount') ? 'table-cell' : 'none' }}">
<input data-bind="value: discount, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[{{ $isTasks ? 'T' : '' }}' + $index() + '][discount]'}"
style="text-align: right" class="form-control invoice-item" name="discount"/>
</td>
<td style="display:none;" data-bind="visible: $root.invoice_item_taxes.show">
{!! Former::select('')
->addOption('', '')
@ -87,3 +93,4 @@
</td>
</tr>
</tbody>
</table>

View File

@ -821,6 +821,7 @@ function ItemModel(data) {
self.notes = ko.observable('');
self.cost = ko.observable(0);
self.qty = ko.observable(0);
self.discount = ko.observable();
self.custom_value1 = ko.observable('');
self.custom_value2 = ko.observable('');
self.tax_name1 = ko.observable('');