mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-12 22:22:32 +01:00
Support delivery notes #462
This commit is contained in:
parent
ddd60c4d3c
commit
7fbe213146
@ -590,6 +590,28 @@ class InvoiceController extends BaseController
|
|||||||
return View::make('invoices.history', $data);
|
return View::make('invoices.history', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deliveryNote(InvoiceRequest $request)
|
||||||
|
{
|
||||||
|
$invoice = $request->entity();
|
||||||
|
$invoice->load('user', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'account.country', 'client.contacts', 'client.country', 'client.shipping_country');
|
||||||
|
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
||||||
|
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
||||||
|
$invoice->features = [
|
||||||
|
'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN),
|
||||||
|
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
|
||||||
|
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
||||||
|
];
|
||||||
|
$invoice->invoice_type_id = intval($invoice->invoice_type_id);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'invoice' => $invoice,
|
||||||
|
'invoiceDesigns' => InvoiceDesign::getDesigns(),
|
||||||
|
'invoiceFonts' => Cache::get('fonts'),
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('invoices.delivery_note', $data);
|
||||||
|
}
|
||||||
|
|
||||||
public function checkInvoiceNumber($invoicePublicId = false)
|
public function checkInvoiceNumber($invoicePublicId = false)
|
||||||
{
|
{
|
||||||
$invoiceNumber = request()->invoice_number;
|
$invoiceNumber = request()->invoice_number;
|
||||||
|
@ -106,11 +106,20 @@ class InvoiceDatatable extends EntityDatatable
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
trans('texts.view_history'),
|
trans("texts.{$entityType}_history"),
|
||||||
function ($model) use ($entityType) {
|
function ($model) use ($entityType) {
|
||||||
return URL::to("{$entityType}s/{$entityType}_history/{$model->public_id}");
|
return URL::to("{$entityType}s/{$entityType}_history/{$model->public_id}");
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
trans('texts.delivery_note'),
|
||||||
|
function ($model) use ($entityType) {
|
||||||
|
return url("invoices/delivery_note/{$model->public_id}");
|
||||||
|
},
|
||||||
|
function ($model) use ($entityType) {
|
||||||
|
return $entityType == ENTITY_INVOICE;
|
||||||
|
},
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'--divider--', function () {
|
'--divider--', function () {
|
||||||
return false;
|
return false;
|
||||||
|
@ -242,6 +242,11 @@ class InvoicePresenter extends EntityPresenter
|
|||||||
}
|
}
|
||||||
|
|
||||||
$actions[] = ['url' => url("{$entityType}s/{$entityType}_history/{$invoice->public_id}"), 'label' => trans('texts.view_history')];
|
$actions[] = ['url' => url("{$entityType}s/{$entityType}_history/{$invoice->public_id}"), 'label' => trans('texts.view_history')];
|
||||||
|
|
||||||
|
if ($entityType == ENTITY_INVOICE) {
|
||||||
|
$actions[] = ['url' => url("invoices/delivery_note/{$invoice->public_id}"), 'label' => trans('texts.delivery_note')];
|
||||||
|
}
|
||||||
|
|
||||||
$actions[] = DropdownButton::DIVIDER;
|
$actions[] = DropdownButton::DIVIDER;
|
||||||
|
|
||||||
if ($entityType == ENTITY_QUOTE) {
|
if ($entityType == ENTITY_QUOTE) {
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -417,6 +417,18 @@ NINJA.invoiceColumns = function(invoice, design, isTasks)
|
|||||||
var field = fields[i];
|
var field = fields[i];
|
||||||
var width = 0;
|
var width = 0;
|
||||||
|
|
||||||
|
if (invoice.is_delivery_note) {
|
||||||
|
var skipFields = [
|
||||||
|
'product.unit_cost',
|
||||||
|
'product.rate',
|
||||||
|
'product.tax',
|
||||||
|
'product.line_total',
|
||||||
|
];
|
||||||
|
if (skipFields.indexOf(field) >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (field == 'product.custom_value1') {
|
if (field == 'product.custom_value1') {
|
||||||
if (invoice.has_custom_item_value1) {
|
if (invoice.has_custom_item_value1) {
|
||||||
width = 10;
|
width = 10;
|
||||||
@ -529,6 +541,12 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
|||||||
var isTasks = isSecondTable || (invoice.hasTasks && !invoice.hasStandard);
|
var isTasks = isSecondTable || (invoice.hasTasks && !invoice.hasStandard);
|
||||||
var grid = [[]];
|
var grid = [[]];
|
||||||
var styles = ['tableHeader'];
|
var styles = ['tableHeader'];
|
||||||
|
var skipFields = [
|
||||||
|
'product.unit_cost',
|
||||||
|
'product.rate',
|
||||||
|
'product.tax',
|
||||||
|
'product.line_total',
|
||||||
|
];
|
||||||
|
|
||||||
if (isSecondTable) {
|
if (isSecondTable) {
|
||||||
styles.push('secondTableHeader');
|
styles.push('secondTableHeader');
|
||||||
@ -539,6 +557,11 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
|||||||
|
|
||||||
for (var i=0; i<fields.length; i++) {
|
for (var i=0; i<fields.length; i++) {
|
||||||
var field = fields[i].split('.')[1]; // split to remove 'product.'
|
var field = fields[i].split('.')[1]; // split to remove 'product.'
|
||||||
|
|
||||||
|
if (invoice.is_delivery_note && skipFields.indexOf(fields[i]) >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var headerStyles = styles.concat([snakeToCamel(field), snakeToCamel(field) + 'TableHeader']);
|
var headerStyles = styles.concat([snakeToCamel(field), snakeToCamel(field) + 'TableHeader']);
|
||||||
var value = invoiceLabels[field];
|
var value = invoiceLabels[field];
|
||||||
|
|
||||||
@ -612,7 +635,7 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
|||||||
custom_value1 = processVariables(item.custom_value1);
|
custom_value1 = processVariables(item.custom_value1);
|
||||||
custom_value2 = processVariables(item.custom_value2);
|
custom_value2 = processVariables(item.custom_value2);
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineTotal = roundSignificant(NINJA.parseFloat(item.cost) * NINJA.parseFloat(item.qty));
|
var lineTotal = roundSignificant(NINJA.parseFloat(item.cost) * NINJA.parseFloat(item.qty));
|
||||||
if (account.include_item_taxes_inline == '1') {
|
if (account.include_item_taxes_inline == '1') {
|
||||||
var taxAmount1 = 0;
|
var taxAmount1 = 0;
|
||||||
@ -633,6 +656,11 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
|||||||
|
|
||||||
for (var j=0; j<fields.length; j++) {
|
for (var j=0; j<fields.length; j++) {
|
||||||
var field = fields[j].split('.')[1]; // split to remove 'product.'
|
var field = fields[j].split('.')[1]; // split to remove 'product.'
|
||||||
|
|
||||||
|
if (invoice.is_delivery_note && skipFields.indexOf(fields[j]) >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var value = item[field];
|
var value = item[field];
|
||||||
var styles = [snakeToCamel(field), rowStyle];
|
var styles = [snakeToCamel(field), rowStyle];
|
||||||
|
|
||||||
@ -742,8 +770,8 @@ NINJA.statementSubtotals = function(invoice)
|
|||||||
|
|
||||||
NINJA.subtotals = function(invoice, hideBalance)
|
NINJA.subtotals = function(invoice, hideBalance)
|
||||||
{
|
{
|
||||||
if (!invoice) {
|
if (! invoice || invoice.is_delivery_note) {
|
||||||
return;
|
return [[]];
|
||||||
}
|
}
|
||||||
|
|
||||||
var account = invoice.account;
|
var account = invoice.account;
|
||||||
@ -818,6 +846,10 @@ NINJA.subtotals = function(invoice, hideBalance)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NINJA.subtotalsBalance = function(invoice) {
|
NINJA.subtotalsBalance = function(invoice) {
|
||||||
|
if (invoice.is_delivery_note) {
|
||||||
|
return [[]];
|
||||||
|
}
|
||||||
|
|
||||||
var isPartial = NINJA.parseFloat(invoice.partial);
|
var isPartial = NINJA.parseFloat(invoice.partial);
|
||||||
return [[
|
return [[
|
||||||
{text: isPartial ? invoiceLabels.partial_due : (invoice.is_quote || invoice.balance_amount < 0 ? invoiceLabels.total : invoiceLabels.balance_due), style:['subtotalsLabel', 'subtotalsBalanceDueLabel']},
|
{text: isPartial ? invoiceLabels.partial_due : (invoice.is_quote || invoice.balance_amount < 0 ? invoiceLabels.total : invoiceLabels.balance_due), style:['subtotalsLabel', 'subtotalsBalanceDueLabel']},
|
||||||
@ -913,6 +945,17 @@ NINJA.invoiceDetails = function(invoice) {
|
|||||||
|
|
||||||
|
|
||||||
NINJA.renderField = function(invoice, field, twoColumn) {
|
NINJA.renderField = function(invoice, field, twoColumn) {
|
||||||
|
if (invoice.is_delivery_note) {
|
||||||
|
var skipFields = [
|
||||||
|
'invoice.due_date',
|
||||||
|
'invoice.balance_due',
|
||||||
|
'invoice.partial_due',
|
||||||
|
];
|
||||||
|
if (skipFields.indexOf(field) >= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var client = invoice.client;
|
var client = invoice.client;
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return false;
|
return false;
|
||||||
@ -939,24 +982,49 @@ NINJA.renderField = function(invoice, field, twoColumn) {
|
|||||||
label = invoiceLabels.vat_number;
|
label = invoiceLabels.vat_number;
|
||||||
}
|
}
|
||||||
} else if (field == 'client.address1') {
|
} else if (field == 'client.address1') {
|
||||||
value = client.address1;
|
if (invoice.is_delivery_note && client.shipping_address1) {
|
||||||
|
value = client.shipping_address1;
|
||||||
|
} else {
|
||||||
|
value = client.address1;
|
||||||
|
}
|
||||||
} else if (field == 'client.address2') {
|
} else if (field == 'client.address2') {
|
||||||
value = client.address2;
|
if (invoice.is_delivery_note && client.shipping_address1) {
|
||||||
|
value = client.shipping_address2;
|
||||||
|
} else {
|
||||||
|
value = client.address2;
|
||||||
|
}
|
||||||
} else if (field == 'client.city_state_postal') {
|
} else if (field == 'client.city_state_postal') {
|
||||||
var cityStatePostal = '';
|
var cityStatePostal = '';
|
||||||
if (client.city || client.state || client.postal_code) {
|
if (invoice.is_delivery_note && client.shipping_address1) {
|
||||||
var swap = client.country && client.country.swap_postal_code;
|
if (client.shipping_city || client.shipping_state || client.shipping_postal_code) {
|
||||||
cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap);
|
var swap = client.shipping_country && client.shipping_country.swap_postal_code;
|
||||||
|
cityStatePostal = formatAddress(client.shipping_city, client.shipping_state, client.shipping_postal_code, swap);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (client.city || client.state || client.postal_code) {
|
||||||
|
var swap = client.country && client.country.swap_postal_code;
|
||||||
|
cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value = cityStatePostal;
|
value = cityStatePostal;
|
||||||
} else if (field == 'client.postal_city_state') {
|
} else if (field == 'client.postal_city_state') {
|
||||||
var postalCityState = '';
|
var postalCityState = '';
|
||||||
if (client.city || client.state || client.postal_code) {
|
if (invoice.is_delivery_note && client.shipping_address1) {
|
||||||
postalCityState = formatAddress(client.city, client.state, client.postal_code, true);
|
if (client.shipping_city || client.shipping_state || client.shipping_postal_code) {
|
||||||
|
postalCityState = formatAddress(client.shipping_city, client.shipping_state, client.shipping_postal_code, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (client.city || client.state || client.postal_code) {
|
||||||
|
postalCityState = formatAddress(client.city, client.state, client.postal_code, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value = postalCityState;
|
value = postalCityState;
|
||||||
} else if (field == 'client.country') {
|
} else if (field == 'client.country') {
|
||||||
value = client.country ? client.country.name : '';
|
if (invoice.is_delivery_note && client.shipping_address1) {
|
||||||
|
value = client.shipping_country ? client.shipping_country.name : '';
|
||||||
|
} else {
|
||||||
|
value = client.country ? client.country.name : '';
|
||||||
|
}
|
||||||
} else if (field == 'client.email') {
|
} else if (field == 'client.email') {
|
||||||
value = contact.email == clientName ? '' : contact.email;
|
value = contact.email == clientName ? '' : contact.email;
|
||||||
} else if (field == 'client.phone') {
|
} else if (field == 'client.phone') {
|
||||||
|
@ -2543,6 +2543,7 @@ $LANG = array(
|
|||||||
'classify' => 'Classify',
|
'classify' => 'Classify',
|
||||||
'show_shipping_address_help' => 'Require client to provide their shipping address',
|
'show_shipping_address_help' => 'Require client to provide their shipping address',
|
||||||
'ship_to_billing_address' => 'Ship to billing address',
|
'ship_to_billing_address' => 'Ship to billing address',
|
||||||
|
'delivery_note' => 'Delivery Note',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
75
resources/views/invoices/delivery_note.blade.php
Normal file
75
resources/views/invoices/delivery_note.blade.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
@extends('header')
|
||||||
|
|
||||||
|
@section('head')
|
||||||
|
@parent
|
||||||
|
|
||||||
|
@include('money_script')
|
||||||
|
@foreach (Auth::user()->account->getFontFolders() as $font)
|
||||||
|
<script src="{{ asset('js/vfs_fonts/'.$font.'.js') }}" type="text/javascript"></script>
|
||||||
|
@endforeach
|
||||||
|
<script src="{{ asset('pdf.built.js') }}?no_cache={{ NINJA_VERSION }}" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var invoice = {!! $invoice !!};
|
||||||
|
var invoiceDesign = false;
|
||||||
|
var invoiceDesigns = {!! $invoiceDesigns !!};
|
||||||
|
var invoiceFonts = {!! $invoiceFonts !!};
|
||||||
|
|
||||||
|
function getPDFString(cb) {
|
||||||
|
invoice.image = window.accountLogo;
|
||||||
|
invoice.is_delivery_note = true;
|
||||||
|
var invoiceDesignId = parseInt(invoice.invoice_design_id);
|
||||||
|
invoiceDesign = _.findWhere(invoiceDesigns, {id: invoiceDesignId});
|
||||||
|
if (!invoiceDesign) {
|
||||||
|
invoiceDesign = invoiceDesigns[0];
|
||||||
|
}
|
||||||
|
generatePDF(invoice, invoiceDesign.javascript, true, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDownloadClick() {
|
||||||
|
trackEvent('/activity', '/download_pdf');
|
||||||
|
var doc = generatePDF(invoice, invoiceDesign.javascript, true);
|
||||||
|
doc.save('{{ str_replace(' ', '_', trans('texts.delivery_note')) }}-{{ $invoice->invoice_number }}.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
refreshPDF();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('top-right')
|
||||||
|
<div class="pull-right">
|
||||||
|
{!! Button::normal(trans('texts.download_pdf'))
|
||||||
|
->withAttributes(['onclick' => 'onDownloadClick()', 'id' => 'downloadPdfButton'])
|
||||||
|
->appendIcon(Icon::create('download-alt')) !!}
|
||||||
|
|
||||||
|
{!! Button::primary(trans('texts.edit_' . $invoice->getEntityType()))
|
||||||
|
->asLinkTo(url('/' . $invoice->getEntityType() . 's/' . $invoice->public_id . '/edit'))
|
||||||
|
->appendIcon(Icon::create('edit')) !!}
|
||||||
|
</div>
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
|
||||||
|
@include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800])
|
||||||
|
|
||||||
|
@if (Utils::hasFeature(FEATURE_DOCUMENTS) && $invoice->account->invoice_embed_documents)
|
||||||
|
@foreach ($invoice->documents as $document)
|
||||||
|
@if($document->isPDFEmbeddable())
|
||||||
|
<script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@foreach ($invoice->expenses as $expense)
|
||||||
|
@foreach ($expense->documents as $document)
|
||||||
|
@if($document->isPDFEmbeddable())
|
||||||
|
<script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@stop
|
@ -61,7 +61,10 @@
|
|||||||
->style('background-color: white !important') !!}
|
->style('background-color: white !important') !!}
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{!! Button::primary(trans('texts.edit_' . $invoice->getEntityType()))->asLinkTo(URL::to('/' . $invoice->getEntityType() . 's/' . $invoice->public_id . '/edit'))->withAttributes(array('class' => 'pull-right')) !!}
|
{!! Button::primary(trans('texts.edit_' . $invoice->getEntityType()))
|
||||||
|
->asLinkTo(URL::to('/' . $invoice->getEntityType() . 's/' . $invoice->public_id . '/edit'))
|
||||||
|
->appendIcon(Icon::create('edit'))
|
||||||
|
->withAttributes(array('class' => 'pull-right')) !!}
|
||||||
{!! Former::close() !!}
|
{!! Former::close() !!}
|
||||||
|
|
||||||
<br/> <br/>
|
<br/> <br/>
|
||||||
|
@ -155,6 +155,7 @@ Route::group(['middleware' => ['lookup:user', 'auth:user']], function () {
|
|||||||
|
|
||||||
Route::get('api/recurring_invoices/{client_id?}', 'InvoiceController@getRecurringDatatable');
|
Route::get('api/recurring_invoices/{client_id?}', 'InvoiceController@getRecurringDatatable');
|
||||||
|
|
||||||
|
Route::get('invoices/delivery_note/{invoice_id}', 'InvoiceController@deliveryNote');
|
||||||
Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
||||||
Route::get('quotes/quote_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
Route::get('quotes/quote_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user