mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-30 22:44:33 -04:00
Dynamic invoice tables for PDF generation
This commit is contained in:
parent
ea07174df6
commit
e60bcf2d23
@ -45,7 +45,7 @@ class InvoiceItemCalc
|
|||||||
|
|
||||||
public function process()
|
public function process()
|
||||||
{
|
{
|
||||||
$this->line_total = $this->formatValue($this->item->cost, $this->settings->precision) * $this->formatValue($this->item->qty, $this->settings->precision);
|
$this->line_total = $this->formatValue($this->item->cost, $this->settings->precision) * $this->formatValue($this->item->quantity, $this->settings->precision);
|
||||||
|
|
||||||
$this->setDiscount()
|
$this->setDiscount()
|
||||||
->calcTaxes();
|
->calcTaxes();
|
||||||
|
@ -28,7 +28,7 @@ class ClientPresenter extends EntityPresenter
|
|||||||
|
|
||||||
public function primary_contact_name()
|
public function primary_contact_name()
|
||||||
{
|
{
|
||||||
return $this->entity->primary_contact->first()->first_name . ' '. $this->entity->primary_contact->first()->last_name;;
|
return $this->entity->primary_contact->first() !== null ? $this->entity->primary_contact->first()->first_name . ' '. $this->entity->primary_contact->first()->last_name : 'No primary contact set';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function address()
|
public function address()
|
||||||
@ -73,6 +73,13 @@ class ClientPresenter extends EntityPresenter
|
|||||||
return $str;
|
return $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function phone()
|
||||||
|
{
|
||||||
|
return $this->entity->phone ?: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function website()
|
||||||
|
{
|
||||||
|
return $this->entity->website ?: '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class InvoiceItemTransformer extends EntityTransformer
|
|||||||
'archived_at' => $item->deleted_at,
|
'archived_at' => $item->deleted_at,
|
||||||
'notes' => $item->notes,
|
'notes' => $item->notes,
|
||||||
'cost' => (float) $item->cost,
|
'cost' => (float) $item->cost,
|
||||||
'qty' => (float) ($item->qty ?: 0.0),
|
'quantity' => (float) ($item->quantity ?: 0.0),
|
||||||
'tax_name1' => $item->tax_name1 ? $item->tax_name1 : '',
|
'tax_name1' => $item->tax_name1 ? $item->tax_name1 : '',
|
||||||
'tax_rate1' => (float) ($item->tax_rate1 ?: 0.0),
|
'tax_rate1' => (float) ($item->tax_rate1 ?: 0.0),
|
||||||
'tax_name2' => $item->tax_name2 ? $item->tax_name2 : '',
|
'tax_name2' => $item->tax_name2 ? $item->tax_name2 : '',
|
||||||
|
@ -20,6 +20,24 @@ use Illuminate\Support\Facades\Log;
|
|||||||
*/
|
*/
|
||||||
trait MakesInvoiceValues
|
trait MakesInvoiceValues
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
private static $master_columns = [
|
||||||
|
'date',
|
||||||
|
'discount',
|
||||||
|
'product_key',
|
||||||
|
'notes',
|
||||||
|
'cost',
|
||||||
|
'quantity',
|
||||||
|
'tax_name1',
|
||||||
|
'tax_name2',
|
||||||
|
'line_total',
|
||||||
|
'custom_label1',
|
||||||
|
'custom_label2',
|
||||||
|
'custom_label3',
|
||||||
|
'custom_label4',
|
||||||
|
];
|
||||||
|
|
||||||
private static $labels = [
|
private static $labels = [
|
||||||
'invoice',
|
'invoice',
|
||||||
'invoice_date',
|
'invoice_date',
|
||||||
@ -115,7 +133,7 @@ trait MakesInvoiceValues
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach(self::$labels as $label)
|
foreach(self::$labels as $label)
|
||||||
$data[][$label . '_label'] = ctrans('texts'.$label);
|
$data[$label . '_label'] = ctrans('texts.'.$label);
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
@ -177,8 +195,8 @@ trait MakesInvoiceValues
|
|||||||
$data['email'] = isset($this->client->primary_contact()->first()->email) ?: 'no primary contact set';
|
$data['email'] = isset($this->client->primary_contact()->first()->email) ?: 'no primary contact set';
|
||||||
$data['contact_name'] = $this->client->present()->primary_contact_name();
|
$data['contact_name'] = $this->client->present()->primary_contact_name();
|
||||||
$data['company_name'] = $this->company->name;
|
$data['company_name'] = $this->company->name;
|
||||||
$data['website'] = $this->client->website;
|
$data['website'] = $this->client->present()->website();
|
||||||
$data['phone'] = $this->client->primary_contact->first()->phone;
|
$data['phone'] = $this->client->present()->phone();
|
||||||
//$data['blank'] = ;
|
//$data['blank'] = ;
|
||||||
//$data['surcharge'] = ;
|
//$data['surcharge'] = ;
|
||||||
/*
|
/*
|
||||||
@ -224,45 +242,82 @@ trait MakesInvoiceValues
|
|||||||
*
|
*
|
||||||
* @return string[HTML string
|
* @return string[HTML string
|
||||||
*/
|
*/
|
||||||
public function table(array $columns) :string
|
public function table(array $columns) :?string
|
||||||
{
|
{
|
||||||
|
|
||||||
$data = '<table class="table table-hover table-striped">';
|
$data = '<table class="table table-hover table-striped">';
|
||||||
|
|
||||||
$data .= '<thead><tr class="heading">';
|
$data .= '<thead><tr class="heading">';
|
||||||
|
|
||||||
foreach($columns as $column)
|
$column_headers = $this->transformColumnsForHeader($columns);
|
||||||
$data .= '<td>' . ctrans('texts.column') . '</td>';
|
|
||||||
|
foreach($column_headers as $column)
|
||||||
|
$data .= '<td>' . ctrans('texts.'.$column.'') . '</td>';
|
||||||
|
|
||||||
$data .= '</tr></thead>';
|
$data .= '</tr></thead>';
|
||||||
|
|
||||||
$columns = $this->transformColumns($columns);
|
$columns = $this->transformColumnsForLineItems($columns);
|
||||||
|
|
||||||
$items = $this->transformLineItems($this->line_items);
|
$items = $this->transformLineItems($this->line_items);
|
||||||
|
|
||||||
foreach($items as $item)
|
foreach($items as $item)
|
||||||
{
|
{
|
||||||
|
|
||||||
$data .= '<tr class="item">';
|
|
||||||
|
|
||||||
foreach($columns as $column)
|
$data .= '<tr class="item">';
|
||||||
$data .= '<td>'. $item->{$column} . '</td>';
|
|
||||||
|
|
||||||
$data .= '</tr>';
|
foreach($columns as $column)
|
||||||
|
{
|
||||||
|
|
||||||
|
$data .= '<td>'. $item->{$column} . '</td>';
|
||||||
|
|
||||||
|
}
|
||||||
|
$data .= '</tr>';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data .= '</table>';
|
$data .= '</table>';
|
||||||
|
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
|
* Transform the column headers into translated header values
|
||||||
|
*
|
||||||
|
* @param array $columns The column header values
|
||||||
|
* @return array The new column header variables
|
||||||
|
*/
|
||||||
|
private function transformColumnsForHeader(array $columns) :array
|
||||||
|
{
|
||||||
|
|
||||||
|
$columns = array_intersect(self::$master_columns, $columns);
|
||||||
|
|
||||||
|
return str_replace([
|
||||||
|
'tax_name1',
|
||||||
|
'tax_name2'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'tax',
|
||||||
|
'tax',
|
||||||
|
],
|
||||||
|
$columns);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
* Transform the column headers into invoice variables
|
* Transform the column headers into invoice variables
|
||||||
|
*
|
||||||
* @param array $columns The column header values
|
* @param array $columns The column header values
|
||||||
* @return array The invoice variables
|
* @return array The invoice variables
|
||||||
*/
|
*/
|
||||||
private function transformColumns(array $columns) :array
|
private function transformColumnsForLineItems(array $columns) :array
|
||||||
{
|
{
|
||||||
|
/* Removes any invalid columns the user has entered. */
|
||||||
|
$columns = array_intersect(self::$master_columns, $columns);
|
||||||
|
|
||||||
return str_replace([
|
return str_replace([
|
||||||
'custom_invoice_label1',
|
'custom_invoice_label1',
|
||||||
'custom_invoice_label2',
|
'custom_invoice_label2',
|
||||||
@ -294,18 +349,18 @@ trait MakesInvoiceValues
|
|||||||
foreach($items as $item)
|
foreach($items as $item)
|
||||||
{
|
{
|
||||||
|
|
||||||
$item->cost = Number::formatMoney($item->cost, $this->client->currency(), $this->client->country, $this->client->getMergedSettings);
|
$item->cost = Number::formatMoney($item->cost, $this->client->currency(), $this->client->country, $this->client->getMergedSettings());
|
||||||
$item->line_total = Number::formatMoney($item->line_total, $this->client->currency(), $this->client->country, $this->client->getMergedSettings);
|
$item->line_total = Number::formatMoney($item->line_total, $this->client->currency(), $this->client->country, $this->client->getMergedSettings());
|
||||||
|
|
||||||
if(isset($item->discount) && $item->discount > 0)
|
if(isset($item->discount) && $item->discount > 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
if($item->is_amount_discount)
|
if($item->is_amount_discount)
|
||||||
$item->discount = Number::formatMoney($item->discount, $this->client->currency(), $this->client->country, $this->client->getMergedSettings);
|
$item->discount = Number::formatMoney($item->discount, $this->client->currency(), $this->client->country, $this->client->getMergedSettings());
|
||||||
else
|
else
|
||||||
$item->discount = $item->discount . '%';
|
$item->discount = $item->discount . '%';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,9 +115,9 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
Invoice #: {{ $invoice->invoice_number }}<br>
|
{{$invoice_number_label}}: {{ $invoice->invoice_number }}<br>
|
||||||
Created: {{ $invoice->invoice_date }}<br>
|
{{$invoice_date_label}}: {{ $invoice->invoice_date }}<br>
|
||||||
Due: {{ $invoice->due_date }}
|
{{$invoice_due_date_label}}: {{ $invoice->due_date }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -129,33 +129,21 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Sparksuite, Inc.<br>
|
{{$client_name}}<br>
|
||||||
12345 Sunny Road<br>
|
{{$address1}}<br>
|
||||||
Sunnyville, CA 12345
|
{{$address2}}<br>
|
||||||
|
{{$city_state_postal}}<br>
|
||||||
|
{{$country}}<br>
|
||||||
|
{{$vat_number}}<br>
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
Acme Corp.<br>
|
{{$company_name}}<br>
|
||||||
John Doe<br>
|
{{$phone}}<br>
|
||||||
john@example.com
|
{{$email}}<br>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
|
||||||
{{$client_name}}<br>
|
|
||||||
{{$address1}}<br>
|
|
||||||
{{$address2}}<br>
|
|
||||||
{{$id_number}}<br>
|
|
||||||
{{$vat_number}}<br>
|
|
||||||
{{$city_state_postal}}<br>
|
|
||||||
{{$postal_city_state}}<br>
|
|
||||||
{{$country}}<br>
|
|
||||||
{{$email}}<br>
|
|
||||||
{{$contact_name}}<br>
|
|
||||||
{{$company_name}}<br>
|
|
||||||
{{$website}}<br>
|
|
||||||
{{$phone}}<br>
|
|
||||||
{{$terms}}<br>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
@ -167,7 +155,6 @@
|
|||||||
date
|
date
|
||||||
discount
|
discount
|
||||||
product_key
|
product_key
|
||||||
item
|
|
||||||
notes
|
notes
|
||||||
cost
|
cost
|
||||||
quantity
|
quantity
|
||||||
@ -179,301 +166,10 @@
|
|||||||
custom_label3 ( will show as the following parameter as its value -> custom_invoice_value3 )
|
custom_label3 ( will show as the following parameter as its value -> custom_invoice_value3 )
|
||||||
custom_label4 ( will show as the following parameter as its value -> custom_invoice_value4 )
|
custom_label4 ( will show as the following parameter as its value -> custom_invoice_value4 )
|
||||||
--}}
|
--}}
|
||||||
{{ $invoice->table(['item','description','cost','quantity', 'tax_name1', 'line_total']) }}
|
{!! $invoice->table(['product_key', 'notes', 'cost','quantity', 'line_total']) !!}
|
||||||
|
|
||||||
<table cellpadding="0" cellspacing="0">
|
|
||||||
|
<table>
|
||||||
|
|
||||||
<thead>
|
|
||||||
<tr class="heading">
|
|
||||||
<td>
|
|
||||||
Item
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
Price
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr> <tr class="item">
|
|
||||||
<td>
|
|
||||||
Website design
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$300.00
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr class="item">
|
|
||||||
<td>
|
|
||||||
Hosting (3 months)
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$75.00
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr class="item last">
|
|
||||||
<td>
|
|
||||||
Domain name (1 year)
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
$10.00
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td></td>
|
<td></td>
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ class InvoiceTest extends TestCase
|
|||||||
$data = [
|
$data = [
|
||||||
'first_name' => $this->faker->firstName,
|
'first_name' => $this->faker->firstName,
|
||||||
'last_name' => $this->faker->lastName,
|
'last_name' => $this->faker->lastName,
|
||||||
'name' => $this->faker->company,
|
'name' => $this->faker->company,
|
||||||
'email' => $this->faker->unique()->safeEmail,
|
'email' => $this->faker->unique()->safeEmail,
|
||||||
'password' => 'ALongAndBrilliantPassword123',
|
'password' => 'ALongAndBrilliantPassword123',
|
||||||
'_token' => csrf_token(),
|
'_token' => csrf_token(),
|
||||||
'privacy_policy' => 1,
|
'privacy_policy' => 1,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user