From 7cd6debd0e75f9cb6b0ca94c70667d3d38546a74 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 4 Apr 2024 16:18:20 +0000 Subject: [PATCH 01/35] fix: missing def expense if no exp in first month (cherry picked from commit 5c9ce575f65eb0d0282f38e23e7c5eebd628518b) --- erpnext/accounts/deferred_revenue.py | 74 ++++++++++++++-------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 9ffdf186f02..a48ce9b4c63 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -360,45 +360,45 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): ) if not amount: - return - - gl_posting_date = end_date - prev_posting_date = None - # check if books nor frozen till endate: - if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto): - gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1)) prev_posting_date = end_date - - if via_journal_entry: - book_revenue_via_journal_entry( - doc, - credit_account, - debit_account, - amount, - base_amount, - gl_posting_date, - project, - account_currency, - item.cost_center, - item, - deferred_process, - submit_journal_entry, - ) else: - make_gl_entries( - doc, - credit_account, - debit_account, - against, - amount, - base_amount, - gl_posting_date, - project, - account_currency, - item.cost_center, - item, - deferred_process, - ) + gl_posting_date = end_date + prev_posting_date = None + # check if books nor frozen till endate: + if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto): + gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1)) + prev_posting_date = end_date + + if via_journal_entry: + book_revenue_via_journal_entry( + doc, + credit_account, + debit_account, + amount, + base_amount, + gl_posting_date, + project, + account_currency, + item.cost_center, + item, + deferred_process, + submit_journal_entry, + ) + else: + make_gl_entries( + doc, + credit_account, + debit_account, + against, + amount, + base_amount, + gl_posting_date, + project, + account_currency, + item.cost_center, + item, + deferred_process, + ) # Returned in case of any errors because it tries to submit the same record again and again in case of errors if frappe.flags.deferred_accounting_error: From 186701b54f4bbe79c4e824766cd8679b8759c06d Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 5 Apr 2024 17:59:48 +0000 Subject: [PATCH 02/35] fix: test case for zero deferred expense (cherry picked from commit 7ef4dbcaf66e213bfd06c9e75cf7c0828f89dd63) --- .../test_deferred_revenue_and_expense.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py index f8a965b699c..983eb0ee6af 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py @@ -279,3 +279,78 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): {"key": "aug_2021", "total": 0, "actual": 0}, ] self.assertEqual(report.period_total, expected) + + @change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"}) + def test_zero_amount(self): + self.create_item("_Test Office Desk", 0, self.warehouse, self.company) + item = frappe.get_doc("Item", self.item) + item.enable_deferred_expense = 1 + item.item_defaults[0].deferred_expense_account = self.deferred_expense_account + item.no_of_months_exp = 12 + item.save() + + pi = make_purchase_invoice( + item=self.item, + company=self.company, + supplier=self.supplier, + is_return=False, + update_stock=False, + posting_date=frappe.utils.datetime.date(2023, 12, 30), + parent_cost_center=self.cost_center, + cost_center=self.cost_center, + do_not_save=True, + rate=3910, + price_list_rate=3910, + warehouse=self.warehouse, + qty=1, + ) + pi.set_posting_time = True + pi.items[0].enable_deferred_expense = 1 + pi.items[0].service_start_date = "2023-12-30" + pi.items[0].service_end_date = "2024-12-30" + pi.items[0].deferred_expense_account = self.deferred_expense_account + pi.items[0].expense_account = self.expense_account + pi.save() + pi.submit() + + pda = frappe.get_doc( + dict( + doctype="Process Deferred Accounting", + posting_date=nowdate(), + start_date="2024-01-01", + end_date="2024-01-31", + type="Expense", + company=self.company, + ) + ) + pda.insert() + pda.submit() + + # execute report + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2024-01-31")) + self.filters = frappe._dict( + { + "company": self.company, + "filter_based_on": "Date Range", + "period_start_date": "2024-01-01", + "period_end_date": "2024-01-31", + "from_fiscal_year": fiscal_year.year, + "to_fiscal_year": fiscal_year.year, + "periodicity": "Monthly", + "type": "Expense", + "with_upcoming_postings": False, + } + ) + + report = Deferred_Revenue_and_Expense_Report(filters=self.filters) + report.run() + + # fetch the invoice from deferred invoices list + inv = [d for d in report.deferred_invoices if d.name == pi.name] + # make sure the list isn't empty + self.assertTrue(inv) + # calculate the total deferred expense for the period + inv = inv[0].calculate_invoice_revenue_expense_for_period() + deferred_exp = sum([inv[idx].actual for idx in range(len(report.period_total))]) + # make sure the total deferred expense is greater than 0 + self.assertGreater(deferred_exp, 0) From c67be052801f52abde44b7dff21624da5078ef0c Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 5 Apr 2024 18:16:26 +0000 Subject: [PATCH 03/35] chore: semgrep (cherry picked from commit 581af4ecedc3a8e134a8a1d545a789329d26fccc) --- .../test_deferred_revenue_and_expense.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py index 983eb0ee6af..5b848ac2955 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py @@ -314,14 +314,12 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): pi.submit() pda = frappe.get_doc( - dict( - doctype="Process Deferred Accounting", - posting_date=nowdate(), - start_date="2024-01-01", - end_date="2024-01-31", - type="Expense", - company=self.company, - ) + doctype="Process Deferred Accounting", + posting_date=nowdate(), + start_date="2024-01-01", + end_date="2024-01-31", + type="Expense", + company=self.company, ) pda.insert() pda.submit() @@ -351,6 +349,6 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): self.assertTrue(inv) # calculate the total deferred expense for the period inv = inv[0].calculate_invoice_revenue_expense_for_period() - deferred_exp = sum([inv[idx].actual for idx in range(len(report.period_total))]) + deferred_exp = sum([inv[idx].actual for idx in range(len(report.period_list))]) # make sure the total deferred expense is greater than 0 - self.assertGreater(deferred_exp, 0) + self.assertLess(deferred_exp, 0) From a9fa9e86ea6858f3664cadf50d8b235760269d63 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Sat, 6 Apr 2024 06:13:42 +0000 Subject: [PATCH 04/35] fix: expense causing p&l test case to fail (cherry picked from commit 01888c98bc8a9aaca42840381ce26bab7351510a) --- .../test_deferred_revenue_and_expense.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py index 5b848ac2955..4ca65dc04e5 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py @@ -280,7 +280,10 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): ] self.assertEqual(report.period_total, expected) - @change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"}) + @change_settings( + "Accounts Settings", + {"book_deferred_entries_based_on": "Months", "book_deferred_entries_via_journal_entry": 0}, + ) def test_zero_amount(self): self.create_item("_Test Office Desk", 0, self.warehouse, self.company) item = frappe.get_doc("Item", self.item) @@ -295,7 +298,7 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): supplier=self.supplier, is_return=False, update_stock=False, - posting_date=frappe.utils.datetime.date(2023, 12, 30), + posting_date=frappe.utils.datetime.date(2021, 12, 30), parent_cost_center=self.cost_center, cost_center=self.cost_center, do_not_save=True, @@ -306,8 +309,8 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): ) pi.set_posting_time = True pi.items[0].enable_deferred_expense = 1 - pi.items[0].service_start_date = "2023-12-30" - pi.items[0].service_end_date = "2024-12-30" + pi.items[0].service_start_date = "2021-12-30" + pi.items[0].service_end_date = "2022-12-30" pi.items[0].deferred_expense_account = self.deferred_expense_account pi.items[0].expense_account = self.expense_account pi.save() @@ -316,8 +319,8 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): pda = frappe.get_doc( doctype="Process Deferred Accounting", posting_date=nowdate(), - start_date="2024-01-01", - end_date="2024-01-31", + start_date="2022-01-01", + end_date="2022-01-31", type="Expense", company=self.company, ) @@ -325,13 +328,13 @@ class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2024-01-31")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2022-01-31")) self.filters = frappe._dict( { "company": self.company, "filter_based_on": "Date Range", - "period_start_date": "2024-01-01", - "period_end_date": "2024-01-31", + "period_start_date": "2022-01-01", + "period_end_date": "2022-01-31", "from_fiscal_year": fiscal_year.year, "to_fiscal_year": fiscal_year.year, "periodicity": "Monthly", From b73df8f5f020111146106fe7000a046d7ae2beca Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 31 Jul 2024 18:49:19 +0530 Subject: [PATCH 05/35] fix: dimensions in common party journal entry (cherry picked from commit ac629ede796a27bf0d8eaaa862baae1f1e39dd47) --- erpnext/controllers/accounts_controller.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a9494cf6e80..ed4a4f52b8f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2312,6 +2312,15 @@ class AccountsController(TransactionBase): advance_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(self.company) advance_entry.is_advance = "Yes" + # update dimesions + dimensions_dict = frappe._dict() + active_dimensions = get_dimensions()[0] + for dim in active_dimensions: + dimensions_dict[dim.fieldname] = self.get(dim.fieldname) + + reconcilation_entry.update(dimensions_dict) + advance_entry.update(dimensions_dict) + if self.doctype == "Sales Invoice": reconcilation_entry.credit_in_account_currency = self.outstanding_amount advance_entry.debit_in_account_currency = self.outstanding_amount From fa74efc1fcc2c6e146220062109bf12512364cbd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 6 Aug 2024 17:46:19 +0530 Subject: [PATCH 06/35] test: dimension inheritance on Exc Gain/Loss JV on Common party (cherry picked from commit 8040544216eb93bbca209a7120dd612b4e6f4e31) --- .../sales_invoice/test_sales_invoice.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 9e81fcf502f..dd61a594706 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3025,6 +3025,84 @@ class TestSalesInvoice(FrappeTestCase): party_link.delete() frappe.db.set_value("Accounts Settings", None, "enable_common_party_accounting", 0) + def test_sales_invoice_against_supplier_usd_with_dimensions(self): + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + from erpnext.accounts.doctype.party_link.party_link import create_party_link + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + + # create a customer + customer = make_customer(customer="_Test Common Supplier USD") + cust_doc = frappe.get_doc("Customer", customer) + cust_doc.default_currency = "USD" + cust_doc.save() + # create a supplier + supplier = create_supplier(supplier_name="_Test Common Supplier USD").name + supp_doc = frappe.get_doc("Supplier", supplier) + supp_doc.default_currency = "USD" + supp_doc.save() + + # create a party link between customer & supplier + party_link = create_party_link("Supplier", supplier, customer) + + # enable common party accounting + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1) + + # create a dimension and make it mandatory + if not frappe.get_all("Accounting Dimension", filters={"document_type": "Department"}): + dim = frappe.get_doc( + { + "doctype": "Accounting Dimension", + "document_type": "Department", + "dimension_defaults": [{"company": "_Test Company", "mandatory_for_bs": True}], + } + ) + dim.save() + else: + dim = frappe.get_doc( + "Accounting Dimension", + frappe.get_all("Accounting Dimension", filters={"document_type": "Department"})[0], + ) + dim.disabled = False + dim.dimension_defaults = [] + dim.append("dimension_defaults", {"company": "_Test Company", "mandatory_for_bs": True}) + dim.save() + + # create a sales invoice + si = create_sales_invoice( + customer=customer, parent_cost_center="_Test Cost Center - _TC", do_not_submit=True + ) + si.department = "All Departments" + si.save().submit() + + # check outstanding of sales invoice + si.reload() + self.assertEqual(si.status, "Paid") + self.assertEqual(flt(si.outstanding_amount), 0.0) + + # check creation of journal entry + jv = frappe.get_all( + "Journal Entry Account", + { + "account": si.debit_to, + "party_type": "Customer", + "party": si.customer, + "reference_type": si.doctype, + "reference_name": si.name, + "department": "All Departments", + }, + pluck="credit_in_account_currency", + ) + + self.assertTrue(jv) + self.assertEqual(jv[0], si.grand_total) + + dim.disabled = True + dim.save() + party_link.delete() + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) + def test_payment_statuses(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry From 8d7e150afbe6f8c4393f45bdfe028959b8551f7c Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Thu, 1 Aug 2024 17:01:43 +0200 Subject: [PATCH 07/35] feat: changes in opportunity.py to show contacts and addresses from referenced and opportunities (cherry picked from commit 61576ca03092a2a60eb5d468ee62495a868673ad) --- erpnext/crm/doctype/opportunity/opportunity.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index a0301f9ea0a..af1fc5c8792 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -27,7 +27,20 @@ from erpnext.utilities.transaction_base import TransactionBase class Opportunity(TransactionBase, CRMNote): def onload(self): ref_doc = frappe.get_doc(self.opportunity_from, self.party_name) + load_address_and_contact(ref_doc) + load_address_and_contact(self) + + ref_doc_contact_list = ref_doc.get("__onload").get('contact_list') + opportunity_doc_contact_list = [contact for contact in self.get('__onload').get('contact_list') if contact not in ref_doc_contact_list] + ref_doc_contact_list.extend(opportunity_doc_contact_list) + ref_doc.set_onload("contact_list", ref_doc_contact_list) + + ref_doc_addr_list = ref_doc.get("__onload").get('addr_list') + opportunity_doc_addr_list = [addr for addr in self.get('__onload').get('addr_list') if addr not in ref_doc_addr_list] + ref_doc_addr_list.extend(opportunity_doc_addr_list) + ref_doc.set_onload("addr_list", ref_doc_addr_list) + self.set("__onload", ref_doc.get("__onload")) def after_insert(self): From 8f19832aa55794da1c4342faf2a91bfa1e1ca394 Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Fri, 2 Aug 2024 12:51:48 +0200 Subject: [PATCH 08/35] fix: pre-commit for better code formatting (cherry picked from commit 511a0b9f375bd6f86fd689d35eb6ee395c85a5c6) --- erpnext/crm/doctype/opportunity/opportunity.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index af1fc5c8792..c7b0304203c 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -30,17 +30,23 @@ class Opportunity(TransactionBase, CRMNote): load_address_and_contact(ref_doc) load_address_and_contact(self) - - ref_doc_contact_list = ref_doc.get("__onload").get('contact_list') - opportunity_doc_contact_list = [contact for contact in self.get('__onload').get('contact_list') if contact not in ref_doc_contact_list] + + ref_doc_contact_list = ref_doc.get("__onload").get("contact_list") + opportunity_doc_contact_list = [ + contact + for contact in self.get("__onload").get("contact_list") + if contact not in ref_doc_contact_list + ] ref_doc_contact_list.extend(opportunity_doc_contact_list) ref_doc.set_onload("contact_list", ref_doc_contact_list) - ref_doc_addr_list = ref_doc.get("__onload").get('addr_list') - opportunity_doc_addr_list = [addr for addr in self.get('__onload').get('addr_list') if addr not in ref_doc_addr_list] + ref_doc_addr_list = ref_doc.get("__onload").get("addr_list") + opportunity_doc_addr_list = [ + addr for addr in self.get("__onload").get("addr_list") if addr not in ref_doc_addr_list + ] ref_doc_addr_list.extend(opportunity_doc_addr_list) ref_doc.set_onload("addr_list", ref_doc_addr_list) - + self.set("__onload", ref_doc.get("__onload")) def after_insert(self): From 29449cbaf962c0ebc09f760624f2b0608b8e41b4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 8 Aug 2024 11:58:58 +0530 Subject: [PATCH 09/35] fix: Maintain same rate on qty change on Quotation to Sales Order (cherry picked from commit 91ce9fce9b20444e62b80dc5abab5624ddab2d72) --- erpnext/public/js/controllers/transaction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index bf7e5b9e48f..98c1434e4d0 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1139,6 +1139,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "Sales Invoice Item": ["dn_detail", "so_detail", "sales_invoice_item"], "Purchase Receipt Item": ["purchase_order_item", "purchase_invoice_item", "purchase_receipt_item"], "Purchase Invoice Item": ["purchase_order_item", "pr_detail", "po_detail"], + "Sales Order Item": ["prevdoc_docname", "quotation_item"], }; const mappped_fields = mapped_item_field_map[item.doctype] || []; From db27dd8702fec764e30085675b19ea719fd0df0e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:20:22 +0200 Subject: [PATCH 10/35] fix: german translation of exit (cherry picked from commit a2df2768802c54001286913ac17ebaf207e40351) --- 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..e93fd851fa0 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1351,7 +1351,7 @@ Lead to Quotation,Vom Lead zum Angebot, Learn,Lernen, Leave Management,Urlaube verwalten, Leave and Attendance,Urlaub und Anwesenheit, -Leave application {0} already exists against the student {1},Verlassen der Anwendung {0} ist bereits für den Schüler {1} vorhanden, +Leave application {0} already exists against the student {1},Abwesenheitsantrag {0} existiert bereits für den Schüler {1}, Leaves has been granted sucessfully,Urlaub wurde genehmigt, Leaves must be allocated in multiples of 0.5,"Abwesenheiten müssen ein Vielfaches von 0,5 sein", Ledger,Hauptbuch, @@ -5655,10 +5655,10 @@ Guardian Details,Erziehungsberechtigten-Details, Guardians,Erziehungsberechtigte, Sibling Details,Geschwister-Details, Siblings,Geschwister, -Exit,Verlassen, +Exit,Austritt, Date of Leaving,Austrittsdatum, Leaving Certificate Number,Leaving Certificate Nummer, -Reason For Leaving,Grund für das Verlassen, +Reason For Leaving,Grund für den Austritt, Student Admission,Studenten Eintritt, Admission Start Date,Stichtag zum Zulassungsbeginn, Admission End Date,Stichtag für Zulassungsende, From c4c001b6f9fcb9ca0b4f714650c43a987ae0c34b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Aug 2024 11:34:05 +0530 Subject: [PATCH 11/35] fix: typeerror on payment entry (cherry picked from commit 9cdca0d66274d23f947c694c06a8be44d4ada2b5) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 8ec055e28a4..3d5d030a9ee 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -196,7 +196,7 @@ frappe.ui.form.on('Payment Entry', { }, hide_unhide_fields: function(frm) { - var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; + var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company)?.default_currency: ""; frm.toggle_display("source_exchange_rate", (frm.doc.paid_amount && frm.doc.paid_from_account_currency != company_currency)); From 29cfb7cf2509cb0fb030cb48f92343dc1887668d Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Mon, 22 Jul 2024 10:11:37 +0200 Subject: [PATCH 12/35] fix: Sort lists before calling itertools.groupby (cherry picked from commit 45a6ecbd38e60c6403ee268e3b865072fa50c2dd) --- .../opportunity_summary_by_sales_stage.py | 4 +++- .../sales_pipeline_analytics/sales_pipeline_analytics.py | 4 +++- erpnext/selling/page/sales_funnel/sales_funnel.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.py | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py index 5a36c999747..2e8ae7e340e 100644 --- a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py +++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py @@ -108,7 +108,9 @@ class OpportunitySummaryBySalesStage: self.grouped_data = [] grouping_key = lambda o: (o["sales_stage"], o[based_on]) # noqa - for (sales_stage, _based_on), rows in groupby(self.query_result, grouping_key): + for (sales_stage, _based_on), rows in groupby( + sorted(self.query_result, key=grouping_key), key=grouping_key + ): self.grouped_data.append( { "sales_stage": sales_stage, diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py index e5d34231a87..9c19e9f0148 100644 --- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py +++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py @@ -122,7 +122,9 @@ class SalesPipelineAnalytics: self.grouped_data = [] grouping_key = lambda o: (o.get(self.pipeline_by) or "Not Assigned", o[self.period_by]) # noqa - for (pipeline_by, period_by), rows in groupby(self.query_result, grouping_key): + for (pipeline_by, period_by), rows in groupby( + sorted(self.query_result, key=grouping_key), grouping_key + ): self.grouped_data.append( { self.pipeline_by: pipeline_by, diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py index 24bb0d2fe71..73deb8fe66b 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.py +++ b/erpnext/selling/page/sales_funnel/sales_funnel.py @@ -93,7 +93,7 @@ def get_opp_by_lead_source(from_date, to_date, company): summary = {} sales_stages = set() group_key = lambda o: (o["source"], o["sales_stage"]) # noqa - for (source, sales_stage), rows in groupby(cp_opportunities, group_key): + for (source, sales_stage), rows in groupby(sorted(cp_opportunities, key=group_key), group_key): summary.setdefault(source, {})[sales_stage] = sum(r["compound_amount"] for r in rows) sales_stages.add(sales_stage) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 2a5e527f591..aa25d625ffe 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -837,7 +837,8 @@ def create_delivery_note(source_name, target_doc=None): ) ) - for customer, rows in groupby(sales_orders, key=lambda so: so["customer"]): + group_key = lambda so: so["customer"] # noqa + for customer, rows in groupby(sorted(sales_orders, key=group_key), key=group_key): sales_dict[customer] = {row.sales_order for row in rows} if sales_dict: From e44daf73fb9637bc25b0d18ed4f67cffc534ffb2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:20:44 +0530 Subject: [PATCH 13/35] fix: warning message for negative stock (backport #42683) (#42709) * fix: warning message for negative stock (#42683) (cherry picked from commit deccb007c1e6626f2febaf2407bbfc84cdc477bb) # Conflicts: # erpnext/stock/stock_ledger.py * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/stock/stock_ledger.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 2864c00864b..8df785a0c1f 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -4,10 +4,10 @@ import json import frappe -from frappe import _ +from frappe import _, bold from frappe.model.meta import get_field_precision from frappe.query_builder.functions import Sum -from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, now, nowdate +from frappe.utils import cint, cstr, flt, format_date, get_link_to_form, getdate, now, nowdate import erpnext from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty @@ -542,11 +542,31 @@ class update_entries_after: return self.distinct_item_warehouses[key].dependent_voucher_detail_nos + def validate_previous_sle_qty(self, sle): + previous_sle = self.data[sle.warehouse].previous_sle + if previous_sle and previous_sle.get("qty_after_transaction") < 0 and sle.get("actual_qty") > 0: + frappe.msgprint( + _( + "The stock for the item {0} in the {1} warehouse was negative on the {2}. You should create a positive entry {3} before the date {4} and time {5} to post the correct valuation rate. For more details, please read the documentation." + ).format( + bold(sle.item_code), + bold(sle.warehouse), + bold(format_date(previous_sle.posting_date)), + sle.voucher_no, + bold(format_date(previous_sle.posting_date)), + bold(previous_sle.posting_time), + ), + title=_("Warning on Negative Stock"), + indicator="blue", + ) + def process_sle(self, sle): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos # previous sle data for this warehouse self.wh_data = self.data[sle.warehouse] + + self.validate_previous_sle_qty(sle) self.affected_transactions.add((sle.voucher_type, sle.voucher_no)) if (sle.serial_no and not self.via_landed_cost_voucher) or not cint(self.allow_negative_stock): From 365ef6b88ac2d5b6ee951b8119b5e2f52a6fd246 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 8 Aug 2024 10:37:57 +0530 Subject: [PATCH 14/35] fix: update 'Paid Amount' on forex payment request (cherry picked from commit 7b0dfb2a0561b30aaa45427697ac3c5b5b424576) --- .../doctype/payment_request/payment_request.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 0fa2e7835eb..106000a78f3 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -284,6 +284,17 @@ class PaymentRequest(Document): payment_entry.received_amount = amount payment_entry.get("references")[0].allocated_amount = amount + # Update 'Paid Amount' on Forex transactions + if self.currency != ref_doc.company_currency: + if ( + self.payment_request_type == "Outward" + and payment_entry.paid_from_account_currency == ref_doc.company_currency + and payment_entry.paid_from_account_currency != payment_entry.paid_to_account_currency + ): + payment_entry.paid_amount = payment_entry.base_paid_amount = ( + payment_entry.target_exchange_rate * payment_entry.received_amount + ) + for dimension in get_accounting_dimensions(): payment_entry.update({dimension: self.get(dimension)}) From e2897933088ba52b739b618868a24e9c47b9cc18 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Aug 2024 16:10:05 +0530 Subject: [PATCH 15/35] test: make use of test fixture (cherry picked from commit d6d0a1b38d7952d6ca289e9b3320e67aaa1c2f3c) # Conflicts: # erpnext/accounts/doctype/payment_request/test_payment_request.py --- .../payment_request/test_payment_request.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 932060895b0..8ce56740dcf 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -4,6 +4,7 @@ import unittest import frappe +from frappe.tests.utils import FrappeTestCase from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice @@ -32,7 +33,7 @@ payment_method = [ ] -class TestPaymentRequest(unittest.TestCase): +class TestPaymentRequest(FrappeTestCase): def setUp(self): if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"): frappe.get_doc(payment_gateway).insert(ignore_permissions=True) @@ -45,6 +46,31 @@ class TestPaymentRequest(unittest.TestCase): ): frappe.get_doc(method).insert(ignore_permissions=True) +<<<<<<< HEAD +======= + send_email = patch( + "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email", + return_value=None, + ) + self.send_email = send_email.start() + self.addCleanup(send_email.stop) + get_payment_url = patch( + # this also shadows one (1) call to _get_payment_gateway_controller + "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url", + return_value=PAYMENT_URL, + ) + self.get_payment_url = get_payment_url.start() + self.addCleanup(get_payment_url.stop) + _get_payment_gateway_controller = patch( + "erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller", + ) + self._get_payment_gateway_controller = _get_payment_gateway_controller.start() + self.addCleanup(_get_payment_gateway_controller.stop) + + def tearDown(self): + frappe.db.rollback() + +>>>>>>> d6d0a1b38d (test: make use of test fixture) def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR", do_not_save=True) so_inr.disable_rounded_total = 1 From 71416902f6260fb4b0fbffc130c88602e9b8514b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Aug 2024 17:53:43 +0530 Subject: [PATCH 16/35] test: currency conversion on foreign currency account (cherry picked from commit f913c0fde1899fb8e871f1488f77fc8b8ab0218b) --- .../payment_request/test_payment_request.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 8ce56740dcf..059edabb08d 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -9,6 +9,7 @@ from frappe.tests.utils import FrappeTestCase from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.setup.utils import get_exchange_rate @@ -286,3 +287,19 @@ class TestPaymentRequest(FrappeTestCase): # Try to make Payment Request more than SO amount, should give validation pr2.grand_total = 900 self.assertRaises(frappe.ValidationError, pr2.save) + + def test_conversion_on_foreign_currency_accounts(self): + po_doc = create_purchase_order(supplier="_Test Supplier USD", currency="USD", do_not_submit=1) + po_doc.conversion_rate = 80 + po_doc.items[0].qty = 1 + po_doc.items[0].rate = 10 + po_doc.save().submit() + + pr = make_payment_request(dt=po_doc.doctype, dn=po_doc.name, recipient_id="nabin@erpnext.com") + pr = frappe.get_doc(pr).save().submit() + + pe = pr.create_payment_entry() + self.assertEqual(pe.base_paid_amount, 800) + self.assertEqual(pe.paid_amount, 800) + self.assertEqual(pe.base_received_amount, 800) + self.assertEqual(pe.received_amount, 10) From 1844cb60a4ae8abc463fc134bab9b5463eed50c4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 16:14:29 +0530 Subject: [PATCH 17/35] chore: resolve conflict --- .../payment_request/test_payment_request.py | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 059edabb08d..6d15f84d7cf 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -47,31 +47,6 @@ class TestPaymentRequest(FrappeTestCase): ): frappe.get_doc(method).insert(ignore_permissions=True) -<<<<<<< HEAD -======= - send_email = patch( - "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email", - return_value=None, - ) - self.send_email = send_email.start() - self.addCleanup(send_email.stop) - get_payment_url = patch( - # this also shadows one (1) call to _get_payment_gateway_controller - "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url", - return_value=PAYMENT_URL, - ) - self.get_payment_url = get_payment_url.start() - self.addCleanup(get_payment_url.stop) - _get_payment_gateway_controller = patch( - "erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller", - ) - self._get_payment_gateway_controller = _get_payment_gateway_controller.start() - self.addCleanup(_get_payment_gateway_controller.stop) - - def tearDown(self): - frappe.db.rollback() - ->>>>>>> d6d0a1b38d (test: make use of test fixture) def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR", do_not_save=True) so_inr.disable_rounded_total = 1 From a9871e379a4374055466111d8fdd70a78e33cd93 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 10:37:55 +0530 Subject: [PATCH 18/35] refactor: combine vouchers from both ignore (cherry picked from commit 4cc5cd5a71fbc434ca7e95b24b9b7a491840854a) --- erpnext/accounts/report/general_ledger/general_ledger.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 05bbc4a79c9..b7f035b1e80 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -246,7 +246,10 @@ def get_conditions(filters): as_list=True, ) if system_generated_cr_dr_journals: - filters.update({"voucher_no_not_in": [x[0] for x in system_generated_cr_dr_journals]}) + vouchers_to_ignore = (filters.get("voucher_no_not_in") or []) + [ + x[0] for x in system_generated_cr_dr_journals + ] + filters.update({"voucher_no_not_in": vouchers_to_ignore}) if filters.get("voucher_no_not_in"): conditions.append("voucher_no not in %(voucher_no_not_in)s") From 1ebcc33cdd3c3914ad82e9efac915f0472e064d6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 10:41:31 +0530 Subject: [PATCH 19/35] refactor: cr and dr note filter in Statement of Accounts (cherry picked from commit 0cf478c4c26f61b1e23195a27468f69cbb21b7b4) # Conflicts: # erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json # erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py --- .../process_statement_of_accounts.json | 11 ++++ .../process_statement_of_accounts.py | 57 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index bdbb1419784..0c7f8eb2aad 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -16,6 +16,7 @@ "cost_center", "territory", "ignore_exchange_rate_revaluation_journals", + "ignore_cr_dr_notes", "column_break_14", "to_date", "finance_book", @@ -381,10 +382,20 @@ "fieldname": "ignore_exchange_rate_revaluation_journals", "fieldtype": "Check", "label": "Ignore Exchange Rate Revaluation Journals" + }, + { + "default": "0", + "fieldname": "ignore_cr_dr_notes", + "fieldtype": "Check", + "label": "Ignore System Generated Credit / Debit Notes" } ], "links": [], +<<<<<<< HEAD "modified": "2023-12-18 12:20:08.965120", +======= + "modified": "2024-08-13 10:41:18.381165", +>>>>>>> 0cf478c4c2 (refactor: cr and dr note filter in Statement of Accounts) "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index a602e0d7df3..3daee8f430f 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -22,6 +22,63 @@ from erpnext.accounts.report.general_ledger.general_ledger import execute as get class ProcessStatementOfAccounts(Document): +<<<<<<< HEAD +======= + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.accounts.doctype.process_statement_of_accounts_customer.process_statement_of_accounts_customer import ( + ProcessStatementOfAccountsCustomer, + ) + from erpnext.accounts.doctype.psoa_cost_center.psoa_cost_center import PSOACostCenter + from erpnext.accounts.doctype.psoa_project.psoa_project import PSOAProject + + account: DF.Link | None + ageing_based_on: DF.Literal["Due Date", "Posting Date"] + based_on_payment_terms: DF.Check + body: DF.TextEditor | None + cc_to: DF.Link | None + collection_name: DF.DynamicLink | None + company: DF.Link + cost_center: DF.TableMultiSelect[PSOACostCenter] + currency: DF.Link | None + customer_collection: DF.Literal["", "Customer Group", "Territory", "Sales Partner", "Sales Person"] + customers: DF.Table[ProcessStatementOfAccountsCustomer] + enable_auto_email: DF.Check + filter_duration: DF.Int + finance_book: DF.Link | None + frequency: DF.Literal["Weekly", "Monthly", "Quarterly"] + from_date: DF.Date | None + group_by: DF.Literal["", "Group by Voucher", "Group by Voucher (Consolidated)"] + ignore_cr_dr_notes: DF.Check + ignore_exchange_rate_revaluation_journals: DF.Check + include_ageing: DF.Check + include_break: DF.Check + letter_head: DF.Link | None + orientation: DF.Literal["Landscape", "Portrait"] + payment_terms_template: DF.Link | None + pdf_name: DF.Data | None + posting_date: DF.Date | None + primary_mandatory: DF.Check + project: DF.TableMultiSelect[PSOAProject] + report: DF.Literal["General Ledger", "Accounts Receivable"] + sales_partner: DF.Link | None + sales_person: DF.Link | None + sender: DF.Link | None + show_net_values_in_party_account: DF.Check + start_date: DF.Date | None + subject: DF.Data | None + terms_and_conditions: DF.Link | None + territory: DF.Link | None + to_date: DF.Date | None + # end: auto-generated types + +>>>>>>> 0cf478c4c2 (refactor: cr and dr note filter in Statement of Accounts) def validate(self): if not self.subject: self.subject = "Statement Of Accounts for {{ customer.customer_name }}" From cef3573d7945dc9c3fbbbfbb26083e297b1b844a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 10:42:32 +0530 Subject: [PATCH 20/35] refactor: pass filter to General Ledger (cherry picked from commit 90880c8c01642cd40782f51c08d30d0896ac066e) --- .../process_statement_of_accounts.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 3daee8f430f..7584b812053 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -136,6 +136,9 @@ def get_statement_dict(doc, get_statement_dict=False): if doc.ignore_exchange_rate_revaluation_journals: filters.update({"ignore_err": True}) + if doc.ignore_cr_dr_notes: + filters.update({"ignore_cr_dr_notes": True}) + if doc.report == "General Ledger": filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency)) col, res = get_soa(filters) From e421e16fdc1f2e6ca46c36262a27e077d609dd5a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 11:22:52 +0530 Subject: [PATCH 21/35] chore: resolve conflicts --- .../process_statement_of_accounts.json | 4 -- .../process_statement_of_accounts.py | 57 ------------------- 2 files changed, 61 deletions(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index 0c7f8eb2aad..8584cbb31cb 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -391,11 +391,7 @@ } ], "links": [], -<<<<<<< HEAD - "modified": "2023-12-18 12:20:08.965120", -======= "modified": "2024-08-13 10:41:18.381165", ->>>>>>> 0cf478c4c2 (refactor: cr and dr note filter in Statement of Accounts) "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 7584b812053..078ac034eeb 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -22,63 +22,6 @@ from erpnext.accounts.report.general_ledger.general_ledger import execute as get class ProcessStatementOfAccounts(Document): -<<<<<<< HEAD -======= - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - from erpnext.accounts.doctype.process_statement_of_accounts_customer.process_statement_of_accounts_customer import ( - ProcessStatementOfAccountsCustomer, - ) - from erpnext.accounts.doctype.psoa_cost_center.psoa_cost_center import PSOACostCenter - from erpnext.accounts.doctype.psoa_project.psoa_project import PSOAProject - - account: DF.Link | None - ageing_based_on: DF.Literal["Due Date", "Posting Date"] - based_on_payment_terms: DF.Check - body: DF.TextEditor | None - cc_to: DF.Link | None - collection_name: DF.DynamicLink | None - company: DF.Link - cost_center: DF.TableMultiSelect[PSOACostCenter] - currency: DF.Link | None - customer_collection: DF.Literal["", "Customer Group", "Territory", "Sales Partner", "Sales Person"] - customers: DF.Table[ProcessStatementOfAccountsCustomer] - enable_auto_email: DF.Check - filter_duration: DF.Int - finance_book: DF.Link | None - frequency: DF.Literal["Weekly", "Monthly", "Quarterly"] - from_date: DF.Date | None - group_by: DF.Literal["", "Group by Voucher", "Group by Voucher (Consolidated)"] - ignore_cr_dr_notes: DF.Check - ignore_exchange_rate_revaluation_journals: DF.Check - include_ageing: DF.Check - include_break: DF.Check - letter_head: DF.Link | None - orientation: DF.Literal["Landscape", "Portrait"] - payment_terms_template: DF.Link | None - pdf_name: DF.Data | None - posting_date: DF.Date | None - primary_mandatory: DF.Check - project: DF.TableMultiSelect[PSOAProject] - report: DF.Literal["General Ledger", "Accounts Receivable"] - sales_partner: DF.Link | None - sales_person: DF.Link | None - sender: DF.Link | None - show_net_values_in_party_account: DF.Check - start_date: DF.Date | None - subject: DF.Data | None - terms_and_conditions: DF.Link | None - territory: DF.Link | None - to_date: DF.Date | None - # end: auto-generated types - ->>>>>>> 0cf478c4c2 (refactor: cr and dr note filter in Statement of Accounts) def validate(self): if not self.subject: self.subject = "Statement Of Accounts for {{ customer.customer_name }}" From 25096185c91c9188d8e887ed9d00fca7d07470ef Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 12 Aug 2024 17:09:39 +0530 Subject: [PATCH 22/35] fix: text color in sales funnel report based on theme (cherry picked from commit 61bc0925d52291d87ad73b72ffb8b59b6360d0b7) # Conflicts: # erpnext/selling/page/sales_funnel/sales_funnel.js --- erpnext/selling/page/sales_funnel/sales_funnel.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index 954705b5c32..efe38305a8a 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -248,7 +248,11 @@ erpnext.SalesFunnel = class SalesFunnel { context.fill(); // draw text +<<<<<<< HEAD context.fillStyle = ""; +======= + context.fillStyle = getComputedStyle(document.body).getPropertyValue("--text-color"); +>>>>>>> 61bc0925d5 (fix: text color in sales funnel report based on theme) context.textBaseline = "middle"; context.font = "1.1em sans-serif"; context.fillText(__(title), width + 20, y_mid); From b0b5b25a532d59b49fbe4a31c03caa186e8ea188 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 14:38:44 +0530 Subject: [PATCH 23/35] chore: resolve conflict --- erpnext/selling/page/sales_funnel/sales_funnel.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index efe38305a8a..d9a0be9eedd 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -248,11 +248,7 @@ erpnext.SalesFunnel = class SalesFunnel { context.fill(); // draw text -<<<<<<< HEAD - context.fillStyle = ""; -======= context.fillStyle = getComputedStyle(document.body).getPropertyValue("--text-color"); ->>>>>>> 61bc0925d5 (fix: text color in sales funnel report based on theme) context.textBaseline = "middle"; context.font = "1.1em sans-serif"; context.fillText(__(title), width + 20, y_mid); From f35bc432423c80a057638b8d965d142acadc1a61 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 17:37:48 +0530 Subject: [PATCH 24/35] fix: force fetch updates for subcription (cherry picked from commit 1ef890db732caff9fc77716fd3932ae9c4055b4f) # Conflicts: # erpnext/accounts/doctype/subscription/subscription.js --- .../doctype/subscription/subscription.js | 36 +++++++++++++++++++ .../doctype/subscription/subscription.py | 19 ++++++++++ 2 files changed, 55 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 60981f4b1d1..6d11044f614 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -29,6 +29,7 @@ frappe.ui.form.on("Subscription", { }, refresh: function (frm) { +<<<<<<< HEAD if (!frm.is_new()) { if (frm.doc.status !== "Cancelled") { frm.add_custom_button(__("Cancel Subscription"), () => @@ -42,6 +43,34 @@ frappe.ui.form.on("Subscription", { frm.events.renew_this_subscription(frm) ); } +======= + if (frm.is_new()) return; + + if (frm.doc.status !== "Cancelled") { + frm.add_custom_button( + __("Fetch Subscription Updates"), + () => frm.trigger("get_subscription_updates"), + __("Actions") + ); + + frm.add_custom_button( + __("Force Fetch Subscription Updates"), + () => frm.trigger("force_fetch_subscription_updates"), + __("Actions") + ); + + frm.add_custom_button( + __("Cancel Subscription"), + () => frm.trigger("cancel_this_subscription"), + __("Actions") + ); + } else if (frm.doc.status === "Cancelled") { + frm.add_custom_button( + __("Restart Subscription"), + () => frm.trigger("renew_this_subscription"), + __("Actions") + ); +>>>>>>> 1ef890db73 (fix: force fetch updates for subcription) } }, @@ -96,4 +125,11 @@ frappe.ui.form.on("Subscription", { }, }); }, + force_fetch_subscription_updates: function (frm) { + frm.call("force_fetch_subscription_updates").then((r) => { + if (!r.exec) { + frm.reload_doc(); + } + }); + }, }); diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index fea2ae9bf8e..c4e4114857b 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -674,6 +674,25 @@ class Subscription(Document): if invoice: return invoice.precision("grand_total") + @frappe.whitelist() + def force_fetch_subscription_updates(self): + """ + Process Subscription and create Invoices even if current date doesn't lie between current_invoice_start and currenct_invoice_end + It makes use of 'Proces Subscription' to force processing in a specific 'posting_date' + """ + processing_date = None + if self.generate_invoice_at == "Beginning of the current subscription period": + processing_date = self.current_invoice_start + elif self.generate_invoice_at == "End of the current subscription period": + processing_date = self.current_invoice_end + 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() + def get_calendar_months(billing_interval): calendar_months = [] From f0f663e55218cc4c6b0104ad5f1dd242a4c5f276 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 12:34:10 +0530 Subject: [PATCH 25/35] refactor: don't process future subscriptions (cherry picked from commit 3a115774114a1986435739936c195a17a0b65af3) --- erpnext/accounts/doctype/subscription/subscription.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index c4e4114857b..96c97659ad8 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -680,6 +680,12 @@ class Subscription(Document): Process Subscription and create Invoices even if current date doesn't lie between current_invoice_start and currenct_invoice_end It makes use of 'Proces Subscription' to force processing in a specific 'posting_date' """ + + # Don't process future subscriptions + if nowdate() < self.current_invoice_start: + frappe.msgprint(_("Subscription for Future dates cannot be processed.")) + return + processing_date = None if self.generate_invoice_at == "Beginning of the current subscription period": processing_date = self.current_invoice_start From 7752de3a1cc2f7f6f735a6d5b89b8bebf20bef70 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 12:35:16 +0530 Subject: [PATCH 26/35] chore: minor naming change (cherry picked from commit d8b6767697afe6e59b33e62466ffcb8653b23b96) --- erpnext/accounts/doctype/subscription/subscription.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 6d11044f614..48bbc733fe4 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -54,7 +54,7 @@ frappe.ui.form.on("Subscription", { ); frm.add_custom_button( - __("Force Fetch Subscription Updates"), + __("Force-Fetch Subscription Updates"), () => frm.trigger("force_fetch_subscription_updates"), __("Actions") ); From 366f383d1f8e0678f1c33f1814605b941e56d70d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 16:05:06 +0530 Subject: [PATCH 27/35] refactor: test for force-fetch on future subscription (cherry picked from commit fd680380bbb93313a0d0853eaf9144726c44551c) # Conflicts: # erpnext/accounts/doctype/subscription/test_subscription.py --- .../doctype/subscription/test_subscription.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 2f0b87e9ea2..3b32c727731 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -711,4 +711,113 @@ class TestSubscription(FrappeTestCase): pi = frappe.get_doc("Purchase Invoice", subscription.invoices[0].invoice) self.assertEqual(pi.total, 55333.33) +<<<<<<< HEAD subscription.delete() +======= + subscription.process(posting_date="2023-01-22") + self.assertEqual(len(subscription.invoices), 2) + + def test_future_subscription(self): + """Force-Fetch should not process future subscriptions""" + subscription = create_subscription( + start_date=add_months(nowdate(), 1), + submit_invoice=0, + generate_new_invoices_past_due_date=1, + party="_Test Subscription Customer John Doe", + ) + subscription.force_fetch_subscription_updates() + subscription.reload() + self.assertEqual(len(subscription.invoices), 0) + + +def make_plans(): + create_plan(plan_name="_Test Plan Name", cost=900, currency="INR") + create_plan(plan_name="_Test Plan Name 2", cost=1999, currency="INR") + create_plan( + plan_name="_Test Plan Name 3", + cost=1999, + billing_interval="Day", + billing_interval_count=14, + currency="INR", + ) + create_plan( + plan_name="_Test Plan Name 4", + cost=20000, + billing_interval="Month", + billing_interval_count=3, + currency="INR", + ) + create_plan(plan_name="_Test Plan Multicurrency", cost=50, billing_interval="Month", currency="USD") + + +def create_plan(**kwargs): + if not frappe.db.exists("Subscription Plan", kwargs.get("plan_name")): + plan = frappe.new_doc("Subscription Plan") + plan.plan_name = kwargs.get("plan_name") or "_Test Plan Name" + plan.item = kwargs.get("item") or "_Test Non Stock Item" + plan.price_determination = kwargs.get("price_determination") or "Fixed Rate" + plan.cost = kwargs.get("cost") or 1000 + plan.billing_interval = kwargs.get("billing_interval") or "Month" + plan.billing_interval_count = kwargs.get("billing_interval_count") or 1 + plan.currency = kwargs.get("currency") + plan.insert() + + +def create_parties(): + if not frappe.db.exists("Supplier", "_Test Supplier"): + supplier = frappe.new_doc("Supplier") + supplier.supplier_name = "_Test Supplier" + supplier.supplier_group = "All Supplier Groups" + supplier.insert() + + if not frappe.db.exists("Customer", "_Test Subscription Customer"): + customer = frappe.new_doc("Customer") + customer.customer_name = "_Test Subscription Customer" + customer.default_currency = "USD" + customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"}) + customer.insert() + + if not frappe.db.exists("Customer", "_Test Subscription Customer John Doe"): + customer = frappe.new_doc("Customer") + customer.customer_name = "_Test Subscription Customer John Doe" + customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable - _TC"}) + customer.insert() + + +def reset_settings(): + settings = frappe.get_single("Subscription Settings") + settings.grace_period = 0 + settings.cancel_after_grace = 0 + settings.save() + + +def create_subscription(**kwargs): + subscription = frappe.new_doc("Subscription") + subscription.party_type = (kwargs.get("party_type") or "Customer",) + subscription.company = kwargs.get("company") or "_Test Company" + subscription.party = kwargs.get("party") or "_Test Customer" + subscription.trial_period_start = kwargs.get("trial_period_start") + subscription.trial_period_end = kwargs.get("trial_period_end") + subscription.start_date = kwargs.get("start_date") + subscription.generate_invoice_at = kwargs.get("generate_invoice_at") + subscription.additional_discount_percentage = kwargs.get("additional_discount_percentage") + subscription.additional_discount_amount = kwargs.get("additional_discount_amount") + subscription.follow_calendar_months = kwargs.get("follow_calendar_months") + subscription.generate_new_invoices_past_due_date = kwargs.get("generate_new_invoices_past_due_date") + subscription.submit_invoice = kwargs.get("submit_invoice") + subscription.days_until_due = kwargs.get("days_until_due") + subscription.number_of_days = kwargs.get("number_of_days") + + if not kwargs.get("plans"): + subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1}) + else: + for plan in kwargs.get("plans"): + subscription.append("plans", plan) + + if kwargs.get("do_not_save"): + return subscription + + subscription.save() + + return subscription +>>>>>>> fd680380bb (refactor: test for force-fetch on future subscription) From cca5aa8a96ec9a3e03b580711d7e9233db552ff9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 16:28:07 +0530 Subject: [PATCH 28/35] chore: resolve conflict --- .../doctype/subscription/subscription.js | 35 ++++--------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 48bbc733fe4..caaa60b5757 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -29,7 +29,6 @@ frappe.ui.form.on("Subscription", { }, refresh: function (frm) { -<<<<<<< HEAD if (!frm.is_new()) { if (frm.doc.status !== "Cancelled") { frm.add_custom_button(__("Cancel Subscription"), () => @@ -38,39 +37,17 @@ frappe.ui.form.on("Subscription", { frm.add_custom_button(__("Fetch Subscription Updates"), () => frm.events.get_subscription_updates(frm) ); + + frm.add_custom_button( + __("Force-Fetch Subscription Updates"), + () => frm.trigger("force_fetch_subscription_updates"), + __("Actions") + ); } else if (frm.doc.status === "Cancelled") { frm.add_custom_button(__("Restart Subscription"), () => frm.events.renew_this_subscription(frm) ); } -======= - if (frm.is_new()) return; - - if (frm.doc.status !== "Cancelled") { - frm.add_custom_button( - __("Fetch Subscription Updates"), - () => frm.trigger("get_subscription_updates"), - __("Actions") - ); - - frm.add_custom_button( - __("Force-Fetch Subscription Updates"), - () => frm.trigger("force_fetch_subscription_updates"), - __("Actions") - ); - - frm.add_custom_button( - __("Cancel Subscription"), - () => frm.trigger("cancel_this_subscription"), - __("Actions") - ); - } else if (frm.doc.status === "Cancelled") { - frm.add_custom_button( - __("Restart Subscription"), - () => frm.trigger("renew_this_subscription"), - __("Actions") - ); ->>>>>>> 1ef890db73 (fix: force fetch updates for subcription) } }, From 2d0dca943a29acb950af9948e6a0433ee01cd77c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 16:31:50 +0530 Subject: [PATCH 29/35] chore: resolve conflict --- .../doctype/subscription/test_subscription.py | 112 ++---------------- 1 file changed, 9 insertions(+), 103 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 3b32c727731..26dcb2413f4 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -711,113 +711,19 @@ class TestSubscription(FrappeTestCase): pi = frappe.get_doc("Purchase Invoice", subscription.invoices[0].invoice) self.assertEqual(pi.total, 55333.33) -<<<<<<< HEAD subscription.delete() -======= - subscription.process(posting_date="2023-01-22") - self.assertEqual(len(subscription.invoices), 2) def test_future_subscription(self): """Force-Fetch should not process future subscriptions""" - subscription = create_subscription( - start_date=add_months(nowdate(), 1), - submit_invoice=0, - generate_new_invoices_past_due_date=1, - party="_Test Subscription Customer John Doe", - ) + subscription = frappe.new_doc("Subscription") + subscription.party_type = "Customer" + subscription.party = "_Test Customer" + subscription.generate_invoice_at_period_start = 1 + subscription.generate_new_invoices_past_due_date = 1 + subscription.start_date = add_months(nowdate(), 1) + subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1}) + subscription.save() + subscription.force_fetch_subscription_updates() subscription.reload() self.assertEqual(len(subscription.invoices), 0) - - -def make_plans(): - create_plan(plan_name="_Test Plan Name", cost=900, currency="INR") - create_plan(plan_name="_Test Plan Name 2", cost=1999, currency="INR") - create_plan( - plan_name="_Test Plan Name 3", - cost=1999, - billing_interval="Day", - billing_interval_count=14, - currency="INR", - ) - create_plan( - plan_name="_Test Plan Name 4", - cost=20000, - billing_interval="Month", - billing_interval_count=3, - currency="INR", - ) - create_plan(plan_name="_Test Plan Multicurrency", cost=50, billing_interval="Month", currency="USD") - - -def create_plan(**kwargs): - if not frappe.db.exists("Subscription Plan", kwargs.get("plan_name")): - plan = frappe.new_doc("Subscription Plan") - plan.plan_name = kwargs.get("plan_name") or "_Test Plan Name" - plan.item = kwargs.get("item") or "_Test Non Stock Item" - plan.price_determination = kwargs.get("price_determination") or "Fixed Rate" - plan.cost = kwargs.get("cost") or 1000 - plan.billing_interval = kwargs.get("billing_interval") or "Month" - plan.billing_interval_count = kwargs.get("billing_interval_count") or 1 - plan.currency = kwargs.get("currency") - plan.insert() - - -def create_parties(): - if not frappe.db.exists("Supplier", "_Test Supplier"): - supplier = frappe.new_doc("Supplier") - supplier.supplier_name = "_Test Supplier" - supplier.supplier_group = "All Supplier Groups" - supplier.insert() - - if not frappe.db.exists("Customer", "_Test Subscription Customer"): - customer = frappe.new_doc("Customer") - customer.customer_name = "_Test Subscription Customer" - customer.default_currency = "USD" - customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"}) - customer.insert() - - if not frappe.db.exists("Customer", "_Test Subscription Customer John Doe"): - customer = frappe.new_doc("Customer") - customer.customer_name = "_Test Subscription Customer John Doe" - customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable - _TC"}) - customer.insert() - - -def reset_settings(): - settings = frappe.get_single("Subscription Settings") - settings.grace_period = 0 - settings.cancel_after_grace = 0 - settings.save() - - -def create_subscription(**kwargs): - subscription = frappe.new_doc("Subscription") - subscription.party_type = (kwargs.get("party_type") or "Customer",) - subscription.company = kwargs.get("company") or "_Test Company" - subscription.party = kwargs.get("party") or "_Test Customer" - subscription.trial_period_start = kwargs.get("trial_period_start") - subscription.trial_period_end = kwargs.get("trial_period_end") - subscription.start_date = kwargs.get("start_date") - subscription.generate_invoice_at = kwargs.get("generate_invoice_at") - subscription.additional_discount_percentage = kwargs.get("additional_discount_percentage") - subscription.additional_discount_amount = kwargs.get("additional_discount_amount") - subscription.follow_calendar_months = kwargs.get("follow_calendar_months") - subscription.generate_new_invoices_past_due_date = kwargs.get("generate_new_invoices_past_due_date") - subscription.submit_invoice = kwargs.get("submit_invoice") - subscription.days_until_due = kwargs.get("days_until_due") - subscription.number_of_days = kwargs.get("number_of_days") - - if not kwargs.get("plans"): - subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1}) - else: - for plan in kwargs.get("plans"): - subscription.append("plans", plan) - - if kwargs.get("do_not_save"): - return subscription - - subscription.save() - - return subscription ->>>>>>> fd680380bb (refactor: test for force-fetch on future subscription) From 714a432c1c1fc5d61f3480600f6513c146231fa9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 11:29:39 +0530 Subject: [PATCH 30/35] fix: ledger entries for pos return with update outstanding for self (cherry picked from commit 2cd9b28e5bbf376a8d57f61486e37f1fe88d068a) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a254c917189..9dd62d76fef 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1294,6 +1294,10 @@ class SalesInvoice(SellingController): if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount: payment_mode.base_amount -= flt(self.change_amount) + against_voucher = self.name + if self.is_return and self.return_against and not self.update_outstanding_for_self: + against_voucher = self.return_against + if payment_mode.base_amount: # POS, make payment entries gl_entries.append( @@ -1307,7 +1311,7 @@ class SalesInvoice(SellingController): "credit_in_account_currency": payment_mode.base_amount if self.party_account_currency == self.company_currency else payment_mode.amount, - "against_voucher": self.name, + "against_voucher": against_voucher, "against_voucher_type": self.doctype, "cost_center": self.cost_center, }, From 54735469c1ef47eb0eeba3e01faa22f97ad65319 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 13:15:57 +0530 Subject: [PATCH 31/35] fix: patch to fix incorrect against_voucher references in ledger (cherry picked from commit 487d0a55f551a9b92ad662eed9ba94521e97766e) # Conflicts: # erpnext/patches/v14_0/update_pos_return_ledger_entries.py --- .../v14_0/update_pos_return_ledger_entries.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 erpnext/patches/v14_0/update_pos_return_ledger_entries.py diff --git a/erpnext/patches/v14_0/update_pos_return_ledger_entries.py b/erpnext/patches/v14_0/update_pos_return_ledger_entries.py new file mode 100644 index 00000000000..60e867d1bcb --- /dev/null +++ b/erpnext/patches/v14_0/update_pos_return_ledger_entries.py @@ -0,0 +1,61 @@ +import frappe +from frappe import qb + + +def execute(): + sinv = qb.DocType("Sales Invoice") + pos_returns_without_self = ( + qb.from_(sinv) + .select(sinv.name) + .where( + sinv.docstatus.eq(1) + & sinv.is_pos.eq(1) + & sinv.is_return.eq(1) + & sinv.return_against.notnull() + & sinv.update_outstanding_for_self.eq(0) + ) + .run() + ) + if pos_returns_without_self: + pos_returns_without_self = [x[0] for x in pos_returns_without_self] + + gle = qb.DocType("GL Entry") + gl_against_references = ( + qb.from_(gle) + .select(gle.voucher_no, gle.against_voucher) + .where( + gle.voucher_no.isin(pos_returns_without_self) + & gle.against_voucher.notnull() + & gle.against_voucher.eq(gle.voucher_no) + & gle.is_cancelled.eq(0) + ) + .run() + ) + + _vouchers = list(set([x[0] for x in gl_against_references])) + invoice_return_against = ( + qb.from_(sinv) + .select(sinv.name, sinv.return_against) + .where(sinv.name.isin(_vouchers) & sinv.return_against.notnull()) + .orderby(sinv.name) + .run() + ) + + valid_references = set(invoice_return_against) + actual_references = set(gl_against_references) + + invalid_references = actual_references.difference(valid_references) + + if invalid_references: + # Repost Accounting Ledger + pos_for_reposting = ( + qb.from_(sinv) + .select(sinv.company, sinv.name) + .where(sinv.name.isin([x[0] for x in invalid_references])) + .run(as_dict=True) + ) + for x in pos_for_reposting: + ral = frappe.new_doc("Repost Accounting Ledger") + ral.company = x.company + ral.append("vouchers", {"voucher_type": "Sales Invoice", "voucher_no": x.name}) + ral.save().submit() From fa44b0d745125b25802a0e52a4615f509eb84014 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 13:58:04 +0530 Subject: [PATCH 32/35] refactor: update patches.txt (cherry picked from commit 4dc0d3a003720f6bd1f6d87e5a3540d4a03a2fa9) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1f6363d56cb..b5266c56a54 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -357,6 +357,11 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles erpnext.patches.v14_0.update_total_asset_cost_field erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 +<<<<<<< HEAD +======= +erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request +erpnext.patches.v15_0.update_pos_return_ledger_entries +>>>>>>> 4dc0d3a003 (refactor: update patches.txt) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 From aaa6d666669c1ceb544967969339158ca4f84be6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Aug 2024 11:32:26 +0530 Subject: [PATCH 33/35] test: against_voucher for pos_returns without updating for self (cherry picked from commit 3fb08583218fe9890ab2703292a327fea047ee97) --- .../sales_invoice/test_sales_invoice.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dd61a594706..bb51223db16 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -5,6 +5,7 @@ import copy import json import frappe +from frappe import qb from frappe.model.dynamic_links import get_dynamic_link_map from frappe.model.naming import make_autoname from frappe.tests.utils import FrappeTestCase, change_settings @@ -3590,6 +3591,40 @@ class TestSalesInvoice(FrappeTestCase): ] self.assertEqual(expected, actual) + def test_pos_returns_without_update_outstanding_for_self(self): + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return + + pos_profile = make_pos_profile() + pos_profile.payments = [] + pos_profile.append("payments", {"default": 1, "mode_of_payment": "Cash"}) + pos_profile.save() + + pos = create_sales_invoice(qty=10, do_not_save=True) + pos.is_pos = 1 + pos.pos_profile = pos_profile.name + pos.append( + "payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 500} + ) + pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 500}) + pos.save().submit() + + pos_return = make_sales_return(pos.name) + pos_return.update_outstanding_for_self = False + pos_return.save().submit() + + gle = qb.DocType("GL Entry") + res = ( + qb.from_(gle) + .select(gle.against_voucher) + .distinct() + .where( + gle.is_cancelled.eq(0) & gle.voucher_no.eq(pos_return.name) & gle.against_voucher.notnull() + ) + .run(as_list=1) + ) + self.assertEqual(len(res), 1) + self.assertEqual(res[0][0], pos_return.return_against) + def check_gl_entries(doc, voucher_no, expected_gle, posting_date): gl_entries = frappe.db.sql( From 1cddb4ff3998cb323e926f4c128d39c7a3661348 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Aug 2024 11:35:05 +0530 Subject: [PATCH 34/35] refactor: move patch to v14 and update patches.txt (cherry picked from commit da2286802a1216bffe3a7d832340124eb091be4b) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b5266c56a54..b8a08a363cc 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -360,8 +360,12 @@ erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 <<<<<<< HEAD ======= erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request +<<<<<<< HEAD erpnext.patches.v15_0.update_pos_return_ledger_entries >>>>>>> 4dc0d3a003 (refactor: update patches.txt) +======= +erpnext.patches.v14_0.update_pos_return_ledger_entries +>>>>>>> da2286802a (refactor: move patch to v14 and update patches.txt) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 From 8882b85888be1c62a0a05a2a0691dd2d66c81e47 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Aug 2024 12:04:30 +0530 Subject: [PATCH 35/35] chore: resolve conflict --- erpnext/patches.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b8a08a363cc..3e17a07c5c1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -357,15 +357,7 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles erpnext.patches.v14_0.update_total_asset_cost_field erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 -<<<<<<< HEAD -======= -erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request -<<<<<<< HEAD -erpnext.patches.v15_0.update_pos_return_ledger_entries ->>>>>>> 4dc0d3a003 (refactor: update patches.txt) -======= erpnext.patches.v14_0.update_pos_return_ledger_entries ->>>>>>> da2286802a (refactor: move patch to v14 and update patches.txt) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20