diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 586e465026e16b9ac9c9fad629bdf858302470c7..378bba93d6438d58f83ec3b1326c416510ca507f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -275,6 +275,7 @@ erpnext.patches.dokos.v3_0.single_accounting_journal_for_bank_transactions execute:frappe.rename_doc("Report", "Fichier des Ecritures Comptables [FEC]", "Fichier des Ecritures Comptables", force=True) erpnext.patches.dokos.v4_0.remove_payment_gateway_custom_fields erpnext.patches.dokos.v4_0.migrate_integration_references_to_customer +erpnext.patches.dokos.v4_0.reset_itemised_taxes_for_multi_vat [post_model_sync] diff --git a/erpnext/patches/dokos/v4_0/reset_itemised_taxes_for_multi_vat.py b/erpnext/patches/dokos/v4_0/reset_itemised_taxes_for_multi_vat.py new file mode 100644 index 0000000000000000000000000000000000000000..90943d46825cdd18e78a57cbed17c54bd356a1ab --- /dev/null +++ b/erpnext/patches/dokos/v4_0/reset_itemised_taxes_for_multi_vat.py @@ -0,0 +1,101 @@ +import json + +import frappe +from frappe.utils import flt + +from erpnext.controllers.taxes_and_totals import get_itemised_tax + + +def execute(): + frappe.db.auto_commit_on_many_writes = 1 + for key, value in { + "Sales Invoice": "Sales Taxes and Charges", + "Purchase Invoice": "Purchase Taxes and Charges", + }.items(): + for si in frappe.get_all( + value, filters={"rate": 0.0, "docstatus": 1, "parenttype": key}, pluck="parent", distinct=True + ): + doc = frappe.get_doc(key, si) + update_itemised_tax_data(doc) + frappe.db.auto_commit_on_many_writes = 0 + + +def update_itemised_tax_data(doc): + if not doc.taxes: + return + + itemised_tax = get_itemised_tax(doc.taxes, True) + + # Remove non tax fees + tax_accounts = set( + itemised_tax[item][tax].get("tax_account") for item in itemised_tax for tax in itemised_tax[item] + ) + tax_accounts = frappe.get_all( + "Account", + filters={"name": ("in", list(tax_accounts)), "account_type": "Tax"}, + fields=["name", "account_number"], + ) + valid_tax_accounts = [t.name for t in tax_accounts] + account_numbers = {t.name: t.accounr_number for t in tax_accounts} + + valid_itemised_tax = {} + for item in itemised_tax: + valid_itemised_tax[item] = {} + for tax in itemised_tax[item]: + if itemised_tax[item][tax].get("tax_account") in valid_tax_accounts and ( + itemised_tax[item][tax].get("tax_rate") or itemised_tax[item][tax].get("tax_amount") + ): + valid_itemised_tax[item][tax] = itemised_tax[item][tax] + + for row in doc.items: + tax_rate = 0.0 + item_tax_rate = 0.0 + item_specific_rates = [] + + if row.item_tax_rate: + item_tax_rate = frappe.parse_json(row.item_tax_rate) + + # First check if tax rate is present + # If not then look up in item_wise_tax_detail + + if item_tax_rate: + for tax in item_tax_rate: + tax_rate += tax.get("rate") + elif row.item_code and valid_itemised_tax.get(row.item_code): + item_specific_rates = [ + tax + for tax in valid_itemised_tax.get(row.item_code).items() + if flt(tax[1].get("form_rate", 0)) != 0.0 + ] + + tax_rate = sum( + [ + tax.get("tax_rate", 0) * (-1 if tax.get("add_deduct_tax") == "Deduct" else 1) + for d, tax in (item_specific_rates or valid_itemised_tax.get(row.item_code, {}).items()) + ] + ) + + row_tax_rate = flt(tax_rate, row.precision("tax_rate")) + row.tax_rate = row_tax_rate + frappe.db.set_value(row.doctype, row.name, "tax_rate", row_tax_rate) + row_tax_amount = flt((row.base_net_amount * tax_rate) / 100, row.precision("base_net_amount")) + row.tax_amount = row_tax_amount + frappe.db.set_value(row.doctype, row.name, "tax_amount", row_tax_amount) + row_total_amount = flt((row.base_net_amount + row.tax_amount), row.precision("total_amount")) + row.total_amount = row_total_amount + frappe.db.set_value(row.doctype, row.name, "total_amount", row_total_amount) + + item_tax_rate = json.dumps( + [ + { + "account": tax.get("tax_account"), + "account_number": account_numbers.get(tax.get("tax_account")), + "rate": tax.get("tax_rate", 0) * (-1 if tax.get("add_deduct_tax") == "Deduct" else 1), + "taxable_amount": row.get("base_net_amount"), + "tax_amount": row.get("tax_amount"), + } + for d, tax in (item_specific_rates or valid_itemised_tax.get(row.item_code, {}).items()) + ] + ) + row.item_tax_rate = item_tax_rate + frappe.db.set_value(row.doctype, row.name, "item_tax_rate", item_tax_rate) diff --git a/erpnext/regional/doctype/france_tax_declaration_preparation/france_tax_declaration_preparation.py b/erpnext/regional/doctype/france_tax_declaration_preparation/france_tax_declaration_preparation.py index 1f0c0eb05136549042b4ff5ca8646a4eec8a1149..3c328bab5ff8e9917c54f3dd29945ceee64481bf 100644 --- a/erpnext/regional/doctype/france_tax_declaration_preparation/france_tax_declaration_preparation.py +++ b/erpnext/regional/doctype/france_tax_declaration_preparation/france_tax_declaration_preparation.py @@ -88,7 +88,10 @@ class FranceTaxDeclarationPreparation(Document): # Handle UE witholding taxes if not gl_entry_line["tax_amount"] and gl_entry_line["tax_rate"] != 0.0: gl_entry_line["tax_amount"] = flt( - flt(gl_entry_line["taxable_amount"]) * flt(gl_entry_line["tax_rate"]) / 100.0, 2 + flt(gl_entry_line["taxable_amount"]) + * flt(gl_entry_line["tax_rate"]) + / 100.0, + 2, ) tax_amount += gl_entry_line["tax_amount"] @@ -122,9 +125,9 @@ class FranceTaxDeclarationPreparation(Document): ) voucher_gls.append(gl_entry_line) - elif len( - [e for e in linked_entries if e.account in list(deductible_accounts.keys())] - ) == 1 and [e for e in linked_entries if e.account in expense_accounts]: + elif len([e for e in linked_entries if e.account in list(deductible_accounts.keys())]) == 1 and [ + e for e in linked_entries if e.account in expense_accounts + ]: gl_entry["taxable_amount"] = sum( flt(e.debit) - flt(e.credit) for e in linked_entries if e.account in expense_accounts ) @@ -212,9 +215,10 @@ class FranceTaxDeclarationPreparation(Document): # Handle UE witholding taxes if not gl_entry_line["tax_amount"] and gl_entry_line["tax_rate"] != 0.0: - gl_entry_line["tax_rate"] = gl_entry_line["tax_rate"] * -1 gl_entry_line["tax_amount"] = ( - flt(gl_entry_line["taxable_amount"]) * flt(gl_entry_line["tax_rate"]) / 100.0 + flt(gl_entry_line["taxable_amount"]) + * flt(gl_entry_line["tax_rate"]) + / 100.0 ) tax_amount += gl_entry_line["tax_amount"] @@ -234,7 +238,8 @@ class FranceTaxDeclarationPreparation(Document): doc.taxes[cint(tax_row.row_id) - 1].get("base_tax_amount") ) gl_entry_line["tax_amount"] = flt( - flt(gl_entry_line["taxable_amount"]) * flt(gl_entry_line["tax_rate"]) / 100.0, 2 + flt(gl_entry_line["taxable_amount"]) * flt(gl_entry_line["tax_rate"]) / 100.0, + 2, ) voucher_gls.append(gl_entry_line) tax_amount += gl_entry_line["tax_amount"] @@ -249,9 +254,9 @@ class FranceTaxDeclarationPreparation(Document): ) voucher_gls.append(gl_entry_line) - elif len( - [e for e in linked_entries if e.account in list(collection_accounts.keys())] - ) == 1 and [e for e in linked_entries if e.account in income_accounts]: + elif len([e for e in linked_entries if e.account in list(collection_accounts.keys())]) == 1 and [ + e for e in linked_entries if e.account in income_accounts + ]: gl_entry["taxable_amount"] = sum( flt(e.credit) - flt(e.debit) for e in linked_entries if e.account in income_accounts ) @@ -351,9 +356,7 @@ class FranceTaxDeclarationPreparation(Document): ).run(as_dict=True) def get_deductible_accounts(self): - return { - t.name: t.tax_rate for t in self.get_vat_accounts() if t.account_number.startswith("4456") - } + return {t.name: t.tax_rate for t in self.get_vat_accounts() if t.account_number.startswith("4456")} def get_all_tax_accounts(self): return {t.name: t.tax_rate for t in self.get_vat_accounts()} @@ -412,7 +415,9 @@ class FranceTaxDeclarationPreparation(Document): "tax_rate": f"{rate} %", "taxable_amount": fmt_money(values.get("taxable_amount"), currency=default_currency), "tax_amount": fmt_money(values.get("tax_amount"), currency=default_currency), - "adjustment_amount": fmt_money(values.get("adjustment_amount"), currency=default_currency), + "adjustment_amount": fmt_money( + values.get("adjustment_amount"), currency=default_currency + ), } ) @@ -441,7 +446,9 @@ class FranceTaxDeclarationPreparation(Document): "tax_rate": f"{rate} %", "taxable_amount": fmt_money(values.get("taxable_amount"), currency=default_currency), "tax_amount": fmt_money(values.get("tax_amount"), currency=default_currency), - "adjustment_amount": fmt_money(values.get("adjustment_amount"), currency=default_currency), + "adjustment_amount": fmt_money( + values.get("adjustment_amount"), currency=default_currency + ), } ) diff --git a/erpnext/regional/france/taxes.py b/erpnext/regional/france/taxes.py index 47e68503a70ee41993338eb1ad24505faff62aa4..9420e865786f20b132e3a876d234336166b4fbf6 100644 --- a/erpnext/regional/france/taxes.py +++ b/erpnext/regional/france/taxes.py @@ -26,7 +26,9 @@ def update_itemised_tax_data(doc): for item in itemised_tax: valid_itemised_tax[item] = {} for tax in itemised_tax[item]: - if itemised_tax[item][tax].get("tax_account") in valid_tax_accounts: + if itemised_tax[item][tax].get("tax_account") in valid_tax_accounts and ( + itemised_tax[item][tax].get("tax_rate") or itemised_tax[item][tax].get("tax_amount") + ): valid_itemised_tax[item][tax] = itemised_tax[item][tax] for row in doc.items: