From c3973fe88f0822fa4e0bd340734f49d57d5442e6 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Mon, 23 Dec 2024 15:57:47 +0100 Subject: [PATCH 1/7] feat: Option to use suspense accounts for VAT on payments --- .../doctype/payment_entry/payment_entry.json | 58 +++++++++++-------- .../doctype/payment_entry/payment_entry.py | 8 +++ .../doctype/suspense_vat_accounts/__init__.py | 0 .../suspense_vat_accounts.json | 50 ++++++++++++++++ .../suspense_vat_accounts.py | 24 ++++++++ erpnext/hooks.py | 2 + erpnext/patches.txt | 1 + .../suspense_accounts_for_vat_on_payments.py | 56 ++++++++++++++++++ erpnext/regional/france/extensions/company.py | 28 +++++++++ .../france/extensions/payment_entry.py | 53 +++++++++++++++++ erpnext/setup/doctype/company/company.json | 26 ++++++++- erpnext/setup/doctype/company/company.py | 10 +++- .../setup/doctype/company/regional/france.js | 21 +++++++ 13 files changed, 311 insertions(+), 26 deletions(-) create mode 100644 erpnext/accounts/doctype/suspense_vat_accounts/__init__.py create mode 100644 erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.json create mode 100644 erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.py create mode 100644 erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py create mode 100644 erpnext/regional/france/extensions/payment_entry.py diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index d63f0bc02c..75b6198521 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -63,18 +63,6 @@ "unallocated_amount", "difference_amount", "write_off_difference_amount", - "taxes_and_charges_section", - "purchase_taxes_and_charges_template", - "sales_taxes_and_charges_template", - "column_break_55", - "apply_tax_withholding_amount", - "tax_withholding_category", - "section_break_56", - "taxes", - "section_break_60", - "base_total_taxes_and_charges", - "column_break_61", - "total_taxes_and_charges", "deductions_or_loss_section", "deductions", "transaction_references", @@ -87,6 +75,20 @@ "project", "dimension_col_break", "cost_center", + "taxes_and_charges_tab", + "taxes_and_charges_section", + "purchase_taxes_and_charges_template", + "sales_taxes_and_charges_template", + "column_break_55", + "apply_tax_withholding_amount", + "tax_withholding_category", + "section_break_56", + "taxes", + "section_break_60", + "base_total_taxes_and_charges", + "column_break_61", + "total_taxes_and_charges", + "more_information_tab", "section_break_12", "status", "accounting_journal", @@ -102,6 +104,7 @@ "payment_request", "payment_order", "in_words", + "subscription_tab", "subscription_section", "subscription", "auto_repeat", @@ -435,7 +438,6 @@ "label": "Write Off Difference Amount" }, { - "collapsible": 1, "collapsible_depends_on": "deductions", "depends_on": "eval:(doc.paid_amount && doc.received_amount)", "fieldname": "deductions_or_loss_section", @@ -484,11 +486,9 @@ "read_only": 1 }, { - "collapsible": 1, "depends_on": "eval:(doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_12", - "fieldtype": "Section Break", - "label": "More Information" + "fieldtype": "Section Break" }, { "allow_on_submit": 1, @@ -547,10 +547,8 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "subscription_section", - "fieldtype": "Section Break", - "label": "Subscription" + "fieldtype": "Section Break" }, { "allow_on_submit": 1, @@ -681,10 +679,9 @@ "label": "Apply Tax Withholding Amount" }, { - "collapsible": 1, "fieldname": "taxes_and_charges_section", "fieldtype": "Section Break", - "label": "Taxes and Charges" + "hide_border": 1 }, { "depends_on": "eval:doc.party_type == 'Supplier'", @@ -848,6 +845,21 @@ "options": "No\nYes", "print_hide": 1, "search_index": 1 + }, + { + "fieldname": "taxes_and_charges_tab", + "fieldtype": "Tab Break", + "label": "Taxes and Charges" + }, + { + "fieldname": "more_information_tab", + "fieldtype": "Tab Break", + "label": "More Information" + }, + { + "fieldname": "subscription_tab", + "fieldtype": "Tab Break", + "label": "Subscription" } ], "index_web_pages_for_search": 1, @@ -863,7 +875,7 @@ "table_fieldname": "payment_entries" } ], - "modified": "2024-11-08 13:54:35.257266", + "modified": "2024-12-23 14:10:15.883272", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", @@ -911,4 +923,4 @@ "states": [], "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 5dee8810b0..2d4e08a4d8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -176,6 +176,9 @@ class PaymentEntry(AccountsController): self.validate_reference_documents() self.set_amounts() self.validate_amounts() + + add_regional_taxes(self) + self.apply_taxes() self.set_amounts_after_tax() self.clear_unallocated_reference_document_rows() @@ -3640,6 +3643,11 @@ def add_regional_gl_entries(gl_entries, doc): return +@erpnext.allow_regional +def add_regional_taxes(doc): + return + + @frappe.whitelist() def set_payment_entry_as_reconciled(payment_entry): # @dokos for field in ["unreconciled_from_amount", "unreconciled_to_amount", "unreconciled_amount"]: diff --git a/erpnext/accounts/doctype/suspense_vat_accounts/__init__.py b/erpnext/accounts/doctype/suspense_vat_accounts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.json b/erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.json new file mode 100644 index 0000000000..e6fa358931 --- /dev/null +++ b/erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-12-23 14:58:24.439991", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "vat_suspense_account", + "vat_control_account", + "tax_rate" + ], + "fields": [ + { + "fieldname": "vat_suspense_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "VAT Suspense Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "vat_control_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "VAT Control Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "tax_rate", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Tax Rate", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-12-23 15:00:56.043889", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Suspense VAT Accounts", + "owner": "Administrator", + "permissions": [], + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.py b/erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.py new file mode 100644 index 0000000000..0a11462b74 --- /dev/null +++ b/erpnext/accounts/doctype/suspense_vat_accounts/suspense_vat_accounts.py @@ -0,0 +1,24 @@ +# Copyright (c) 2024, Dokos SAS and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class SuspenseVATAccounts(Document): + # 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 + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + tax_rate: DF.Float + vat_control_account: DF.Link + vat_suspense_account: DF.Link + # end: auto-generated types + + pass diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1e2a18363d..0f06af346b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -662,6 +662,8 @@ regional_overrides = { "erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule.get_total_days": "erpnext.regional.france.assets.get_total_days", "erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule.date_difference": "erpnext.regional.france.assets.date_difference", "erpnext.buying.doctype.supplier.supplier.company_query": "erpnext.regional.france.extensions.supplier.company_query", + "erpnext.accounts.doctype.payment_entry.payment_entry.add_regional_taxes": "erpnext.regional.france.extensions.payment_entry.add_regional_taxes", + "erpnext.setup.doctype.company.company.regional_validation": "erpnext.regional.france.extensions.company.regional_validation", }, "United Arab Emirates": { "erpnext.controllers.taxes_and_totals.update_itemised_tax_data": "erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 00a97da192..24ff6e7241 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -396,6 +396,7 @@ execute:frappe.delete_doc_if_exists("DocType", "Integration References") execute:frappe.delete_doc_if_exists("DocType", "Social Media Post") erpnext.patches.dokos.v4_0.update_advance_for_down_payments #2024-05-207 erpnext.patches.dokos.v4_0.check_enable_pappers +erpnext.patches.dokos.v4_0.suspense_accounts_for_vat_on_payments # @dokos diff --git a/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py b/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py new file mode 100644 index 0000000000..b807b4a484 --- /dev/null +++ b/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py @@ -0,0 +1,56 @@ +import frappe + +from erpnext.regional.france.setup import setup + + +def execute(): + companies = frappe.get_all("Company", filters={"country": "France"}) + if not companies: + return + + setup() + + if not frappe.db.exists("Account", dict(account_number=4457, is_group=1)): + return + + for acc in [ + { + "account_number": 4457420, + "account_name": "TVA collectée sur encaissements - 20%", + "tax_rate": 20 + }, + { + "account_number": 4457410, + "account_name": "TVA collectée sur encaissements - 10%", + "tax_rate": 10 + }, + { + "account_number": 4457455, + "account_name": "TVA collectée sur encaissements - 5.5%", + "tax_rate": 5.5 + }, + { + "account_number": 4456420, + "account_name": "TVA déductible sur encaissements - 20%", + "tax_rate": 20 + }, + { + "account_number": 4456410, + "account_name": "TVA déductible sur encaissements - 10%", + "tax_rate": 10 + }, + { + "account_number": 4456455, + "account_name": "TVA déductible sur encaissements - 55%", + "tax_rate": 5.5 + } + ]: + for company in companies: + account = frappe.new_doc("Account") + account.account_name = acc["account_name"] + account.account_number = acc["account_number"] + account.parent_account = frappe.db.get_value("Account", filters=dict(account_number=4457, is_group=1, company=company.name)) + account.account_type = "Tax" + account.tax_rate = acc["tax_rate"] + account.company = company.name + account.insert(ignore_if_duplicate=True) diff --git a/erpnext/regional/france/extensions/company.py b/erpnext/regional/france/extensions/company.py index a81b2d5922..2e2e22cd95 100644 --- a/erpnext/regional/france/extensions/company.py +++ b/erpnext/regional/france/extensions/company.py @@ -1,9 +1,37 @@ import frappe from frappe import _ +from frappe.utils import flt from erpnext.regional.france.pappers.document import PappersDocument +def regional_validation(doc): + for row in doc.suspense_accounts_settings: + if row.vat_suspense_account == row.vat_control_account: + frappe.throw(_("Please set different account for VAT Suspense Account and VAT Control Account")) + + for field in ("vat_suspense_account", "vat_control_account"): + if flt(frappe.db.get_value("Account", field, "tax_rate")) != flt(row.tax_rate): + frappe.db.set_value("Account", field, "tax_rate", flt(row.tax_rate)) + + if not doc.suspense_accounts_settings: + for acc_numbers in ("44574%", "44564%"): + for account in frappe.get_all("Account", filters=dict(account_number=("like", acc_numbers), account_type="Tax", company=doc.name, is_group=0), fields=["name", "tax_rate"]): + if control_account := frappe.db.get_value("Account", dict( + account_number=("like", f"{acc_numbers[:4]}%"), + name=("!=", account.name), + tax_rate=account.tax_rate, + account_type="Tax", + company=doc.name + ), + ): + doc.append("suspense_accounts_settings", { + "vat_suspense_account": account.name, + "vat_control_account": control_account, + "tax_rate": account.tax_rate + }) + + @frappe.whitelist() def get_extrait_pappers(siren): response = PappersDocument().get_extrait_pappers(siren) diff --git a/erpnext/regional/france/extensions/payment_entry.py b/erpnext/regional/france/extensions/payment_entry.py new file mode 100644 index 0000000000..da96e66b3f --- /dev/null +++ b/erpnext/regional/france/extensions/payment_entry.py @@ -0,0 +1,53 @@ + +import frappe +from frappe import _ + + +def add_regional_taxes(doc): + if frappe.db.get_value("Company", doc.company, "country") != "France": + return + + company = frappe.get_cached_doc("Company", doc.company) + if not company.suspense_accounts_settings: + return + + supense_accounts_mapping = {account.vat_suspense_account: account.vat_control_account for account in company.suspense_accounts_settings} + supense_accounts = supense_accounts_mapping.keys() + + doc.set("taxes", []) + for reference in doc.references: + res = get_tax_reversal_entries(doc.company, reference.reference_doctype, reference.reference_name, supense_accounts) + for row in res: + tax_rate = frappe.get_cached_value("Account", row["account"], "tax_rate") + + doc.append("taxes", { + "add_deduct_tax": "Deduct", + "included_in_paid_amount": 1, + "charge_type": "On Paid Amount", + "account_head": row["account"], + "rate": tax_rate, + "description": row["remarks"] + }) + + doc.append("taxes", { + "add_deduct_tax": "Add", + "included_in_paid_amount": 1, + "charge_type": "On Paid Amount", + "account_head": supense_accounts_mapping.get(row.account), + "rate": tax_rate, + "description": row["remarks"] + }) + + +def get_tax_reversal_entries(company, voucher_type, voucher_no, pending_vat_accounts): + filters = { + "is_cancelled": 0, + "credit": [">", 0], + "account": ["in", pending_vat_accounts], + "voucher_type": voucher_type, + "voucher_no": voucher_no, + "company": company + } + + return frappe.db.get_all("GL Entry", filters=filters, fields=["account", "sum(credit) as credit", "remarks"], group_by="account") + diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index b3e627a84a..3df6dca0c0 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -121,7 +121,12 @@ "default_in_transit_warehouse", "manufacturing_section", "default_operating_cost_account", - "dashboard_tab" + "dashboard_tab", + "discount_allowed_account", + "discount_received_account", + "taxes_tab", + "section_break_wogu", + "suspense_accounts_settings" ], "fields": [ { @@ -853,6 +858,23 @@ { "fieldname": "column_break_pepz", "fieldtype": "Column Break" + }, + { + "fieldname": "taxes_tab", + "fieldtype": "Tab Break", + "label": "Taxes" + }, + { + "fieldname": "section_break_wogu", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.country==\"France\"", + "documentation_url": "https://doc.dokos.io/dokos/parametrage/demarrage/taxes", + "fieldname": "suspense_accounts_settings", + "fieldtype": "Table", + "label": "Payment Suspense Accounts Settings", + "options": "Suspense VAT Accounts" } ], "icon": "fa fa-building", @@ -927,4 +949,4 @@ "sort_order": "ASC", "states": [], "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 561b755dcd..6940d03d19 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -13,6 +13,7 @@ from frappe.desk.page.setup_wizard.setup_wizard import make_records from frappe.utils import cint, formatdate, get_timestamp, today from frappe.utils.nestedset import NestedSet, rebuild_tree +import erpnext from erpnext.accounts.doctype.account.account import get_account_currency from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges @@ -24,6 +25,7 @@ class Company(NestedSet): from typing import TYPE_CHECKING if TYPE_CHECKING: + from erpnext.accounts.doctype.suspense_vat_accounts.suspense_vat_accounts import SuspenseVATAccounts from frappe.types import DF abbr: DF.Data @@ -32,7 +34,6 @@ class Company(NestedSet): asset_received_but_not_billed: DF.Link | None auto_err_frequency: DF.Literal["Daily", "Weekly", "Monthly"] auto_exchange_rate_revaluation: DF.Check - book_advance_payments_in_separate_party_account: DF.Check capital_work_in_progress_account: DF.Link | None chart_of_accounts: DF.Literal[None] company_description: DF.TextEditor | None @@ -98,6 +99,7 @@ class Company(NestedSet): stock_adjustment_account: DF.Link | None stock_received_but_not_billed: DF.Link | None submit_err_jv: DF.Check + suspense_accounts_settings: DF.Table[SuspenseVATAccounts] tax_id: DF.Data | None total_monthly_sales: DF.Currency transactions_annual_history: DF.Code | None @@ -152,6 +154,8 @@ class Company(NestedSet): self.set_chart_of_accounts() self.validate_parent_company() + regional_validation(self) + def validate_abbr(self): if not self.abbr: self.abbr = "".join(c[0] for c in self.company_name.split()).upper() @@ -904,3 +908,7 @@ def get_default_company_address(name, sort_key="is_primary_address", existing_ad return max(out, key=lambda x: x[1])[0] # find max by sort_key else: return None + +@erpnext.allow_regional +def regional_validation(doc): + return diff --git a/erpnext/setup/doctype/company/regional/france.js b/erpnext/setup/doctype/company/regional/france.js index 418d3a1642..b1ba904b17 100644 --- a/erpnext/setup/doctype/company/regional/france.js +++ b/erpnext/setup/doctype/company/regional/france.js @@ -1,4 +1,25 @@ frappe.ui.form.on("Company", { + setup(frm) { + frm.set_query("vat_suspense_account", "suspense_accounts_settings", () => { + return { + filters: { + company: frm.doc.name, + is_group: 0, + account_type: "Tax" + } + }; + }); + + frm.set_query("vat_control_account", "suspense_accounts_settings", () => { + return { + filters: { + company: frm.doc.name, + is_group: 0, + account_type: "Tax" + } + }; + }); + }, refresh(frm) { if (frm.doc.siren_number) { frm.add_custom_button( -- GitLab From a164630469fbeee3aeb686946a5b0bd2441250a6 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Mon, 23 Dec 2024 18:20:23 +0100 Subject: [PATCH 2/7] tests: basic tests on sales invoices --- .pre-commit-config.yaml | 2 +- .../suspense_accounts_for_vat_on_payments.py | 36 +----- .../france/tests/test_tax_templates.py | 8 +- erpnext/regional/france/tests/test_vat.py | 115 ++++++++++++++++++ .../setup_wizard/data/country_wise_tax.json | 78 ++++++++++++ 5 files changed, 205 insertions(+), 34 deletions(-) create mode 100644 erpnext/regional/france/tests/test_vat.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6e09929534..10deb3709b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ exclude: 'node_modules|.git' -default_stages: [commit] +default_stages: [pre-commit] fail_fast: false diff --git a/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py b/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py index b807b4a484..f1334ec69c 100644 --- a/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py +++ b/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py @@ -14,42 +14,16 @@ def execute(): return for acc in [ - { - "account_number": 4457420, - "account_name": "TVA collectée sur encaissements - 20%", - "tax_rate": 20 - }, - { - "account_number": 4457410, - "account_name": "TVA collectée sur encaissements - 10%", - "tax_rate": 10 - }, - { - "account_number": 4457455, - "account_name": "TVA collectée sur encaissements - 5.5%", - "tax_rate": 5.5 - }, - { - "account_number": 4456420, - "account_name": "TVA déductible sur encaissements - 20%", - "tax_rate": 20 - }, - { - "account_number": 4456410, - "account_name": "TVA déductible sur encaissements - 10%", - "tax_rate": 10 - }, - { - "account_number": 4456455, - "account_name": "TVA déductible sur encaissements - 55%", - "tax_rate": 5.5 - } + {"account_number": 445742, "account_name": "TVA collectée sur encaissements - 20%", "tax_rate": 20}, + {"account_number": 445642, "account_name": "TVA déductible sur encaissements - 20%", "tax_rate": 20}, ]: for company in companies: account = frappe.new_doc("Account") account.account_name = acc["account_name"] account.account_number = acc["account_number"] - account.parent_account = frappe.db.get_value("Account", filters=dict(account_number=4457, is_group=1, company=company.name)) + account.parent_account = frappe.db.get_value( + "Account", filters=dict(account_number=4457, is_group=1, company=company.name) + ) account.account_type = "Tax" account.tax_rate = acc["tax_rate"] account.company = company.name diff --git a/erpnext/regional/france/tests/test_tax_templates.py b/erpnext/regional/france/tests/test_tax_templates.py index d4f9fd4e7a..05f4614513 100644 --- a/erpnext/regional/france/tests/test_tax_templates.py +++ b/erpnext/regional/france/tests/test_tax_templates.py @@ -1,6 +1,5 @@ import frappe from frappe.tests import IntegrationTestCase -from frappe.utils import cstr, flt from erpnext.regional.france.tests.test_assets import create_company @@ -24,7 +23,12 @@ class TestTaxTemplatesForFrance(IntegrationTestCase): frappe.get_all( "Sales Taxes and Charges Template", {"company": "_Test French Company 2"}, pluck="name" ), - ["TVA 20% Collectée - _FC2", "TVA 10% Collectée - _FC2", "TVA 5.5% Collectée - _FC2", "TVA 2.1% Collectée - _FC2"], + [ + "TVA 20% Collectée - _FC2", + "TVA 10% Collectée - _FC2", + "TVA 5.5% Collectée - _FC2", + "TVA 2.1% Collectée - _FC2", + ], ) self.assertSequenceSubset( frappe.get_all("Account", {"company": "_Test French Company 2"}, pluck="name"), diff --git a/erpnext/regional/france/tests/test_vat.py b/erpnext/regional/france/tests/test_vat.py new file mode 100644 index 0000000000..376427da9a --- /dev/null +++ b/erpnext/regional/france/tests/test_vat.py @@ -0,0 +1,115 @@ +import frappe +from frappe.tests import IntegrationTestCase +from frappe.utils import nowdate + +from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.regional.france.tests.test_assets import create_company + + +class TestVATForFrance(IntegrationTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + company = "_Test French Company 3" + cls.company = create_company( + company_name=company, country="France", currency="EUR", chart_of_accounts="Plan Comptable Général" + ) + frappe.flags.country = "France" + + cls.account = frappe.new_doc("Account") + cls.account.update( + { + "account_name": "_Test Receivable EUR", + "parent_account": "C - Créances" + " - " + frappe.db.get_value("Company", company, "abbr"), + "company": company, + "is_group": 0, + "account_type": "Receivable", + "account_currency": "EUR", + } + ) + cls.account.insert(ignore_if_duplicate=True) + + cls.customer = frappe.new_doc("Customer") + cls.customer.update( + { + "customer_group": "_Test Customer Group", + "customer_name": "_Test Customer EUR", + "customer_type": "Individual", + "territory": "_Test Territory", + "default_currency": "EUR", + } + ) + cls.customer.insert(ignore_if_duplicate=True) + + @classmethod + def tearDownClass(cls): + frappe.flags.country = None + + def test_accounts_are_created(self): + all_accounts = frappe.get_all( + "Account", filters={"account_number": ("is", "set")}, pluck="account_number" + ) + self.assertIn("445742", all_accounts) + self.assertIn("445642", all_accounts) + + company = frappe.get_doc("Company", TestVATForFrance.company) + company.save() + for row in company.suspense_accounts_settings: + self.assertEqual(row.vat_suspense_account[:4], row.vat_control_account[:4]) + + @IntegrationTestCase.change_settings("Accounts Settings", {"mandatory_accounting_journal": 0}) + def test_vat_on_payments(self): + company = frappe.get_doc("Company", TestVATForFrance.company) + company.save() + company_abbr = company.abbr + + si = create_sales_invoice( + company=TestVATForFrance.company, + currency="EUR", + customer=TestVATForFrance.customer.name, + debit_to=TestVATForFrance.account.name, + cost_center=f"Main - {company_abbr}", + income_account=f"701 - Ventes de produits finis - {company_abbr}", + do_not_save=1, + ) + si.taxes_and_charges = f"TVA collectée sur encaissements - 20% - {company_abbr}" + si.set_missing_values() + si.set_taxes() + si.calculate_taxes_and_totals() + si.insert() + si.submit() + + gl_entries = frappe.get_all( + "GL Entry", filters={"voucher_type": si.doctype, "voucher_no": si.name}, pluck="account" + ) + + self.assertIn(f"445742 - TVA collectée sur encaissements - 20% - {company_abbr}", gl_entries) + + pe = get_payment_entry("Sales Invoice", si.name) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.insert() + pe.submit() + + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": pe.doctype, "voucher_no": pe.name}, + fields=["account", "debit", "credit"], + ) + accounts = [gl.account for gl in gl_entries] + self.assertIn(f"445742 - TVA collectée sur encaissements - 20% - {company_abbr}", accounts) + self.assertIn(f"445720 - TVA 20% Collectée - {company_abbr}", accounts) + + tax_map = { + f"445742 - TVA collectée sur encaissements - 20% - {company_abbr}": "debit", + f"445720 - TVA 20% Collectée - {company_abbr}": "credit", + } + + result = [] + for gl_entry in gl_entries: + if field := tax_map.get(gl_entry.account): + result.append(gl_entry.get(field)) + + self.assertTrue(len(result), 2) + self.assertEqual(result[0], result[1]) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index cbe638f278..2b0c5f4d8b 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -418,6 +418,21 @@ } ] }, + { + "title": "TVA collectée sur encaissements - 20%", + "taxes": [ + { + "account_head": { + "account_name": "TVA collectée sur encaissements - 20%", + "account_number": "445742", + "root_type": "Liability", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "rate": 20 + } + ] + }, { "title": "TVA 10% Collectée", "taxes": [ @@ -480,6 +495,21 @@ } ] }, + { + "title": "TVA déductible sur encaissements - 20%", + "taxes": [ + { + "account_head": { + "account_name": "TVA déductible sur encaissements - 20%", + "account_number": "445642", + "root_type": "Asset", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "rate": 20 + } + ] + }, { "title": "TVA 10% Déductible", "taxes": [ @@ -618,6 +648,21 @@ } ] }, + { + "title": "TVA collectée sur encaissements - 20%", + "taxes": [ + { + "tax_type": { + "account_name": "TVA collectée sur encaissements - 20%", + "account_number": "445742", + "root_type": "Liability", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "tax_rate": 20 + } + ] + }, { "title": "TVA 10% Collectée", "taxes": [ @@ -679,6 +724,22 @@ ], "applicable_for": "Purchases" }, + { + "title": "TVA déductible sur encaissements - 20%", + "taxes": [ + { + "tax_type": { + "account_name": "TVA déductible sur encaissements - 20%", + "account_number": "445642", + "root_type": "Asset", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "tax_rate": 20 + } + ], + "applicable_for": "Purchases" + }, { "title": "TVA 10% Déductible", "taxes": [ @@ -744,6 +805,23 @@ ], "applicable_for": "Purchases" }, + { + "title": "TVA déductible sur encaissements - 20%", + "taxes": [ + { + "tax_type": { + "account_name": "TVA déductible sur encaissements - 20%", + "account_number": "445642", + "root_type": "Asset", + "tax_rate": 20.0 + }, + "included_in_print_rate": 1, + "description": "TVA 20%", + "tax_rate": 20 + } + ], + "applicable_for": "Purchases" + }, { "title": "TVA 10% Déductible - Incluse dans le prix", "taxes": [ -- GitLab From a622df0e58f6120b881fdc66738a27fb1a7e492c Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Mon, 23 Dec 2024 20:07:14 +0100 Subject: [PATCH 3/7] fix: missing tax template in tests --- erpnext/regional/france/tests/test_tax_templates.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/regional/france/tests/test_tax_templates.py b/erpnext/regional/france/tests/test_tax_templates.py index 05f4614513..2d8b34dd91 100644 --- a/erpnext/regional/france/tests/test_tax_templates.py +++ b/erpnext/regional/france/tests/test_tax_templates.py @@ -25,6 +25,7 @@ class TestTaxTemplatesForFrance(IntegrationTestCase): ), [ "TVA 20% Collectée - _FC2", + "TVA collectée sur encaissements - 20% - _FC2", "TVA 10% Collectée - _FC2", "TVA 5.5% Collectée - _FC2", "TVA 2.1% Collectée - _FC2", -- GitLab From 0a3bd0962d67a8b4e40d59578461b4b19c7b54ba Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Fri, 27 Dec 2024 16:44:37 +0100 Subject: [PATCH 4/7] test: additional tests --- .../suspense_accounts_for_vat_on_payments.py | 9 + .../france/extensions/payment_entry.py | 80 ++++-- erpnext/regional/france/tests/test_vat.py | 245 +++++++++++++++++- erpnext/setup/doctype/company/company.py | 5 +- 4 files changed, 306 insertions(+), 33 deletions(-) diff --git a/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py b/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py index f1334ec69c..08360088bd 100644 --- a/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py +++ b/erpnext/patches/dokos/v4_0/suspense_accounts_for_vat_on_payments.py @@ -28,3 +28,12 @@ def execute(): account.tax_rate = acc["tax_rate"] account.company = company.name account.insert(ignore_if_duplicate=True) + + for company in companies: + try: + doc = frappe.get_doc("Company", company) + doc.flags.ignore_mandatory = True + doc.flags.ignore_links = True + doc.save() + except Exception: + continue diff --git a/erpnext/regional/france/extensions/payment_entry.py b/erpnext/regional/france/extensions/payment_entry.py index da96e66b3f..f3dbb7dfad 100644 --- a/erpnext/regional/france/extensions/payment_entry.py +++ b/erpnext/regional/france/extensions/payment_entry.py @@ -1,6 +1,6 @@ - import frappe from frappe import _ +from frappe.utils import flt def add_regional_taxes(doc): @@ -11,43 +11,69 @@ def add_regional_taxes(doc): if not company.suspense_accounts_settings: return - supense_accounts_mapping = {account.vat_suspense_account: account.vat_control_account for account in company.suspense_accounts_settings} + supense_accounts_mapping = { + account.vat_suspense_account: account.vat_control_account + for account in company.suspense_accounts_settings + } supense_accounts = supense_accounts_mapping.keys() doc.set("taxes", []) for reference in doc.references: - res = get_tax_reversal_entries(doc.company, reference.reference_doctype, reference.reference_name, supense_accounts) - for row in res: - tax_rate = frappe.get_cached_value("Account", row["account"], "tax_rate") - - doc.append("taxes", { - "add_deduct_tax": "Deduct", - "included_in_paid_amount": 1, - "charge_type": "On Paid Amount", - "account_head": row["account"], - "rate": tax_rate, - "description": row["remarks"] - }) - - doc.append("taxes", { - "add_deduct_tax": "Add", - "included_in_paid_amount": 1, - "charge_type": "On Paid Amount", - "account_head": supense_accounts_mapping.get(row.account), - "rate": tax_rate, - "description": row["remarks"] - }) + res = get_tax_reversal_entries( + doc.company, reference.reference_doctype, reference.reference_name, supense_accounts + ) + if len(res) == 1: + amount = 0.0 + append_taxes(doc, res[0], supense_accounts_mapping, amount) + else: + for row in res: + amount = abs(row.get("total")) + append_taxes(doc, row, supense_accounts_mapping, amount) + + +def append_taxes(doc, row, supense_accounts_mapping, amount=None): + tax_rate = frappe.get_cached_value("Account", row["account"], "tax_rate") + tax_amount = min(amount, (doc.get("received_amount") * flt(tax_rate) / 100.0)) + + doc.append( + "taxes", + { + "add_deduct_tax": "Deduct", + "included_in_paid_amount": 1 if not amount else 0, + "charge_type": "On Paid Amount" if not amount else "Actual", + "account_head": row["account"], + "rate": tax_rate, + "tax_amount": tax_amount, + "description": row["remarks"], + }, + ) + + doc.append( + "taxes", + { + "add_deduct_tax": "Add", + "included_in_paid_amount": 1 if not amount else 0, + "charge_type": "On Paid Amount" if not amount else "Actual", + "account_head": supense_accounts_mapping.get(row.account), + "rate": tax_rate, + "tax_amount": tax_amount, + "description": row["remarks"], + }, + ) def get_tax_reversal_entries(company, voucher_type, voucher_no, pending_vat_accounts): filters = { "is_cancelled": 0, - "credit": [">", 0], "account": ["in", pending_vat_accounts], "voucher_type": voucher_type, "voucher_no": voucher_no, - "company": company + "company": company, } - return frappe.db.get_all("GL Entry", filters=filters, fields=["account", "sum(credit) as credit", "remarks"], group_by="account") - + return frappe.db.get_all( + "GL Entry", + filters=filters, + fields=["account", "sum(credit-debit) as total", "remarks"], + group_by="account", + ) diff --git a/erpnext/regional/france/tests/test_vat.py b/erpnext/regional/france/tests/test_vat.py index 376427da9a..3b5153b54c 100644 --- a/erpnext/regional/france/tests/test_vat.py +++ b/erpnext/regional/france/tests/test_vat.py @@ -3,6 +3,7 @@ from frappe.tests import IntegrationTestCase from frappe.utils import nowdate from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry +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.regional.france.tests.test_assets import create_company @@ -17,8 +18,8 @@ class TestVATForFrance(IntegrationTestCase): ) frappe.flags.country = "France" - cls.account = frappe.new_doc("Account") - cls.account.update( + cls.debit_account = frappe.new_doc("Account") + cls.debit_account.update( { "account_name": "_Test Receivable EUR", "parent_account": "C - Créances" + " - " + frappe.db.get_value("Company", company, "abbr"), @@ -28,7 +29,22 @@ class TestVATForFrance(IntegrationTestCase): "account_currency": "EUR", } ) - cls.account.insert(ignore_if_duplicate=True) + cls.debit_account.insert(ignore_if_duplicate=True) + + cls.credit_account = frappe.new_doc("Account") + cls.credit_account.update( + { + "account_name": "_Test Payables EUR", + "parent_account": "F - Dettes fournisseurs et comptes rattachés - Liability" + + " - " + + frappe.db.get_value("Company", company, "abbr"), + "company": company, + "is_group": 0, + "account_type": "Payable", + "account_currency": "EUR", + } + ) + cls.credit_account.insert(ignore_if_duplicate=True) cls.customer = frappe.new_doc("Customer") cls.customer.update( @@ -42,6 +58,22 @@ class TestVATForFrance(IntegrationTestCase): ) cls.customer.insert(ignore_if_duplicate=True) + cls.supplier = frappe.new_doc("Supplier") + cls.supplier.update( + { + "supplier_group": "_Test Supplier Group", + "supplier_name": "_Test Supplier EUR", + "territory": "_Test Territory", + "default_currency": "EUR", + } + ) + cls.supplier.insert(ignore_if_duplicate=True) + + if tax_template := frappe.db.get_value( + "Sales Taxes and Charges Template", dict(company=company, is_default=True) + ): + frappe.db.set_value("Sales Taxes and Charges Template", tax_template, "is_default", False) + @classmethod def tearDownClass(cls): frappe.flags.country = None @@ -59,7 +91,7 @@ class TestVATForFrance(IntegrationTestCase): self.assertEqual(row.vat_suspense_account[:4], row.vat_control_account[:4]) @IntegrationTestCase.change_settings("Accounts Settings", {"mandatory_accounting_journal": 0}) - def test_vat_on_payments(self): + def test_vat_on_sales_payments(self): company = frappe.get_doc("Company", TestVATForFrance.company) company.save() company_abbr = company.abbr @@ -68,7 +100,7 @@ class TestVATForFrance(IntegrationTestCase): company=TestVATForFrance.company, currency="EUR", customer=TestVATForFrance.customer.name, - debit_to=TestVATForFrance.account.name, + debit_to=TestVATForFrance.debit_account.name, cost_center=f"Main - {company_abbr}", income_account=f"701 - Ventes de produits finis - {company_abbr}", do_not_save=1, @@ -113,3 +145,206 @@ class TestVATForFrance(IntegrationTestCase): self.assertTrue(len(result), 2) self.assertEqual(result[0], result[1]) + + @IntegrationTestCase.change_settings("Accounts Settings", {"mandatory_accounting_journal": 0}) + def test_multiple_vat_on_sales_payments(self): + company = frappe.get_doc("Company", TestVATForFrance.company) + + create_sales_tax_templates(company) + create_test_items(company) + + company.save() + + si = create_sales_invoice( + company=TestVATForFrance.company, + currency="EUR", + customer=TestVATForFrance.customer.name, + debit_to=TestVATForFrance.debit_account.name, + cost_center=f"Main - {company.abbr}", + income_account=f"701 - Ventes de produits finis - {company.abbr}", + do_not_save=1, + ) + si.set("items", []) + for item in ["VATONPAYMENTS_10", "VATONPAYMENTS_20"]: + si.append("items", {"item_code": item, "rate": 100}) + + si.set_missing_values() + si.set_taxes() + si.calculate_taxes_and_totals() + si.insert() + si.submit() + + gl_entries = frappe.get_all( + "GL Entry", filters={"voucher_type": si.doctype, "voucher_no": si.name}, pluck="account" + ) + si.reload() + + self.assertIn(f"445742 - TVA collectée sur encaissements - 20% - {company.abbr}", gl_entries) + self.assertIn(f"445741 - TVA collectée sur encaissements - 10% - {company.abbr}", gl_entries) + + pe = get_payment_entry("Sales Invoice", si.name) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.insert() + pe.submit() + + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": pe.doctype, "voucher_no": pe.name}, + fields=["account", "debit", "credit"], + ) + accounts = [gl.account for gl in gl_entries] + self.assertIn(f"445742 - TVA collectée sur encaissements - 20% - {company.abbr}", accounts) + self.assertIn(f"445720 - TVA 20% Collectée - {company.abbr}", accounts) + self.assertIn(f"445741 - TVA collectée sur encaissements - 10% - {company.abbr}", accounts) + self.assertIn(f"445710 - TVA 10% Collectée - {company.abbr}", accounts) + + tax_map = { + f"445742 - TVA collectée sur encaissements - 20% - {company.abbr}": "debit", + f"445720 - TVA 20% Collectée - {company.abbr}": "credit", + f"445741 - TVA collectée sur encaissements - 10% - {company.abbr}": "debit", + f"445710 - TVA 10% Collectée - {company.abbr}": "credit", + } + + result = [] + for gl_entry in gl_entries: + if field := tax_map.get(gl_entry.account): + result.append(gl_entry.get(field)) + + self.assertTrue(len(result), 4) + self.assertEqual(result[0], result[1]) + self.assertEqual(result[2], result[3]) + + @IntegrationTestCase.change_settings("Accounts Settings", {"mandatory_accounting_journal": 0}) + def test_vat_on_purchase_payments(self): + company = frappe.get_doc("Company", TestVATForFrance.company) + + create_purchase_tax_templates(company) + + company.save() + company_abbr = company.abbr + + pi = make_purchase_invoice( + company=TestVATForFrance.company, + currency="EUR", + supplier=TestVATForFrance.supplier.name, + credit_to=TestVATForFrance.credit_account.name, + cost_center=f"Main - {company_abbr}", + warehouse=frappe.db.get_value("Warehouse", dict(is_group=0, company=company.name)), + expense_account=f"600 - Achats - {company_abbr}", + do_not_save=1, + ) + pi.taxes_and_charges = f"TVA déductible sur encaissements - 20% - {company_abbr}" + pi.set_missing_values() + pi.set_taxes() + pi.calculate_taxes_and_totals() + pi.insert() + pi.submit() + + gl_entries = frappe.get_all( + "GL Entry", filters={"voucher_type": pi.doctype, "voucher_no": pi.name}, pluck="account" + ) + + self.assertIn(f"445642 - TVA déductible sur encaissements - 20% - {company_abbr}", gl_entries) + + pe = get_payment_entry("Purchase Invoice", pi.name) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.insert() + pe.submit() + + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": pe.doctype, "voucher_no": pe.name}, + fields=["account", "debit", "credit"], + ) + accounts = [gl.account for gl in gl_entries] + self.assertIn(f"445642 - TVA déductible sur encaissements - 20% - {company_abbr}", accounts) + self.assertIn( + f"445662 - TVA déductible sur acquisition intracommunautaires - {company_abbr}", accounts + ) + + tax_map = { + f"445642 - TVA déductible sur encaissements - 20% - {company_abbr}": "credit", + f"445662 - TVA déductible sur acquisition intracommunautaires - {company_abbr}": "debit", + } + + result = [] + for gl_entry in gl_entries: + if field := tax_map.get(gl_entry.account): + result.append(gl_entry.get(field)) + + self.assertTrue(len(result), 2) + self.assertEqual(result[0], result[1]) + + +def create_sales_tax_templates(company): + if not frappe.db.exists("Account", f"445741 - TVA collectée sur encaissements - 10% - {company.abbr}"): + account = frappe.new_doc("Account") + account.account_number = 445741 + account.account_name = "TVA collectée sur encaissements - 10%" + account.company = company.name + account.account_type = "Tax" + account.parent_account = ( + f"4457 - Taxes sur le chiffre d'affaires collectées par l'entreprise - {company.abbr}" + ) + account.tax_rate = 10.0 + account.insert(ignore_if_duplicate=True) + + if not frappe.db.exists("Item Tax Template", f"TVA collectée sur encaissements - 10% - {company.abbr}"): + tax_template = frappe.new_doc("Item Tax Template") + tax_template.title = "TVA collectée sur encaissements - 10%" + tax_template.company = company.name + tax_template.applicable_for = "Sales" + tax_template.append( + "taxes", + { + "tax_type": frappe.db.get_value("Account", dict(account_number=445741, company=company.name)), + "tax_rate": 10.0, + "description": "TVA 10%", + }, + ) + tax_template.insert(ignore_if_duplicate=True) + + +def create_purchase_tax_templates(company): + if not frappe.db.exists("Account", f"445642 - TVA déductible sur encaissements - 20% - {company.abbr}"): + account = frappe.new_doc("Account") + account.account_number = 445642 + account.account_name = "TVA déductible sur encaissements -20%" + account.company = company.name + account.account_type = "Tax" + account.parent_account = f"4456 - Taxes sur le chiffre d'affaires déductibles - {company.abbr}" + account.tax_rate = 20.0 + account.insert(ignore_if_duplicate=True) + + if not frappe.db.exists("Item Tax Template", f"TVA déductible sur encaissements - 20% - {company.abbr}"): + tax_template = frappe.new_doc("Item Tax Template") + tax_template.title = "TVA déductible sur encaissements - 20%" + tax_template.company = company.name + tax_template.applicable_for = "Purchases" + tax_template.append( + "taxes", + { + "tax_type": frappe.db.get_value("Account", dict(account_number=445642, company=company.name)), + "tax_rate": 20.0, + "description": "TVA 20%", + }, + ) + tax_template.insert(ignore_if_duplicate=True) + + +def create_test_items(company): + for item in ["VATONPAYMENTS_10", "VATONPAYMENTS_20"]: + doc = frappe.new_doc("Item") + doc.item_code = item + doc.item_group = "Services" + doc.stock_uom = "_Test UOM" + doc.append( + "taxes", + { + "company": company.name, + "item_tax_template": f"TVA collectée sur encaissements - {item[-2:]}% - {company.abbr}", + }, + ) + doc.insert(ignore_if_duplicate=True) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 6940d03d19..7b4b17ef89 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -25,9 +25,10 @@ class Company(NestedSet): from typing import TYPE_CHECKING if TYPE_CHECKING: - from erpnext.accounts.doctype.suspense_vat_accounts.suspense_vat_accounts import SuspenseVATAccounts from frappe.types import DF + from erpnext.accounts.doctype.suspense_vat_accounts.suspense_vat_accounts import SuspenseVATAccounts + abbr: DF.Data accumulated_depreciation_account: DF.Link | None allow_account_creation_against_child_company: DF.Check @@ -154,6 +155,7 @@ class Company(NestedSet): self.set_chart_of_accounts() self.validate_parent_company() + frappe.flags.company = self.name regional_validation(self) def validate_abbr(self): @@ -909,6 +911,7 @@ def get_default_company_address(name, sort_key="is_primary_address", existing_ad else: return None + @erpnext.allow_regional def regional_validation(doc): return -- GitLab From 34c87aaac89a85ff71f66ad44ef152b15a2644b6 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Sun, 29 Dec 2024 19:19:05 +0100 Subject: [PATCH 5/7] fix: disable regional tax handling for France --- erpnext/controllers/taxes_and_totals.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index fff66cf974..3f14cfe05c 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -543,6 +543,7 @@ class calculate_taxes_and_totals: tax_rate=tax_rate, tax_amount=flt(item_wise_tax_amount, tax.precision("tax_amount")), net_amount=flt(item_wise_net_amount, tax.precision("net_amount")), + add_deduct_tax=tax.get("add_deduct_tax"), # @dokos ) else: if tax_data := tax.item_wise_tax_detail.get(key): @@ -553,6 +554,7 @@ class calculate_taxes_and_totals: tax_rate=tax_rate, tax_amount=item_wise_tax_amount, net_amount=item_wise_net_amount, + add_deduct_tax=tax.get("add_deduct_tax"), # @dokos ) def round_off_totals(self, tax): -- GitLab From 0cd08e98baa2b05c9e5cf7cf86b2c72b96c0842f Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Sun, 29 Dec 2024 21:23:36 +0100 Subject: [PATCH 6/7] revert: don't add add_deduct_tax to item wise details --- erpnext/controllers/taxes_and_totals.py | 2 -- erpnext/setup/doctype/company/company.json | 8 +++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 3f14cfe05c..fff66cf974 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -543,7 +543,6 @@ class calculate_taxes_and_totals: tax_rate=tax_rate, tax_amount=flt(item_wise_tax_amount, tax.precision("tax_amount")), net_amount=flt(item_wise_net_amount, tax.precision("net_amount")), - add_deduct_tax=tax.get("add_deduct_tax"), # @dokos ) else: if tax_data := tax.item_wise_tax_detail.get(key): @@ -554,7 +553,6 @@ class calculate_taxes_and_totals: tax_rate=tax_rate, tax_amount=item_wise_tax_amount, net_amount=item_wise_net_amount, - add_deduct_tax=tax.get("add_deduct_tax"), # @dokos ) def round_off_totals(self, tax): diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 3df6dca0c0..486a5728a4 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -121,12 +121,10 @@ "default_in_transit_warehouse", "manufacturing_section", "default_operating_cost_account", - "dashboard_tab", - "discount_allowed_account", - "discount_received_account", "taxes_tab", "section_break_wogu", - "suspense_accounts_settings" + "suspense_accounts_settings", + "dashboard_tab" ], "fields": [ { @@ -949,4 +947,4 @@ "sort_order": "ASC", "states": [], "track_changes": 1 -} \ No newline at end of file +} -- GitLab From 85e9f6de33c44413b14bd7a466a09692bbe6f68c Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Mon, 30 Dec 2024 09:06:12 +0100 Subject: [PATCH 7/7] fix: Reset suspense accounts settings for each test --- erpnext/regional/france/tests/test_vat.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/france/tests/test_vat.py b/erpnext/regional/france/tests/test_vat.py index 3b5153b54c..00a9be857e 100644 --- a/erpnext/regional/france/tests/test_vat.py +++ b/erpnext/regional/france/tests/test_vat.py @@ -86,6 +86,7 @@ class TestVATForFrance(IntegrationTestCase): self.assertIn("445642", all_accounts) company = frappe.get_doc("Company", TestVATForFrance.company) + company.suspense_accounts_settings = [] company.save() for row in company.suspense_accounts_settings: self.assertEqual(row.vat_suspense_account[:4], row.vat_control_account[:4]) @@ -93,6 +94,7 @@ class TestVATForFrance(IntegrationTestCase): @IntegrationTestCase.change_settings("Accounts Settings", {"mandatory_accounting_journal": 0}) def test_vat_on_sales_payments(self): company = frappe.get_doc("Company", TestVATForFrance.company) + company.suspense_accounts_settings = [] company.save() company_abbr = company.abbr @@ -152,7 +154,7 @@ class TestVATForFrance(IntegrationTestCase): create_sales_tax_templates(company) create_test_items(company) - + company.suspense_accounts_settings = [] company.save() si = create_sales_invoice( @@ -221,6 +223,7 @@ class TestVATForFrance(IntegrationTestCase): create_purchase_tax_templates(company) + company.suspense_accounts_settings = [] company.save() company_abbr = company.abbr -- GitLab