1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-13 22:54:25 +01:00

Support delivery notes #462

This commit is contained in:
Hillel Coren 2017-11-20 15:40:12 +02:00
parent ddd60c4d3c
commit 7fbe213146
10 changed files with 200 additions and 16 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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];
@ -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') {
if (invoice.is_delivery_note && client.shipping_address1) {
value = client.shipping_address1;
} else {
value = client.address1; value = client.address1;
}
} else if (field == 'client.address2') { } else if (field == 'client.address2') {
if (invoice.is_delivery_note && client.shipping_address1) {
value = client.shipping_address2;
} else {
value = client.address2; value = client.address2;
}
} else if (field == 'client.city_state_postal') { } else if (field == 'client.city_state_postal') {
var cityStatePostal = ''; var cityStatePostal = '';
if (invoice.is_delivery_note && client.shipping_address1) {
if (client.shipping_city || client.shipping_state || client.shipping_postal_code) {
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) { if (client.city || client.state || client.postal_code) {
var swap = client.country && client.country.swap_postal_code; var swap = client.country && client.country.swap_postal_code;
cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap); 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 (invoice.is_delivery_note && client.shipping_address1) {
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) { if (client.city || client.state || client.postal_code) {
postalCityState = formatAddress(client.city, client.state, client.postal_code, true); postalCityState = formatAddress(client.city, client.state, client.postal_code, true);
} }
}
value = postalCityState; value = postalCityState;
} else if (field == 'client.country') { } else if (field == 'client.country') {
if (invoice.is_delivery_note && client.shipping_address1) {
value = client.shipping_country ? client.shipping_country.name : '';
} else {
value = client.country ? client.country.name : ''; 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') {

View File

@ -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',
); );

View 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

View File

@ -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/>&nbsp;<br/> <br/>&nbsp;<br/>

View File

@ -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');