From 2010b1b6e847911cda7491c5c218d8a074b5d895 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 8 Dec 2022 16:26:07 -0500 Subject: [PATCH 01/13] fix: use highest precision for exchange rate. --- erpnext/accounts/doctype/payment_entry/payment_entry.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 3fc1adff2d3..4a7a57b6275 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -305,6 +305,7 @@ "fieldname": "source_exchange_rate", "fieldtype": "Float", "label": "Exchange Rate", + "precision": "9", "print_hide": 1, "reqd": 1 }, @@ -334,6 +335,7 @@ "fieldname": "target_exchange_rate", "fieldtype": "Float", "label": "Exchange Rate", + "precision": "9", "print_hide": 1, "reqd": 1 }, @@ -731,7 +733,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-02-23 20:08:39.559814", + "modified": "2022-12-08 16:25:43.824051", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", From d512919f5c95605e98495d5b2f1b11f838622b79 Mon Sep 17 00:00:00 2001 From: artykbasar <78363421+artykbasar@users.noreply.github.com> Date: Sun, 11 Dec 2022 08:51:34 +0000 Subject: [PATCH 02/13] Subscription Cost center value is fixed to Default value(Bug fix) self.cost_center field value keeps changing to erpnext.get_default_cost_center(self.get("company")) after validation of this doctype. This overrides the user input. simple fix, it will first check if the field is empty, if empty then puts the company's default cost center value if not then user input will be saved. --- erpnext/accounts/doctype/subscription/subscription.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 9dab4e91fba..8708342b118 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -280,7 +280,8 @@ class Subscription(Document): self.validate_plans_billing_cycle(self.get_billing_cycle_and_interval()) self.validate_end_date() self.validate_to_follow_calendar_months() - self.cost_center = erpnext.get_default_cost_center(self.get("company")) + if not self.cost_center: + self.cost_center = erpnext.get_default_cost_center(self.get("company")) def validate_trial_period(self): """ From e057e1dfe7a9328d48ad7febdcf155d14c57cc83 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 12 Dec 2022 18:49:47 +0100 Subject: [PATCH 03/13] feat: incoterm named place --- .../doctype/purchase_invoice/purchase_invoice.json | 13 ++++++++++--- .../doctype/sales_invoice/sales_invoice.json | 13 ++++++++++--- .../doctype/purchase_order/purchase_order.json | 13 ++++++++++--- .../supplier_quotation/supplier_quotation.json | 13 ++++++++++--- erpnext/selling/doctype/quotation/quotation.json | 13 ++++++++++--- .../selling/doctype/sales_order/sales_order.json | 13 ++++++++++--- .../stock/doctype/delivery_note/delivery_note.json | 13 ++++++++++--- .../doctype/purchase_receipt/purchase_receipt.json | 13 ++++++++++--- 8 files changed, 80 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index a03157ebd4b..6281400fbcc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -64,12 +64,13 @@ "tax_withholding_net_total", "base_tax_withholding_net_total", "taxes_section", + "tax_category", "taxes_and_charges", "column_break_58", - "tax_category", - "column_break_49", "shipping_rule", + "column_break_49", "incoterm", + "named_place", "section_break_51", "taxes", "totals", @@ -1541,13 +1542,19 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-11-25 12:44:29.935567", + "modified": "2022-12-12 18:37:38.142688", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index b38bce72168..4729d9c3db9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -61,12 +61,13 @@ "total", "net_total", "taxes_section", + "tax_category", "taxes_and_charges", "column_break_38", "shipping_rule", - "incoterm", "column_break_55", - "tax_category", + "incoterm", + "named_place", "section_break_40", "taxes", "section_break_43", @@ -2122,6 +2123,12 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-file-text", @@ -2134,7 +2141,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-12-05 16:18:14.532114", + "modified": "2022-12-12 18:34:33.409895", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 93496261aa0..ce7de874c56 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -62,12 +62,13 @@ "set_reserve_warehouse", "supplied_items", "taxes_section", + "tax_category", "taxes_and_charges", "column_break_53", - "tax_category", - "column_break_50", "shipping_rule", + "column_break_50", "incoterm", + "named_place", "section_break_52", "taxes", "totals", @@ -1256,13 +1257,19 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-11-17 17:28:07.729943", + "modified": "2022-12-12 18:36:37.455134", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 7776ab8ec84..c5b369bedd5 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -40,12 +40,13 @@ "total", "net_total", "taxes_section", + "tax_category", "taxes_and_charges", "column_break_34", - "tax_category", - "column_break_36", "shipping_rule", + "column_break_36", "incoterm", + "named_place", "section_break_38", "taxes", "totals", @@ -830,6 +831,12 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-shopping-cart", @@ -837,7 +844,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-11-17 17:27:32.179686", + "modified": "2022-12-12 18:35:39.740974", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 08918f4d61a..eb2c0a48ac5 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -43,12 +43,13 @@ "total", "net_total", "taxes_section", + "tax_category", "taxes_and_charges", "column_break_36", - "tax_category", - "column_break_34", "shipping_rule", + "column_break_34", "incoterm", + "named_place", "section_break_36", "taxes", "section_break_39", @@ -1059,13 +1060,19 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-shopping-cart", "idx": 82, "is_submittable": 1, "links": [], - "modified": "2022-11-17 17:20:54.984348", + "modified": "2022-12-12 18:32:28.671332", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 9ec32cbfc67..ccea8407ab8 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -58,12 +58,13 @@ "total", "net_total", "taxes_section", + "tax_category", "taxes_and_charges", "column_break_38", - "tax_category", - "column_break_49", "shipping_rule", + "column_break_49", "incoterm", + "named_place", "section_break_40", "taxes", "section_break_43", @@ -1630,13 +1631,19 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-11-17 17:22:00.413878", + "modified": "2022-12-12 18:34:00.681780", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 80e4bcb640f..165a56b7839 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -57,12 +57,13 @@ "total", "net_total", "taxes_section", + "tax_category", "taxes_and_charges", "column_break_43", - "tax_category", - "column_break_39", "shipping_rule", + "column_break_39", "incoterm", + "named_place", "section_break_41", "taxes", "section_break_44", @@ -1388,13 +1389,19 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-11-17 17:22:42.860790", + "modified": "2022-12-12 18:38:53.067799", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index ab91d7c8c94..8f043585b89 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -58,12 +58,13 @@ "total", "net_total", "taxes_charges_section", + "tax_category", "taxes_and_charges", "shipping_col", - "tax_category", - "column_break_53", "shipping_rule", + "column_break_53", "incoterm", + "named_place", "taxes_section", "taxes", "totals", @@ -1225,13 +1226,19 @@ "fieldtype": "Link", "label": "Incoterm", "options": "Incoterm" + }, + { + "depends_on": "incoterm", + "fieldname": "named_place", + "fieldtype": "Data", + "label": "Named Place" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2022-11-17 17:29:30.067536", + "modified": "2022-12-12 18:40:32.447752", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From f20370c5ef6ed5cd3c146fa115e7d43527373969 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 13 Dec 2022 02:36:12 +0100 Subject: [PATCH 04/13] refactor: translatable strings and guard clause --- .../doctype/payment_entry/payment_entry.py | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9354e44d249..0a704ac442e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -684,35 +684,34 @@ class PaymentEntry(AccountsController): ) def validate_payment_against_negative_invoice(self): - if (self.payment_type == "Pay" and self.party_type == "Customer") or ( - self.payment_type == "Receive" and self.party_type == "Supplier" + if (self.payment_type != "Pay" or self.party_type != "Customer") and ( + self.payment_type != "Receive" or self.party_type != "Supplier" ): + return - total_negative_outstanding = sum( - abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0 + total_negative_outstanding = sum( + abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0 + ) + + paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount + additional_charges = sum(flt(d.amount) for d in self.deductions) + + if not total_negative_outstanding: + if self.party_type == "Customer": + msg = _("Cannot pay to Customer without any negative outstanding invoice") + else: + msg = _("Cannot receive from Supplier without any negative outstanding invoice") + + frappe.throw(msg, InvalidPaymentEntry) + + elif paid_amount - additional_charges > total_negative_outstanding: + frappe.throw( + _("Paid Amount cannot be greater than total negative outstanding amount {0}").format( + total_negative_outstanding + ), + InvalidPaymentEntry, ) - paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount - additional_charges = sum([flt(d.amount) for d in self.deductions]) - - if not total_negative_outstanding: - frappe.throw( - _("Cannot {0} {1} {2} without any negative outstanding invoice").format( - _(self.payment_type), - (_("to") if self.party_type == "Customer" else _("from")), - self.party_type, - ), - InvalidPaymentEntry, - ) - - elif paid_amount - additional_charges > total_negative_outstanding: - frappe.throw( - _("Paid Amount cannot be greater than total negative outstanding amount {0}").format( - total_negative_outstanding - ), - InvalidPaymentEntry, - ) - def set_title(self): if frappe.flags.in_import and self.title: # do not set title dynamically if title exists during data import. From 15e3b7f218cd881d28f616a6286dca27a5223744 Mon Sep 17 00:00:00 2001 From: kunhi Date: Tue, 13 Dec 2022 12:41:56 +0400 Subject: [PATCH 05/13] fix: at create_customer_or_supplier on session creation --- erpnext/portal/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/portal/utils.py b/erpnext/portal/utils.py index 7be8c5df180..c8b03e678bc 100644 --- a/erpnext/portal/utils.py +++ b/erpnext/portal/utils.py @@ -102,7 +102,7 @@ def create_party_contact(doctype, fullname, user, party_name): contact = frappe.new_doc("Contact") contact.update({"first_name": fullname, "email_id": user}) contact.append("links", dict(link_doctype=doctype, link_name=party_name)) - contact.append("email_ids", dict(email_id=user)) + contact.append("email_ids", dict(email_id=user, is_primary=True)) contact.flags.ignore_mandatory = True contact.insert(ignore_permissions=True) From 36997d9788d7e677a1b5bb3a2c772689a9146c24 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 13 Dec 2022 18:59:20 +0100 Subject: [PATCH 06/13] fix: translation for warning on Overbilling/-receipt/-delivery --- erpnext/controllers/status_updater.py | 21 +++++++++++++-------- erpnext/translations/de.csv | 3 ++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index bf077282bf8..d4972973d0b 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -347,16 +347,21 @@ class StatusUpdater(Document): ) def warn_about_bypassing_with_role(self, item, qty_or_amount, role): - action = _("Over Receipt/Delivery") if qty_or_amount == "qty" else _("Overbilling") + if qty_or_amount == "qty": + msg = _("Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role.") + else: + msg = _("Overbilling of {0} {1} ignored for item {2} because you have {3} role.") - msg = _("{0} of {1} {2} ignored for item {3} because you have {4} role.").format( - action, - _(item["target_ref_field"].title()), - frappe.bold(item["reduce_by"]), - frappe.bold(item.get("item_code")), - role, + frappe.msgprint( + msg.format( + _(item["target_ref_field"].title()), + frappe.bold(item["reduce_by"]), + frappe.bold(item.get("item_code")), + role, + ), + indicator="orange", + alert=True, ) - frappe.msgprint(msg, indicator="orange", alert=True) def update_qty(self, update_modified=True): """Updates qty or amount at row level diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index f1d830245f4..1014e27d6cf 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1849,6 +1849,8 @@ Outstanding Amt,Offener Betrag, Outstanding Cheques and Deposits to clear,Ausstehende Schecks und Anzahlungen zum verbuchen, Outstanding for {0} cannot be less than zero ({1}),Ausstände für {0} können nicht kleiner als Null sein ({1}), Outward taxable supplies(zero rated),Steuerpflichtige Lieferungen aus dem Ausland (null bewertet), +Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role.,"Überhöhte Annahme bzw. Lieferung von Artikel {2} mit {0} {1} wurde ignoriert, weil Sie die Rolle {3} haben." +Overbilling of {0} {1} ignored for item {2} because you have {3} role.,"Überhöhte Abrechnung von Artikel {2} mit {0} {1} wurde ignoriert, weil Sie die Rolle {3} haben." Overdue,Überfällig, Overlap in scoring between {0} and {1},Überlappung beim Scoring zwischen {0} und {1}, Overlapping conditions found between:,Überlagernde Bedingungen gefunden zwischen:, @@ -9914,4 +9916,3 @@ Cost and Freight,Kosten und Fracht, Delivered at Place,Geliefert benannter Ort, Delivered at Place Unloaded,Geliefert benannter Ort entladen, Delivered Duty Paid,Geliefert verzollt, -{0} of {1} {2} ignored for item {3} because you have {4} role,"{0} von Artikel {3} mit {1} {2} wurde ignoriert, weil Sie die Rolle {4} haben." From 41fc3be339cc78dc7418776556830c29aa875999 Mon Sep 17 00:00:00 2001 From: Gokulnath17 Date: Wed, 14 Dec 2022 10:46:11 +0530 Subject: [PATCH 07/13] fixes in gross profit report --- erpnext/accounts/report/gross_profit/gross_profit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index dacc809da0d..99e86ae78e9 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -503,7 +503,7 @@ class GrossProfitGenerator(object): invoice_portion = 100 elif row.invoice_portion: invoice_portion = row.invoice_portion - else: + elif row.payment_amount: invoice_portion = row.payment_amount * 100 / row.base_net_amount if i == 0: From 6d9d730759af258eadf93bc105a0bd159f2b7ba2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Dec 2022 16:05:15 +0530 Subject: [PATCH 08/13] fix: cost_center filter gives incorrect output filtering on cost center gives invoices that are reconciled as having outstanding --- .../doctype/payment_reconciliation/payment_reconciliation.py | 4 +++- erpnext/accounts/utils.py | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 52efd33fefa..ff212f2a35f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -23,6 +23,7 @@ class PaymentReconciliation(Document): def __init__(self, *args, **kwargs): super(PaymentReconciliation, self).__init__(*args, **kwargs) self.common_filter_conditions = [] + self.accounting_dimension_filter_conditions = [] self.ple_posting_date_filter = [] @frappe.whitelist() @@ -193,6 +194,7 @@ class PaymentReconciliation(Document): posting_date=self.ple_posting_date_filter, min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None, max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None, + accounting_dimensions=self.accounting_dimension_filter_conditions, ) if self.invoice_limit: @@ -381,7 +383,7 @@ class PaymentReconciliation(Document): self.common_filter_conditions.append(ple.company == self.company) if self.get("cost_center") and (get_invoices or get_return_invoices): - self.common_filter_conditions.append(ple.cost_center == self.cost_center) + self.accounting_dimension_filter_conditions.append(ple.cost_center == self.cost_center) if get_invoices: if self.from_invoice_date: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 41702d65b49..1e573b01bad 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -836,6 +836,7 @@ def get_outstanding_invoices( posting_date=None, min_outstanding=None, max_outstanding=None, + accounting_dimensions=None, ): ple = qb.DocType("Payment Ledger Entry") @@ -866,6 +867,7 @@ def get_outstanding_invoices( min_outstanding=min_outstanding, max_outstanding=max_outstanding, get_invoices=True, + accounting_dimensions=accounting_dimensions or [], ) for d in invoice_list: @@ -1615,6 +1617,7 @@ class QueryPaymentLedger(object): .where(ple.delinked == 0) .where(Criterion.all(filter_on_voucher_no)) .where(Criterion.all(self.common_filter)) + .where(Criterion.all(self.dimensions_filter)) .where(Criterion.all(self.voucher_posting_date)) .groupby(ple.voucher_type, ple.voucher_no, ple.party_type, ple.party) ) @@ -1702,6 +1705,7 @@ class QueryPaymentLedger(object): max_outstanding=None, get_payments=False, get_invoices=False, + accounting_dimensions=None, ): """ Fetch voucher amount and outstanding amount from Payment Ledger using Database CTE @@ -1717,6 +1721,7 @@ class QueryPaymentLedger(object): self.reset() self.vouchers = vouchers self.common_filter = common_filter or [] + self.dimensions_filter = accounting_dimensions or [] self.voucher_posting_date = posting_date or [] self.min_outstanding = min_outstanding self.max_outstanding = max_outstanding From 8eb93004f7f5947ab535cb42c377f2d795dac255 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Dec 2022 16:16:22 +0530 Subject: [PATCH 09/13] fix: cost_center filter fix for 'Get Outstanding Invoice' in PE --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9354e44d249..e7e7ce5c9d1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1188,6 +1188,7 @@ def get_outstanding_reference_documents(args): ple = qb.DocType("Payment Ledger Entry") common_filter = [] + accounting_dimensions_filter = [] posting_and_due_date = [] # confirm that Supplier is not blocked @@ -1217,7 +1218,7 @@ def get_outstanding_reference_documents(args): # Add cost center condition if args.get("cost_center"): condition += " and cost_center='%s'" % args.get("cost_center") - common_filter.append(ple.cost_center == args.get("cost_center")) + accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center")) date_fields_dict = { "posting_date": ["from_posting_date", "to_posting_date"], @@ -1243,6 +1244,7 @@ def get_outstanding_reference_documents(args): posting_date=posting_and_due_date, min_outstanding=args.get("outstanding_amt_greater_than"), max_outstanding=args.get("outstanding_amt_less_than"), + accounting_dimensions=accounting_dimensions_filter, ) outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices) From a998a8a2dad20e2671de5dbf70816c31c09e4c0e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 15 Dec 2022 12:57:53 +0530 Subject: [PATCH 10/13] test: cost center should not affect outstanding calculation --- .../test_payment_reconciliation.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index dae029b4084..6030134fff2 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -8,6 +8,8 @@ from frappe import qb from frappe.tests.utils import FrappeTestCase from frappe.utils import add_days, nowdate +from erpnext import get_default_cost_center +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.party import get_party_account @@ -20,6 +22,7 @@ class TestPaymentReconciliation(FrappeTestCase): self.create_item() self.create_customer() self.create_account() + self.create_cost_center() self.clear_old_entries() def tearDown(self): @@ -216,6 +219,22 @@ class TestPaymentReconciliation(FrappeTestCase): ) return je + def create_cost_center(self): + # Setup cost center + cc_name = "Sub" + + self.main_cc = frappe.get_doc("Cost Center", get_default_cost_center(self.company)) + + cc_exists = frappe.db.get_list("Cost Center", filters={"cost_center_name": cc_name}) + if cc_exists: + self.sub_cc = frappe.get_doc("Cost Center", cc_exists[0].name) + else: + sub_cc = frappe.new_doc("Cost Center") + sub_cc.cost_center_name = "Sub" + sub_cc.parent_cost_center = self.main_cc.parent_cost_center + sub_cc.company = self.main_cc.company + self.sub_cc = sub_cc.save() + def test_filter_min_max(self): # check filter condition minimum and maximum amount self.create_sales_invoice(qty=1, rate=300) @@ -578,3 +597,24 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.payments), 1) self.assertEqual(pr.payments[0].amount, amount) self.assertEqual(pr.payments[0].currency, "EUR") + + def test_differing_cost_center_on_invoice_and_payment(self): + """ + Cost Center filter should not affect outstanding amount calculation + """ + + si = self.create_sales_invoice(qty=1, rate=100, do_not_submit=True) + si.cost_center = self.main_cc.name + si.submit() + pr = get_payment_entry(si.doctype, si.name) + pr.cost_center = self.sub_cc.name + pr = pr.save().submit() + + pr = self.create_payment_reconciliation() + pr.cost_center = self.main_cc.name + + pr.get_unreconciled_entries() + + # check PR tool output + self.assertEqual(len(pr.get("invoices")), 0) + self.assertEqual(len(pr.get("payments")), 0) From 0f28074e5a8b39773ccadf4427f83eaa1ed8821e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 15 Dec 2022 16:34:06 +0530 Subject: [PATCH 11/13] fix: unsupported operand type(s) for +: 'int' and 'NoneType' --- erpnext/stock/get_item_details.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 31dccf6944d..1741d654601 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -828,9 +828,9 @@ def insert_item_price(args): ): if frappe.has_permission("Item Price", "write"): price_list_rate = ( - (args.rate + args.discount_amount) / args.get("conversion_factor") + (flt(args.rate) + flt(args.discount_amount)) / args.get("conversion_factor") if args.get("conversion_factor") - else (args.rate + args.discount_amount) + else (flt(args.rate) + flt(args.discount_amount)) ) item_price = frappe.db.get_value( From ae31ff1c48cb4a9baab98f61bfe9ed85914a9034 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 15 Dec 2022 17:05:53 +0530 Subject: [PATCH 12/13] fix: disabled items showing in the report 'Itemwise Recommended Reorder Level ' --- .../itemwise_recommended_reorder_level.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index a6fc049cbde..c4358b809fc 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -82,7 +82,7 @@ def get_item_info(filters): item.safety_stock, item.lead_time_days, ) - .where(item.is_stock_item == 1) + .where((item.is_stock_item == 1) & (item.disabled == 0)) ) if brand := filters.get("brand"): From 1a40c04b72986232d40be1620788ed5b6b68ffab Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 15 Dec 2022 18:51:58 +0530 Subject: [PATCH 13/13] feat: Ignore company related doctype for other apps via hooks --- .../transaction_deletion_record.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index c18a4b2214b..4256a7d8312 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -204,7 +204,7 @@ class TransactionDeletionRecord(Document): @frappe.whitelist() def get_doctypes_to_be_ignored(): - doctypes_to_be_ignored_list = [ + doctypes_to_be_ignored = [ "Account", "Cost Center", "Warehouse", @@ -223,4 +223,7 @@ def get_doctypes_to_be_ignored(): "Customer", "Supplier", ] - return doctypes_to_be_ignored_list + + doctypes_to_be_ignored.extend(frappe.get_hooks("company_data_to_be_ignored") or []) + + return doctypes_to_be_ignored