Merge pull request #30097 from nextchamp-saqib/fix-pos-round-off
fix(pos): multiple pos round off cases
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index ddca68a..d4513c6 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -84,20 +84,12 @@
sales_invoice.set_posting_time = 1
sales_invoice.posting_date = getdate(self.posting_date)
sales_invoice.save()
- self.write_off_fractional_amount(sales_invoice, data)
sales_invoice.submit()
self.consolidated_invoice = sales_invoice.name
return sales_invoice.name
- def write_off_fractional_amount(self, invoice, data):
- pos_invoice_grand_total = sum(d.grand_total for d in data)
-
- if abs(pos_invoice_grand_total - invoice.grand_total) < 1:
- invoice.write_off_amount += -1 * (pos_invoice_grand_total - invoice.grand_total)
- invoice.save()
-
def process_merging_into_credit_note(self, data):
credit_note = self.get_new_sales_invoice()
credit_note.is_return = 1
@@ -110,7 +102,6 @@
# TODO: return could be against multiple sales invoice which could also have been consolidated?
# credit_note.return_against = self.consolidated_invoice
credit_note.save()
- self.write_off_fractional_amount(credit_note, data)
credit_note.submit()
self.consolidated_credit_note = credit_note.name
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
index 5930aa0..89f7f18 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
@@ -5,6 +5,7 @@
import unittest
import frappe
+from frappe.tests.utils import change_settings
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
@@ -280,3 +281,100 @@
frappe.set_user("Administrator")
frappe.db.sql("delete from `tabPOS Profile`")
frappe.db.sql("delete from `tabPOS Invoice`")
+
+ @change_settings("System Settings", {"number_format": "#,###.###", "currency_precision": 3, "float_precision": 3})
+ def test_consolidation_round_off_error_3(self):
+ frappe.db.sql("delete from `tabPOS Invoice`")
+
+ try:
+ make_stock_entry(
+ to_warehouse="_Test Warehouse - _TC",
+ item_code="_Test Item",
+ rate=8000,
+ qty=10,
+ )
+ init_user_and_profile()
+
+ item_rates = [69, 59, 29]
+ for i in [1, 2]:
+ inv = create_pos_invoice(is_return=1, do_not_save=1)
+ inv.items = []
+ for rate in item_rates:
+ inv.append("items", {
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC",
+ "qty": -1,
+ "rate": rate,
+ "income_account": "Sales - _TC",
+ "expense_account": "Cost of Goods Sold - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ })
+ inv.append("taxes", {
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "rate": 15,
+ "included_in_print_rate": 1
+ })
+ inv.payments = []
+ inv.append('payments', {
+ 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': -157
+ })
+ inv.paid_amount = -157
+ inv.save()
+ inv.submit()
+
+ consolidate_pos_invoices()
+
+ inv.load_from_db()
+ consolidated_invoice = frappe.get_doc('Sales Invoice', inv.consolidated_invoice)
+ self.assertEqual(consolidated_invoice.status, 'Return')
+ self.assertEqual(consolidated_invoice.rounding_adjustment, -0.001)
+
+ finally:
+ frappe.set_user("Administrator")
+ frappe.db.sql("delete from `tabPOS Profile`")
+ frappe.db.sql("delete from `tabPOS Invoice`")
+
+ def test_consolidation_rounding_adjustment(self):
+ '''
+ Test if the rounding adjustment is calculated correctly
+ '''
+ frappe.db.sql("delete from `tabPOS Invoice`")
+
+ try:
+ make_stock_entry(
+ to_warehouse="_Test Warehouse - _TC",
+ item_code="_Test Item",
+ rate=8000,
+ qty=10,
+ )
+
+ init_user_and_profile()
+
+ inv = create_pos_invoice(qty=1, rate=69.5, do_not_save=True)
+ inv.append('payments', {
+ 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 70
+ })
+ inv.insert()
+ inv.submit()
+
+ inv2 = create_pos_invoice(qty=1, rate=59.5, do_not_save=True)
+ inv2.append('payments', {
+ 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60
+ })
+ inv2.insert()
+ inv2.submit()
+
+ consolidate_pos_invoices()
+
+ inv.load_from_db()
+ consolidated_invoice = frappe.get_doc('Sales Invoice', inv.consolidated_invoice)
+ self.assertEqual(consolidated_invoice.rounding_adjustment, 1)
+
+ finally:
+ frappe.set_user("Administrator")
+ frappe.db.sql("delete from `tabPOS Profile`")
+ frappe.db.sql("delete from `tabPOS Invoice`")
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index b894f90..573da27 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -263,6 +263,9 @@
self.process_common_party_accounting()
def validate_pos_return(self):
+ if self.is_consolidated:
+ # pos return is already validated in pos invoice
+ return
if self.is_pos and self.is_return:
total_amount_in_payments = 0
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index dc58e11..a1bb667 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -277,7 +277,8 @@
shipping_rule.apply(self.doc)
def calculate_taxes(self):
- if not self.doc.get('is_consolidated'):
+ rounding_adjustment_computed = self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment')
+ if not rounding_adjustment_computed:
self.doc.rounding_adjustment = 0
# maintain actual tax rate based on idx
@@ -333,7 +334,7 @@
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
and self.doc.discount_amount \
and self.doc.apply_discount_on == "Grand Total" \
- and not self.doc.get('is_consolidated'):
+ and not rounding_adjustment_computed:
self.doc.rounding_adjustment = flt(self.doc.grand_total
- flt(self.doc.discount_amount) - tax.total,
self.doc.precision("rounding_adjustment"))
@@ -472,20 +473,22 @@
self.doc.total_net_weight += d.total_weight
def set_rounded_total(self):
- if not self.doc.get('is_consolidated'):
- if self.doc.meta.get_field("rounded_total"):
- if self.doc.is_rounded_total_disabled():
- self.doc.rounded_total = self.doc.base_rounded_total = 0
- return
+ if self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment'):
+ return
- self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
- self.doc.currency, self.doc.precision("rounded_total"))
+ if self.doc.meta.get_field("rounded_total"):
+ if self.doc.is_rounded_total_disabled():
+ self.doc.rounded_total = self.doc.base_rounded_total = 0
+ return
- #if print_in_rate is set, we would have already calculated rounding adjustment
- self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
- self.doc.precision("rounding_adjustment"))
+ self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
+ self.doc.currency, self.doc.precision("rounded_total"))
- self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
+ #if print_in_rate is set, we would have already calculated rounding adjustment
+ self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
+ self.doc.precision("rounding_adjustment"))
+
+ self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
def _cleanup(self):
if not self.doc.get('is_consolidated'):