From 003653c572109b697b8670ac582b56e4fb690f1c Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:08:54 +0200 Subject: [PATCH 01/29] fix: german translations for incoterms (cherry picked from commit 9789648175e61ea64d9a326c2f4b274067eecbc0) --- erpnext/translations/de.csv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 0905c06896c..3bc84222bf0 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -9108,13 +9108,13 @@ Warehouse wise Stock Value,Warenwert nach Lager, Ex Works,Ab Werk, Free Carrier,Frei Frachtführer, Free Alongside Ship,Frei Längsseite Schiff, -Free on Board,Frei an Bord, +Free On Board,Frei an Bord, Carriage Paid To,Frachtfrei, Carriage and Insurance Paid to,Frachtfrei versichert, Cost and Freight,Kosten und Fracht, "Cost, Insurance and Freight","Kosten, Versicherung und Fracht", -Delivered at Place,Geliefert benannter Ort, -Delivered at Place Unloaded,Geliefert benannter Ort entladen, +Delivered At Place,Geliefert benannter Ort, +Delivered At Place Unloaded,Geliefert benannter Ort entladen, Delivered Duty Paid,Geliefert verzollt, Discount Validity,Frist für den Rabatt, Discount Validity Based On,Frist für den Rabatt berechnet sich nach, From 333c7577ebe47fd85109af046d13290d7189399e Mon Sep 17 00:00:00 2001 From: kurogeek Date: Fri, 16 Aug 2024 13:52:54 +0700 Subject: [PATCH 02/29] fix: currency symbol on `amount` field suppose to show as `Company`'s currency not as default company's currency --- erpnext/accounts/doctype/share_transfer/share_transfer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.json b/erpnext/accounts/doctype/share_transfer/share_transfer.json index 59a305317d8..c0b1e91977c 100644 --- a/erpnext/accounts/doctype/share_transfer/share_transfer.json +++ b/erpnext/accounts/doctype/share_transfer/share_transfer.json @@ -147,6 +147,7 @@ "fieldname": "amount", "fieldtype": "Currency", "label": "Amount", + "options": "Company:company:default_currency", "read_only": 1 }, { From cf34219c71a0e7b6c2921fecc5d62b9d76ea8807 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 29 Jan 2024 09:32:44 +0530 Subject: [PATCH 03/29] fix: specify precision for net_amount (#39481) * fix: specify precision for net_amount * fix: correct existing test to account for precision * fix: rounding issue in test cases * fix: optional grand total manipulation * fix: use `grand_total_diff` for manipulation * fix: patch to set default for grand total manipulation * fix: wrong rounding assertion for USD * fix: undefined this.frm error * chore: linters * fix: `net_amount` percision and method rename * fix: missing frm reference * chore: minor cleanups and depr message * refactor: remove optional adjusting of grand total (cherry picked from commit 50d56db0c2ffcbb5ac466e0b8f1b7330dc78c9b3) # Conflicts: # erpnext/public/js/controllers/accounts.js # erpnext/public/js/controllers/taxes_and_totals.js --- .../accounts_settings/accounts_settings.json | 2 +- .../doctype/pos_invoice/test_pos_invoice.py | 2 +- .../test_pos_invoice_merge_log.py | 2 +- .../sales_invoice/test_sales_invoice.py | 65 +++++++++++-------- erpnext/controllers/taxes_and_totals.py | 24 +++++-- erpnext/public/js/controllers/accounts.js | 40 ++++++++++++ .../public/js/controllers/taxes_and_totals.js | 32 +++++++-- 7 files changed, 122 insertions(+), 45 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 0370fa2ce87..5581ab038c5 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -469,7 +469,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-11-20 09:37:47.650347", + "modified": "2024-01-22 12:10:10.151819", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 186c0ea7fd4..b44262cc280 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -87,7 +87,7 @@ class TestPOSInvoice(unittest.TestCase): inv.save() - self.assertEqual(inv.net_total, 4298.25) + self.assertEqual(inv.net_total, 4298.24) self.assertEqual(inv.grand_total, 4900.00) def test_tax_calculation_with_multiple_items(self): 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 144523624e8..5f5427c0764 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 @@ -340,7 +340,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): 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) + self.assertEqual(consolidated_invoice.rounding_adjustment, -0.002) finally: frappe.set_user("Administrator") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index bb51223db16..0ff26ba4d74 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -307,7 +307,8 @@ class TestSalesInvoice(FrappeTestCase): si.insert() # with inclusive tax - self.assertEqual(si.items[0].net_amount, 3947.368421052631) + self.assertEqual(si.items[0].net_amount, 3947.37) + self.assertEqual(si.net_total, si.base_net_total) self.assertEqual(si.net_total, 3947.37) self.assertEqual(si.grand_total, 5000) @@ -651,7 +652,7 @@ class TestSalesInvoice(FrappeTestCase): 62.5, 625.0, 50, - 499.97600115194473, + 499.98, ], "_Test Item Home Desktop 200": [ 190.66, @@ -662,7 +663,7 @@ class TestSalesInvoice(FrappeTestCase): 190.66, 953.3, 150, - 749.9968530500239, + 750, ], } @@ -675,20 +676,21 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(d.get(k), expected_values[d.item_code][i]) # check net total - self.assertEqual(si.net_total, 1249.97) + self.assertEqual(si.base_net_total, si.net_total) + self.assertEqual(si.net_total, 1249.98) self.assertEqual(si.total, 1578.3) # check tax calculation expected_values = { "keys": ["tax_amount", "total"], - "_Test Account Excise Duty - _TC": [140, 1389.97], - "_Test Account Education Cess - _TC": [2.8, 1392.77], - "_Test Account S&H Education Cess - _TC": [1.4, 1394.17], - "_Test Account CST - _TC": [27.88, 1422.05], - "_Test Account VAT - _TC": [156.25, 1578.30], - "_Test Account Customs Duty - _TC": [125, 1703.30], - "_Test Account Shipping Charges - _TC": [100, 1803.30], - "_Test Account Discount - _TC": [-180.33, 1622.97], + "_Test Account Excise Duty - _TC": [140, 1389.98], + "_Test Account Education Cess - _TC": [2.8, 1392.78], + "_Test Account S&H Education Cess - _TC": [1.4, 1394.18], + "_Test Account CST - _TC": [27.88, 1422.06], + "_Test Account VAT - _TC": [156.25, 1578.31], + "_Test Account Customs Duty - _TC": [125, 1703.31], + "_Test Account Shipping Charges - _TC": [100, 1803.31], + "_Test Account Discount - _TC": [-180.33, 1622.98], } for d in si.get("taxes"): @@ -724,7 +726,7 @@ class TestSalesInvoice(FrappeTestCase): "base_rate": 2500, "base_amount": 25000, "net_rate": 40, - "net_amount": 399.9808009215558, + "net_amount": 399.98, "base_net_rate": 2000, "base_net_amount": 19999, }, @@ -738,7 +740,7 @@ class TestSalesInvoice(FrappeTestCase): "base_rate": 7500, "base_amount": 37500, "net_rate": 118.01, - "net_amount": 590.0531205155963, + "net_amount": 590.05, "base_net_rate": 5900.5, "base_net_amount": 29502.5, }, @@ -776,8 +778,13 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(si.base_grand_total, 60795) self.assertEqual(si.grand_total, 1215.90) - self.assertEqual(si.rounding_adjustment, 0.01) - self.assertEqual(si.base_rounding_adjustment, 0.50) + # no rounding adjustment as the Smallest Currency Fraction Value of USD is 0.01 + if frappe.db.get_value("Currency", "USD", "smallest_currency_fraction_value") < 0.01: + self.assertEqual(si.rounding_adjustment, 0.10) + self.assertEqual(si.base_rounding_adjustment, 5.0) + else: + self.assertEqual(si.rounding_adjustment, 0.0) + self.assertEqual(si.base_rounding_adjustment, 0.0) def test_outstanding(self): w = self.make() @@ -2126,7 +2133,7 @@ class TestSalesInvoice(FrappeTestCase): def test_rounding_adjustment_2(self): si = create_sales_invoice(rate=400, do_not_save=True) - for rate in [400, 600, 100]: + for rate in [400.25, 600.30, 100.65]: si.append( "items", { @@ -2152,17 +2159,18 @@ class TestSalesInvoice(FrappeTestCase): ) si.save() si.submit() - self.assertEqual(si.net_total, 1271.19) - self.assertEqual(si.grand_total, 1500) - self.assertEqual(si.total_taxes_and_charges, 228.82) - self.assertEqual(si.rounding_adjustment, -0.01) + self.assertEqual(si.net_total, si.base_net_total) + self.assertEqual(si.net_total, 1272.20) + self.assertEqual(si.grand_total, 1501.20) + self.assertEqual(si.total_taxes_and_charges, 229) + self.assertEqual(si.rounding_adjustment, -0.20) expected_values = [ - ["_Test Account Service Tax - _TC", 0.0, 114.41], - ["_Test Account VAT - _TC", 0.0, 114.41], - [si.debit_to, 1500, 0.0], - ["Round Off - _TC", 0.01, 0.01], - ["Sales - _TC", 0.0, 1271.18], + ["_Test Account Service Tax - _TC", 0.0, 114.50], + ["_Test Account VAT - _TC", 0.0, 114.50], + [si.debit_to, 1501, 0.0], + ["Round Off - _TC", 0.20, 0.0], + ["Sales - _TC", 0.0, 1272.20], ] gl_entries = frappe.db.sql( @@ -2220,7 +2228,8 @@ class TestSalesInvoice(FrappeTestCase): si.save() si.submit() - self.assertEqual(si.net_total, 4007.16) + self.assertEqual(si.net_total, si.base_net_total) + self.assertEqual(si.net_total, 4007.15) self.assertEqual(si.grand_total, 4488.02) self.assertEqual(si.total_taxes_and_charges, 480.86) self.assertEqual(si.rounding_adjustment, -0.02) @@ -2232,7 +2241,7 @@ class TestSalesInvoice(FrappeTestCase): ["_Test Account Service Tax - _TC", 0.0, 240.43], ["_Test Account VAT - _TC", 0.0, 240.43], ["Sales - _TC", 0.0, 4007.15], - ["Round Off - _TC", 0.02, 0.01], + ["Round Off - _TC", 0.01, 0.0], ] ) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index e92356e30a8..7d371bdf9a6 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -8,6 +8,7 @@ import frappe from frappe import _, scrub from frappe.model.document import Document from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction +from frappe.utils.deprecations import deprecated import erpnext from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate @@ -71,7 +72,7 @@ class calculate_taxes_and_totals: self.calculate_net_total() self.calculate_tax_withholding_net_total() self.calculate_taxes() - self.manipulate_grand_total_for_inclusive_tax() + self.adjust_grand_total_for_inclusive_tax() self.calculate_totals() self._cleanup() self.calculate_total_net_weight() @@ -280,7 +281,7 @@ class calculate_taxes_and_totals: ): amount = flt(item.amount) - total_inclusive_tax_amount_per_qty - item.net_amount = flt(amount / (1 + cumulated_tax_fraction)) + item.net_amount = flt(amount / (1 + cumulated_tax_fraction), item.precision("net_amount")) item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate")) item.discount_percentage = flt( item.discount_percentage, item.precision("discount_percentage") @@ -505,7 +506,12 @@ class calculate_taxes_and_totals: tax.base_tax_amount = round(tax.base_tax_amount, 0) tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0) + @deprecated def manipulate_grand_total_for_inclusive_tax(self): + # for backward compatablility - if in case used by an external application + return self.adjust_grand_total_for_inclusive_tax() + + def adjust_grand_total_for_inclusive_tax(self): # if fully inclusive taxes and diff if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")): last_tax = self.doc.get("taxes")[-1] @@ -527,17 +533,21 @@ class calculate_taxes_and_totals: diff = flt(diff, self.doc.precision("rounding_adjustment")) if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")): - self.doc.rounding_adjustment = diff + self.doc.grand_total_diff = diff + else: + self.doc.grand_total_diff = 0 def calculate_totals(self): if self.doc.get("taxes"): - self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) + self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt( + self.doc.get("grand_total_diff") + ) else: self.doc.grand_total = flt(self.doc.net_total) if self.doc.get("taxes"): self.doc.total_taxes_and_charges = flt( - self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment), + self.doc.grand_total - self.doc.net_total - flt(self.doc.get("grand_total_diff")), self.doc.precision("total_taxes_and_charges"), ) else: @@ -600,8 +610,8 @@ class calculate_taxes_and_totals: self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total") ) - # if print_in_rate is set, we would have already calculated rounding adjustment - self.doc.rounding_adjustment += flt( + # rounding adjustment should always be the difference vetween grand and rounded total + self.doc.rounding_adjustment = flt( self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment") ) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 1ba941ab6f7..79770b83202 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -22,6 +22,7 @@ frappe.ui.form.on(cur_frm.doctype, { } else { var account_type = ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]; } +<<<<<<< HEAD return { query: "erpnext.controllers.queries.tax_account_query", @@ -29,6 +30,18 @@ frappe.ui.form.on(cur_frm.doctype, { "account_type": account_type, "company": doc.company, } +======= + }, + included_in_print_rate: function(frm, cdt, cdn) { + let tax = frappe.get_doc(cdt, cdn); + try { + me.validate_taxes_and_charges(cdt, cdn); + me.validate_inclusive_tax(tax, frm); + } catch(e) { + tax.included_in_print_rate = 0; + refresh_field("included_in_print_rate", tax.name, tax.parentfield); + throw e; +>>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) } }); frm.set_query("cost_center", "taxes", function(doc) { @@ -53,14 +66,24 @@ frappe.ui.form.on(cur_frm.doctype, { erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm.open_grid_row()); }, +<<<<<<< HEAD allocate_advances_automatically: function(frm) { frm.trigger('fetch_advances'); }, +======= + validate_inclusive_tax: function(tax, frm) { + this.frm = this.frm || frm; + let actual_type_error = function() { + var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx]) + frappe.throw(msg); + }; +>>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) only_include_allocated_payments: function(frm) { frm.trigger('fetch_advances'); }, +<<<<<<< HEAD fetch_advances: function(frm) { if(frm.doc.allocate_advances_automatically) { frappe.call({ @@ -68,6 +91,23 @@ frappe.ui.form.on(cur_frm.doctype, { method: "set_advances", callback: function(r, rt) { refresh_field("advances"); +======= + if(cint(tax.included_in_print_rate)) { + if(tax.charge_type == "Actual") { + // inclusive tax cannot be of type Actual + actual_type_error(); + } else if (tax.charge_type == "On Previous Row Amount" && this.frm && + !cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_print_rate) + ) { + // referred row should also be an inclusive tax + on_previous_row_error(tax.row_id); + } else if (tax.charge_type == "On Previous Row Total" && this.frm) { + var taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id), + function(t) { return cint(t.included_in_print_rate) ? null : t; }); + if(taxes_not_included.length > 0) { + // all rows above this tax should be inclusive + on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id); +>>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) } }) } diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 670cf35bb11..b164453e68c 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -103,7 +103,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this.determine_exclusive_rate(); this.calculate_net_total(); this.calculate_taxes(); - this.manipulate_grand_total_for_inclusive_tax(); + this.adjust_grand_total_for_inclusive_tax(); this.calculate_totals(); this._cleanup(); } @@ -183,9 +183,15 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { $.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; }); +<<<<<<< HEAD if (!this.discount_amount_applied && cur_frm) { cur_frm.cscript.validate_taxes_and_charges(tax.doctype, tax.name); me.validate_inclusive_tax(tax); +======= + if (!this.discount_amount_applied) { + erpnext.accounts.taxes.validate_taxes_and_charges(tax.doctype, tax.name); + erpnext.accounts.taxes.validate_inclusive_tax(tax, this.frm); +>>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) } frappe.model.round_floats_in(tax); }); @@ -243,7 +249,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) { var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty; - item.net_amount = flt(amount / (1 + cumulated_tax_fraction)); + item.net_amount = flt(amount / (1 + cumulated_tax_fraction), precision("net_amount", item)); item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0; me.set_in_company_currency(item, ["net_rate", "net_amount"]); @@ -298,6 +304,8 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { me.frm.doc.net_total += item.net_amount; me.frm.doc.base_net_total += item.base_net_amount; }); + + frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]); } calculate_shipping_charges() { @@ -506,8 +514,17 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } } + /** + * @deprecated Use adjust_grand_total_for_inclusive_tax instead. + */ manipulate_grand_total_for_inclusive_tax() { + // for backward compatablility - if in case used by an external application + this.adjust_grand_total_for_inclusive_tax() + } + + adjust_grand_total_for_inclusive_tax() { var me = this; + // if fully inclusive taxes and diff if (this.frm.doc["taxes"] && this.frm.doc["taxes"].length) { var any_inclusive_tax = false; @@ -533,7 +550,9 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { diff = flt(diff, precision("rounding_adjustment")); if ( diff && Math.abs(diff) <= (5.0 / Math.pow(10, precision("tax_amount", last_tax))) ) { - me.frm.doc.rounding_adjustment = diff; + me.frm.doc.grand_total_diff = diff; + } else { + me.frm.doc.grand_total_diff = 0; } } } @@ -544,7 +563,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { var me = this; var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0; this.frm.doc.grand_total = flt(tax_count - ? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.rounding_adjustment) + ? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.grand_total_diff) : this.frm.doc.net_total); if(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype)) { @@ -604,7 +623,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) { this.frm.doc.rounded_total = round_based_on_smallest_currency_fraction(this.frm.doc.grand_total, this.frm.doc.currency, precision("rounded_total")); - this.frm.doc.rounding_adjustment += flt(this.frm.doc.rounded_total - this.frm.doc.grand_total, + this.frm.doc.rounding_adjustment = flt(this.frm.doc.rounded_total - this.frm.doc.grand_total, precision("rounding_adjustment")); this.set_in_company_currency(this.frm.doc, ["rounding_adjustment", "rounded_total"]); @@ -672,8 +691,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if (total_for_discount_amount) { $.each(this.frm._items || [], function(i, item) { distributed_amount = flt(me.frm.doc.discount_amount) * item.net_amount / total_for_discount_amount; - item.net_amount = flt(item.net_amount - distributed_amount, - precision("base_amount", item)); + item.net_amount = flt(item.net_amount - distributed_amount, precision("net_amount", item)); net_total += item.net_amount; // discount amount rounding loss adjustment if no taxes From c5dedab30478bef17bbc0b166795dab95f3578b7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 19 Aug 2024 10:25:27 +0530 Subject: [PATCH 04/29] chore: resolve conflicts --- erpnext/public/js/controllers/accounts.js | 49 ++----------------- .../public/js/controllers/taxes_and_totals.js | 6 --- 2 files changed, 5 insertions(+), 50 deletions(-) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 79770b83202..304b71e33d4 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -22,7 +22,6 @@ frappe.ui.form.on(cur_frm.doctype, { } else { var account_type = ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]; } -<<<<<<< HEAD return { query: "erpnext.controllers.queries.tax_account_query", @@ -30,18 +29,6 @@ frappe.ui.form.on(cur_frm.doctype, { "account_type": account_type, "company": doc.company, } -======= - }, - included_in_print_rate: function(frm, cdt, cdn) { - let tax = frappe.get_doc(cdt, cdn); - try { - me.validate_taxes_and_charges(cdt, cdn); - me.validate_inclusive_tax(tax, frm); - } catch(e) { - tax.included_in_print_rate = 0; - refresh_field("included_in_print_rate", tax.name, tax.parentfield); - throw e; ->>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) } }); frm.set_query("cost_center", "taxes", function(doc) { @@ -66,24 +53,14 @@ frappe.ui.form.on(cur_frm.doctype, { erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm.open_grid_row()); }, -<<<<<<< HEAD allocate_advances_automatically: function(frm) { frm.trigger('fetch_advances'); }, -======= - validate_inclusive_tax: function(tax, frm) { - this.frm = this.frm || frm; - let actual_type_error = function() { - var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx]) - frappe.throw(msg); - }; ->>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) only_include_allocated_payments: function(frm) { frm.trigger('fetch_advances'); }, -<<<<<<< HEAD fetch_advances: function(frm) { if(frm.doc.allocate_advances_automatically) { frappe.call({ @@ -91,23 +68,6 @@ frappe.ui.form.on(cur_frm.doctype, { method: "set_advances", callback: function(r, rt) { refresh_field("advances"); -======= - if(cint(tax.included_in_print_rate)) { - if(tax.charge_type == "Actual") { - // inclusive tax cannot be of type Actual - actual_type_error(); - } else if (tax.charge_type == "On Previous Row Amount" && this.frm && - !cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_print_rate) - ) { - // referred row should also be an inclusive tax - on_previous_row_error(tax.row_id); - } else if (tax.charge_type == "On Previous Row Total" && this.frm) { - var taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id), - function(t) { return cint(t.included_in_print_rate) ? null : t; }); - if(taxes_not_included.length > 0) { - // all rows above this tax should be inclusive - on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id); ->>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) } }) } @@ -270,7 +230,8 @@ cur_frm.cscript.validate_taxes_and_charges = function(cdt, cdn) { } -cur_frm.cscript.validate_inclusive_tax = function(tax) { +cur_frm.cscript.validate_inclusive_tax = function(tax, frm) { + this.frm = this.frm || frm; var actual_type_error = function() { var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx]) frappe.throw(msg); @@ -286,12 +247,12 @@ cur_frm.cscript.validate_inclusive_tax = function(tax) { if(tax.charge_type == "Actual") { // inclusive tax cannot be of type Actual actual_type_error(); - } else if(tax.charge_type == "On Previous Row Amount" && + } else if(tax.charge_type == "On Previous Row Amount" && this.frm && !cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_print_rate) ) { // referred row should also be an inclusive tax on_previous_row_error(tax.row_id); - } else if(tax.charge_type == "On Previous Row Total") { + } else if(tax.charge_type == "On Previous Row Total" && this.frm) { var taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id), function(t) { return cint(t.included_in_print_rate) ? null : t; }); if(taxes_not_included.length > 0) { @@ -334,7 +295,7 @@ if(!erpnext.taxes.flags[cur_frm.cscript.tax_table]) { var tax = frappe.get_doc(cdt, cdn); try { cur_frm.cscript.validate_taxes_and_charges(cdt, cdn); - cur_frm.cscript.validate_inclusive_tax(tax); + cur_frm.cscript.validate_inclusive_tax(tax, frm); } catch(e) { tax.included_in_print_rate = 0; refresh_field("included_in_print_rate", tax.name, tax.parentfield); diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index b164453e68c..82e1d518362 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -183,15 +183,9 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { $.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; }); -<<<<<<< HEAD if (!this.discount_amount_applied && cur_frm) { cur_frm.cscript.validate_taxes_and_charges(tax.doctype, tax.name); me.validate_inclusive_tax(tax); -======= - if (!this.discount_amount_applied) { - erpnext.accounts.taxes.validate_taxes_and_charges(tax.doctype, tax.name); - erpnext.accounts.taxes.validate_inclusive_tax(tax, this.frm); ->>>>>>> 50d56db0c2 (fix: specify precision for net_amount (#39481)) } frappe.model.round_floats_in(tax); }); From 678f08898a5bbbe1ceafb82e5df61e7c80953823 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 15:23:57 +0530 Subject: [PATCH 05/29] refactor(test): update tast case of rounding value --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 0ff26ba4d74..8c246702874 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -415,7 +415,7 @@ class TestSalesInvoice(FrappeTestCase): for i, k in enumerate(expected_values["keys"]): self.assertEqual(d.get(k), expected_values[d.account_head][i]) - self.assertEqual(si.base_grand_total, 1500) + self.assertEqual(si.base_grand_total, 1500.01) self.assertEqual(si.grand_total, 1500) self.assertEqual(si.rounding_adjustment, -0.01) @@ -2106,7 +2106,7 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(si.net_total, 19453.13) self.assertEqual(si.grand_total, 24900) self.assertEqual(si.total_taxes_and_charges, 5446.88) - self.assertEqual(si.rounding_adjustment, -0.01) + self.assertEqual(si.rounding_adjustment, 0.00) expected_values = dict( (d[0], d) From c2fdef4d5b5809fc054f670b13ee26a8705feaf5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 17:36:11 +0530 Subject: [PATCH 06/29] refactor(test): update grand total --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 8c246702874..7b471ed418e 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -416,7 +416,7 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(d.get(k), expected_values[d.account_head][i]) self.assertEqual(si.base_grand_total, 1500.01) - self.assertEqual(si.grand_total, 1500) + self.assertEqual(si.grand_total, 1500.01) self.assertEqual(si.rounding_adjustment, -0.01) def test_discount_amount_gl_entry(self): From 651e8531569e28c85c61669fee9dde4416e72363 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 11:16:41 +0530 Subject: [PATCH 07/29] refactor: allow equity types on Payment Entry (cherry picked from commit 6cbf98294ae9231843764cda9aa60b7bd15444f8) # Conflicts: # erpnext/accounts/doctype/payment_entry/payment_entry.js --- .../doctype/payment_entry/payment_entry.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 3d5d030a9ee..4a099f7b860 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -21,8 +21,19 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_from", function() { frm.events.validate_company(frm); +<<<<<<< HEAD var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; +======= + var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) + ? ["Bank", "Cash"] + : [frappe.boot.party_account_types[frm.doc.party_type]]; + + if (frm.doc.party_type == "Shareholder") { + account_types.push("Equity"); + } + +>>>>>>> 6cbf98294a (refactor: allow equity types on Payment Entry) return { filters: { "account_type": ["in", account_types], @@ -75,8 +86,17 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); +<<<<<<< HEAD var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; +======= + var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) + ? ["Bank", "Cash"] + : [frappe.boot.party_account_types[frm.doc.party_type]]; + if (frm.doc.party_type == "Shareholder") { + account_types.push("Equity"); + } +>>>>>>> 6cbf98294a (refactor: allow equity types on Payment Entry) return { filters: { "account_type": ["in", account_types], From 30ad25d86c37ec07573aa04e6a4b32f25501007a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 14:19:30 +0530 Subject: [PATCH 08/29] refactor: filter shareholder on company (cherry picked from commit 63ad9f4f86f841ffbf7c2e8b31fc16adc694259f) # Conflicts: # erpnext/accounts/doctype/payment_entry/payment_entry.js --- .../accounts/doctype/payment_entry/payment_entry.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 4a099f7b860..25261d91fe3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -339,6 +339,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("party", function() { if(frm.doc.party_type == 'Employee'){ return { +<<<<<<< HEAD query: "erpnext.controllers.queries.employee_query" } } @@ -346,6 +347,16 @@ frappe.ui.form.on('Payment Entry', { return { query: "erpnext.controllers.queries.customer_query" } +======= + query: "erpnext.controllers.queries.employee_query", + }; + } else if (frm.doc.party_type == "Shareholder") { + return { + filters: { + company: frm.doc.company, + }, + }; +>>>>>>> 63ad9f4f86 (refactor: filter shareholder on company) } }); From 8412938442b230d9fd924bd732cb6a3d42896769 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 22 Aug 2024 16:58:39 +0530 Subject: [PATCH 09/29] fix: call 'process' directly instead of creating 'process_subscripti reason: 'process' follows simple DB transaction model. (cherry picked from commit b4d22c29369ba92c0b59be56c8b275e6e05f778c) --- erpnext/accounts/doctype/subscription/subscription.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 96c97659ad8..a08eb4dda7d 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -694,10 +694,7 @@ class Subscription(Document): elif self.generate_invoice_at == "Days before the current subscription period": processing_date = add_days(self.current_invoice_start, -self.number_of_days) - process_subscription = frappe.new_doc("Process Subscription") - process_subscription.posting_date = processing_date - process_subscription.subscription = self.name - process_subscription.save().submit() + self.process(posting_date=processing_date) def get_calendar_months(billing_interval): From 164b417136dee1a32e6097e9a64fdd9b8c211c26 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 22 Aug 2024 17:33:26 +0530 Subject: [PATCH 10/29] chore: resolve conflict --- .../doctype/payment_entry/payment_entry.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 25261d91fe3..137dedf80ec 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -21,19 +21,13 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_from", function() { frm.events.validate_company(frm); -<<<<<<< HEAD var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; -======= - var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) - ? ["Bank", "Cash"] - : [frappe.boot.party_account_types[frm.doc.party_type]]; if (frm.doc.party_type == "Shareholder") { account_types.push("Equity"); } ->>>>>>> 6cbf98294a (refactor: allow equity types on Payment Entry) return { filters: { "account_type": ["in", account_types], @@ -86,17 +80,11 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); -<<<<<<< HEAD var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; -======= - var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) - ? ["Bank", "Cash"] - : [frappe.boot.party_account_types[frm.doc.party_type]]; if (frm.doc.party_type == "Shareholder") { account_types.push("Equity"); } ->>>>>>> 6cbf98294a (refactor: allow equity types on Payment Entry) return { filters: { "account_type": ["in", account_types], @@ -339,7 +327,6 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("party", function() { if(frm.doc.party_type == 'Employee'){ return { -<<<<<<< HEAD query: "erpnext.controllers.queries.employee_query" } } @@ -347,16 +334,12 @@ frappe.ui.form.on('Payment Entry', { return { query: "erpnext.controllers.queries.customer_query" } -======= - query: "erpnext.controllers.queries.employee_query", - }; } else if (frm.doc.party_type == "Shareholder") { return { filters: { company: frm.doc.company, }, }; ->>>>>>> 63ad9f4f86 (refactor: filter shareholder on company) } }); From 54842ea0b360187585b081ab82d4c4dd3505c166 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Aug 2024 19:33:44 +0530 Subject: [PATCH 11/29] fix: stock reco with incorrect serial numbers (#42865) --- .../stock_reconciliation.py | 28 ++++++------------- .../test_stock_reconciliation.py | 27 ++++++++++++++++++ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index d78e3c14048..0201321785f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -5,7 +5,7 @@ import frappe from frappe import _, bold, msgprint from frappe.query_builder.functions import CombineDatetime, Sum -from frappe.utils import add_to_date, cint, cstr, flt +from frappe.utils import add_to_date, cint, cstr, flt, get_link_to_form import erpnext from erpnext.accounts.utils import get_company_default @@ -357,7 +357,6 @@ class StockReconciliation(StockController): sl_entries.append(args) - qty_after_transaction = 0 for serial_no in serial_nos: args = self.get_sle_for_items(row, [serial_no]) @@ -373,27 +372,16 @@ class StockReconciliation(StockController): if previous_sle and row.warehouse != previous_sle.get("warehouse"): # If serial no exists in different warehouse - warehouse = previous_sle.get("warehouse", "") or row.warehouse - - if not qty_after_transaction: - qty_after_transaction = get_stock_balance( - row.item_code, warehouse, self.posting_date, self.posting_time + frappe.throw( + _( + "The Serial No {0} already exists in the warehouse {1}. It cannot be transferred to the warehouse {2}" + ).format( + get_link_to_form("Serial No", serial_no), + bold(previous_sle.get("warehouse")), + row.warehouse, ) - - qty_after_transaction -= 1 - - new_args = args.copy() - new_args.update( - { - "actual_qty": -1, - "qty_after_transaction": qty_after_transaction, - "warehouse": warehouse, - "valuation_rate": previous_sle.get("valuation_rate"), - } ) - sl_entries.append(new_args) - if row.qty: args = self.get_sle_for_items(row) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 8f3c9891334..2f4755cf511 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1119,6 +1119,33 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): active_serial_no = frappe.get_all("Serial No", filters={"status": "Active", "item_code": item_code}) self.assertEqual(len(active_serial_no), 5) + def test_stock_reco_for_serialized_item_with_different_warehouse(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + # Add new serial nos + serial_item_code = "Stock-Reco-Serial-Item-11" + warehouse = "_Test Warehouse - _TC" + serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC" + + self.make_item( + serial_item_code, {"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SNT-SRS11.####"} + ) + + make_stock_entry(item_code=serial_item_code, target=warehouse, qty=10, basic_rate=100) + stock_entry = make_stock_entry( + item_code=serial_item_code, target=serial_warehouse, qty=10, basic_rate=200 + ) + + sr = create_stock_reconciliation( + item_code=serial_item_code, warehouse=warehouse, qty=11, rate=200, do_not_submit=True + ) + + serial_nos = get_serial_nos(stock_entry.items[0].serial_no) + + sr.items[0].serial_no += f"\n{serial_nos[0]}" + sr.save() + self.assertRaises(frappe.ValidationError, sr.submit) + def create_batch_item_with_batch(item_name, batch_id): batch_item_doc = create_item(item_name, is_stock_item=1) From 5186872b9b9005aa6253f733492d6b11395cbcd9 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 20 Aug 2024 13:45:57 +0530 Subject: [PATCH 12/29] fix: do not copy date fields in opportunity doctype (cherry picked from commit 74afa57a9fd6ef1e49eba1e284d02528c5bf08bf) # Conflicts: # erpnext/crm/doctype/opportunity/opportunity.json --- erpnext/crm/doctype/opportunity/opportunity.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 07641d20c33..8ca7bcf5c7e 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -185,7 +185,8 @@ { "fieldname": "expected_closing", "fieldtype": "Date", - "label": "Expected Closing Date" + "label": "Expected Closing Date", + "no_copy": 1 }, { "fieldname": "section_break_14", @@ -357,6 +358,7 @@ "fieldname": "transaction_date", "fieldtype": "Date", "label": "Opportunity Date", + "no_copy": 1, "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, @@ -388,6 +390,7 @@ "fieldname": "first_response_time", "fieldtype": "Duration", "label": "First Response Time", + "no_copy": 1, "read_only": 1 }, { @@ -622,7 +625,11 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], +<<<<<<< HEAD "modified": "2022-10-13 12:42:21.545636", +======= + "modified": "2024-08-20 04:12:29.095761", +>>>>>>> 74afa57a9f (fix: do not copy date fields in opportunity doctype) "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From 3a02800fbce6faee7784a6158f2f6b528aa2f157 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Sat, 24 Aug 2024 12:38:11 +0530 Subject: [PATCH 13/29] chore: resolve conflicts --- erpnext/crm/doctype/opportunity/opportunity.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 8ca7bcf5c7e..75373398b09 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -625,11 +625,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], -<<<<<<< HEAD - "modified": "2022-10-13 12:42:21.545636", -======= "modified": "2024-08-20 04:12:29.095761", ->>>>>>> 74afa57a9f (fix: do not copy date fields in opportunity doctype) "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From 12d5e247c5f5c01a6708814f8be9177addc28cca Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:53:23 +0530 Subject: [PATCH 14/29] perf: data import for stock entries (backport #42711) (#42903) * perf: data import for stock entries (#42711) (cherry picked from commit 151128046440ee32f8bf8056942c47f65b033365) # Conflicts: # erpnext/stock/doctype/stock_entry/test_stock_entry.py * chore: fix conflicts * chore: fix linters issue --------- Co-authored-by: rohitwaghchaure --- .../doctype/stock_entry/test_stock_entry.py | 70 ++++++++++++++++++- .../test_stock_ledger_entry.py | 3 + erpnext/stock/stock_ledger.py | 6 ++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 085f6b36f29..ff3a8be0419 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -4,7 +4,7 @@ from frappe.permissions import add_user_permission, remove_user_permission from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, nowtime, today +from frappe.utils import add_days, cstr, flt, get_time, getdate, nowtime, today from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.stock.doctype.item.test_item import ( @@ -1727,6 +1727,74 @@ class TestStockEntry(FrappeTestCase): mr.cancel() mr.delete() + def test_stock_entry_for_same_posting_date_and_time(self): + warehouse = "_Test Warehouse - _TC" + item_code = "Test Stock Entry For Same Posting Datetime 1" + make_item(item_code, {"is_stock_item": 1}) + posting_date = nowdate() + posting_time = nowtime() + + for index in range(25): + se = make_stock_entry( + item_code=item_code, + qty=1, + to_warehouse=warehouse, + posting_date=posting_date, + posting_time=posting_time, + do_not_submit=True, + purpose="Material Receipt", + basic_rate=100, + ) + + se.append( + "items", + { + "item_code": item_code, + "item_name": se.items[0].item_name, + "description": se.items[0].description, + "t_warehouse": se.items[0].t_warehouse, + "basic_rate": 100, + "qty": 1, + "stock_qty": 1, + "conversion_factor": 1, + "expense_account": se.items[0].expense_account, + "cost_center": se.items[0].cost_center, + "uom": se.items[0].uom, + "stock_uom": se.items[0].stock_uom, + }, + ) + + se.remarks = f"The current number is {cstr(index)}" + + se.submit() + + sles = frappe.get_all( + "Stock Ledger Entry", + fields=[ + "posting_date", + "posting_time", + "actual_qty", + "qty_after_transaction", + "incoming_rate", + "stock_value_difference", + "stock_value", + ], + filters={"item_code": item_code, "warehouse": warehouse}, + order_by="creation", + ) + + self.assertEqual(len(sles), 50) + i = 0 + for sle in sles: + i += 1 + self.assertEqual(getdate(sle.posting_date), getdate(posting_date)) + self.assertEqual(get_time(sle.posting_time), get_time(posting_time)) + self.assertEqual(sle.actual_qty, 1) + self.assertEqual(sle.qty_after_transaction, i) + self.assertEqual(sle.incoming_rate, 100) + self.assertEqual(sle.stock_value_difference, 100) + self.assertEqual(sle.stock_value, 100 * i) + def make_serialized_item(**args): args = frappe._dict(args) diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 2b01ada1092..b926dd07869 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1028,6 +1028,8 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): self.assertEqual(50, _get_stock_credit(final_consumption)) def test_tie_breaking(self): + from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_entries + frappe.flags.dont_execute_stock_reposts = True self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts") @@ -1070,6 +1072,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): self.assertEqual([10, 11], ordered_qty_after_transaction()) first.cancel() + repost_entries() self.assertEqual([1], ordered_qty_after_transaction()) backdated = make_stock_entry( diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 8df785a0c1f..dc1ee7f2d85 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -445,6 +445,7 @@ class update_entries_after: and ( posting_datetime = %(posting_datetime)s ) + and creation = %(creation)s order by creation ASC for update @@ -1236,6 +1237,11 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc voucher_no = args.get("voucher_no") voucher_condition = f"and voucher_no != '{voucher_no}'" + elif args.get("creation"): + creation = args.get("creation") + operator = "<=" + voucher_condition = f"and creation < '{creation}'" + sle = frappe.db.sql( f""" select *, posting_datetime as "timestamp" From 18a75fef9acaa9714512dc08d3bb7b8db8b267ab Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:05:19 +0530 Subject: [PATCH 15/29] refactor: better err msg on clearance tool (cherry picked from commit 092411b54ff21d5bed74eb896b8d6bf7bc0d0ad9) # Conflicts: # erpnext/accounts/doctype/bank_clearance/bank_clearance.py --- .../doctype/bank_clearance/bank_clearance.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 6445deba6c3..dd0b6bf812e 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -6,7 +6,12 @@ import frappe from frappe import _, msgprint from frappe.model.document import Document from frappe.query_builder.custom import ConstantColumn +<<<<<<< HEAD from frappe.utils import flt, fmt_money, getdate +======= +from frappe.utils import flt, fmt_money, get_link_to_form, getdate +from pypika import Order +>>>>>>> 092411b54f (refactor: better err msg on clearance tool) import erpnext @@ -210,8 +215,11 @@ class BankClearance(Document): if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): frappe.throw( - _("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format( - d.idx, d.clearance_date, d.cheque_date + _("Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3}").format( + d.idx, + get_link_to_form(d.payment_document, d.payment_entry), + d.clearance_date, + d.cheque_date, ) ) From e6438b293dca68511ed83af18b1816379f0b244c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 15:30:53 +0530 Subject: [PATCH 16/29] chore: resolve conflict --- erpnext/accounts/doctype/bank_clearance/bank_clearance.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index dd0b6bf812e..b361a1ebb82 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -6,12 +6,7 @@ import frappe from frappe import _, msgprint from frappe.model.document import Document from frappe.query_builder.custom import ConstantColumn -<<<<<<< HEAD -from frappe.utils import flt, fmt_money, getdate -======= from frappe.utils import flt, fmt_money, get_link_to_form, getdate -from pypika import Order ->>>>>>> 092411b54f (refactor: better err msg on clearance tool) import erpnext From c29b95e3068f57e0f74433f1ff0e88a8a4f7bb20 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:20:21 +0530 Subject: [PATCH 17/29] feat: report to identify incorrectly cleared cheques (cherry picked from commit 28890fa833b31a40fd4f11affef4a15274335ebb) --- .../__init__.py | 0 ...heques_and_deposits_incorrectly_cleared.js | 6 ++++ ...ques_and_deposits_incorrectly_cleared.json | 29 +++++++++++++++++++ ...heques_and_deposits_incorrectly_cleared.py | 9 ++++++ 4 files changed, 44 insertions(+) create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js new file mode 100644 index 00000000000..f28714d70fa --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -0,0 +1,6 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { + filters: [], +}; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json new file mode 100644 index 00000000000..50cf765ca3d --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2024-07-30 17:20:07.570971", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letterhead": null, + "modified": "2024-07-30 17:20:07.570971", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Cheques and Deposits Incorrectly cleared", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Payment Entry", + "report_name": "Cheques and Deposits Incorrectly cleared", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py new file mode 100644 index 00000000000..9a1d41262fc --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe + + +def execute(filters=None): + columns, data = [], [] + return columns, data From ef93c96f4351236c3a9c47ab5d1c4d7d9896a908 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:24:46 +0530 Subject: [PATCH 18/29] refactor: barebones functions (cherry picked from commit ceaa1be72935e8bd46921f465659d04edb164482) --- ...heques_and_deposits_incorrectly_cleared.js | 45 ++++++++++++++++++- ...heques_and_deposits_incorrectly_cleared.py | 37 ++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js index f28714d70fa..f2554c4442c 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -2,5 +2,48 @@ // For license information, please see license.txt frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { - filters: [], + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), + }, + { + fieldname: "account", + label: __("Bank Account"), + fieldtype: "Link", + options: "Account", + default: frappe.defaults.get_user_default("Company") + ? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"] + : "", + reqd: 1, + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); + return { + query: "erpnext.controllers.queries.get_account_list", + filters: [ + ["Account", "account_type", "in", "Bank, Cash"], + ["Account", "is_group", "=", 0], + ["Account", "disabled", "=", 0], + ["Account", "company", "=", company], + ], + }; + }, + }, + { + fieldname: "report_date", + label: __("Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + fieldname: "include_pos_transactions", + label: __("Include POS Transactions"), + fieldtype: "Check", + }, + ], }; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 9a1d41262fc..774c843b90a 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -1,9 +1,44 @@ # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe +from frappe import _ def execute(filters=None): columns, data = [], [] return columns, data + + +def get_columns(): + return [ + {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, + { + "fieldname": "payment_document", + "label": _("Payment Document Type"), + "fieldtype": "Data", + "width": 220, + }, + { + "fieldname": "payment_entry", + "label": _("Payment Document"), + "fieldtype": "Dynamic Link", + "options": "payment_document", + "width": 220, + }, + { + "fieldname": "debit", + "label": _("Debit"), + "fieldtype": "Currency", + "options": "account_currency", + "width": 120, + }, + { + "fieldname": "credit", + "label": _("Credit"), + "fieldtype": "Currency", + "options": "account_currency", + "width": 120, + }, + {"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110}, + ] From c58987ba38744f511cf918c5cfd5e885237917e2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:52:52 +0530 Subject: [PATCH 19/29] refactor: working state with minimum functions (cherry picked from commit 4cd023444acc8b951d24c1a04ec8bbb120c4aeb7) --- ...heques_and_deposits_incorrectly_cleared.py | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 774c843b90a..14295a5a711 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -2,14 +2,80 @@ # For license information, please see license.txt import frappe -from frappe import _ +from frappe import _, qb +from frappe.query_builder import CustomFunction def execute(filters=None): - columns, data = [], [] + columns = get_columns() + data = build_data(filters) return columns, data +def build_data(filters): + vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) + data = [] + for x in vouchers: + data.append( + frappe._dict( + payment_document="Payment Entry", + payment_entry=x.name, + debit=x.amount, + credit=0, + posting_date=x.posting_date, + clearance_date=x.clearance_date, + ) + ) + return data + + +def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters): + je = qb.DocType("Journal Entry") + jea = qb.DocType("Journal Entry Account") + + journals = ( + qb.from_(je) + .inner_join(jea) + .on(je.name == jea.parent) + .select( + je.name, + jea.debit_in_account_currency, + jea.credit_in_account_currency, + je.posting_date, + je.clearance_date, + ) + .where( + je.docstatus.eq(1) + & jea.account.eq(filters.account) + & je.posting_date.gt(filters.report_date) + & je.clearance_date.lte(filters.report_date) + & (je.is_opening.isnull() | je.is_opening.eq("No")) + ) + .run(as_dict=1) + ) + + ifelse = CustomFunction("IF", ["condition", "then", "else"]) + pe = qb.DocType("Payment Entry") + payments = ( + qb.from_(pe) + .select( + pe.name, + ifelse(pe.paid_from.eq(filters.account), pe.paid_amount, pe.received_amount).as_("amount"), + pe.posting_date, + pe.clearance_date, + ) + .where( + pe.docstatus.eq(1) + & (pe.paid_from.eq(filters.account) | pe.paid_to.eq(filters.account)) + & pe.posting_date.gt(filters.report_date) + & pe.clearance_date.lte(filters.report_date) + ) + .run(as_dict=1) + ) + + return journals + payments + + def get_columns(): return [ {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, @@ -40,5 +106,6 @@ def get_columns(): "options": "account_currency", "width": 120, }, + {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 110}, {"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110}, ] From 29fe7bea6dd5552712c042605eb995f9767a9c85 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 31 Jul 2024 17:57:25 +0530 Subject: [PATCH 20/29] refactor: build dict for payment entry (cherry picked from commit 784dec24c8c521461f232658aca8c58c82c39ff8) --- ...heques_and_deposits_incorrectly_cleared.py | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 14295a5a711..995ce4efd0b 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -4,6 +4,7 @@ import frappe from frappe import _, qb from frappe.query_builder import CustomFunction +from frappe.query_builder.custom import ConstantColumn def execute(filters=None): @@ -12,20 +13,39 @@ def execute(filters=None): return columns, data +def build_payment_entry_dict(row: dict) -> dict: + row_dict = frappe._dict() + row_dict.update( + { + "payment_document": row.get("doctype"), + "payment_entry": row.get("name"), + "posting_date": row.get("posting_date"), + "clearance_date": row.get("clearance_date"), + } + ) + if row.get("payment_type") == "Receive" and row.get("party_type") in ["Customer", "Supplier"]: + row_dict.update( + { + "debit": row.get("amount"), + "credit": 0, + } + ) + else: + row_dict.update( + { + "debit": 0, + "credit": row.get("amount"), + } + ) + return row_dict + + def build_data(filters): vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) data = [] for x in vouchers: - data.append( - frappe._dict( - payment_document="Payment Entry", - payment_entry=x.name, - debit=x.amount, - credit=0, - posting_date=x.posting_date, - clearance_date=x.clearance_date, - ) - ) + if x.doctype == "Payment Entry": + data.append(build_payment_entry_dict(x)) return data @@ -56,11 +76,15 @@ def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filter ifelse = CustomFunction("IF", ["condition", "then", "else"]) pe = qb.DocType("Payment Entry") + doctype_name = ConstantColumn("Payment Entry") payments = ( qb.from_(pe) .select( + doctype_name.as_("doctype"), pe.name, ifelse(pe.paid_from.eq(filters.account), pe.paid_amount, pe.received_amount).as_("amount"), + pe.payment_type, + pe.party_type, pe.posting_date, pe.clearance_date, ) From 15aeec8a2d1419eeec2549ca806d2a7e2eb4822b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:16:32 +0530 Subject: [PATCH 21/29] chore: remove redundant column (cherry picked from commit 74b36db24e54e8e0a881259f51e79270a7057be2) --- .../cheques_and_deposits_incorrectly_cleared.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 995ce4efd0b..6bb090fd1b7 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -102,7 +102,6 @@ def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filter def get_columns(): return [ - {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, { "fieldname": "payment_document", "label": _("Payment Document Type"), From 09946c7ea7d137c43bd97bdd4fe74bab43fe36f6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:29:47 +0530 Subject: [PATCH 22/29] refactor: build dictionary for Journal remove redundant filter (cherry picked from commit 2144e0337d8194e466c315b8585872f8b21b7a44) --- ...heques_and_deposits_incorrectly_cleared.js | 5 ----- ...heques_and_deposits_incorrectly_cleared.py | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js index f2554c4442c..e83fc6f5b57 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -40,10 +40,5 @@ frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { default: frappe.datetime.get_today(), reqd: 1, }, - { - fieldname: "include_pos_transactions", - label: __("Include POS Transactions"), - fieldtype: "Check", - }, ], }; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 6bb090fd1b7..891dc2c4bb1 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -40,24 +40,43 @@ def build_payment_entry_dict(row: dict) -> dict: return row_dict +def build_journal_entry_dict(row: dict) -> dict: + row_dict = frappe._dict() + row_dict.update( + { + "payment_document": row.get("doctype"), + "payment_entry": row.get("name"), + "posting_date": row.get("posting_date"), + "clearance_date": row.get("clearance_date"), + "debit": row.get("debit_in_account_currency"), + "credit": row.get("credit_in_account_currency"), + } + ) + return row_dict + + def build_data(filters): vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) data = [] for x in vouchers: if x.doctype == "Payment Entry": data.append(build_payment_entry_dict(x)) + elif x.doctype == "Journal Entry": + data.append(build_journal_entry_dict(x)) return data def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters): je = qb.DocType("Journal Entry") jea = qb.DocType("Journal Entry Account") + doctype_name = ConstantColumn("Journal Entry") journals = ( qb.from_(je) .inner_join(jea) .on(je.name == jea.parent) .select( + doctype_name.as_("doctype"), je.name, jea.debit_in_account_currency, jea.credit_in_account_currency, From 820dcd79bb372963dea56cd117605238cc4a398d Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 22 Aug 2024 11:50:19 +0530 Subject: [PATCH 23/29] fix: update dimesions in exchange_gain_loss jv based on base document (cherry picked from commit 96df19149de806cea9798420640f9088f55925bc) --- erpnext/controllers/accounts_controller.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ed4a4f52b8f..cd95a2d46da 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1192,6 +1192,12 @@ class AccountsController(TransactionBase): # Cancelling existing exchange gain/loss journals is handled during the `on_cancel` event. # see accounts/utils.py:cancel_exchange_gain_loss_journal() if self.docstatus == 1: + if dimensions_dict is None: + dimensions_dict = frappe._dict() + active_dimensions = get_dimensions()[0] + for dim in active_dimensions: + dimensions_dict[dim.fieldname] = self.get(dim.fieldname) + if self.get("doctype") == "Journal Entry": # 'args' is populated with exchange gain/loss account and the amount to be booked. # These are generated by Sales/Purchase Invoice during reconciliation and advance allocation. From 2f4f2a8eec7c3f214794d08af0719067c578d37b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 09:30:01 +0530 Subject: [PATCH 24/29] fix: unsupported operand type(s) for *: 'float' and 'NoneType' (backport #42916) (#42917) fix: unsupported operand type(s) for *: 'float' and 'NoneType' (#42916) (cherry picked from commit 10434742e965d4003bb42f1e2335b8785ba3667f) Co-authored-by: rohitwaghchaure --- erpnext/accounts/doctype/pricing_rule/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index ab9894a52a8..b5d4af1ab90 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -485,7 +485,7 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules, row_item): continue stock_qty = row.get("qty") * (row.get("conversion_factor") or 1.0) - amount = stock_qty * (row.get("price_list_rate") or row.get("rate")) + amount = stock_qty * (flt(row.get("price_list_rate")) or flt(row.get("rate"))) pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, row) if pricing_rules and pricing_rules[0]: From 73eab91631e679545d264e574d8363b5b717759e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:29:41 +0530 Subject: [PATCH 25/29] fix: timeout while submitting stock entry (backport #42929) (#42930) * fix: timeout while submitting stock entry (#42929) (cherry picked from commit ca2fde891ec32b162f6b6c33d9ff10b60e4ca57c) # Conflicts: # erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index 835002f0e16..94571b8ab87 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -319,7 +319,8 @@ { "fieldname": "posting_datetime", "fieldtype": "Datetime", - "label": "Posting Datetime" + "label": "Posting Datetime", + "search_index": 1 } ], "hide_toolbar": 1, @@ -328,7 +329,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-02-07 09:18:13.999231", + "modified": "2024-08-27 09:29:03.961443", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", From c0b5f7c8ebe90245e440294c445933ecf5c5e35e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:33:56 +0530 Subject: [PATCH 26/29] fix: same posting date and time, creation causing incorrect balance qty (backport #42904) (#42919) * fix: same posting date and time, creation causing incorrect balance qty (#42904) fix: same posting date and time, creation causing incorrect balance quantity (cherry picked from commit 27364b7e6b0b03aa0303e5b180cd867bbb312232) # Conflicts: # erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py # erpnext/stock/stock_ledger.py * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../purchase_receipt/test_purchase_receipt.py | 67 ++++++++++++++++++- .../test_stock_ledger_entry.py | 4 +- erpnext/stock/stock_ledger.py | 13 ++-- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c0cf79c63da..c87699f5f8c 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3,7 +3,7 @@ import frappe from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, cint, cstr, flt, nowtime, today +from frappe.utils import add_days, cint, cstr, flt, get_datetime, getdate, nowtime, today from pypika import functions as fn import erpnext @@ -2603,6 +2603,71 @@ class TestPurchaseReceipt(FrappeTestCase): company_doc.default_inventory_account = None company_doc.save() + def test_sles_with_same_posting_datetime_and_creation(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + from erpnext.stock.report.stock_balance.stock_balance import execute + + item_code = "Test Item for SLE with same posting datetime and creation" + create_item(item_code) + + pr = make_purchase_receipt( + item_code=item_code, + qty=10, + rate=100, + posting_date="2023-11-06", + posting_time="00:00:00", + ) + + sr = make_stock_entry( + item_code=item_code, + source=pr.items[0].warehouse, + qty=10, + posting_date="2023-11-07", + posting_time="14:28:0.330404", + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code}, + "name", + ) + + sle_doc = frappe.get_doc("Stock Ledger Entry", sle) + sle_doc.db_set("creation", "2023-11-07 14:28:01.208930") + + sle_doc.reload() + self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.208930")) + + sr = make_stock_entry( + item_code=item_code, + target=pr.items[0].warehouse, + qty=50, + posting_date="2023-11-07", + posting_time="14:28:0.920825", + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code}, + "name", + ) + + sle_doc = frappe.get_doc("Stock Ledger Entry", sle) + sle_doc.db_set("creation", "2023-11-07 14:28:01.044561") + + sle_doc.reload() + self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.044561")) + + pr.repost_future_sle_and_gle(force=True) + + columns, data = execute( + filters=frappe._dict( + {"item_code": item_code, "warehouse": pr.items[0].warehouse, "company": pr.company} + ) + ) + + self.assertEqual(data[0].get("bal_qty"), 50.0) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index b926dd07869..b85453d2572 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1172,7 +1172,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): qty=5, posting_date="2021-01-01", rate=10, - posting_time="02:00:00.1234", + posting_time="02:00:00", ) time.sleep(3) @@ -1184,7 +1184,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): qty=100, rate=10, posting_date="2021-01-01", - posting_time="02:00:00", + posting_time="02:00:00.1234", ) sle = frappe.get_all( diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index dc1ee7f2d85..826d8eb0060 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1253,7 +1253,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc and ( posting_datetime {operator} %(posting_datetime)s ) - order by posting_datetime desc, creation desc + order by posting_date desc, posting_time desc, creation desc limit 1 for update""", { @@ -1347,7 +1347,7 @@ def get_stock_ledger_entries( where item_code = %(item_code)s and is_cancelled = 0 {conditions} - order by posting_datetime {order}, creation {order} + order by posting_date {order}, posting_time {order}, creation {order} {limit} {for_update}""".format( conditions=conditions, limit=limit or "", @@ -1452,7 +1452,7 @@ def get_valuation_rate( AND valuation_rate >= 0 AND is_cancelled = 0 AND NOT (voucher_no = %s AND voucher_type = %s) - order by posting_datetime desc, name desc limit 1""", + order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type), ) @@ -1701,7 +1701,8 @@ def get_future_sle_with_negative_qty(sle): & (SLE.is_cancelled == 0) & (SLE.qty_after_transaction < 0) ) - .orderby(SLE.posting_datetime) + .orderby(SLE.posting_date) + .orderby(SLE.posting_time) .limit(1) ) @@ -1717,14 +1718,14 @@ def get_future_sle_with_negative_batch_qty(args): with batch_ledger as ( select posting_date, posting_time, posting_datetime, voucher_type, voucher_no, - sum(actual_qty) over (order by posting_datetime, creation) as cumulative_total + sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total from `tabStock Ledger Entry` where item_code = %(item_code)s and warehouse = %(warehouse)s and batch_no=%(batch_no)s and is_cancelled = 0 - order by posting_datetime, creation + order by posting_date, posting_time, creation ) select * from batch_ledger where From 094411f5f0d84378e4c9a3b64d29a992939fd93c Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 26 Aug 2024 20:04:51 +0530 Subject: [PATCH 27/29] fix: get amount with taxes and charges from payment entry (cherry picked from commit b3a901b631e800f52554214261a59608b2fe8a4e) --- .../bank_reconciliation_statement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py index 7c2389d8dd5..105b592a529 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -150,8 +150,8 @@ def get_payment_entries(filters): select "Payment Entry" as payment_document, name as payment_entry, reference_no, reference_date as ref_date, - if(paid_to=%(account)s, received_amount, 0) as debit, - if(paid_from=%(account)s, paid_amount, 0) as credit, + if(paid_to=%(account)s, received_amount_after_tax, 0) as debit, + if(paid_from=%(account)s, paid_amount_after_tax, 0) as credit, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency from `tabPayment Entry` From 0c2d7f2d9ac36a5c42adafe4603ffff67f76e438 Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Wed, 24 Jul 2024 11:59:45 +0530 Subject: [PATCH 28/29] fix: Update get_amount to return currency precision grand total In case of multi-currency purchase invoice, we are getting the error "Total Payment Request amount cannot be greater than Purchase Invoice amount" because of rounding difference. (cherry picked from commit 976abf7b3c92cd6387275442f87c784d4adf16e1) --- erpnext/accounts/doctype/payment_request/payment_request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 106000a78f3..78567c37cb5 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -19,7 +19,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import ( ) from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate from erpnext.accounts.party import get_party_account, get_party_bank_account -from erpnext.accounts.utils import get_account_currency +from erpnext.accounts.utils import get_account_currency, get_currency_precision from erpnext.erpnext_integrations.stripe_integration import create_stripe_subscription from erpnext.utilities import payment_app_import_guard @@ -520,7 +520,7 @@ def get_amount(ref_doc, payment_account=None): grand_total = ref_doc.outstanding_amount if grand_total > 0: - return grand_total + return flt(grand_total, get_currency_precision()) else: frappe.throw(_("Payment Entry is already created")) From ff14d72a4620f55c4282e0cc29b6ffc08419334d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 27 Aug 2024 14:40:12 +0530 Subject: [PATCH 29/29] fix: use of incorrect attribute (cherry picked from commit fb32d2cafb1757216ec3f7db5f3e745ea5b15906) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9384ee180d2..6d9565fb21c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1563,7 +1563,7 @@ def get_outstanding_reference_documents(args): d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no") # Get negative outstanding sales /purchase invoices - if args.get("party_type") != "Employee" and not args.get("voucher_no"): + if args.get("party_type") != "Employee": negative_outstanding_invoices = get_negative_outstanding_invoices( args.get("party_type"), args.get("party"),