mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 12:37:32 -04: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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user