From cc0a4785590b86852dbbcc89b1fc1d953b44b406 Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Thu, 19 Dec 2024 12:27:06 +0530 Subject: [PATCH 01/19] fix: refactor query in get_total_allocated_amount in bank_transaction (cherry picked from commit 6b847cdb623322a64096337dcbe86cdbdc60bb6b) # Conflicts: # erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py # erpnext/accounts/doctype/bank_transaction/bank_transaction.py --- .../bank_reconciliation_tool.py | 9 ++++ .../bank_transaction/bank_transaction.py | 41 +++++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 2987bac677c..2a65792cc15 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -455,13 +455,22 @@ def get_linked_payments( def subtract_allocations(gl_account, vouchers): "Look up & subtract any existing Bank Transaction allocations" copied = [] + + voucher_docs = [(voucher.get("doctype"), voucher.get("name")) for voucher in vouchers] + voucher_allocated_amounts = get_total_allocated_amount(voucher_docs) + for voucher in vouchers: +<<<<<<< HEAD rows = get_total_allocated_amount(voucher[1], voucher[2]) amount = None for row in rows: if row["gl_account"] == gl_account: amount = row["total"] break +======= + rows = voucher_allocated_amounts.get((voucher.get("doctype"), voucher.get("name"))) + filtered_row = list(filter(lambda row: row.get("gl_account") == gl_account, rows)) +>>>>>>> 6b847cdb62 (fix: refactor query in get_total_allocated_amount in bank_transaction) if amount: l = list(voucher) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 89565e9908b..b67478a7f3d 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -93,10 +93,20 @@ class BankTransaction(StatusUpdater): - clear means: set the latest transaction date as clearance date """ remaining_amount = self.unallocated_amount +<<<<<<< HEAD +======= + to_remove = [] + payment_entry_docs = [(pe.payment_document, pe.payment_entry) for pe in self.payment_entries] + pe_bt_allocations = get_total_allocated_amount(payment_entry_docs) + +>>>>>>> 6b847cdb62 (fix: refactor query in get_total_allocated_amount in bank_transaction) for payment_entry in self.payment_entries: if payment_entry.allocated_amount == 0.0: unallocated_amount, should_clear, latest_transaction = get_clearance_details( - self, payment_entry + self, + payment_entry, + pe_bt_allocations.get((payment_entry.payment_document, payment_entry.payment_entry)) + or [], ) if 0.0 == unallocated_amount: @@ -182,7 +192,7 @@ def get_doctypes_for_bank_reconciliation(): return frappe.get_hooks("bank_reconciliation_doctypes") -def get_clearance_details(transaction, payment_entry): +def get_clearance_details(transaction, payment_entry, bt_allocations): """ There should only be one bank gle for a voucher. Could be none for a Bank Transaction. @@ -191,7 +201,6 @@ def get_clearance_details(transaction, payment_entry): """ gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry) - bt_allocations = get_total_allocated_amount(payment_entry.payment_document, payment_entry.payment_entry) unallocated_amount = min( transaction.unallocated_amount, @@ -247,44 +256,52 @@ def get_related_bank_gl_entries(doctype, docname): return result -def get_total_allocated_amount(doctype, docname): +def get_total_allocated_amount(docs): """ Gets the sum of allocations for a voucher on each bank GL account along with the latest bank transaction name & date NOTE: query may also include just saved vouchers/payments but with zero allocated_amount """ + if not docs: + return {} + # nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql result = frappe.db.sql( """ - SELECT total, latest_name, latest_date, gl_account FROM ( + SELECT total, latest_name, latest_date, gl_account, payment_document, payment_entry FROM ( SELECT ROW_NUMBER() OVER w AS rownum, - SUM(btp.allocated_amount) OVER(PARTITION BY ba.account) AS total, + SUM(btp.allocated_amount) OVER(PARTITION BY ba.account, btp.payment_document, btp.payment_entry) AS total, FIRST_VALUE(bt.name) OVER w AS latest_name, FIRST_VALUE(bt.date) OVER w AS latest_date, - ba.account AS gl_account + ba.account AS gl_account, + btp.payment_document, + btp.payment_entry FROM `tabBank Transaction Payments` btp LEFT JOIN `tabBank Transaction` bt ON bt.name=btp.parent LEFT JOIN `tabBank Account` ba ON ba.name=bt.bank_account WHERE - btp.payment_document = %(doctype)s - AND btp.payment_entry = %(docname)s + (btp.payment_document, btp.payment_entry) IN %(docs)s AND bt.docstatus = 1 - WINDOW w AS (PARTITION BY ba.account ORDER BY bt.date desc) + WINDOW w AS (PARTITION BY ba.account, btp.payment_document, btp.payment_entry ORDER BY bt.date DESC) ) temp WHERE rownum = 1 """, - dict(doctype=doctype, docname=docname), + dict(docs=docs), as_dict=True, ) + + payment_allocation_details = {} for row in result: # Why is this *sometimes* a byte string? if isinstance(row["latest_name"], bytes): row["latest_name"] = row["latest_name"].decode() row["latest_date"] = frappe.utils.getdate(row["latest_date"]) - return result + payment_allocation_details.setdefault((row["payment_document"], row["payment_entry"]), []).append(row) + + return payment_allocation_details def get_paid_amount(payment_entry, currency, gl_bank_account): From 498abf7c83e77ad4bde13b81c09a8a86e5c55f8d Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Thu, 19 Dec 2024 12:55:11 +0530 Subject: [PATCH 02/19] fix: failing tests fixed (cherry picked from commit 2ce07865d36d74ef058d613dfe4714ddfd6b58e6) # Conflicts: # erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py --- .../bank_reconciliation_tool/bank_reconciliation_tool.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 2a65792cc15..55ea4414f30 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -460,6 +460,7 @@ def subtract_allocations(gl_account, vouchers): voucher_allocated_amounts = get_total_allocated_amount(voucher_docs) for voucher in vouchers: +<<<<<<< HEAD <<<<<<< HEAD rows = get_total_allocated_amount(voucher[1], voucher[2]) amount = None @@ -469,6 +470,9 @@ def subtract_allocations(gl_account, vouchers): break ======= rows = voucher_allocated_amounts.get((voucher.get("doctype"), voucher.get("name"))) +======= + rows = voucher_allocated_amounts.get((voucher.get("doctype"), voucher.get("name"))) or [] +>>>>>>> 2ce07865d3 (fix: failing tests fixed) filtered_row = list(filter(lambda row: row.get("gl_account") == gl_account, rows)) >>>>>>> 6b847cdb62 (fix: refactor query in get_total_allocated_amount in bank_transaction) From 78e0799a06585afe18554873c42fc0a83404ac59 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2024 11:25:59 +0530 Subject: [PATCH 03/19] fix: Show order tax amount in customer currency on the portal (backport #44915) (#44922) * fix: Show order tax amount in customer currency on the portal (#44915) (cherry picked from commit b998933ef032f1fb2be78c0e9e4a3ba374db6b57) # Conflicts: # erpnext/templates/includes/order/order_taxes.html * fix: resolved conflict --------- Co-authored-by: Nabin Hait --- erpnext/templates/includes/order/order_taxes.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html index 0060ab39cc9..42f46ac8d12 100644 --- a/erpnext/templates/includes/order/order_taxes.html +++ b/erpnext/templates/includes/order/order_taxes.html @@ -12,14 +12,14 @@ {% endif %} {% for d in doc.taxes %} - {% if d.base_tax_amount %} + {% if d.tax_amount %}
{{ d.description }}
- {{ doc.get_formatted("net_total") }} + {{ d.get_formatted("tax_amount") }}
From cdd3763a8b8df186c947a9dc41004f38df7c5b52 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Tue, 24 Dec 2024 16:38:51 +0530 Subject: [PATCH 04/19] fix: clear payment schedule in purchase invoice for is_paid (cherry picked from commit e1fc239f3d6e737d1acf022db274fedf4a396af0) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js # erpnext/controllers/accounts_controller.py --- .../doctype/purchase_invoice/purchase_invoice.js | 7 +++++++ erpnext/controllers/accounts_controller.py | 14 +++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index dc879cfc1b4..ff82055d19b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -366,8 +366,15 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. hide_fields(this.frm.doc); if(cint(this.frm.doc.is_paid)) { this.frm.set_value("allocate_advances_automatically", 0); +<<<<<<< HEAD if(!this.frm.doc.company) { this.frm.set_value("is_paid", 0) +======= + this.frm.set_value("payment_terms_template", ""); + this.frm.set_value("payment_schedule", []); + if (!this.frm.doc.company) { + this.frm.set_value("is_paid", 0); +>>>>>>> e1fc239f3d (fix: clear payment schedule in purchase invoice for is_paid) frappe.msgprint(__("Please specify Company to proceed")); } } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 6a5914ddbe0..58e9b86e24b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -415,10 +415,14 @@ class AccountsController(TransactionBase): ) def validate_invoice_documents_schedule(self): - if self.is_return: + if ( + self.is_return + or (self.doctype == "Purchase Invoice" and self.is_paid) + or (self.doctype == "Sales Invoice" and self.is_pos) + or self.get("is_opening") == "Yes" + ): self.payment_terms_template = "" self.payment_schedule = [] - return self.validate_payment_schedule_dates() self.set_due_date() @@ -2177,9 +2181,6 @@ class AccountsController(TransactionBase): dates = [] li = [] - if self.doctype == "Sales Invoice" and self.is_pos: - return - for d in self.get("payment_schedule"): if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date): frappe.throw( @@ -2196,9 +2197,12 @@ class AccountsController(TransactionBase): frappe.throw(_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates)) def validate_payment_schedule_amount(self): +<<<<<<< HEAD if self.doctype == "Sales Invoice" and self.is_pos: return +======= +>>>>>>> e1fc239f3d (fix: clear payment schedule in purchase invoice for is_paid) party_account_currency = self.get("party_account_currency") if not party_account_currency: party_type, party = self.get_party() From d5f30202b431621177fb3e671220c513fe5c59b4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 27 Dec 2024 12:48:25 +0530 Subject: [PATCH 05/19] refactor: early return is always better validate_advance_entries() has a heavy IO bound operation. Early return on unwanted cases is always better. (cherry picked from commit 0589fa7f3efcc826d856be71c20c7ad684f1c822) # Conflicts: # erpnext/controllers/accounts_controller.py --- erpnext/controllers/accounts_controller.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 58e9b86e24b..27b656ef51e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -424,6 +424,9 @@ class AccountsController(TransactionBase): self.payment_terms_template = "" self.payment_schedule = [] + if self.is_return: + return + self.validate_payment_schedule_dates() self.set_due_date() self.set_payment_schedule() @@ -2181,6 +2184,9 @@ class AccountsController(TransactionBase): dates = [] li = [] + if self.doctype == "Sales Invoice" and self.is_pos: + return + for d in self.get("payment_schedule"): if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date): frappe.throw( @@ -2197,12 +2203,18 @@ class AccountsController(TransactionBase): frappe.throw(_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates)) def validate_payment_schedule_amount(self): +<<<<<<< HEAD <<<<<<< HEAD if self.doctype == "Sales Invoice" and self.is_pos: return ======= >>>>>>> e1fc239f3d (fix: clear payment schedule in purchase invoice for is_paid) +======= + if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes": + return + +>>>>>>> 0589fa7f3e (refactor: early return is always better) party_account_currency = self.get("party_account_currency") if not party_account_currency: party_type, party = self.get_party() From ce7dc5ecb32f41c1d60716b96cba3b7ccd96dfa9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 27 Dec 2024 13:49:09 +0530 Subject: [PATCH 06/19] chore: resolve conflicts --- .../doctype/purchase_invoice/purchase_invoice.js | 9 ++------- erpnext/controllers/accounts_controller.py | 9 --------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index ff82055d19b..4a8c8ad82a7 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -366,15 +366,10 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. hide_fields(this.frm.doc); if(cint(this.frm.doc.is_paid)) { this.frm.set_value("allocate_advances_automatically", 0); -<<<<<<< HEAD - if(!this.frm.doc.company) { - this.frm.set_value("is_paid", 0) -======= this.frm.set_value("payment_terms_template", ""); this.frm.set_value("payment_schedule", []); - if (!this.frm.doc.company) { - this.frm.set_value("is_paid", 0); ->>>>>>> e1fc239f3d (fix: clear payment schedule in purchase invoice for is_paid) + if(!this.frm.doc.company) { + this.frm.set_value("is_paid", 0) frappe.msgprint(__("Please specify Company to proceed")); } } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 27b656ef51e..15748868510 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2203,18 +2203,9 @@ class AccountsController(TransactionBase): frappe.throw(_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates)) def validate_payment_schedule_amount(self): -<<<<<<< HEAD -<<<<<<< HEAD - if self.doctype == "Sales Invoice" and self.is_pos: - return - -======= ->>>>>>> e1fc239f3d (fix: clear payment schedule in purchase invoice for is_paid) -======= if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes": return ->>>>>>> 0589fa7f3e (refactor: early return is always better) party_account_currency = self.get("party_account_currency") if not party_account_currency: party_type, party = self.get_party() From 1da47129192094e3a77f2792e88347c73d11818d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:54:14 +0530 Subject: [PATCH 07/19] fix: update payment amount for partial pos return (backport #44065) (#44961) fix: update payment amount for partial pos return (cherry picked from commit 53ef6336b62fab0dedc19b74dc1e5c293bf933c3) Co-authored-by: Kavin <78342682+kavin0411@users.noreply.github.com> --- erpnext/public/js/controllers/taxes_and_totals.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 82e1d518362..8ca72d55c23 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -828,13 +828,13 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { ); } - if(!this.frm.doc.is_return){ - this.frm.doc.payments.find(payment => { - if (payment.default) { - payment.amount = total_amount_to_pay; - } - }); - } + this.frm.doc.payments.find(payment => { + if (payment.default) { + payment.amount = total_amount_to_pay; + } else { + payment.amount = 0 + } + }); this.frm.refresh_fields(); } From d348d587e268f1a7208cfc1dc5483f3e30cf2e0c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 30 Dec 2024 11:01:55 +0530 Subject: [PATCH 08/19] refactor(test): make manufacturing test idempotent (cherry picked from commit f3be246df36e3e5295db3e3794c87c8dafe8c0fc) --- erpnext/manufacturing/report/test_reports.py | 102 ++++++++++--------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/erpnext/manufacturing/report/test_reports.py b/erpnext/manufacturing/report/test_reports.py index 3e20f310ff9..f9c3154a63b 100644 --- a/erpnext/manufacturing/report/test_reports.py +++ b/erpnext/manufacturing/report/test_reports.py @@ -4,61 +4,67 @@ import frappe from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report -DEFAULT_FILTERS = { - "company": "_Test Company", - "from_date": "2010-01-01", - "to_date": "2030-01-01", - "warehouse": "_Test Warehouse - _TC", -} - - -REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [ - ("BOM Explorer", {"bom": frappe.get_last_doc("BOM").name}), - ("BOM Operations Time", {}), - ("BOM Stock Calculated", {"bom": frappe.get_last_doc("BOM").name, "qty_to_make": 2}), - ("BOM Stock Report", {"bom": frappe.get_last_doc("BOM").name, "qty_to_produce": 2}), - ("Cost of Poor Quality Report", {"item": "_Test Item", "serial_no": "00"}), - ("Downtime Analysis", {}), - ( - "Exponential Smoothing Forecasting", - { - "based_on_document": "Sales Order", - "based_on_field": "Qty", - "no_of_years": 3, - "periodicity": "Yearly", - "smoothing_constant": 0.3, - }, - ), - ("Job Card Summary", {"fiscal_year": "2021-2022"}), - ("Production Analytics", {"range": "Monthly"}), - ("Quality Inspection Summary", {}), - ("Process Loss Report", {}), - ("Work Order Stock Report", {}), - ("Work Order Summary", {"fiscal_year": "2021-2022", "age": 0}), -] - - -if frappe.db.a_row_exists("Production Plan"): - REPORT_FILTER_TEST_CASES.append( - ("Production Plan Summary", {"production_plan": frappe.get_last_doc("Production Plan").name}) - ) - -OPTIONAL_FILTERS = { - "warehouse": "_Test Warehouse - _TC", - "item": "_Test Item", - "item_group": "_Test Item Group", -} - class TestManufacturingReports(unittest.TestCase): + def setUp(self): + self.setup_default_filters() + + def tearDown(self): + frappe.db.rollback() + + def setup_default_filters(self): + self.last_bom = frappe.get_last_doc("BOM").name + self.DEFAULT_FILTERS = { + "company": "_Test Company", + "from_date": "2010-01-01", + "to_date": "2030-01-01", + "warehouse": "_Test Warehouse - _TC", + } + + self.REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [ + ("BOM Explorer", {"bom": self.last_bom}), + ("BOM Operations Time", {}), + ("BOM Stock Calculated", {"bom": self.last_bom, "qty_to_make": 2}), + ("BOM Stock Report", {"bom": self.last_bom, "qty_to_produce": 2}), + ("Cost of Poor Quality Report", {"item": "_Test Item", "serial_no": "00"}), + ("Downtime Analysis", {}), + ( + "Exponential Smoothing Forecasting", + { + "based_on_document": "Sales Order", + "based_on_field": "Qty", + "no_of_years": 3, + "periodicity": "Yearly", + "smoothing_constant": 0.3, + }, + ), + ("Job Card Summary", {"fiscal_year": "2021-2022"}), + ("Production Analytics", {"range": "Monthly"}), + ("Quality Inspection Summary", {}), + ("Process Loss Report", {}), + ("Work Order Stock Report", {}), + ("Work Order Summary", {"fiscal_year": "2021-2022", "age": 0}), + ] + + if frappe.db.a_row_exists("Production Plan"): + self.REPORT_FILTER_TEST_CASES.append( + ("Production Plan Summary", {"production_plan": frappe.get_last_doc("Production Plan").name}) + ) + + self.OPTIONAL_FILTERS = { + "warehouse": "_Test Warehouse - _TC", + "item": "_Test Item", + "item_group": "_Test Item Group", + } + def test_execute_all_manufacturing_reports(self): """Test that all script report in manufacturing modules are executable with supported filters""" - for report, filter in REPORT_FILTER_TEST_CASES: + for report, filter in self.REPORT_FILTER_TEST_CASES: with self.subTest(report=report): execute_script_report( report_name=report, module="Manufacturing", filters=filter, - default_filters=DEFAULT_FILTERS, - optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None, + default_filters=self.DEFAULT_FILTERS, + optional_filters=self.OPTIONAL_FILTERS if filter.get("_optional") else None, ) From 443a0b81f9abad0bc7425bfe647d3feb14541a77 Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Mon, 30 Dec 2024 12:27:14 +0530 Subject: [PATCH 09/19] fix: resolve conflicts --- .../bank_reconciliation_tool.py | 13 ++----------- .../doctype/bank_transaction/bank_transaction.py | 4 ---- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 55ea4414f30..316fc8564c1 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -456,25 +456,16 @@ def subtract_allocations(gl_account, vouchers): "Look up & subtract any existing Bank Transaction allocations" copied = [] - voucher_docs = [(voucher.get("doctype"), voucher.get("name")) for voucher in vouchers] + voucher_docs = [(voucher[1], voucher[2]) for voucher in vouchers] voucher_allocated_amounts = get_total_allocated_amount(voucher_docs) for voucher in vouchers: -<<<<<<< HEAD -<<<<<<< HEAD - rows = get_total_allocated_amount(voucher[1], voucher[2]) + rows = voucher_allocated_amounts.get((voucher[1], voucher[2])) or [] amount = None for row in rows: if row["gl_account"] == gl_account: amount = row["total"] break -======= - rows = voucher_allocated_amounts.get((voucher.get("doctype"), voucher.get("name"))) -======= - rows = voucher_allocated_amounts.get((voucher.get("doctype"), voucher.get("name"))) or [] ->>>>>>> 2ce07865d3 (fix: failing tests fixed) - filtered_row = list(filter(lambda row: row.get("gl_account") == gl_account, rows)) ->>>>>>> 6b847cdb62 (fix: refactor query in get_total_allocated_amount in bank_transaction) if amount: l = list(voucher) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index b67478a7f3d..6c1beb16d5d 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -93,13 +93,9 @@ class BankTransaction(StatusUpdater): - clear means: set the latest transaction date as clearance date """ remaining_amount = self.unallocated_amount -<<<<<<< HEAD -======= - to_remove = [] payment_entry_docs = [(pe.payment_document, pe.payment_entry) for pe in self.payment_entries] pe_bt_allocations = get_total_allocated_amount(payment_entry_docs) ->>>>>>> 6b847cdb62 (fix: refactor query in get_total_allocated_amount in bank_transaction) for payment_entry in self.payment_entries: if payment_entry.allocated_amount == 0.0: unallocated_amount, should_clear, latest_transaction = get_clearance_details( From 56d06861d003ffeb926b71a0ccd4a41313b34892 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 26 Dec 2024 17:18:39 +0530 Subject: [PATCH 10/19] fix: get item tax template based on posting date (cherry picked from commit 976e35d5472c1c8cae2b3f3a28dd4487e8ac577e) --- erpnext/stock/get_item_details.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index c078a426c42..79bfc480d6a 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -640,7 +640,10 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): if tax.valid_from or tax.maximum_net_rate: # In purchase Invoice first preference will be given to supplier invoice date # if supplier date is not present then posting date - validation_date = args.get("bill_date") or args.get("transaction_date") + + validation_date = ( + args.get("bill_date") or args.get("posting_date") or args.get("transaction_date") + ) if getdate(tax.valid_from) <= getdate(validation_date) and is_within_valid_range(args, tax): taxes_with_validity.append(tax) From a0063b31c29ba93aed910ad88b7381a614b50ede Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:57:08 +0530 Subject: [PATCH 11/19] fix: pos payment using non-default mode of payment (backport #44920) (#44970) fix: pos payment using non-default mode of payment (#44920) * fix: pos payment using non-default mode of payment (#41108) * fix: included css syntax * refactor: created a function to sanitize the class name * refactor: reusing method to sanitize class name * refactor: function rename (cherry picked from commit 98cbb7e900237d6985d97e266e7486a55dea9cc8) Co-authored-by: Diptanil Saha --- .../selling/page/point_of_sale/pos_payment.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 7e16280eb37..85609e1a8ed 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -235,7 +235,7 @@ erpnext.PointOfSale.Payment = class { frappe.ui.form.on("Sales Invoice Payment", "amount", (frm, cdt, cdn) => { // for setting correct amount after loyalty points are redeemed const default_mop = locals[cdt][cdn]; - const mode = default_mop.mode_of_payment.replace(/ +/g, "_").toLowerCase(); + const mode = this.sanitize_mode_of_payment(default_mop.mode_of_payment); if (this[`${mode}_control`] && this[`${mode}_control`].get_value() != default_mop.amount) { this[`${mode}_control`].set_value(default_mop.amount); } @@ -383,7 +383,7 @@ erpnext.PointOfSale.Payment = class { this.$payment_modes.html( `${payments .map((p, i) => { - const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); + const mode = this.sanitize_mode_of_payment(p.mode_of_payment); const payment_type = p.type; const margin = i % 2 === 0 ? "pr-2" : "pl-2"; const amount = p.amount > 0 ? format_currency(p.amount, currency) : ""; @@ -402,7 +402,7 @@ erpnext.PointOfSale.Payment = class { ); payments.forEach((p) => { - const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); + const mode = this.sanitize_mode_of_payment(p.mode_of_payment); const me = this; this[`${mode}_control`] = frappe.ui.form.make_control({ df: { @@ -437,7 +437,7 @@ erpnext.PointOfSale.Payment = class { const doc = this.events.get_frm().doc; const payments = doc.payments; payments.forEach((p) => { - const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); + const mode = this.sanitize_mode_of_payment(p.mode_of_payment); if (p.default) { setTimeout(() => { this.$payment_modes.find(`.${mode}.mode-of-payment-control`).parent().click(); @@ -607,4 +607,12 @@ erpnext.PointOfSale.Payment = class { toggle_component(show) { show ? this.$component.css("display", "flex") : this.$component.css("display", "none"); } + + sanitize_mode_of_payment(mode_of_payment) { + return mode_of_payment + .replace(/ +/g, "_") + .replace(/[^\p{L}\p{N}_-]/gu, "") + .replace(/^[^_a-zA-Z\p{L}]+/u, "") + .toLowerCase(); + } }; From 0f687f1db7d7a9bb3047efb7bb1b68b4e3f2e169 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 12:52:36 +0530 Subject: [PATCH 12/19] fix: load customer default price list in pos during item selection (backport #44991) (#44992) fix: load customer default price list in pos during item selection (#44991) fix: load customer default price list in pos (cherry picked from commit d1ae0d784e8998d9146e4377e340301be615197f) Co-authored-by: Diptanil Saha --- erpnext/selling/page/point_of_sale/pos_controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 86aa8dd0961..35622a77e2c 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -284,6 +284,7 @@ erpnext.PointOfSale.Controller = class { edit_cart: () => this.payment.edit_cart(), customer_details_updated: (details) => { + this.item_selector.load_items_data(); this.customer_details = details; // will add/remove LP payment method this.payment.render_loyalty_points_payment_mode(); From cf980a13047994cf8425ad5e5357ecf27fb08be5 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 24 Dec 2024 15:15:30 +0530 Subject: [PATCH 13/19] fix: update item_tax_rate in backend (cherry picked from commit de54c0b41f6c3367f39e74a5726d757bcedbc79f) # Conflicts: # erpnext/controllers/taxes_and_totals.py --- .../doctype/sales_invoice/test_sales_invoice.py | 14 ++++++++++++++ erpnext/controllers/taxes_and_totals.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6e4c6002016..a981927a1f4 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -36,6 +36,7 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import ( from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( create_stock_reconciliation, ) +from erpnext.stock.get_item_details import get_item_tax_map from erpnext.stock.utils import get_incoming_rate, get_stock_balance @@ -2817,13 +2818,26 @@ class TestSalesInvoice(FrappeTestCase): item.save() sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True) + item_tax_map = get_item_tax_map( + doc=sales_invoice, + tax_template=sales_invoice.items[0].item_tax_template, + ) + self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC") + self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map) # Apply discount sales_invoice.apply_discount_on = "Net Total" sales_invoice.discount_amount = 300 sales_invoice.save() + + item_tax_map = get_item_tax_map( + doc=sales_invoice, + tax_template=sales_invoice.items[0].item_tax_template, + ) + self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") + self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map) @change_settings("Selling Settings", {"enable_discount_accounting": 1}) def test_sales_invoice_with_discount_accounting_enabled(self): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index f7bf5634878..ef2c2077c80 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -18,7 +18,12 @@ from erpnext.controllers.accounts_controller import ( validate_inclusive_tax, validate_taxes_and_charges, ) +<<<<<<< HEAD from erpnext.stock.get_item_details import _get_item_tax_template +======= +from erpnext.deprecation_dumpster import deprecated +from erpnext.stock.get_item_details import ItemDetailsCtx, _get_item_tax_template, get_item_tax_map +>>>>>>> de54c0b41f (fix: update item_tax_rate in backend) from erpnext.utilities.regional import temporary_flag @@ -67,6 +72,7 @@ class calculate_taxes_and_totals: self.validate_conversion_rate() self.calculate_item_values() self.validate_item_tax_template() + self.update_item_tax_map() self.initialize_taxes() self.determine_exclusive_rate() self.calculate_net_total() @@ -130,6 +136,14 @@ class calculate_taxes_and_totals: ) ) + def update_item_tax_map(self): + for item in self.doc.items: + item.item_tax_rate = get_item_tax_map( + doc=self.doc, + tax_template=item.item_tax_template, + as_json=True, + ) + def validate_conversion_rate(self): # validate conversion rate company_currency = erpnext.get_company_currency(self.doc.company) From 43a40b1e66575c1214ec4c1bf49545d700fdf54c Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 27 Dec 2024 14:21:00 +0530 Subject: [PATCH 14/19] fix: set paid amount in party currency in bank reco payment entry (cherry picked from commit 70b10772869d104d92af4cd2f9ef618e3a9e6c6e) --- .../bank_reconciliation_tool.py | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 316fc8564c1..a122e303c68 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -12,6 +12,7 @@ from frappe.utils import cint, flt from erpnext import get_default_cost_center from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount +from erpnext.accounts.party import get_party_account from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import ( get_amounts_not_reflected_in_system, get_entries, @@ -284,54 +285,56 @@ def create_payment_entry_bts( bank_transaction = frappe.db.get_values( "Bank Transaction", bank_transaction_name, - fieldname=["name", "unallocated_amount", "deposit", "bank_account"], + fieldname=["name", "unallocated_amount", "deposit", "bank_account", "currency"], as_dict=True, )[0] - paid_amount = bank_transaction.unallocated_amount + payment_type = "Receive" if bank_transaction.deposit > 0.0 else "Pay" - company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account") - company = frappe.get_value("Account", company_account, "company") - payment_entry_dict = { - "company": company, - "payment_type": payment_type, - "reference_no": reference_number, - "reference_date": reference_date, - "party_type": party_type, - "party": party, - "posting_date": posting_date, - "paid_amount": paid_amount, - "received_amount": paid_amount, - } - payment_entry = frappe.new_doc("Payment Entry") + bank_account = frappe.get_cached_value("Bank Account", bank_transaction.bank_account, "account") + company = frappe.get_cached_value("Account", bank_account, "company") + party_account = get_party_account(party_type, party, company) - payment_entry.update(payment_entry_dict) + bank_currency = bank_transaction.currency + party_currency = frappe.get_cached_value("Account", party_account, "account_currency") - if mode_of_payment: - payment_entry.mode_of_payment = mode_of_payment - if project: - payment_entry.project = project - if cost_center: - payment_entry.cost_center = cost_center - if payment_type == "Receive": - payment_entry.paid_to = company_account - else: - payment_entry.paid_from = company_account + exc_rate = get_exchange_rate(bank_currency, party_currency, posting_date) - payment_entry.validate() + amt_in_bank_acc_currency = bank_transaction.unallocated_amount + amount_in_party_currency = bank_transaction.unallocated_amount * exc_rate + + pe = frappe.new_doc("Payment Entry") + pe.payment_type = payment_type + pe.company = company + pe.reference_no = reference_number + pe.reference_date = reference_date + pe.party_type = party_type + pe.party = party + pe.posting_date = posting_date + pe.paid_from = party_account if payment_type == "Receive" else bank_account + pe.paid_to = party_account if payment_type == "Pay" else bank_account + pe.paid_from_account_currency = party_currency if payment_type == "Receive" else bank_currency + pe.paid_to_account_currency = party_currency if payment_type == "Pay" else bank_currency + pe.paid_amount = amount_in_party_currency if payment_type == "Receive" else amt_in_bank_acc_currency + pe.received_amount = amount_in_party_currency if payment_type == "Pay" else amt_in_bank_acc_currency + pe.mode_of_payment = mode_of_payment + pe.project = project + pe.cost_center = cost_center + + pe.validate() if allow_edit: - return payment_entry + return pe - payment_entry.insert() + pe.insert() + pe.submit() - payment_entry.submit() vouchers = json.dumps( [ { "payment_doctype": "Payment Entry", - "payment_name": payment_entry.name, - "amount": paid_amount, + "payment_name": pe.name, + "amount": amt_in_bank_acc_currency, } ] ) From bcd3b368211e8acd1fd1a402801f0e32f9cda34c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 31 Dec 2024 15:55:03 +0530 Subject: [PATCH 15/19] chore: resolve conflict --- erpnext/controllers/taxes_and_totals.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index ef2c2077c80..95a6f773403 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -18,12 +18,7 @@ from erpnext.controllers.accounts_controller import ( validate_inclusive_tax, validate_taxes_and_charges, ) -<<<<<<< HEAD -from erpnext.stock.get_item_details import _get_item_tax_template -======= -from erpnext.deprecation_dumpster import deprecated -from erpnext.stock.get_item_details import ItemDetailsCtx, _get_item_tax_template, get_item_tax_map ->>>>>>> de54c0b41f (fix: update item_tax_rate in backend) +from erpnext.stock.get_item_details import _get_item_tax_template, get_item_tax_map from erpnext.utilities.regional import temporary_flag From fd2a1a0ba0b707f66c61881dcedc6e9694c4531d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 31 Dec 2024 16:48:13 +0530 Subject: [PATCH 16/19] refactor: use correct method parameters --- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 8 ++++---- erpnext/controllers/taxes_and_totals.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a981927a1f4..b7546a9ce33 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2819,8 +2819,8 @@ class TestSalesInvoice(FrappeTestCase): sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True) item_tax_map = get_item_tax_map( - doc=sales_invoice, - tax_template=sales_invoice.items[0].item_tax_template, + company=sales_invoice.company, + item_tax_template=sales_invoice.items[0].item_tax_template, ) self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC") @@ -2832,8 +2832,8 @@ class TestSalesInvoice(FrappeTestCase): sales_invoice.save() item_tax_map = get_item_tax_map( - doc=sales_invoice, - tax_template=sales_invoice.items[0].item_tax_template, + company=sales_invoice.company, + item_tax_template=sales_invoice.items[0].item_tax_template, ) self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 95a6f773403..5b6a7b1506b 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -134,8 +134,8 @@ class calculate_taxes_and_totals: def update_item_tax_map(self): for item in self.doc.items: item.item_tax_rate = get_item_tax_map( - doc=self.doc, - tax_template=item.item_tax_template, + company=self.doc.get("company"), + item_tax_template=item.item_tax_template, as_json=True, ) From e2b6615e72bd4b1477616873c338fff18a44e6e8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 09:32:51 +0530 Subject: [PATCH 17/19] fix: duplicate validate for closing stock balance (backport #45015) (#45021) fix: duplicate validate for closing stock balance (#45015) (cherry picked from commit 8d650e56ba9777c2f0df324a15215cac51641719) Co-authored-by: rohitwaghchaure --- .../doctype/closing_stock_balance/closing_stock_balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py index 70aa57f4c12..eda768b1c56 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py @@ -44,7 +44,7 @@ class ClosingStockBalance(Document): & ( (table.from_date.between(self.from_date, self.to_date)) | (table.to_date.between(self.from_date, self.to_date)) - | ((table.from_date >= self.from_date) & (table.to_date >= self.to_date)) + | ((self.from_date >= table.from_date) & (table.from_date >= self.to_date)) ) ) ) From cca507f0bdb6c856547f4b25e52bdd12f9415324 Mon Sep 17 00:00:00 2001 From: DHINESH00 <18csa09@karpagamtech.ac.in> Date: Mon, 30 Dec 2024 22:47:37 +0530 Subject: [PATCH 18/19] fix: apply apply_pricing_rule date change (cherry picked from commit 2cbab9b87590f9d49cbb5bdacde4113d87609389) --- erpnext/public/js/controllers/transaction.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 9f8aa84f836..5d282779e75 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -713,6 +713,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } validate() { + this.apply_pricing_rule() this.calculate_taxes_and_totals(false); } @@ -841,6 +842,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } transaction_date() { + this.apply_pricing_rule() if (this.frm.doc.transaction_date) { this.frm.transaction_date = this.frm.doc.transaction_date; frappe.ui.form.trigger(this.frm.doc.doctype, "currency"); @@ -849,6 +851,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe posting_date() { var me = this; + me.apply_pricing_rule() if (this.frm.doc.posting_date) { this.frm.posting_date = this.frm.doc.posting_date; From 02a0bf35eacd7e67ab5124a8261cd65d5ec7fe6b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 1 Jan 2025 12:06:09 +0530 Subject: [PATCH 19/19] chore: remove tests for 'Tax Detail' report --- .../report/tax_detail/test_tax_detail.json | 840 ------------------ .../report/tax_detail/test_tax_detail.py | 220 ----- erpnext/accounts/test/test_reports.py | 4 - 3 files changed, 1064 deletions(-) delete mode 100644 erpnext/accounts/report/tax_detail/test_tax_detail.json delete mode 100644 erpnext/accounts/report/tax_detail/test_tax_detail.py diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.json b/erpnext/accounts/report/tax_detail/test_tax_detail.json deleted file mode 100644 index e4903167cba..00000000000 --- a/erpnext/accounts/report/tax_detail/test_tax_detail.json +++ /dev/null @@ -1,840 +0,0 @@ -[ - { - "account_manager": null, - "accounts": [], - "companies": [], - "credit_limits": [], - "customer_details": null, - "customer_group": "All Customer Groups", - "customer_name": "_Test Customer", - "customer_pos_id": null, - "customer_primary_address": null, - "customer_primary_contact": null, - "customer_type": "Company", - "default_bank_account": null, - "default_commission_rate": 0.0, - "default_currency": null, - "default_price_list": null, - "default_sales_partner": null, - "disabled": 0, - "dn_required": 0, - "docstatus": 0, - "doctype": "Customer", - "email_id": null, - "gender": null, - "image": null, - "industry": null, - "is_frozen": 0, - "is_internal_customer": 0, - "language": "en", - "lead_name": null, - "loyalty_program": null, - "loyalty_program_tier": null, - "market_segment": null, - "mobile_no": null, - "modified": "2021-02-15 05:18:03.624724", - "name": "_Test Customer", - "naming_series": "CUST-.YYYY.-", - "pan": null, - "parent": null, - "parentfield": null, - "parenttype": null, - "payment_terms": null, - "primary_address": null, - "represents_company": "", - "sales_team": [], - "salutation": null, - "so_required": 0, - "tax_category": null, - "tax_id": null, - "tax_withholding_category": null, - "territory": "All Territories", - "website": null - },{ - "accounts": [], - "allow_purchase_invoice_creation_without_purchase_order": 0, - "allow_purchase_invoice_creation_without_purchase_receipt": 0, - "companies": [], - "country": "United Kingdom", - "default_bank_account": null, - "default_currency": null, - "default_price_list": null, - "disabled": 0, - "docstatus": 0, - "doctype": "Supplier", - "hold_type": "", - "image": null, - "is_frozen": 0, - "is_internal_supplier": 0, - "is_transporter": 0, - "language": "en", - "modified": "2021-03-31 16:47:10.109316", - "name": "_Test Supplier", - "naming_series": "SUP-.YYYY.-", - "on_hold": 0, - "pan": null, - "parent": null, - "parentfield": null, - "parenttype": null, - "payment_terms": null, - "prevent_pos": 0, - "prevent_rfqs": 0, - "release_date": null, - "represents_company": null, - "supplier_details": null, - "supplier_group": "Raw Material", - "supplier_name": "_Test Supplier", - "supplier_type": "Company", - "tax_category": null, - "tax_id": null, - "tax_withholding_category": null, - "warn_pos": 0, - "warn_rfqs": 0, - "website": null - },{ - "account_currency": "GBP", - "account_name": "Debtors", - "account_number": "", - "account_type": "Receivable", - "balance_must_be": "", - "company": "_T", - "disabled": 0, - "docstatus": 0, - "doctype": "Account", - "freeze_account": "No", - "include_in_gross": 0, - "inter_company_account": 0, - "is_group": 0, - "lft": 58, - "modified": "2021-03-26 04:44:19.955468", - "name": "Debtors - _T", - "old_parent": null, - "parent": null, - "parent_account": "Application of Funds (Assets) - _T", - "parentfield": null, - "parenttype": null, - "report_type": "Balance Sheet", - "rgt": 59, - "root_type": "Asset", - "tax_rate": 0.0 - },{ - "account_currency": "GBP", - "account_name": "Sales", - "account_number": "", - "account_type": "Income Account", - "balance_must_be": "", - "company": "_T", - "disabled": 0, - "docstatus": 0, - "doctype": "Account", - "freeze_account": "No", - "include_in_gross": 0, - "inter_company_account": 0, - "is_group": 0, - "lft": 291, - "modified": "2021-03-26 04:50:21.697703", - "name": "Sales - _T", - "old_parent": null, - "parent": null, - "parent_account": "Income - _T", - "parentfield": null, - "parenttype": null, - "report_type": "Profit and Loss", - "rgt": 292, - "root_type": "Income", - "tax_rate": 0.0 - },{ - "account_currency": "GBP", - "account_name": "VAT on Sales", - "account_number": "", - "account_type": "Tax", - "balance_must_be": "", - "company": "_T", - "disabled": 0, - "docstatus": 0, - "doctype": "Account", - "freeze_account": "No", - "include_in_gross": 0, - "inter_company_account": 0, - "is_group": 0, - "lft": 317, - "modified": "2021-03-26 04:50:21.697703", - "name": "VAT on Sales - _T", - "old_parent": null, - "parent": null, - "parent_account": "Source of Funds (Liabilities) - _T", - "parentfield": null, - "parenttype": null, - "report_type": "Balance Sheet", - "rgt": 318, - "root_type": "Liability", - "tax_rate": 0.0 - },{ - "account_currency": "GBP", - "account_name": "Cost of Goods Sold", - "account_number": "", - "account_type": "Cost of Goods Sold", - "balance_must_be": "", - "company": "_T", - "disabled": 0, - "docstatus": 0, - "doctype": "Account", - "freeze_account": "No", - "include_in_gross": 0, - "inter_company_account": 0, - "is_group": 0, - "lft": 171, - "modified": "2021-03-26 04:44:19.994857", - "name": "Cost of Goods Sold - _T", - "old_parent": null, - "parent": null, - "parent_account": "Expenses - _T", - "parentfield": null, - "parenttype": null, - "report_type": "Profit and Loss", - "rgt": 172, - "root_type": "Expense", - "tax_rate": 0.0 - },{ - "account_currency": "GBP", - "account_name": "VAT on Purchases", - "account_number": "", - "account_type": "Tax", - "balance_must_be": "", - "company": "_T", - "disabled": 0, - "docstatus": 0, - "doctype": "Account", - "freeze_account": "No", - "include_in_gross": 0, - "inter_company_account": 0, - "is_group": 0, - "lft": 80, - "modified": "2021-03-26 04:44:19.961983", - "name": "VAT on Purchases - _T", - "old_parent": null, - "parent": null, - "parent_account": "Application of Funds (Assets) - _T", - "parentfield": null, - "parenttype": null, - "report_type": "Balance Sheet", - "rgt": 81, - "root_type": "Asset", - "tax_rate": 0.0 - },{ - "account_currency": "GBP", - "account_name": "Creditors", - "account_number": "", - "account_type": "Payable", - "balance_must_be": "", - "company": "_T", - "disabled": 0, - "docstatus": 0, - "doctype": "Account", - "freeze_account": "No", - "include_in_gross": 0, - "inter_company_account": 0, - "is_group": 0, - "lft": 302, - "modified": "2021-03-26 04:50:21.697703", - "name": "Creditors - _T", - "old_parent": null, - "parent": null, - "parent_account": "Source of Funds (Liabilities) - _T", - "parentfield": null, - "parenttype": null, - "report_type": "Balance Sheet", - "rgt": 303, - "root_type": "Liability", - "tax_rate": 0.0 - },{ - "additional_discount_percentage": 0.0, - "address_display": null, - "adjust_advance_taxes": 0, - "advances": [], - "against_expense_account": "Cost of Goods Sold - _T", - "allocate_advances_automatically": 0, - "amended_from": null, - "apply_discount_on": "Grand Total", - "apply_tds": 0, - "auto_repeat": null, - "base_discount_amount": 0.0, - "base_grand_total": 511.68, - "base_in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.", - "base_net_total": 426.4, - "base_paid_amount": 0.0, - "base_rounded_total": 511.68, - "base_rounding_adjustment": 0.0, - "base_taxes_and_charges_added": 85.28, - "base_taxes_and_charges_deducted": 0.0, - "base_total": 426.4, - "base_total_taxes_and_charges": 85.28, - "base_write_off_amount": 0.0, - "bill_date": null, - "bill_no": null, - "billing_address": null, - "billing_address_display": null, - "buying_price_list": "Standard Buying", - "cash_bank_account": null, - "clearance_date": null, - "company": "_T", - "contact_display": null, - "contact_email": null, - "contact_mobile": null, - "contact_person": null, - "conversion_rate": 1.0, - "cost_center": null, - "credit_to": "Creditors - _T", - "currency": "GBP", - "disable_rounded_total": 0, - "discount_amount": 0.0, - "docstatus": 0, - "doctype": "Purchase Invoice", - "due_date": null, - "from_date": null, - "grand_total": 511.68, - "group_same_items": 0, - "hold_comment": null, - "ignore_pricing_rule": 0, - "in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.", - "inter_company_invoice_reference": null, - "is_internal_supplier": 0, - "is_opening": "No", - "is_paid": 0, - "is_return": 0, - "is_subcontracted": 0, - "items": [ - { - "allow_zero_valuation_rate": 0, - "amount": 426.4, - "asset_category": null, - "asset_location": null, - "base_amount": 426.4, - "base_net_amount": 426.4, - "base_net_rate": 5.33, - "base_price_list_rate": 5.33, - "base_rate": 5.33, - "base_rate_with_margin": 0.0, - "batch_no": null, - "bom": null, - "brand": null, - "conversion_factor": 0.0, - "cost_center": "Main - _T", - "deferred_expense_account": null, - "description": "

Fluid to make widgets

", - "discount_amount": 0.0, - "discount_percentage": 0.0, - "enable_deferred_expense": 0, - "expense_account": "Cost of Goods Sold - _T", - "from_warehouse": null, - "image": null, - "include_exploded_items": 0, - "is_fixed_asset": 0, - "is_free_item": 0, - "item_code": null, - "item_group": null, - "item_name": "Widget Fluid 1Litre", - "item_tax_amount": 0.0, - "item_tax_rate": "{\"VAT on Purchases - _T\": 20.0}", - "item_tax_template": null, - "landed_cost_voucher_amount": 0.0, - "manufacturer": null, - "manufacturer_part_no": null, - "margin_rate_or_amount": 0.0, - "margin_type": "", - "net_amount": 426.4, - "net_rate": 5.33, - "page_break": 0, - "parent": null, - "parentfield": "items", - "parenttype": "Purchase Invoice", - "po_detail": null, - "pr_detail": null, - "price_list_rate": 5.33, - "pricing_rules": null, - "project": null, - "purchase_invoice_item": null, - "purchase_order": null, - "purchase_receipt": null, - "qty": 80.0, - "quality_inspection": null, - "rate": 5.33, - "rate_with_margin": 0.0, - "received_qty": 0.0, - "rejected_qty": 0.0, - "rejected_serial_no": null, - "rejected_warehouse": null, - "rm_supp_cost": 0.0, - "sales_invoice_item": null, - "serial_no": null, - "service_end_date": null, - "service_start_date": null, - "service_stop_date": null, - "stock_qty": 0.0, - "stock_uom": "Nos", - "stock_uom_rate": 0.0, - "total_weight": 0.0, - "uom": "Nos", - "valuation_rate": 0.0, - "warehouse": null, - "weight_per_unit": 0.0, - "weight_uom": null - } - ], - "language": "en", - "letter_head": null, - "mode_of_payment": null, - "modified": "2021-04-03 03:33:09.180453", - "name": null, - "naming_series": "ACC-PINV-.YYYY.-", - "net_total": 426.4, - "on_hold": 0, - "other_charges_calculation": "
\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t
ItemTaxable AmountVAT on Purchases
Widget Fluid 1Litre\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 426.40\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 85.28\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n
", - "outstanding_amount": 511.68, - "paid_amount": 0.0, - "parent": null, - "parentfield": null, - "parenttype": null, - "party_account_currency": "GBP", - "payment_schedule": [], - "payment_terms_template": null, - "plc_conversion_rate": 1.0, - "posting_date": null, - "posting_time": "16:59:56.789522", - "price_list_currency": "GBP", - "pricing_rules": [], - "project": null, - "rejected_warehouse": null, - "release_date": null, - "remarks": "No Remarks", - "represents_company": null, - "return_against": null, - "rounded_total": 511.68, - "rounding_adjustment": 0.0, - "scan_barcode": null, - "select_print_heading": null, - "set_from_warehouse": null, - "set_posting_time": 0, - "set_warehouse": null, - "shipping_address": null, - "shipping_address_display": "", - "shipping_rule": null, - "status": "Unpaid", - "supplied_items": [], - "supplier": "_Test Supplier", - "supplier_address": null, - "supplier_name": "_Test Supplier", - "supplier_warehouse": "Stores - _T", - "tax_category": null, - "tax_id": null, - "tax_withholding_category": null, - "taxes": [ - { - "account_head": "VAT on Purchases - _T", - "add_deduct_tax": "Add", - "base_tax_amount": 85.28, - "base_tax_amount_after_discount_amount": 85.28, - "base_total": 511.68, - "category": "Total", - "charge_type": "On Net Total", - "cost_center": "Main - _T", - "description": "VAT on Purchases", - "included_in_print_rate": 0, - "item_wise_tax_detail": "{\"Widget Fluid 1Litre\":[20.0,85.28]}", - "parent": null, - "parentfield": "taxes", - "parenttype": "Purchase Invoice", - "rate": 0.0, - "row_id": null, - "tax_amount": 85.28, - "tax_amount_after_discount_amount": 85.28, - "total": 511.68 - } - ], - "taxes_and_charges": null, - "taxes_and_charges_added": 85.28, - "taxes_and_charges_deducted": 0.0, - "tc_name": null, - "terms": null, - "title": "_Purchase Invoice", - "to_date": null, - "total": 426.4, - "total_advance": 0.0, - "total_net_weight": 0.0, - "total_qty": 80.0, - "total_taxes_and_charges": 85.28, - "unrealized_profit_loss_account": null, - "update_stock": 0, - "write_off_account": null, - "write_off_amount": 0.0, - "write_off_cost_center": null - },{ - "account_for_change_amount": null, - "additional_discount_percentage": 0.0, - "address_display": null, - "advances": [], - "against_income_account": "Sales - _T", - "allocate_advances_automatically": 0, - "amended_from": null, - "apply_discount_on": "Grand Total", - "auto_repeat": null, - "base_change_amount": 0.0, - "base_discount_amount": 0.0, - "base_grand_total": 868.25, - "base_in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.", - "base_net_total": 825.0, - "base_paid_amount": 0.0, - "base_rounded_total": 868.25, - "base_rounding_adjustment": 0.0, - "base_total": 825.0, - "base_total_taxes_and_charges": 43.25, - "base_write_off_amount": 0.0, - "c_form_applicable": "No", - "c_form_no": null, - "campaign": null, - "cash_bank_account": null, - "change_amount": 0.0, - "commission_rate": 0.0, - "company": "_T", - "company_address": null, - "company_address_display": null, - "company_tax_id": null, - "contact_display": null, - "contact_email": null, - "contact_mobile": null, - "contact_person": null, - "conversion_rate": 1.0, - "cost_center": null, - "currency": "GBP", - "customer": "_Test Customer", - "customer_address": null, - "customer_group": "All Customer Groups", - "customer_name": "_Test Customer", - "debit_to": "Debtors - _T", - "discount_amount": 0.0, - "docstatus": 0, - "doctype": "Sales Invoice", - "due_date": null, - "from_date": null, - "grand_total": 868.25, - "group_same_items": 0, - "ignore_pricing_rule": 0, - "in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.", - "inter_company_invoice_reference": null, - "is_consolidated": 0, - "is_discounted": 0, - "is_internal_customer": 0, - "is_opening": "No", - "is_pos": 0, - "is_return": 0, - "items": [ - { - "actual_batch_qty": 0.0, - "actual_qty": 0.0, - "allow_zero_valuation_rate": 0, - "amount": 200.0, - "asset": null, - "barcode": null, - "base_amount": 200.0, - "base_net_amount": 200.0, - "base_net_rate": 50.0, - "base_price_list_rate": 0.0, - "base_rate": 50.0, - "base_rate_with_margin": 0.0, - "batch_no": null, - "brand": null, - "conversion_factor": 1.0, - "cost_center": "Main - _T", - "customer_item_code": null, - "deferred_revenue_account": null, - "delivered_by_supplier": 0, - "delivered_qty": 0.0, - "delivery_note": null, - "description": "

Used

", - "discount_amount": 0.0, - "discount_percentage": 0.0, - "dn_detail": null, - "enable_deferred_revenue": 0, - "expense_account": null, - "finance_book": null, - "image": null, - "income_account": "Sales - _T", - "incoming_rate": 0.0, - "is_fixed_asset": 0, - "is_free_item": 0, - "item_code": null, - "item_group": null, - "item_name": "Dunlop tyres", - "item_tax_rate": "{\"VAT on Sales - _T\": 20.0}", - "item_tax_template": null, - "margin_rate_or_amount": 0.0, - "margin_type": "", - "net_amount": 200.0, - "net_rate": 50.0, - "page_break": 0, - "parent": null, - "parentfield": "items", - "parenttype": "Sales Invoice", - "price_list_rate": 0.0, - "pricing_rules": null, - "project": null, - "qty": 4.0, - "quality_inspection": null, - "rate": 50.0, - "rate_with_margin": 0.0, - "sales_invoice_item": null, - "sales_order": null, - "serial_no": null, - "service_end_date": null, - "service_start_date": null, - "service_stop_date": null, - "so_detail": null, - "stock_qty": 4.0, - "stock_uom": "Nos", - "stock_uom_rate": 50.0, - "target_warehouse": null, - "total_weight": 0.0, - "uom": "Nos", - "warehouse": null, - "weight_per_unit": 0.0, - "weight_uom": null - }, - { - "actual_batch_qty": 0.0, - "actual_qty": 0.0, - "allow_zero_valuation_rate": 0, - "amount": 65.0, - "asset": null, - "barcode": null, - "base_amount": 65.0, - "base_net_amount": 65.0, - "base_net_rate": 65.0, - "base_price_list_rate": 0.0, - "base_rate": 65.0, - "base_rate_with_margin": 0.0, - "batch_no": null, - "brand": null, - "conversion_factor": 1.0, - "cost_center": "Main - _T", - "customer_item_code": null, - "deferred_revenue_account": null, - "delivered_by_supplier": 0, - "delivered_qty": 0.0, - "delivery_note": null, - "description": "

Used

", - "discount_amount": 0.0, - "discount_percentage": 0.0, - "dn_detail": null, - "enable_deferred_revenue": 0, - "expense_account": null, - "finance_book": null, - "image": null, - "income_account": "Sales - _T", - "incoming_rate": 0.0, - "is_fixed_asset": 0, - "is_free_item": 0, - "item_code": "", - "item_group": null, - "item_name": "Continental tyres", - "item_tax_rate": "{\"VAT on Sales - _T\": 5.0}", - "item_tax_template": null, - "margin_rate_or_amount": 0.0, - "margin_type": "", - "net_amount": 65.0, - "net_rate": 65.0, - "page_break": 0, - "parent": null, - "parentfield": "items", - "parenttype": "Sales Invoice", - "price_list_rate": 0.0, - "pricing_rules": null, - "project": null, - "qty": 1.0, - "quality_inspection": null, - "rate": 65.0, - "rate_with_margin": 0.0, - "sales_invoice_item": null, - "sales_order": null, - "serial_no": null, - "service_end_date": null, - "service_start_date": null, - "service_stop_date": null, - "so_detail": null, - "stock_qty": 1.0, - "stock_uom": null, - "stock_uom_rate": 65.0, - "target_warehouse": null, - "total_weight": 0.0, - "uom": "Nos", - "warehouse": null, - "weight_per_unit": 0.0, - "weight_uom": null - }, - { - "actual_batch_qty": 0.0, - "actual_qty": 0.0, - "allow_zero_valuation_rate": 0, - "amount": 560.0, - "asset": null, - "barcode": null, - "base_amount": 560.0, - "base_net_amount": 560.0, - "base_net_rate": 70.0, - "base_price_list_rate": 0.0, - "base_rate": 70.0, - "base_rate_with_margin": 0.0, - "batch_no": null, - "brand": null, - "conversion_factor": 1.0, - "cost_center": "Main - _T", - "customer_item_code": null, - "deferred_revenue_account": null, - "delivered_by_supplier": 0, - "delivered_qty": 0.0, - "delivery_note": null, - "description": "

New

", - "discount_amount": 0.0, - "discount_percentage": 0.0, - "dn_detail": null, - "enable_deferred_revenue": 0, - "expense_account": null, - "finance_book": null, - "image": null, - "income_account": "Sales - _T", - "incoming_rate": 0.0, - "is_fixed_asset": 0, - "is_free_item": 0, - "item_code": null, - "item_group": null, - "item_name": "Toyo tyres", - "item_tax_rate": "{\"VAT on Sales - _T\": 0.0}", - "item_tax_template": null, - "margin_rate_or_amount": 0.0, - "margin_type": "", - "net_amount": 560.0, - "net_rate": 70.0, - "page_break": 0, - "parent": null, - "parentfield": "items", - "parenttype": "Sales Invoice", - "price_list_rate": 0.0, - "pricing_rules": null, - "project": null, - "qty": 8.0, - "quality_inspection": null, - "rate": 70.0, - "rate_with_margin": 0.0, - "sales_invoice_item": null, - "sales_order": null, - "serial_no": null, - "service_end_date": null, - "service_start_date": null, - "service_stop_date": null, - "so_detail": null, - "stock_qty": 8.0, - "stock_uom": null, - "stock_uom_rate": 70.0, - "target_warehouse": null, - "total_weight": 0.0, - "uom": "Nos", - "warehouse": null, - "weight_per_unit": 0.0, - "weight_uom": null - } - ], - "language": "en", - "letter_head": null, - "loyalty_amount": 0.0, - "loyalty_points": 0, - "loyalty_program": null, - "loyalty_redemption_account": null, - "loyalty_redemption_cost_center": null, - "modified": "2021-02-16 05:18:59.755144", - "name": null, - "naming_series": "ACC-SINV-.YYYY.-", - "net_total": 825.0, - "other_charges_calculation": "
\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t
ItemTaxable AmountVAT on Sales
Dunlop tyres\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 200.00\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 40.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
Continental tyres\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 65.00\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(5.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 3.25\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
Toyo tyres\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 560.00\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(0.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 0.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n
", - "outstanding_amount": 868.25, - "packed_items": [], - "paid_amount": 0.0, - "parent": null, - "parentfield": null, - "parenttype": null, - "party_account_currency": "GBP", - "payment_schedule": [], - "payment_terms_template": null, - "payments": [], - "plc_conversion_rate": 1.0, - "po_date": null, - "po_no": "", - "pos_profile": null, - "posting_date": null, - "posting_time": "5:19:02.994077", - "price_list_currency": "GBP", - "pricing_rules": [], - "project": null, - "redeem_loyalty_points": 0, - "remarks": "No Remarks", - "represents_company": "", - "return_against": null, - "rounded_total": 868.25, - "rounding_adjustment": 0.0, - "sales_partner": null, - "sales_team": [], - "scan_barcode": null, - "select_print_heading": null, - "selling_price_list": "Standard Selling", - "set_posting_time": 0, - "set_target_warehouse": null, - "set_warehouse": null, - "shipping_address": null, - "shipping_address_name": "", - "shipping_rule": null, - "source": null, - "status": "Overdue", - "tax_category": "", - "tax_id": null, - "taxes": [ - { - "account_head": "VAT on Sales - _T", - "base_tax_amount": 43.25, - "base_tax_amount_after_discount_amount": 43.25, - "base_total": 868.25, - "charge_type": "On Net Total", - "cost_center": "Main - _T", - "description": "VAT on Sales", - "included_in_print_rate": 0, - "item_wise_tax_detail": "{\"Dunlop tyres\":[20.0,40.0],\"Continental tyres\":[5.0,3.25],\"Toyo tyres\":[0.0,0.0]}", - "parent": null, - "parentfield": "taxes", - "parenttype": "Sales Invoice", - "rate": 0.0, - "row_id": null, - "tax_amount": 43.25, - "tax_amount_after_discount_amount": 43.25, - "total": 868.25 - } - ], - "taxes_and_charges": null, - "tc_name": null, - "terms": null, - "territory": "All Territories", - "timesheets": [], - "title": "_Sales Invoice", - "to_date": null, - "total": 825.0, - "total_advance": 0.0, - "total_billing_amount": 0.0, - "total_commission": 0.0, - "total_net_weight": 0.0, - "total_qty": 13.0, - "total_taxes_and_charges": 43.25, - "unrealized_profit_loss_account": null, - "update_billed_amount_in_sales_order": 0, - "update_stock": 0, - "write_off_account": null, - "write_off_amount": 0.0, - "write_off_cost_center": null, - "write_off_outstanding_amount_automatically": 0 - } -] diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.py b/erpnext/accounts/report/tax_detail/test_tax_detail.py deleted file mode 100644 index 55ae32a2c6a..00000000000 --- a/erpnext/accounts/report/tax_detail/test_tax_detail.py +++ /dev/null @@ -1,220 +0,0 @@ -import datetime -import json -import os -import unittest - -import frappe -from frappe.utils import ( - add_to_date, - get_first_day, - get_last_day, - get_year_ending, - get_year_start, - getdate, -) - -from .tax_detail import filter_match, save_custom_report - - -class TestTaxDetail(unittest.TestCase): - def load_testdocs(self): - from erpnext.accounts.utils import FiscalYearError, get_fiscal_year - - datapath, _ = os.path.splitext(os.path.realpath(__file__)) - with open(datapath + ".json") as fp: - docs = json.load(fp) - - now = getdate() - self.from_date = get_first_day(now) - self.to_date = get_last_day(now) - - try: - get_fiscal_year(now, company="_T") - except FiscalYearError: - docs = [ - { - "companies": [ - { - "company": "_T", - "parent": "_Test Fiscal", - "parentfield": "companies", - "parenttype": "Fiscal Year", - } - ], - "doctype": "Fiscal Year", - "year": "_Test Fiscal", - "year_end_date": get_year_ending(now), - "year_start_date": get_year_start(now), - }, - *docs, - ] - - docs = [ - { - "abbr": "_T", - "company_name": "_T", - "country": "United Kingdom", - "default_currency": "GBP", - "doctype": "Company", - "name": "_T", - }, - *docs, - ] - - for doc in docs: - try: - db_doc = frappe.get_doc(doc) - if "Invoice" in db_doc.doctype: - db_doc.due_date = add_to_date(now, days=1) - db_doc.insert() - # Create GL Entries: - db_doc.submit() - else: - db_doc.insert(ignore_if_duplicate=True) - except frappe.exceptions.DuplicateEntryError: - pass - - def load_defcols(self): - self.company = frappe.get_doc("Company", "_T") - custom_report = frappe.get_doc("Report", "Tax Detail") - self.default_columns, _ = custom_report.run_query_report( - filters={ - "from_date": "2021-03-01", - "to_date": "2021-03-31", - "company": self.company.name, - "mode": "run", - "report_name": "Tax Detail", - }, - user=frappe.session.user, - ) - - def rm_testdocs(self): - "Remove the Company and all data" - from erpnext.setup.doctype.company.company import create_transaction_deletion_request - - create_transaction_deletion_request(self.company.name) - - def test_report(self): - self.load_testdocs() - self.load_defcols() - report_name = save_custom_report( - "Tax Detail", - "_Test Tax Detail", - json.dumps( - { - "columns": self.default_columns, - "sections": { - "Box1": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Sales"}}}, - "Box2": {"Filter0": {"type": "filter", "filters": {"4": "Acquisition"}}}, - "Box3": {"Box1": {"type": "section"}, "Box2": {"type": "section"}}, - "Box4": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Purchases"}}}, - "Box5": {"Box3": {"type": "section"}, "Box4": {"type": "section"}}, - "Box6": {"Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales"}}}, - "Box7": {"Filter0": {"type": "filter", "filters": {"2": "Expense", "3": "!=Tax"}}}, - "Box8": { - "Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales", "12": "EU"}} - }, - "Box9": { - "Filter0": { - "type": "filter", - "filters": {"2": "Expense", "3": "!=Tax", "12": "EU"}, - } - }, - }, - "show_detail": 1, - } - ), - ) - data = frappe.desk.query_report.run( - report_name, - filters={ - "from_date": self.from_date, - "to_date": self.to_date, - "company": self.company.name, - "mode": "run", - "report_name": report_name, - }, - user=frappe.session.user, - ) - - self.assertListEqual(data.get("columns"), self.default_columns) - expected = ( - ("Box1", 43.25), - ("Box2", 0.0), - ("Box3", 43.25), - ("Box4", -85.28), - ("Box5", -42.03), - ("Box6", 825.0), - ("Box7", -426.40), - ("Box8", 0.0), - ("Box9", 0.0), - ) - exrow = iter(expected) - for row in data.get("result"): - if row.get("voucher_no") and not row.get("posting_date"): - label, value = next(exrow) - self.assertDictEqual(row, {"voucher_no": label, "amount": value}) - self.assertListEqual( - data.get("report_summary"), - [{"label": label, "datatype": "Currency", "value": value} for label, value in expected], - ) - - self.rm_testdocs() - - def test_filter_match(self): - # None - treated as -inf number except range - self.assertTrue(filter_match(None, "!=")) - self.assertTrue(filter_match(None, "<")) - self.assertTrue(filter_match(None, "3.4")) - self.assertFalse(filter_match(None, " <")) - self.assertFalse(filter_match(None, "ew")) - self.assertFalse(filter_match(None, " ")) - self.assertFalse(filter_match(None, " f :")) - - # Numbers - self.assertTrue(filter_match(3.4, "3.4")) - self.assertTrue(filter_match(3.4, ".4")) - self.assertTrue(filter_match(3.4, "3")) - self.assertTrue(filter_match(-3.4, "< -3")) - self.assertTrue(filter_match(-3.4, "> -4")) - self.assertTrue(filter_match(3.4, "= 3.4 ")) - self.assertTrue(filter_match(3.4, "!=4.5")) - self.assertTrue(filter_match(3.4, " 3 : 4 ")) - self.assertTrue(filter_match(0.0, " : ")) - self.assertFalse(filter_match(3.4, "=4.5")) - self.assertFalse(filter_match(3.4, " = 3.4 ")) - self.assertFalse(filter_match(3.4, "!=3.4")) - self.assertFalse(filter_match(3.4, ">6")) - self.assertFalse(filter_match(3.4, "<-4.5")) - self.assertFalse(filter_match(3.4, "4.5")) - self.assertFalse(filter_match(3.4, "5:9")) - - # Strings - self.assertTrue(filter_match("ACC-SINV-2021-00001", "SINV")) - self.assertTrue(filter_match("ACC-SINV-2021-00001", "sinv")) - self.assertTrue(filter_match("ACC-SINV-2021-00001", "-2021")) - self.assertTrue(filter_match(" ACC-SINV-2021-00001", " acc")) - self.assertTrue(filter_match("ACC-SINV-2021-00001", "=2021")) - self.assertTrue(filter_match("ACC-SINV-2021-00001", "!=zz")) - self.assertTrue(filter_match("ACC-SINV-2021-00001", "< zzz ")) - self.assertTrue(filter_match("ACC-SINV-2021-00001", " : sinv ")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", " sinv :")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", " acc")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", "= 2021 ")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", "!=sinv")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", " >")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", ">aa")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", " <")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", "< ")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", " =")) - self.assertFalse(filter_match("ACC-SINV-2021-00001", "=")) - - # Date - always match - self.assertTrue(filter_match(datetime.date(2021, 3, 19), " kdsjkldfs ")) diff --git a/erpnext/accounts/test/test_reports.py b/erpnext/accounts/test/test_reports.py index 9496a1aa664..c2e10f8fd47 100644 --- a/erpnext/accounts/test/test_reports.py +++ b/erpnext/accounts/test/test_reports.py @@ -29,10 +29,6 @@ REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [ ("Sales Register", {}), ("Sales Register", {"item_group": "All Item Groups"}), ("Purchase Register", {}), - ( - "Tax Detail", - {"mode": "run", "report_name": "Tax Detail"}, - ), ] OPTIONAL_FILTERS = {}