diff --git a/csf_ke/csf_ke/company.js b/csf_ke/csf_ke/company.js new file mode 100644 index 00000000..bda978a7 --- /dev/null +++ b/csf_ke/csf_ke/company.js @@ -0,0 +1,84 @@ +frappe.ui.form.on("Company", { + refresh: function(frm) { + toggle_withholding_fields(frm, "tax"); + toggle_withholding_fields(frm, "vat"); + }, + + withholding_tax_party_wise: function(frm) { + enforce_withholding_mode(frm, "tax", "party_wise"); + toggle_withholding_fields(frm, "tax"); + }, + + withholding_tax_no_party: function(frm) { + enforce_withholding_mode(frm, "tax", "no_party"); + toggle_withholding_fields(frm, "tax"); + }, + + withholding_vat_party_wise: function(frm) { + enforce_withholding_mode(frm, "vat", "party_wise"); + toggle_withholding_fields(frm, "vat"); + }, + + withholding_vat_no_party: function(frm) { + enforce_withholding_mode(frm, "vat", "no_party"); + toggle_withholding_fields(frm, "vat"); + }, + + validate: function(frm) { + validate_withholding_mode(frm, "tax"); + validate_withholding_mode(frm, "vat"); + }, +}); + +function enforce_withholding_mode(frm, kind, selected_mode) { + const selected_fieldname = `withholding_${kind}_${selected_mode}`; + const other_mode = selected_mode === "party_wise" ? "no_party" : "party_wise"; + const other_fieldname = `withholding_${kind}_${other_mode}`; + + if (frm.doc[selected_fieldname]) { + frm.set_value(other_fieldname, 0); + } +} + +function toggle_withholding_fields(frm, kind) { + const party_wise = Boolean(frm.doc[`withholding_${kind}_party_wise`]); + const no_party = Boolean(frm.doc[`withholding_${kind}_no_party`]); + + [ + `withholding_${kind}_party_wise_payable_account`, + `withholding_${kind}_party_wise_receivable_account`, + ].forEach((fieldname) => { + frm.toggle_display(fieldname, party_wise); + }); + + [ + `withholding_${kind}_no_party_payable_account`, + `withholding_${kind}_no_party_receivable_account`, + ].forEach((fieldname) => { + frm.toggle_display(fieldname, no_party); + }); +} + +function validate_withholding_mode(frm, kind) { + const has_account = Boolean( + frm.doc[`withholding_${kind}_party_wise_payable_account`] || + frm.doc[`withholding_${kind}_no_party_payable_account`] || + frm.doc[`withholding_${kind}_party_wise_receivable_account`] || + frm.doc[`withholding_${kind}_no_party_receivable_account`] + ); + + const party_wise = Boolean(frm.doc[`withholding_${kind}_party_wise`]); + const no_party = Boolean(frm.doc[`withholding_${kind}_no_party`]); + + if (!has_account) { + return; + } + + if ((party_wise && no_party) || (!party_wise && !no_party)) { + frappe.throw( + __("Please choose exactly one option for Withholding {0}: Party Wise or No Party.", [ + kind.toUpperCase(), + ]) + ); + } +} diff --git a/csf_ke/csf_ke/patches/withholding_tax_and_vat.py b/csf_ke/csf_ke/patches/withholding_tax_and_vat.py new file mode 100644 index 00000000..005a7b9e --- /dev/null +++ b/csf_ke/csf_ke/patches/withholding_tax_and_vat.py @@ -0,0 +1,392 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + + +def execute(): + custom_fields = { + "Company": [ + { + "fieldname": "withholding_tax_section", + "fieldtype": "Section Break", + "insert_after": "default_creditors_withholding_account", + "label": "Withholding Tax", + }, + { + "fieldname": "withholding_tax_party_wise", + "fieldtype": "Check", + "insert_after": "withholding_tax_section", + "label": "Withholding TAX Party Wise", + "default": "", + }, + { + "fieldname": "withholding_tax_no_party", + "fieldtype": "Check", + "insert_after": "withholding_tax_party_wise", + "label": "Withholding TAX No Party", + "default": "", + }, + { + "fieldname": "withholding_tax_party_accounts_section", + "fieldtype": "Section Break", + "insert_after": "withholding_tax_no_party", + "label": "Party Wise Accounts", + "depends_on": "eval:doc.withholding_tax_party_wise", + }, + { + "fieldname": "withholding_tax_party_wise_payable_account", + "fieldtype": "Link", + "insert_after": "withholding_tax_party_accounts_section", + "label": "Purchase Withholding Tax Account (Party Wise)", + "options": "Account", + "depends_on": "eval:doc.withholding_tax_party_wise", + }, + { + "fieldname": "withholding_tax_party_wise_column_break", + "fieldtype": "Column Break", + "insert_after": "withholding_tax_party_wise_payable_account", + }, + { + "fieldname": "withholding_tax_party_wise_receivable_account", + "fieldtype": "Link", + "insert_after": "withholding_tax_party_wise_column_break", + "label": "Sales Withholding Tax Account (Party Wise)", + "options": "Account", + "depends_on": "eval:doc.withholding_tax_party_wise", + }, + { + "fieldname": "withholding_tax_no_party_section", + "fieldtype": "Section Break", + "insert_after": "withholding_tax_party_wise_receivable_account", + "label": "No Party Accounts", + "depends_on": "eval:doc.withholding_tax_no_party", + }, + { + "fieldname": "withholding_tax_no_party_payable_account", + "fieldtype": "Link", + "insert_after": "withholding_tax_no_party_section", + "label": "Purchase Withholding Tax Account (No Party)", + "options": "Account", + "depends_on": "eval:doc.withholding_tax_no_party", + }, + { + "fieldname": "withholding_tax_no_party_column_break", + "fieldtype": "Column Break", + "insert_after": "withholding_tax_no_party_payable_account", + }, + { + "fieldname": "withholding_tax_no_party_receivable_account", + "fieldtype": "Link", + "insert_after": "withholding_tax_no_party_column_break", + "label": "Sales Withholding Tax Account (No Party)", + "options": "Account", + "depends_on": "eval:doc.withholding_tax_no_party", + }, + { + "fieldname": "withholding_vat_section", + "fieldtype": "Section Break", + "insert_after": "withholding_tax_no_party_receivable_account", + "label": "Withholding VAT", + }, + { + "fieldname": "withholding_vat_party_wise", + "fieldtype": "Check", + "insert_after": "withholding_vat_section", + "label": "Withholding VAT Party Wise", + "default": "", + }, + { + "fieldname": "withholding_vat_no_party", + "fieldtype": "Check", + "insert_after": "withholding_vat_party_wise", + "label": "Withholding VAT No Party", + "default": "", + }, + { + "fieldname": "withholding_vat_party_accounts_section", + "fieldtype": "Section Break", + "insert_after": "withholding_vat_no_party", + "label": "Party Wise Accounts", + "depends_on": "eval:doc.withholding_vat_party_wise", + }, + { + "fieldname": "withholding_vat_party_wise_payable_account", + "fieldtype": "Link", + "insert_after": "withholding_vat_party_accounts_section", + "label": "Purchase Withholding VAT Account (Party Wise)", + "options": "Account", + "depends_on": "eval:doc.withholding_vat_party_wise", + }, + { + "fieldname": "withholding_vat_party_wise_column_break", + "fieldtype": "Column Break", + "insert_after": "withholding_vat_party_wise_payable_account", + }, + { + "fieldname": "withholding_vat_party_wise_receivable_account", + "fieldtype": "Link", + "insert_after": "withholding_vat_party_wise_column_break", + "label": "Sales Withholding VAT Account (Party Wise)", + "options": "Account", + "depends_on": "eval:doc.withholding_vat_party_wise", + }, + { + "fieldname": "withholding_vat_no_party_section", + "fieldtype": "Section Break", + "insert_after": "withholding_vat_party_wise_receivable_account", + "label": "No Party Accounts", + "depends_on": "eval:doc.withholding_vat_no_party", + }, + { + "fieldname": "withholding_vat_no_party_payable_account", + "fieldtype": "Link", + "insert_after": "withholding_vat_no_party_section", + "label": "Purchase Withholding VAT Account (No Party)", + "options": "Account", + "depends_on": "eval:doc.withholding_vat_no_party", + }, + { + "fieldname": "withholding_vat_no_party_column_break", + "fieldtype": "Column Break", + "insert_after": "withholding_vat_no_party_payable_account", + }, + { + "fieldname": "withholding_vat_no_party_receivable_account", + "fieldtype": "Link", + "insert_after": "withholding_vat_no_party_column_break", + "label": "Sales Withholding VAT Account (No Party)", + "options": "Account", + "depends_on": "eval:doc.withholding_vat_no_party", + }, + { + "fieldname": "enable_purchase_withholding_section", + "fieldtype": "Section Break", + "insert_after": "withholding_vat_no_party_receivable_account", + "label": "Automation", + }, + { + "fieldname": "auto_create_purchase_withholding_je", + "fieldtype": "Check", + "insert_after": "enable_purchase_withholding_section", + "label": "Auto Create Purchase Withholding JE", + "default": "", + }, + { + "fieldname": "auto_submit_purchase_withholding_je", + "fieldtype": "Check", + "insert_after": "auto_create_purchase_withholding_je", + "label": "Auto Submit Purchase Withholding JE", + "default": "", + }, + { + "fieldname": "withholding_auto_column_break", + "fieldtype": "Column Break", + "insert_after": "auto_submit_purchase_withholding_je", + }, + { + "fieldname": "auto_create_sales_withholding_je", + "fieldtype": "Check", + "insert_after": "withholding_auto_column_break", + "label": "Auto Create Sales Withholding JE", + "default": "", + }, + { + "fieldname": "auto_submit_sales_withholding_je", + "fieldtype": "Check", + "insert_after": "auto_create_sales_withholding_je", + "label": "Auto Submit Sales Withholding JE", + "default": "", + }, + ], + "Item": [ + { + "fieldname": "withholding_tax_rate_on_purchase", + "fieldtype": "Percent", + "insert_after": "stock_uom", + "label": "Withholding Tax Rate on Purchase", + }, + { + "fieldname": "withholding_tax_rate_on_sales", + "fieldtype": "Percent", + "insert_after": "withholding_tax_rate_on_purchase", + "label": "Withholding Tax Rate on Sales", + }, + ], + "Customer": [ + { + "fieldname": "withholding_vat_rate", + "fieldtype": "Percent", + "insert_after": "tax_id", + "label": "Withholding VAT Rate", + }, + ], + "Supplier": [ + { + "fieldname": "withholding_vat_rate", + "fieldtype": "Percent", + "insert_after": "tax_id", + "label": "Withholding VAT Rate", + }, + ], + "Purchase Invoice Item": [ + { + "fieldname": "withholding_tax_rate", + "fieldtype": "Percent", + "insert_after": "item_tax_template", + "label": "Withholding Tax Rate", + "fetch_from": "item_code.withholding_tax_rate_on_purchase", + "fetch_if_empty": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_tax_amount", + "fieldtype": "Currency", + "insert_after": "withholding_tax_rate", + "label": "Withholding Tax Amount", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_tax_entry", + "fieldtype": "Link", + "insert_after": "withholding_tax_amount", + "label": "Withholding Tax Entry", + "options": "Journal Entry", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "csf_ke_wtax_je_created", + "fieldtype": "Check", + "insert_after": "withholding_tax_entry", + "label": "CSF KE Wtax JE Created", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + "default": "0", + }, + ], + "Sales Invoice Item": [ + { + "fieldname": "withholding_tax_rate", + "fieldtype": "Percent", + "insert_after": "item_tax_template", + "label": "Withholding Tax Rate", + "fetch_from": "item_code.withholding_tax_rate_on_sales", + "fetch_if_empty": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_tax_amount", + "fieldtype": "Currency", + "insert_after": "withholding_tax_rate", + "label": "Withholding Tax Amount", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_tax_entry", + "fieldtype": "Link", + "insert_after": "withholding_tax_amount", + "label": "Withholding Tax Entry", + "options": "Journal Entry", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "csf_ke_wtax_je_created", + "fieldtype": "Check", + "insert_after": "withholding_tax_entry", + "label": "CSF KE Wtax JE Created", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + "default": "0", + }, + ], + "Purchase Invoice": [ + { + "fieldname": "withholding_vat_rate", + "fieldtype": "Percent", + "insert_after": "is_filed", + "label": "Withholding VAT Rate", + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_vat_amount", + "fieldtype": "Currency", + "insert_after": "withholding_vat_rate", + "label": "Withholding VAT Amount", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_vat_entry", + "fieldtype": "Link", + "insert_after": "withholding_vat_amount", + "label": "Withholding VAT Entry", + "options": "Journal Entry", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "csf_ke_wvat_je_created", + "fieldtype": "Check", + "insert_after": "withholding_vat_entry", + "label": "CSF KE Wvat JE Created", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + "default": "0", + }, + ], + "Sales Invoice": [ + { + "fieldname": "withholding_vat_rate", + "fieldtype": "Percent", + "insert_after": "is_filed", + "label": "Withholding VAT Rate", + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_vat_amount", + "fieldtype": "Currency", + "insert_after": "withholding_vat_rate", + "label": "Withholding VAT Amount", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "withholding_vat_entry", + "fieldtype": "Link", + "insert_after": "withholding_vat_amount", + "label": "Withholding VAT Entry", + "options": "Journal Entry", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + }, + { + "fieldname": "csf_ke_wvat_je_created", + "fieldtype": "Check", + "insert_after": "withholding_vat_entry", + "label": "CSF KE Wvat JE Created", + "read_only": 1, + "allow_on_submit": 1, + "no_copy": 1, + "default": "0", + }, + ], + } + + create_custom_fields(custom_fields, update=True) diff --git a/csf_ke/csf_ke/purchase_invoice.js b/csf_ke/csf_ke/purchase_invoice.js new file mode 100644 index 00000000..0b8abcc2 --- /dev/null +++ b/csf_ke/csf_ke/purchase_invoice.js @@ -0,0 +1,23 @@ +frappe.ui.form.on("Purchase Invoice", { + supplier: function(frm) { + if (!frm.doc.supplier) { + return; + } + + frappe.db.get_value("Supplier", frm.doc.supplier, "withholding_vat_rate").then((r) => { + frm.set_value("withholding_vat_rate", flt(r.message.withholding_vat_rate || 0)); + frm.trigger("set_withholding_vat_amount"); + }); + }, + + validate: function(frm) { + frm.trigger("set_withholding_vat_amount"); + }, + + set_withholding_vat_amount: function(frm) { + frm.set_value( + "withholding_vat_amount", + flt(frm.doc.net_total) * flt(frm.doc.withholding_vat_rate) / 100 + ); + }, +}); diff --git a/csf_ke/csf_ke/sales_invoice.js b/csf_ke/csf_ke/sales_invoice.js new file mode 100644 index 00000000..fa1373e6 --- /dev/null +++ b/csf_ke/csf_ke/sales_invoice.js @@ -0,0 +1,23 @@ +frappe.ui.form.on("Sales Invoice", { + customer: function(frm) { + if (!frm.doc.customer) { + return; + } + + frappe.db.get_value("Customer", frm.doc.customer, "withholding_vat_rate").then((r) => { + frm.set_value("withholding_vat_rate", flt(r.message.withholding_vat_rate || 0)); + frm.trigger("set_withholding_vat_amount"); + }); + }, + + validate: function(frm) { + frm.trigger("set_withholding_vat_amount"); + }, + + set_withholding_vat_amount: function(frm) { + frm.set_value( + "withholding_vat_amount", + flt(frm.doc.net_total) * flt(frm.doc.withholding_vat_rate) / 100 + ); + }, +}); diff --git a/csf_ke/custom_api.py b/csf_ke/custom_api.py new file mode 100644 index 00000000..ae90be3e --- /dev/null +++ b/csf_ke/custom_api.py @@ -0,0 +1,312 @@ +import frappe +from frappe import _ +from frappe.utils import cint, flt + + +def _get_exchange_rate(doc): + default_currency = frappe.get_value("Company", doc.company, "default_currency") + exchange_rate = 1 if doc.party_account_currency == default_currency else doc.conversion_rate + return default_currency, exchange_rate + + +def _get_party_mode_values(doc, party_wise_field, no_party_field): + party_wise = cint(frappe.get_value("Company", doc.company, party_wise_field) or 0) + no_party = cint(frappe.get_value("Company", doc.company, no_party_field) or 0) + + if party_wise and no_party: + frappe.throw(_("Please select only one mode for {0}").format(party_wise_field)) + + return "With Party" if party_wise else "No Party" + + +def _get_withholding_account(doc, tax_type, posting_side, mode): + mode_map = { + "With Party": "party_wise", + "No Party": "no_party", + } + fieldname = f"withholding_{tax_type}_{mode_map.get(mode, mode.lower().replace(' ', '_'))}_{posting_side}_account" + return frappe.get_value("Company", doc.company, fieldname) + + +def _cancel_journal_entry(journal_entry_name): + if not journal_entry_name: + return + + try: + je = frappe.get_doc("Journal Entry", journal_entry_name) + except frappe.DoesNotExistError: + return + + if je.docstatus == 1: + je.flags.ignore_permissions = True + frappe.flags.ignore_account_permission = True + je.cancel() + + +def _get_invoice_withholding_vat_rate(doctype, party): + if not party: + return 0 + + party_doctype = "Customer" if doctype == "Sales Invoice" else "Supplier" + return flt(frappe.get_value(party_doctype, party, "withholding_vat_rate") or 0) + + +def _set_item_withholding_tax_values(doc, item_rate_field): + for item in doc.items: + if item.item_code and not flt(item.withholding_tax_rate): + item.withholding_tax_rate = flt( + frappe.get_value("Item", item.item_code, item_rate_field) or 0 + ) + + item.withholding_tax_amount = flt(item.base_net_amount) * flt(item.withholding_tax_rate) / 100 + + +def _create_journal_entry(company, posting_date, accounts, user_remark, auto_submit): + je_doc = frappe.get_doc( + { + "doctype": "Journal Entry", + "voucher_type": "Contra Entry", + "posting_date": posting_date, + "accounts": accounts, + "company": company, + "user_remark": user_remark, + } + ) + je_doc.flags.ignore_permissions = True + frappe.flags.ignore_account_permission = True + je_doc.insert() + + if auto_submit: + je_doc.submit() + + return je_doc + + +@frappe.whitelist() +def prepare_purchase_withholding_values(doc, method=None): + doc.withholding_vat_rate = _get_invoice_withholding_vat_rate(doc.doctype, doc.supplier) + doc.withholding_vat_amount = flt(doc.base_net_total) * flt(doc.withholding_vat_rate) / 100 + _set_item_withholding_tax_values(doc, "withholding_tax_rate_on_purchase") + + +@frappe.whitelist() +def prepare_sales_withholding_values(doc, method=None): + doc.withholding_vat_rate = _get_invoice_withholding_vat_rate(doc.doctype, doc.customer) + doc.withholding_vat_amount = flt(doc.base_net_total) * flt(doc.withholding_vat_rate) / 100 + _set_item_withholding_tax_values(doc, "withholding_tax_rate_on_sales") + + +@frappe.whitelist() +def make_purchase_withholding_journal_entries(doc, method=None): + auto_create = cint(frappe.get_value("Company", doc.company, "auto_create_purchase_withholding_je") or 0) + if not auto_create: + return + + auto_submit = cint(frappe.get_value("Company", doc.company, "auto_submit_purchase_withholding_je") or 0) + default_currency, exchange_rate = _get_exchange_rate(doc) + float_precision = cint(frappe.db.get_default("float_precision")) or 3 + + wtax_mode = _get_party_mode_values( + doc, + "withholding_tax_party_wise", + "withholding_tax_no_party", + ) + wvat_mode = _get_party_mode_values( + doc, + "withholding_vat_party_wise", + "withholding_vat_no_party", + ) + wtax_payable_account = _get_withholding_account(doc, "tax", "payable", wtax_mode) + wvat_payable_account = _get_withholding_account(doc, "vat", "payable", wvat_mode) + + for item in doc.items: + if not flt(item.withholding_tax_rate) or cint(item.csf_ke_wtax_je_created): + continue + + if not wtax_payable_account: + frappe.throw(_("Please set Default Withholding Tax Payable Account in Company {0}").format(doc.company)) + + creditor_amount = flt(item.withholding_tax_amount / exchange_rate, float_precision) + wtax_base_amount = creditor_amount * exchange_rate + + je_doc = _create_journal_entry( + doc.company, + doc.posting_date, + [ + { + "account": doc.credit_to, + "party_type": "Supplier", + "party": doc.supplier, + "debit_in_account_currency": creditor_amount, + "exchange_rate": exchange_rate, + "cost_center": item.cost_center, + "reference_type": "Purchase Invoice", + "reference_name": doc.name, + }, + { + "account": wtax_payable_account, + "party_type": "Supplier" if wtax_mode == "With Party" else "", + "party": doc.supplier if wtax_mode == "With Party" else "", + "credit_in_account_currency": wtax_base_amount, + "cost_center": item.cost_center, + "account_curremcy": default_currency, + }, + ], + "Withholding Tax on item {0} for {1}".format(item.item_code, doc.name), + auto_submit, + ) + + item.withholding_tax_entry = je_doc.name + item.csf_ke_wtax_je_created = 1 + item.db_update() + + if flt(doc.withholding_vat_amount) and not cint(doc.csf_ke_wvat_je_created): + if not wvat_payable_account: + frappe.throw(_("Please set Default Withholding VAT Payable Account in Company {0}").format(doc.company)) + + wvat_creditor_amount = flt(doc.withholding_vat_amount / exchange_rate, float_precision) + wvat_base_amount = wvat_creditor_amount * exchange_rate + + je_doc = _create_journal_entry( + doc.company, + doc.posting_date, + [ + { + "account": doc.credit_to, + "party_type": "Supplier", + "party": doc.supplier, + "debit_in_account_currency": wvat_creditor_amount, + "exchange_rate": exchange_rate, + "reference_type": "Purchase Invoice", + "reference_name": doc.name, + }, + { + "account": wvat_payable_account, + "party_type": "Supplier" if wvat_mode == "With Party" else "", + "party": doc.supplier if wvat_mode == "With Party" else "", + "credit_in_account_currency": wvat_base_amount, + "account_curremcy": default_currency, + }, + ], + "Withholding VAT on {0}".format(doc.name), + auto_submit, + ) + + doc.db_set("withholding_vat_entry", je_doc.name) + doc.db_set("csf_ke_wvat_je_created", 1) + + +@frappe.whitelist() +def make_sales_withholding_journal_entries(doc, method=None): + auto_create = cint(frappe.get_value("Company", doc.company, "auto_create_sales_withholding_je") or 0) + if not auto_create: + return + + auto_submit = cint(frappe.get_value("Company", doc.company, "auto_submit_sales_withholding_je") or 0) + default_currency, exchange_rate = _get_exchange_rate(doc) + float_precision = cint(frappe.db.get_default("float_precision")) or 3 + + wtax_mode = _get_party_mode_values( + doc, + "withholding_tax_party_wise", + "withholding_tax_no_party", + ) + wvat_mode = _get_party_mode_values( + doc, + "withholding_vat_party_wise", + "withholding_vat_no_party", + ) + wtax_receivable_account = _get_withholding_account(doc, "tax", "receivable", wtax_mode) + wvat_receivable_account = _get_withholding_account(doc, "vat", "receivable", wvat_mode) + + for item in doc.items: + if not flt(item.withholding_tax_rate) or cint(item.csf_ke_wtax_je_created): + continue + + if not wtax_receivable_account: + frappe.throw(_("Please set Default Withholding Tax Receivable Account in Company {0}").format(doc.company)) + + debtor_amount = flt(item.withholding_tax_amount / exchange_rate, float_precision) + wtax_base_amount = debtor_amount * exchange_rate + + je_doc = _create_journal_entry( + doc.company, + doc.posting_date, + [ + { + "account": doc.debit_to, + "party_type": "Customer", + "party": doc.customer, + "credit_in_account_currency": debtor_amount, + "exchange_rate": exchange_rate, + "cost_center": item.cost_center, + "reference_type": "Sales Invoice", + "reference_name": doc.name, + }, + { + "account": wtax_receivable_account, + "party_type": "Customer" if wtax_mode == "With Party" else "", + "party": doc.customer if wtax_mode == "With Party" else "", + "debit_in_account_currency": wtax_base_amount, + "cost_center": item.cost_center, + "account_curremcy": default_currency, + }, + ], + "Withholding Tax on item {0} for {1}".format(item.item_code, doc.name), + auto_submit, + ) + + item.withholding_tax_entry = je_doc.name + item.csf_ke_wtax_je_created = 1 + item.db_update() + + if flt(doc.withholding_vat_amount) and not cint(doc.csf_ke_wvat_je_created): + if not wvat_receivable_account: + frappe.throw(_("Please set Default Withholding VAT Receivable Account in Company {0}").format(doc.company)) + + wvat_debtor_amount = flt(doc.withholding_vat_amount / exchange_rate, float_precision) + wvat_base_amount = wvat_debtor_amount * exchange_rate + + je_doc = _create_journal_entry( + doc.company, + doc.posting_date, + [ + { + "account": doc.debit_to, + "party_type": "Customer", + "party": doc.customer, + "credit_in_account_currency": wvat_debtor_amount, + "exchange_rate": exchange_rate, + "reference_type": "Sales Invoice", + "reference_name": doc.name, + }, + { + "account": wvat_receivable_account, + "party_type": "Customer" if wvat_mode == "With Party" else "", + "party": doc.customer if wvat_mode == "With Party" else "", + "debit_in_account_currency": wvat_base_amount, + "account_curremcy": default_currency, + }, + ], + "Withholding VAT on {0}".format(doc.name), + auto_submit, + ) + + doc.db_set("withholding_vat_entry", je_doc.name) + doc.db_set("csf_ke_wvat_je_created", 1) + + +@frappe.whitelist() +def cancel_purchase_withholding_journal_entries(doc, method=None): + for item in doc.items: + _cancel_journal_entry(item.withholding_tax_entry) + + _cancel_journal_entry(doc.withholding_vat_entry) + + +@frappe.whitelist() +def cancel_sales_withholding_journal_entries(doc, method=None): + for item in doc.items: + _cancel_journal_entry(item.withholding_tax_entry) + + _cancel_journal_entry(doc.withholding_vat_entry) diff --git a/csf_ke/hooks.py b/csf_ke/hooks.py index b0e2c900..5c7ae932 100644 --- a/csf_ke/hooks.py +++ b/csf_ke/hooks.py @@ -62,7 +62,12 @@ # include js in doctype views -doctype_js = {"Customer": "csf_ke/overrides/customer.js"} +doctype_js = { + "Customer": "csf_ke/overrides/customer.js", + "Company": "csf_ke/company.js", + "Purchase Invoice": "csf_ke/purchase_invoice.js", + "Sales Invoice": "csf_ke/sales_invoice.js", +} # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} @@ -138,7 +143,14 @@ doc_events = { "Purchase Receipt": {"on_submit": "csf_ke.csf_ke.doctype.api.update_item_price_list.update_item_prices"}, - "Purchase Invoice": {"on_submit": "csf_ke.csf_ke.doctype.api.update_item_price_list.update_item_prices"}, + "Purchase Invoice": { + "validate": "csf_ke.custom_api.prepare_purchase_withholding_values", + "on_submit": [ + "csf_ke.csf_ke.doctype.api.update_item_price_list.update_item_prices", + "csf_ke.custom_api.make_purchase_withholding_journal_entries", + ], + "on_cancel": "csf_ke.custom_api.cancel_purchase_withholding_journal_entries", + }, "Item Group": {"before_save": "csf_ke.csf_ke.utils.get_tims_hscode.validate_mandatory_hscode"}, "Customer": {"before_save": "csf_ke.csf_ke.overrides.customer.validate_customer_kra"}, "Sales Order": { @@ -146,8 +158,13 @@ "on_submit": "csf_ke.csf_ke.utils.item_price.update_item_price", }, "Sales Invoice": { + "validate": "csf_ke.custom_api.prepare_sales_withholding_values", "before_submit": "csf_ke.csf_ke.overrides.sales_doc.validate_customer_kra", - "on_submit": "csf_ke.csf_ke.utils.item_price.update_item_price", + "on_submit": [ + "csf_ke.csf_ke.utils.item_price.update_item_price", + "csf_ke.custom_api.make_sales_withholding_journal_entries", + ], + "on_cancel": "csf_ke.custom_api.cancel_sales_withholding_journal_entries", }, "Delivery Note": { "on_submit": "csf_ke.csf_ke.utils.item_price.update_item_price", diff --git a/csf_ke/patches.txt b/csf_ke/patches.txt index a4ad56ab..26d5e07b 100644 --- a/csf_ke/patches.txt +++ b/csf_ke/patches.txt @@ -4,3 +4,4 @@ csf_ke.csf_ke.patches.remove_hscode_description # 006/06/25 011 csf_ke.csf_ke.patches.create_price_list [post_model_sync] +csf_ke.csf_ke.patches.withholding_tax_and_vat