mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Fixes for rounding
This commit is contained in:
parent
d7362c6bbe
commit
ea3c5236c6
@ -633,9 +633,7 @@ class PdfBuilder
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td']];
|
||||
} elseif ($cell == '$product.tax_rate3') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td']];
|
||||
}
|
||||
|
||||
elseif ($cell == '$task.discount' && !$this->service->company->enable_product_discount) {
|
||||
} elseif ($cell == '$task.discount' && !$this->service->company->enable_product_discount) {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['$task.discount'], 'properties' => ['data-ref' => 'task_table-task.discount-td', 'style' => 'display: none;']];
|
||||
} elseif ($cell == '$task.tax_rate1') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.tax1-td']];
|
||||
@ -643,10 +641,7 @@ class PdfBuilder
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.tax2-td']];
|
||||
} elseif ($cell == '$task.tax_rate3') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.tax3-td']];
|
||||
}
|
||||
|
||||
|
||||
elseif ($cell == '$product.unit_cost' || $cell == '$task.rate') {
|
||||
} elseif ($cell == '$product.unit_cost' || $cell == '$task.rate') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
|
||||
} else {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
|
||||
@ -677,7 +672,7 @@ class PdfBuilder
|
||||
$locale_info = localeconv();
|
||||
|
||||
foreach ($items as $key => $item) {
|
||||
/** @var \App\DataMapper\InvoiceItem $item */
|
||||
/** @var \App\DataMapper\InvoiceItem $item */
|
||||
|
||||
if ($table_type == '$product' && $item->type_id != 1) {
|
||||
if ($item->type_id != 4 && $item->type_id != 6 && $item->type_id != 5) {
|
||||
@ -712,9 +707,9 @@ class PdfBuilder
|
||||
$data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}4", $item->custom_value4, $this->service->config->currency_entity) : '';
|
||||
|
||||
if ($item->quantity > 0 || $item->cost > 0) {
|
||||
$data[$key][$table_type.'.quantity'] = $item->quantity;
|
||||
$data[$key][$table_type.'.quantity'] = $this->service->config->formatValueNoTrailingZeroes($item->quantity);
|
||||
|
||||
$data[$key][$table_type.'.unit_cost'] = $this->service->config->formatMoney($item->cost);
|
||||
$data[$key][$table_type.'.unit_cost'] = $this->service->config->formatMoneyNoRounding($item->cost);
|
||||
|
||||
$data[$key][$table_type.'.cost'] = $this->service->config->formatMoney($item->cost);
|
||||
|
||||
@ -820,9 +815,7 @@ class PdfBuilder
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax2-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
|
||||
} elseif ($column == '$product.tax_rate3') {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax3-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
|
||||
}
|
||||
|
||||
elseif ($column == '$task.discount' && !$this->service->company->enable_product_discount) {
|
||||
} elseif ($column == '$task.discount' && !$this->service->company->enable_product_discount) {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
|
||||
} elseif ($column == '$task.tax_rate1') {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-task.tax1-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
|
||||
@ -830,9 +823,7 @@ class PdfBuilder
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-task.tax2-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
|
||||
} elseif ($column == '$task.tax_rate3') {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-task.tax3-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
|
||||
}
|
||||
}
|
||||
|
@ -330,6 +330,125 @@ class PdfConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a given value based on the clients currency.
|
||||
*
|
||||
* @param float $value The number to be formatted
|
||||
*
|
||||
* @return string The formatted value
|
||||
*/
|
||||
public function formatValueNoTrailingZeroes($value) :string
|
||||
{
|
||||
$value = floatval($value);
|
||||
|
||||
$thousand = $this->currency->thousand_separator;
|
||||
$decimal = $this->currency->decimal_separator;
|
||||
|
||||
/* Country settings override client settings */
|
||||
if (isset($this->country->thousand_separator) && strlen($this->country->thousand_separator) >= 1) {
|
||||
$thousand = $this->country->thousand_separator;
|
||||
}
|
||||
|
||||
if (isset($this->country->decimal_separator) && strlen($this->country->decimal_separator) >= 1) {
|
||||
$decimal = $this->country->decimal_separator;
|
||||
}
|
||||
|
||||
$precision = 10;
|
||||
|
||||
return rtrim(rtrim(number_format($value, $precision, $decimal, $thousand), '0'), $decimal);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats a given value based on the clients currency AND country.
|
||||
*
|
||||
* @param float $value The number to be formatted
|
||||
* @return string The formatted value
|
||||
*/
|
||||
public function formatMoneyNoRounding($value) :string
|
||||
{
|
||||
|
||||
$_value = $value;
|
||||
|
||||
$thousand = $this->currency->thousand_separator;
|
||||
$decimal = $this->currency->decimal_separator;
|
||||
$precision = $this->currency->precision;
|
||||
$code = $this->currency->code;
|
||||
$swapSymbol = $this->currency->swap_currency_symbol;
|
||||
|
||||
/* Country settings override client settings */
|
||||
if (isset($this->country->thousand_separator) && strlen($this->country->thousand_separator) >= 1) {
|
||||
$thousand = $this->country->thousand_separator;
|
||||
}
|
||||
|
||||
if (isset($this->country->decimal_separator) && strlen($this->country->decimal_separator) >= 1) {
|
||||
$decimal = $this->country->decimal_separator;
|
||||
}
|
||||
|
||||
if (isset($this->country->swap_currency_symbol) && strlen($this->country->swap_currency_symbol) >= 1) {
|
||||
$swapSymbol = $this->country->swap_currency_symbol;
|
||||
}
|
||||
|
||||
/* 08-01-2022 allow increased precision for unit price*/
|
||||
$v = rtrim(sprintf('%f', $value), '0');
|
||||
$parts = explode('.', $v);
|
||||
|
||||
/* 08-02-2023 special if block to render $0.5 to $0.50*/
|
||||
if ($v < 1 && strlen($v) == 3) {
|
||||
$precision = 2;
|
||||
} elseif ($v < 1) {
|
||||
$precision = strlen($v) - strrpos($v, '.') - 1;
|
||||
}
|
||||
|
||||
if (is_array($parts) && $parts[0] != 0) {
|
||||
$precision = 2;
|
||||
}
|
||||
|
||||
//04-04-2023 if currency = JPY override precision to 0
|
||||
if($this->currency->code == 'JPY') {
|
||||
$precision = 0;
|
||||
}
|
||||
|
||||
$value = number_format($v, $precision, $decimal, $thousand);
|
||||
$symbol = $this->currency->symbol;
|
||||
|
||||
if ($this->settings->show_currency_code === true && $this->currency->code == 'CHF') {
|
||||
return "{$code} {$value}";
|
||||
} elseif ($this->settings->show_currency_code === true) {
|
||||
return "{$value} {$code}";
|
||||
} elseif ($swapSymbol) {
|
||||
return "{$value} ".trim($symbol);
|
||||
} elseif ($this->settings->show_currency_code === false) {
|
||||
if ($_value < 0) {
|
||||
$value = substr($value, 1);
|
||||
$symbol = "-{$symbol}";
|
||||
}
|
||||
|
||||
return "{$symbol}{$value}";
|
||||
} else {
|
||||
return $this->formatValue($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a given value based on the clients currency.
|
||||
*
|
||||
* @param float $value The number to be formatted
|
||||
*
|
||||
* @return string The formatted value
|
||||
*/
|
||||
public function formatValue($value) :string
|
||||
{
|
||||
$value = floatval($value);
|
||||
|
||||
$thousand = $this->currency->thousand_separator;
|
||||
$decimal = $this->currency->decimal_separator;
|
||||
$precision = $this->currency->precision;
|
||||
|
||||
return number_format($value, $precision, $decimal, $thousand);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* date_format
|
||||
*
|
||||
|
@ -11,35 +11,31 @@
|
||||
|
||||
namespace App\Services\Template;
|
||||
|
||||
use Twig\TwigFilter;
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Number;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\Error;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Design;
|
||||
use App\Models\Vendor;
|
||||
use Twig\TwigFunction;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Project;
|
||||
use App\Utils\HtmlEngine;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Utils\VendorHtmlEngine;
|
||||
use Twig\Sandbox\SecurityError;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Vendor;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\PaymentHtmlEngine;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use App\Utils\Traits\Pdf\PdfMaker;
|
||||
use Twig\Extra\Intl\IntlExtension;
|
||||
use App\Utils\VendorHtmlEngine;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
use Twig\Error\Error;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Extra\Intl\IntlExtension;
|
||||
use Twig\Sandbox\SecurityError;
|
||||
|
||||
class TemplateService
|
||||
{
|
||||
@ -88,8 +84,9 @@ class TemplateService
|
||||
|
||||
$this->document = new \DOMDocument();
|
||||
$this->document->validateOnParse = true;
|
||||
$loader = new FilesystemLoader(storage_path());
|
||||
$this->twig = new Environment($loader, [
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader(storage_path());
|
||||
$this->twig = new \Twig\Environment($loader, [
|
||||
'debug' => true,
|
||||
]);
|
||||
$string_extension = new \Twig\Extension\StringLoaderExtension();
|
||||
@ -97,8 +94,8 @@ class TemplateService
|
||||
$this->twig->addExtension(new IntlExtension());
|
||||
$this->twig->addExtension(new \Twig\Extension\DebugExtension());
|
||||
|
||||
$function = new TwigFunction('img', function ($string, $style = '') {
|
||||
return '<img src="'.$string.'" style="'.$style.'"></img>';
|
||||
$function = new \Twig\TwigFunction('img', function ($string, $style = '') {
|
||||
return '<img src="' . $string . '" style="' . $style . '"></img>';
|
||||
});
|
||||
$this->twig->addFunction($function);
|
||||
|
||||
@ -236,16 +233,16 @@ class TemplateService
|
||||
$template = $this->twig->createTemplate(html_entity_decode($template));
|
||||
} catch(SyntaxError $e) {
|
||||
nlog($e->getMessage());
|
||||
continue;
|
||||
throw ($e);
|
||||
} catch(Error $e) {
|
||||
nlog("error = " .$e->getMessage());
|
||||
continue;
|
||||
nlog("error = " . $e->getMessage());
|
||||
throw ($e);
|
||||
} catch(RuntimeError $e) {
|
||||
nlog("runtime = " .$e->getMessage());
|
||||
continue;
|
||||
nlog("runtime = " . $e->getMessage());
|
||||
throw ($e);
|
||||
} catch(LoaderError $e) {
|
||||
nlog("loader = " . $e->getMessage());
|
||||
continue;
|
||||
throw ($e);
|
||||
} catch(SecurityError $e) {
|
||||
nlog("security = " . $e->getMessage());
|
||||
throw ($e);
|
||||
@ -281,7 +278,6 @@ class TemplateService
|
||||
$html = $this->getHtml();
|
||||
|
||||
foreach($this->variables as $key => $variable) {
|
||||
|
||||
if(isset($variable['labels']) && isset($variable['values'])) {
|
||||
$html = strtr($html, $variable['labels']);
|
||||
$html = strtr($html, $variable['values']);
|
||||
@ -363,7 +359,7 @@ class TemplateService
|
||||
|
||||
$processed = [];
|
||||
|
||||
if(in_array($key, ['tasks','projects','aging']) || !$value->first()) {
|
||||
if(in_array($key, ['tasks', 'projects', 'aging']) || !$value->first()) {
|
||||
return $processed;
|
||||
}
|
||||
|
||||
@ -428,6 +424,7 @@ class TemplateService
|
||||
->map(function ($invoice) {
|
||||
|
||||
$payments = [];
|
||||
$this->entity = $invoice;
|
||||
|
||||
if($invoice->payments ?? false) {
|
||||
$payments = $invoice->payments->map(function ($payment) {
|
||||
@ -438,6 +435,8 @@ class TemplateService
|
||||
return [
|
||||
'amount' => Number::formatMoney($invoice->amount, $invoice->client),
|
||||
'balance' => Number::formatMoney($invoice->balance, $invoice->client),
|
||||
'status_id' => $invoice->status_id,
|
||||
'status' => Invoice::stringStatus($invoice->status_id),
|
||||
'balance_raw' => $invoice->balance,
|
||||
'number' => $invoice->number ?: '',
|
||||
'discount' => $invoice->discount,
|
||||
@ -475,7 +474,7 @@ class TemplateService
|
||||
'custom_surcharge_tax2' => (bool) $invoice->custom_surcharge_tax2,
|
||||
'custom_surcharge_tax3' => (bool) $invoice->custom_surcharge_tax3,
|
||||
'custom_surcharge_tax4' => (bool) $invoice->custom_surcharge_tax4,
|
||||
'line_items' => $invoice->line_items ? $this->padLineItems($invoice->line_items, $invoice->client): (array) [],
|
||||
'line_items' => $invoice->line_items ? $this->padLineItems($invoice->line_items, $invoice->client) : (array) [],
|
||||
'reminder1_sent' => $this->translateDate($invoice->reminder1_sent, $invoice->client->date_format(), $invoice->client->locale()),
|
||||
'reminder2_sent' => $this->translateDate($invoice->reminder2_sent, $invoice->client->date_format(), $invoice->client->locale()),
|
||||
'reminder3_sent' => $this->translateDate($invoice->reminder3_sent, $invoice->client->date_format(), $invoice->client->locale()),
|
||||
@ -791,7 +790,7 @@ class TemplateService
|
||||
'custom_surcharge_tax2' => (bool) $credit->custom_surcharge_tax2,
|
||||
'custom_surcharge_tax3' => (bool) $credit->custom_surcharge_tax3,
|
||||
'custom_surcharge_tax4' => (bool) $credit->custom_surcharge_tax4,
|
||||
'line_items' => $credit->line_items ? $this->padLineItems($credit->line_items, $credit->client): (array) [],
|
||||
'line_items' => $credit->line_items ? $this->padLineItems($credit->line_items, $credit->client) : (array) [],
|
||||
'reminder1_sent' => $this->translateDate($credit->reminder1_sent, $credit->client->date_format(), $credit->client->locale()),
|
||||
'reminder2_sent' => $this->translateDate($credit->reminder2_sent, $credit->client->date_format(), $credit->client->locale()),
|
||||
'reminder3_sent' => $this->translateDate($credit->reminder3_sent, $credit->client->date_format(), $credit->client->locale()),
|
||||
|
Loading…
x
Reference in New Issue
Block a user