mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-26 10:08:30 +00:00
feat: add validation for inter company transactions
This commit is contained in:
@@ -38,6 +38,11 @@
|
|||||||
"show_taxes_as_table_in_print",
|
"show_taxes_as_table_in_print",
|
||||||
"column_break_12",
|
"column_break_12",
|
||||||
"show_payment_schedule_in_print",
|
"show_payment_schedule_in_print",
|
||||||
|
"item_price_settings_section",
|
||||||
|
"maintain_same_internal_transaction_rate",
|
||||||
|
"column_break_feyo",
|
||||||
|
"maintain_same_rate_action",
|
||||||
|
"role_to_override_stop_action",
|
||||||
"currency_exchange_section",
|
"currency_exchange_section",
|
||||||
"allow_stale",
|
"allow_stale",
|
||||||
"column_break_yuug",
|
"column_break_yuug",
|
||||||
@@ -556,6 +561,37 @@
|
|||||||
"fieldname": "legacy_section",
|
"fieldname": "legacy_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Legacy Fields"
|
"label": "Legacy Fields"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "maintain_same_internal_transaction_rate",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Maintain Same Rate Throughout Internal Transaction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Stop",
|
||||||
|
"depends_on": "maintain_same_internal_transaction_rate",
|
||||||
|
"fieldname": "maintain_same_rate_action",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Action if Same Rate is Not Maintained Throughout Internal Transaction",
|
||||||
|
"mandatory_depends_on": "maintain_same_internal_transaction_rate",
|
||||||
|
"options": "Stop\nWarn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval: doc.maintain_same_internal_transaction_rate && doc.maintain_same_rate_action == 'Stop'",
|
||||||
|
"fieldname": "role_to_override_stop_action",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Role Allowed to Override Stop Action",
|
||||||
|
"options": "Role"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "item_price_settings_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Item Price Settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_feyo",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ class AccountsSettings(Document):
|
|||||||
general_ledger_remarks_length: DF.Int
|
general_ledger_remarks_length: DF.Int
|
||||||
ignore_account_closing_balance: DF.Check
|
ignore_account_closing_balance: DF.Check
|
||||||
ignore_is_opening_check_for_reporting: DF.Check
|
ignore_is_opening_check_for_reporting: DF.Check
|
||||||
|
maintain_same_internal_transaction_rate: DF.Check
|
||||||
|
maintain_same_rate_action: DF.Literal["Stop", "Warn"]
|
||||||
make_payment_via_journal_entry: DF.Check
|
make_payment_via_journal_entry: DF.Check
|
||||||
merge_similar_account_heads: DF.Check
|
merge_similar_account_heads: DF.Check
|
||||||
over_billing_allowance: DF.Currency
|
over_billing_allowance: DF.Currency
|
||||||
@@ -58,6 +60,7 @@ class AccountsSettings(Document):
|
|||||||
receivable_payable_remarks_length: DF.Int
|
receivable_payable_remarks_length: DF.Int
|
||||||
reconciliation_queue_size: DF.Int
|
reconciliation_queue_size: DF.Int
|
||||||
role_allowed_to_over_bill: DF.Link | None
|
role_allowed_to_over_bill: DF.Link | None
|
||||||
|
role_to_override_stop_action: DF.Link | None
|
||||||
round_row_wise_tax: DF.Check
|
round_row_wise_tax: DF.Check
|
||||||
show_balance_in_coa: DF.Check
|
show_balance_in_coa: DF.Check
|
||||||
show_inclusive_tax_in_print: DF.Check
|
show_inclusive_tax_in_print: DF.Check
|
||||||
|
|||||||
@@ -230,6 +230,8 @@ class AccountsController(TransactionBase):
|
|||||||
self.validate_party_accounts()
|
self.validate_party_accounts()
|
||||||
|
|
||||||
self.validate_inter_company_reference()
|
self.validate_inter_company_reference()
|
||||||
|
# validate inter company transaction rate
|
||||||
|
self.validate_internal_transaction()
|
||||||
|
|
||||||
self.disable_pricing_rule_on_internal_transfer()
|
self.disable_pricing_rule_on_internal_transfer()
|
||||||
self.disable_tax_included_prices_for_internal_transfer()
|
self.disable_tax_included_prices_for_internal_transfer()
|
||||||
@@ -740,6 +742,91 @@ class AccountsController(TransactionBase):
|
|||||||
msg = f"At Row {row.idx}: The field {bold(label)} is mandatory for internal transfer"
|
msg = f"At Row {row.idx}: The field {bold(label)} is mandatory for internal transfer"
|
||||||
frappe.throw(_(msg), title=_("Internal Transfer Reference Missing"))
|
frappe.throw(_(msg), title=_("Internal Transfer Reference Missing"))
|
||||||
|
|
||||||
|
def validate_internal_transaction(self):
|
||||||
|
if not cint(
|
||||||
|
frappe.db.get_single_value("Accounts Settings", "maintain_same_internal_transaction_rate")
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
doctypes_list = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"]
|
||||||
|
|
||||||
|
if self.doctype in doctypes_list and (
|
||||||
|
self.get("is_internal_customer") or self.get("is_internal_supplier")
|
||||||
|
):
|
||||||
|
self.validate_internal_transaction_based_on_voucher_type()
|
||||||
|
|
||||||
|
def validate_internal_transaction_based_on_voucher_type(self):
|
||||||
|
order = ["Sales Order", "Purchase Order"]
|
||||||
|
invoice = ["Sales Invoice", "Purchase Invoice"]
|
||||||
|
|
||||||
|
if self.doctype in order and self.get("inter_company_order_reference"):
|
||||||
|
# Fetch the linked order
|
||||||
|
linked_doctype = "Sales Order" if self.doctype == "Purchase Order" else "Purchase Order"
|
||||||
|
self.validate_line_items(
|
||||||
|
linked_doctype,
|
||||||
|
"sales_order" if linked_doctype == "Sales Order" else "purchase_order",
|
||||||
|
"sales_order_item" if linked_doctype == "Sales Order" else "purchase_order_item",
|
||||||
|
)
|
||||||
|
elif self.doctype in invoice and self.get("inter_company_invoice_reference"):
|
||||||
|
# Fetch the linked invoice
|
||||||
|
linked_doctype = "Sales Invoice" if self.doctype == "Purchase Invoice" else "Purchase Invoice"
|
||||||
|
self.validate_line_items(
|
||||||
|
linked_doctype,
|
||||||
|
"sales_invoice" if linked_doctype == "Sales Invoice" else "purchase_invoice",
|
||||||
|
"sales_invoice_item" if linked_doctype == "Sales Invoice" else "purchase_invoice_item",
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_line_items(self, ref_dt, ref_dn_field, ref_link_field):
|
||||||
|
action, role_allowed_to_override = frappe.get_cached_value(
|
||||||
|
"Accounts Settings", "None", ["maintain_same_rate_action", "role_to_override_stop_action"]
|
||||||
|
)
|
||||||
|
|
||||||
|
reference_names = [d.get(ref_link_field) for d in self.get("items") if d.get(ref_link_field)]
|
||||||
|
reference_details = self.get_reference_details(reference_names, ref_dt + " Item")
|
||||||
|
|
||||||
|
stop_actions = []
|
||||||
|
|
||||||
|
for d in self.get("items"):
|
||||||
|
if d.get(ref_link_field):
|
||||||
|
ref_rate = reference_details.get(d.get(ref_link_field))
|
||||||
|
if ref_rate is not None and abs(flt(d.rate - ref_rate, d.precision("rate"))) >= 0.01:
|
||||||
|
if action == "Stop":
|
||||||
|
user_roles = [
|
||||||
|
r["role"]
|
||||||
|
for r in frappe.get_all(
|
||||||
|
"Has Role", filters={"parent": frappe.session.user}, fields=["role"]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if role_allowed_to_override not in user_roles:
|
||||||
|
stop_actions.append(
|
||||||
|
_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
|
||||||
|
d.idx,
|
||||||
|
ref_dt,
|
||||||
|
self.inter_company_invoice_reference
|
||||||
|
if d.parenttype in ("Sales Invoice", "Purchase Invoice")
|
||||||
|
else d.get(ref_dn_field),
|
||||||
|
d.rate,
|
||||||
|
ref_rate,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
frappe.msgprint(
|
||||||
|
_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
|
||||||
|
d.idx,
|
||||||
|
ref_dt,
|
||||||
|
self.inter_company_invoice_reference
|
||||||
|
if d.parenttype in ("Sales Invoice", "Purchase Invoice")
|
||||||
|
else d.get(ref_dn_field),
|
||||||
|
d.rate,
|
||||||
|
ref_rate,
|
||||||
|
),
|
||||||
|
title=_("Warning"),
|
||||||
|
indicator="orange",
|
||||||
|
)
|
||||||
|
|
||||||
|
if stop_actions:
|
||||||
|
frappe.throw(stop_actions, as_list=True)
|
||||||
|
|
||||||
def disable_pricing_rule_on_internal_transfer(self):
|
def disable_pricing_rule_on_internal_transfer(self):
|
||||||
if not self.get("ignore_pricing_rule") and self.is_internal_transfer():
|
if not self.get("ignore_pricing_rule") and self.is_internal_transfer():
|
||||||
self.ignore_pricing_rule = 1
|
self.ignore_pricing_rule = 1
|
||||||
|
|||||||
Reference in New Issue
Block a user