Skip to content

Commit

Permalink
fix: precision loss when transferring (backport #30834) (#31032)
Browse files Browse the repository at this point in the history
* fix: stock transfer value when precision differs

(cherry picked from commit b1c90e9)

# Conflicts:
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py

* fix: Merge conflicts

* chore: Remove unused `flt` (sider)

Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
  • Loading branch information
3 people authored May 16, 2022
1 parent 1f016e9 commit fc80a50
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 8 deletions.
11 changes: 5 additions & 6 deletions erpnext/stock/doctype/stock_entry/stock_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,8 @@ def set_basic_rate(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
raise_error_if_no_rate=raise_error_if_no_rate,
)

d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
# do not round off basic rate to avoid precision loss
d.basic_rate = flt(d.basic_rate)
if d.is_process_loss:
d.basic_rate = flt(0.0)
d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
Expand Down Expand Up @@ -718,7 +719,7 @@ def get_basic_rate_for_repacked_items(self, finished_item_qty, outgoing_items_co
total_fg_qty = sum([flt(d.transfer_qty) for d in self.items if d.is_finished_item])
return flt(outgoing_items_cost / total_fg_qty)

def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0):
def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0) -> float:
scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])

# Get raw materials cost from BOM if multiple material consumption entries
Expand Down Expand Up @@ -758,10 +759,8 @@ def update_valuation_rate(self):
for d in self.get("items"):
if d.transfer_qty:
d.amount = flt(flt(d.basic_amount) + flt(d.additional_cost), d.precision("amount"))
d.valuation_rate = flt(
flt(d.basic_rate) + (flt(d.additional_cost) / flt(d.transfer_qty)),
d.precision("valuation_rate"),
)
# Do not round off valuation rate to avoid precision loss
d.valuation_rate = flt(d.basic_rate) + (flt(d.additional_cost) / flt(d.transfer_qty))

def set_total_incoming_outgoing_value(self):
self.total_incoming_value = self.total_outgoing_value = 0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
from frappe.core.page.permission_manager.permission_manager import reset
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, today
from frappe.utils.data import add_to_date
from frappe.utils import add_days, add_to_date, today

from erpnext.accounts.doctype.gl_entry.gl_entry import rename_gle_sle_docs
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
Expand Down Expand Up @@ -719,6 +718,41 @@ def test_timestamp_clash(self):
except Exception as e:
self.fail("Double processing of qty for clashing timestamp.")

@change_settings("System Settings", {"float_precision": 3, "currency_precision": 2})
def test_transfer_invariants(self):
"""Extact stock value should be transferred."""

item = make_item(
properties={
"valuation_method": "Moving Average",
"stock_uom": "Kg",
}
).name
source_warehouse = "Stores - TCP1"
target_warehouse = "Finished Goods - TCP1"

make_purchase_receipt(
item=item,
warehouse=source_warehouse,
qty=20,
conversion_factor=1000,
uom="Tonne",
rate=156_526.0,
company="_Test Company with perpetual inventory",
)
transfer = make_stock_entry(
item=item, from_warehouse=source_warehouse, to_warehouse=target_warehouse, qty=1_728.0
)

filters = {"voucher_no": transfer.name, "voucher_type": transfer.doctype, "is_cancelled": 0}
sles = frappe.get_all(
"Stock Ledger Entry",
fields=["*"],
filters=filters,
order_by="timestamp(posting_date, posting_time), creation",
)
self.assertEqual(abs(sles[0].stock_value_difference), sles[1].stock_value_difference)


def create_repack_entry(**args):
args = frappe._dict(args)
Expand Down

0 comments on commit fc80a50

Please sign in to comment.