Support higher precision for unit cost and quantity #252

This commit is contained in:
Hillel Coren 2017-08-14 14:06:50 +03:00
parent 4e285c8ac6
commit 7a4a5d51f3
11 changed files with 117 additions and 47 deletions

View File

@ -1228,6 +1228,18 @@ class Utils
return round($amount + $tax1 + $tax2, 2);
}
public static function roundSignificant($value, $precision = 2) {
if (round($value, 3) != $value) {
$precision = 4;
} elseif (round($value, 2) != $value) {
$precision = 3;
} elseif (round($value, 1) != $value) {
$precision = 2;
}
return number_format($value, $precision, '.', '');
}
public static function truncateString($string, $length)
{
return strlen($string) > $length ? rtrim(substr($string, 0, $length)) . '...' : $string;

View File

@ -4,6 +4,7 @@ namespace App\Ninja\Reports;
use App\Models\Client;
use Auth;
use Utils;
class ProductReport extends AbstractReport
{
@ -48,8 +49,8 @@ class ProductReport extends AbstractReport
$this->isExport ? $invoice->invoice_number : $invoice->present()->link,
$invoice->present()->invoice_date,
$item->product_key,
$item->qty,
$account->formatMoney($item->cost, $client),
Utils::roundSignificant($item->qty, 0),
Utils::roundSignificant($item->cost, 2),
];
}

View File

@ -526,8 +526,8 @@ class InvoiceRepository extends BaseRepository
continue;
}
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
$invoiceItemCost = Utils::roundSignificant(Utils::parseFloat($item['cost']));
$invoiceItemQty = Utils::roundSignificant(Utils::parseFloat($item['qty']));
$lineTotal = $invoiceItemCost * $invoiceItemQty;
$total += round($lineTotal, 2);
@ -535,8 +535,8 @@ class InvoiceRepository extends BaseRepository
foreach ($data['invoice_items'] as $item) {
$item = (array) $item;
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
$invoiceItemCost = Utils::roundSignificant(Utils::parseFloat($item['cost']));
$invoiceItemQty = Utils::roundSignificant(Utils::parseFloat($item['qty']));
$lineTotal = $invoiceItemCost * $invoiceItemQty;
if ($invoice->discount > 0) {

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class IncreasePrecision extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('products', function ($table) {
$table->decimal('cost', 15, 4)->change();
$table->decimal('qty', 15, 4)->change();
});
Schema::table('invoice_items', function ($table) {
$table->decimal('cost', 15, 4)->change();
$table->decimal('qty', 15, 4)->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -494,7 +494,7 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
var row = [];
var item = invoice.invoice_items[i];
var cost = formatMoneyInvoice(item.cost, invoice, 'none');
var cost = formatMoneyInvoice(item.cost, invoice, 'none', getPrecision(item.cost));
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
var notes = item.notes;
var productKey = item.product_key;
@ -537,7 +537,7 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
custom_value2 = processVariables(item.custom_value2);
}
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
var lineTotal = roundSignificant(NINJA.parseFloat(item.cost)) * roundSignificant(NINJA.parseFloat(item.qty));
if (account.include_item_taxes_inline == '1') {
var taxAmount1 = 0;
var taxAmount2 = 0;

View File

@ -666,7 +666,7 @@ function calculateAmounts(invoice) {
// sum line item
for (var i=0; i<invoice.invoice_items.length; i++) {
var item = invoice.invoice_items[i];
var lineTotal = invoice.is_statement ? roundToTwo(NINJA.parseFloat(item.balance)) : roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
var lineTotal = invoice.is_statement ? roundToTwo(NINJA.parseFloat(item.balance)) : roundSignificant(NINJA.parseFloat(item.cost)) * roundSignificant(NINJA.parseFloat(item.qty));
lineTotal = roundToTwo(lineTotal);
if (lineTotal) {
total += lineTotal;
@ -1042,6 +1042,22 @@ function toggleDatePicker(field) {
$('#'+field).datepicker('show');
}
function getPrecision(number) {
if (roundToPrecision(number, 3) != number) {
return 4;
} else if (roundToPrecision(number, 2) != number) {
return 3;
} else {
return 2;
}
}
function roundSignificant(number) {
var precision = getPrecision(number);
var value = roundToPrecision(number, precision);
return isNaN(value) ? 0 : value;
}
function roundToTwo(number, toString) {
var val = roundToPrecision(number, 2);
return toString ? val.toFixed(2) : (val || 0);

View File

@ -16,7 +16,7 @@
@if ($product)
{{ Former::populate($product) }}
{{ Former::populateField('cost', number_format($product->cost, 2, '.', '')) }}
{{ Former::populateField('cost', Utils::roundSignificant($product->cost)) }}
@endif
{!! Former::text('product_key')->label('texts.product') !!}

View File

@ -811,7 +811,7 @@ function ItemModel(data) {
this.prettyQty = ko.computed({
read: function () {
return NINJA.parseFloat(this.qty()) ? NINJA.parseFloat(this.qty()) : '';
return NINJA.parseFloat(this.qty()) ? roundSignificant(NINJA.parseFloat(this.qty())) : '';
},
write: function (value) {
this.qty(value);
@ -821,7 +821,7 @@ function ItemModel(data) {
this.prettyCost = ko.computed({
read: function () {
return this.cost() ? this.cost() : '';
return this.cost() ? roundSignificant(this.cost()) : '';
},
write: function (value) {
this.cost(value);
@ -836,8 +836,8 @@ function ItemModel(data) {
this.totals = ko.observable();
this.totals.rawTotal = ko.computed(function() {
var cost = roundToTwo(NINJA.parseFloat(self.cost()));
var qty = roundToTwo(NINJA.parseFloat(self.qty()));
var cost = roundSignificant(NINJA.parseFloat(self.cost()));
var qty = roundSignificant(NINJA.parseFloat(self.qty()));
var value = cost * qty;
return value ? roundToTwo(value) : 0;
});
@ -979,7 +979,7 @@ ko.bindingHandlers.productTypeahead = {
model.notes(datum.notes);
}
if (datum.cost) {
model.cost(accounting.toFixed(datum.cost, 2));
model.cost(roundSignificant(datum.cost, 2));
}
if (!model.qty()) {
model.qty(1);

View File

@ -33,14 +33,14 @@
return window.parseFloat(str);
}
function formatMoneyInvoice(value, invoice, decorator) {
function formatMoneyInvoice(value, invoice, decorator, precision) {
var account = invoice.account;
var client = invoice.client;
return formatMoneyAccount(value, account, client, decorator);
return formatMoneyAccount(value, account, client, decorator, precision);
}
function formatMoneyAccount(value, account, client, decorator) {
function formatMoneyAccount(value, account, client, decorator, precision) {
var currencyId = false;
var countryId = false;
@ -60,7 +60,7 @@
decorator = parseInt(account.show_currency_code) ? 'code' : 'symbol';
}
return formatMoney(value, currencyId, countryId, decorator)
return formatMoney(value, currencyId, countryId, decorator, precision)
}
function formatAmount(value, currencyId) {
@ -81,19 +81,25 @@
}
}
function formatMoney(value, currencyId, countryId, decorator) {
function formatMoney(value, currencyId, countryId, decorator, precision) {
value = NINJA.parseFloat(value);
if (!currencyId) {
currencyId = {{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY) }};
}
var currency = currencyMap[currencyId];
if (!decorator) {
decorator = '{{ Session::get(SESSION_CURRENCY_DECORATOR, CURRENCY_DECORATOR_SYMBOL) }}';
}
var currency = currencyMap[currencyId];
var precision = currency.precision;
if (!precision) {
precision = currency.precision;
} else if (currency.precision == 0) {
precision = 0;
}
var thousand = currency.thousand_separator;
var decimal = currency.decimal_separator;
var code = currency.code;