From 5b7b431dc97ef77fd888e8a233269219063428e6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:25:37 +0530 Subject: [PATCH 01/32] fix(Timesheet): reset billing hours equal to hours if they exceed actual hours (backport #38134) (#38153) fix(Timesheet): reset billing hours equal to hours if they exceed actual hours (#38134) (cherry picked from commit 20c6e9fca29d80016cefb83deee4e44ac9bc1d47) Co-authored-by: Rucha Mahabal --- erpnext/projects/doctype/timesheet/timesheet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 11156f4b50..8e464b5c6b 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -69,7 +69,7 @@ class Timesheet(Document): def update_billing_hours(self, args): if args.is_billable: - if flt(args.billing_hours) == 0.0: + if flt(args.billing_hours) == 0.0 or flt(args.billing_hours) > flt(args.hours): args.billing_hours = args.hours else: args.billing_hours = 0 From b89a4a7218338c48026abec660ac23184a9c6eaf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:07:53 +0530 Subject: [PATCH 02/32] feat: add `Supplier Delivery Note` field in SCR (backport #38127) (#38156) feat: add `Supplier Delivery Note` field in SCR (cherry picked from commit da80e4dbce07707d57e3739b7bb2138adf199905) Co-authored-by: s-aga-r --- .../subcontracting_receipt/subcontracting_receipt.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 8be1c1ba97..383a83b3fc 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -11,6 +11,7 @@ "naming_series", "supplier", "supplier_name", + "supplier_delivery_note", "column_break1", "company", "posting_date", @@ -634,12 +635,17 @@ "fieldtype": "Button", "label": "Get Scrap Items", "options": "get_scrap_items" + }, + { + "fieldname": "supplier_delivery_note", + "fieldtype": "Data", + "label": "Supplier Delivery Note" } ], "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2023-08-26 10:52:04.050829", + "modified": "2023-11-16 13:04:00.710534", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", From a420e13765e0a92a62cadfbea07c59f70a6f16c1 Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Sat, 18 Nov 2023 17:10:01 +0530 Subject: [PATCH 03/32] fix: pass check permission in render_address (cherry picked from commit 45299fe4b3e23774920d7c35f7fb66e786cf5aa5) --- erpnext/accounts/party.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 16e73ea52f..d81ff8a768 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -231,7 +231,9 @@ def set_address_details( if shipping_address: party_details.update( shipping_address=shipping_address, - shipping_address_display=render_address(shipping_address), + shipping_address_display=render_address( + shipping_address, check_permissions=not ignore_permissions + ), **get_fetch_values(doctype, "shipping_address", shipping_address) ) From 7d4ac7e7ffd92ed874d32e1f635311d245ec7fda Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 17 Nov 2023 12:37:14 +0530 Subject: [PATCH 04/32] feat: Add accounting dimensions to Supplier Quotation (cherry picked from commit 089da459f73791b86d33cf066e6879e77af200b5) # Conflicts: # erpnext/buying/doctype/supplier_quotation/supplier_quotation.json --- .../supplier_quotation.json | 81 +++++++++++++++++++ .../supplier_quotation_item.json | 15 +++- erpnext/hooks.py | 2 + erpnext/patches.txt | 1 + ...unting_dimensions_in_supplier_quotation.py | 8 ++ 5 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v14_0/create_accounting_dimensions_in_supplier_quotation.py diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 7b635b36ba..fdb17d287e 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -20,6 +20,10 @@ "valid_till", "quotation_number", "amended_from", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project", "currency_and_price_list", "currency", "conversion_rate", @@ -838,6 +842,79 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" +<<<<<<< HEAD +======= + }, + { + "fieldname": "shipping_address", + "fieldtype": "Link", + "label": "Shipping Address", + "options": "Address", + "print_hide": 1 + }, + { + "fieldname": "column_break_zjaq", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_address_display", + "fieldtype": "Small Text", + "label": "Shipping Address Details", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "supplier_address_section", + "fieldtype": "Section Break", + "label": "Supplier Address" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Company Billing Address", + "options": "Address" + }, + { + "fieldname": "column_break_gcth", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address Details", + "read_only": 1 + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" +>>>>>>> 089da459f7 (feat: Add accounting dimensions to Supplier Quotation) } ], "icon": "fa fa-shopping-cart", @@ -845,7 +922,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-06-03 16:20:15.880114", +======= + "modified": "2023-11-17 12:34:30.083077", +>>>>>>> 089da459f7 (feat: Add accounting dimensions to Supplier Quotation) "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json index 4bbcacfef3..a6229b5950 100644 --- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json +++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json @@ -68,6 +68,8 @@ "column_break_15", "manufacturer_part_no", "ad_sec_break", + "cost_center", + "dimension_col_break", "project", "section_break_44", "page_break" @@ -133,7 +135,6 @@ "fieldtype": "Column Break" }, { - "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -554,13 +555,23 @@ "fieldname": "expected_delivery_date", "fieldtype": "Date", "label": "Expected Delivery Date" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:35:03.435817", + "modified": "2023-11-17 12:25:26.235367", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation Item", diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 5483a10b57..55ffaca419 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -539,6 +539,8 @@ accounting_dimension_doctypes = [ "Subcontracting Receipt", "Subcontracting Receipt Item", "Account Closing Balance", + "Supplier Quotation", + "Supplier Quotation Item", ] get_matching_queries = ( diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d394db6d96..51b2a1b727 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -349,5 +349,6 @@ erpnext.patches.v14_0.add_default_for_repost_settings erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based erpnext.patches.v15_0.set_reserved_stock_in_bin +erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_supplier_quotation.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_supplier_quotation.py new file mode 100644 index 0000000000..6966db1fd7 --- /dev/null +++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_supplier_quotation.py @@ -0,0 +1,8 @@ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + create_accounting_dimensions_for_doctype, +) + + +def execute(): + create_accounting_dimensions_for_doctype(doctype="Supplier Quotation") + create_accounting_dimensions_for_doctype(doctype="Supplier Quotation Item") From aaccfeb918d7427cc110626bc94a2eeb921be6a9 Mon Sep 17 00:00:00 2001 From: kunhi Date: Fri, 17 Nov 2023 11:22:08 +0400 Subject: [PATCH 05/32] fix: issue occured when creating supplier with contact details (cherry picked from commit 7842c9fba8b521b9ba7f65281c4f2384f62b06e1) --- erpnext/selling/doctype/customer/customer.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 27bb8845dd..ed9bc6dae8 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -644,8 +644,12 @@ def make_contact(args, is_primary_contact=1): "is_primary_contact": is_primary_contact, "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}], } - if args.customer_type == "Individual": - first, middle, last = parse_full_name(args.get("customer_name")) + + party_type = args.customer_type if args.doctype == "Customer" else args.supplier_type + party_name_key = "customer_name" if args.doctype == "Customer" else "supplier_name" + + if party_type == "Individual": + first, middle, last = parse_full_name(args.get(party_name_key)) values.update( { "first_name": first, @@ -656,9 +660,10 @@ def make_contact(args, is_primary_contact=1): else: values.update( { - "company_name": args.get("customer_name"), + "company_name": args.get(party_name_key), } ) + contact = frappe.get_doc(values) if args.get("email_id"): From 2b9448962ff9195bd6ffd535cee0f010876558d3 Mon Sep 17 00:00:00 2001 From: Kunhi Date: Fri, 17 Nov 2023 11:43:36 +0400 Subject: [PATCH 06/32] fix: Suppier name was not taken when creating address from supplier (cherry picked from commit 545ef3c23491e895bf288ddf7666ea80251b41e7) --- erpnext/selling/doctype/customer/customer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index ed9bc6dae8..ef42d8a6d4 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -692,10 +692,12 @@ def make_address(args, is_primary_address=1, is_shipping_address=1): title=_("Missing Values Required"), ) + party_name_key = "customer_name" if args.doctype == "Customer" else "supplier_name" + address = frappe.get_doc( { "doctype": "Address", - "address_title": args.get("customer_name"), + "address_title": args.get(party_name_key), "address_line1": args.get("address_line1"), "address_line2": args.get("address_line2"), "city": args.get("city"), From 3172448c31c80612d0b9b7e4e08ab535101fc36f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 18 Nov 2023 19:58:10 +0530 Subject: [PATCH 07/32] chore: Resolve conflicts --- .../supplier_quotation.json | 56 ------------------- 1 file changed, 56 deletions(-) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index fdb17d287e..5d315ac31e 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -842,57 +842,6 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" -<<<<<<< HEAD -======= - }, - { - "fieldname": "shipping_address", - "fieldtype": "Link", - "label": "Shipping Address", - "options": "Address", - "print_hide": 1 - }, - { - "fieldname": "column_break_zjaq", - "fieldtype": "Column Break" - }, - { - "fieldname": "shipping_address_display", - "fieldtype": "Small Text", - "label": "Shipping Address Details", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "shipping_address_section", - "fieldtype": "Section Break", - "label": "Shipping Address" - }, - { - "fieldname": "supplier_address_section", - "fieldtype": "Section Break", - "label": "Supplier Address" - }, - { - "fieldname": "company_billing_address_section", - "fieldtype": "Section Break", - "label": "Company Billing Address" - }, - { - "fieldname": "billing_address", - "fieldtype": "Link", - "label": "Company Billing Address", - "options": "Address" - }, - { - "fieldname": "column_break_gcth", - "fieldtype": "Column Break" - }, - { - "fieldname": "billing_address_display", - "fieldtype": "Small Text", - "label": "Billing Address Details", - "read_only": 1 }, { "fieldname": "cost_center", @@ -914,7 +863,6 @@ "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", "label": "Accounting Dimensions" ->>>>>>> 089da459f7 (feat: Add accounting dimensions to Supplier Quotation) } ], "icon": "fa fa-shopping-cart", @@ -922,11 +870,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-06-03 16:20:15.880114", -======= "modified": "2023-11-17 12:34:30.083077", ->>>>>>> 089da459f7 (feat: Add accounting dimensions to Supplier Quotation) "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", From 00b9c2326cfbabb6dd58c40f9af94043bbbaf788 Mon Sep 17 00:00:00 2001 From: PatrickDenis-stack <77415730+PatrickDenis-stack@users.noreply.github.com> Date: Sat, 18 Nov 2023 11:16:06 +0100 Subject: [PATCH 08/32] fix: attributes were mandatory for manufacturers (cherry picked from commit 434c2a1815f27c59de5cd94528bc371593bdd402) --- erpnext/stock/doctype/item/item.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 54491bbee3..a2e173c683 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -504,7 +504,7 @@ "fieldtype": "Table", "hidden": 1, "label": "Variant Attributes", - "mandatory_depends_on": "has_variants", + "mandatory_depends_on": "eval:(doc.has_variants || doc.variant_of) && doc.variant_based_on==='Item Attribute'", "options": "Item Variant Attribute" }, { From a492e574def2661edb2a54cfbde8b231781a231e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:43:23 +0100 Subject: [PATCH 09/32] fix: update modified timestamp Was missing in 434c2a1815f27c59de5cd94528bc371593bdd402 --- erpnext/stock/doctype/item/item.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index a2e173c683..d0b90c4565 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -888,7 +888,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, - "modified": "2023-09-11 13:46:32.688051", + "modified": "2023-09-18 15:41:32.688051", "modified_by": "Administrator", "module": "Stock", "name": "Item", From f71234e3af77d66a34a6a6305c050ffe685b2bf4 Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:54:34 +0100 Subject: [PATCH 10/32] fix: valuation rate in report Item Prices (#38161) Co-authored-by: PatrickDenis-stack <77415730+PatrickDenis-stack@users.noreply.github.com> (cherry picked from commit 32f622ef8061f7db50b4c8f63134f947afdcb306) --- erpnext/stock/report/item_prices/item_prices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/item_prices/item_prices.py b/erpnext/stock/report/item_prices/item_prices.py index ab47b4a8b9..a53a9f24f5 100644 --- a/erpnext/stock/report/item_prices/item_prices.py +++ b/erpnext/stock/report/item_prices/item_prices.py @@ -202,7 +202,7 @@ def get_valuation_rate(): bin_data = ( frappe.qb.from_(bin) .select( - bin.item_code, Sum(bin.actual_qty * bin.valuation_rate) / Sum(bin.actual_qty).as_("val_rate") + bin.item_code, (Sum(bin.actual_qty * bin.valuation_rate) / Sum(bin.actual_qty)).as_("val_rate") ) .where(bin.actual_qty > 0) .groupby(bin.item_code) From 384d6b516c152bb7e2f72d28a30752b6bd82eb71 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Sat, 18 Nov 2023 14:36:20 +0000 Subject: [PATCH 11/32] fix: payment entry rounding error (cherry picked from commit 3d1e3a9cded735c420cb6c0ceb17c62034983076) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index b3ae627c6e..9a6f8ec8ac 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -853,6 +853,7 @@ frappe.ui.form.on('Payment Entry', { var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding; } else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) { + total_negative_outstanding = flt(total_negative_outstanding, precision("outstanding_amount")) if(paid_amount > total_negative_outstanding) { if(total_negative_outstanding == 0) { frappe.msgprint( From 70eccf7da0d52c3cfc21f8a5c892993b60de868e Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Sat, 18 Nov 2023 13:32:04 +0000 Subject: [PATCH 12/32] fix: wrong round off and rounded total (cherry picked from commit 3a487bd33af1972d9ee8b7bb2f6277775c8e018e) --- erpnext/controllers/taxes_and_totals.py | 1 + erpnext/public/js/controllers/taxes_and_totals.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 96284d612f..f9f68a119b 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -54,6 +54,7 @@ class calculate_taxes_and_totals(object): if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"): self.doc.grand_total -= self.doc.discount_amount self.doc.base_grand_total -= self.doc.base_discount_amount + self.doc.rounding_adjustment = self.doc.base_rounding_adjustment = 0.0 self.set_rounded_total() self.calculate_shipping_charges() diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 6b613ce9ec..d24c4e6075 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -43,6 +43,9 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) { this.frm.doc.grand_total -= this.frm.doc.discount_amount; this.frm.doc.base_grand_total -= this.frm.doc.base_discount_amount; + this.frm.doc.rounding_adjustment = 0; + this.frm.doc.base_rounding_adjustment = 0; + this.set_rounded_total(); } await this.calculate_shipping_charges(); From eab18e6f710637d0eee63d1d41f2b75a4f4e0fa9 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Sun, 19 Nov 2023 02:53:09 +0000 Subject: [PATCH 13/32] fix: test case for rounded total with cash disc (cherry picked from commit cc60c328fe349f7d53c77799e8e91973c2790746) --- .../sales_invoice/test_sales_invoice.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 4272218de6..e8a53f8819 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -789,6 +789,28 @@ class TestSalesInvoice(FrappeTestCase): w = self.make() self.assertEqual(w.outstanding_amount, w.base_rounded_total) + def test_rounded_total_with_cash_discount(self): + si = frappe.copy_doc(test_records[2]) + + item = copy.deepcopy(si.get("items")[0]) + item.update( + { + "qty": 1, + "rate": 14960.66, + } + ) + + si.set("items", [item]) + si.set("taxes", []) + si.apply_discount_on = "Grand Total" + si.is_cash_or_non_trade_discount = 1 + si.discount_amount = 1 + si.insert() + + self.assertEqual(si.grand_total, 14959.66) + self.assertEqual(si.rounded_total, 14960) + self.assertEqual(si.rounding_adjustment, 0.34) + def test_payment(self): w = self.make() From c29bc8c97fc57b65a214346123d1e12f6703f175 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 19 Nov 2023 19:20:06 +0530 Subject: [PATCH 14/32] chore: linting issues --- erpnext/selling/doctype/customer/customer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index ef42d8a6d4..7ef929fc22 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -648,7 +648,7 @@ def make_contact(args, is_primary_contact=1): party_type = args.customer_type if args.doctype == "Customer" else args.supplier_type party_name_key = "customer_name" if args.doctype == "Customer" else "supplier_name" - if party_type == "Individual": + if party_type == "Individual": first, middle, last = parse_full_name(args.get(party_name_key)) values.update( { From 9ee8a7808327287cd1640ad92a4ace40cd9e1520 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 18 Nov 2023 09:04:36 +0530 Subject: [PATCH 15/32] refactor: provision to set `remarks` length in accounts settings (cherry picked from commit 97090ff3679104d77a031f29d4acafb8b7ac1580) --- .../accounts_settings/accounts_settings.json | 37 ++++++++++++++++++- .../report/general_ledger/general_ledger.py | 7 +++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 061bab320e..fd052d0476 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -66,7 +66,12 @@ "show_balance_in_coa", "banking_tab", "enable_party_matching", - "enable_fuzzy_matching" + "enable_fuzzy_matching", + "reports_tab", + "remarks_section", + "general_ledger_remarks_length", + "column_break_lvjk", + "receivable_payable_remarks_length" ], "fields": [ { @@ -422,6 +427,34 @@ "fieldname": "round_row_wise_tax", "fieldtype": "Check", "label": "Round Tax Amount Row-wise" + }, + { + "fieldname": "reports_tab", + "fieldtype": "Tab Break", + "label": "Reports" + }, + { + "default": "0", + "description": "Truncates 'Remarks' column to set character length", + "fieldname": "general_ledger_remarks_length", + "fieldtype": "Int", + "label": "General Ledger" + }, + { + "default": "0", + "description": "Truncates 'Remarks' column to set character length", + "fieldname": "receivable_payable_remarks_length", + "fieldtype": "Int", + "label": "Accounts Receivable/Payable" + }, + { + "fieldname": "column_break_lvjk", + "fieldtype": "Column Break" + }, + { + "fieldname": "remarks_section", + "fieldtype": "Section Break", + "label": "Remarks Column Length" } ], "icon": "icon-cog", @@ -429,7 +462,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-08-28 00:12:02.740633", + "modified": "2023-11-20 09:37:47.650347", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 94cd293615..fa557a133f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -164,7 +164,12 @@ def get_gl_entries(filters, accounting_dimensions): credit_in_account_currency """ if filters.get("show_remarks"): - select_fields += """,remarks""" + if remarks_length := frappe.db.get_single_value( + "Accounts Settings", "general_ledger_remarks_length" + ): + select_fields += f",substr(remarks, 1, {remarks_length}) as 'remarks'" + else: + select_fields += """,remarks""" order_by_statement = "order by posting_date, account, creation" From c2cb86b40a6162c755128c6104154beda2b5ca1b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 18 Nov 2023 10:35:56 +0530 Subject: [PATCH 16/32] refactor: add substring logic in ar/ap report (cherry picked from commit a9bf906545dc7c89613c7f6211c35e62b8d7b989) --- .../report/accounts_receivable/accounts_receivable.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index c43c749f09..a2ade382d4 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -7,7 +7,7 @@ from collections import OrderedDict import frappe from frappe import _, qb, scrub from frappe.query_builder import Criterion -from frappe.query_builder.functions import Date, Sum +from frappe.query_builder.functions import Date, Substring, Sum from frappe.utils import cint, cstr, flt, getdate, nowdate from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -760,7 +760,12 @@ class ReceivablePayableReport(object): ) if self.filters.get("show_remarks"): - query = query.select(ple.remarks) + if remarks_length := frappe.db.get_single_value( + "Accounts Settings", "receivable_payable_remarks_length" + ): + query = query.select(Substring(ple.remarks, 1, remarks_length).as_("remarks")) + else: + query = query.select(ple.remarks) if self.filters.get("group_by_party"): query = query.orderby(self.ple.party, self.ple.posting_date) From aef955c92040705e11a437419379b7e29633c339 Mon Sep 17 00:00:00 2001 From: Vishakh Desai <78500008+vishakhdesai@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:26:34 +0530 Subject: [PATCH 17/32] fix: Supplier Quotation fields (#37963) (cherry picked from commit c2bda2c70535ad9e8a61e752bbb551f387460125) # Conflicts: # erpnext/buying/doctype/supplier_quotation/supplier_quotation.json --- erpnext/accounts/party.py | 7 ++- .../request_for_quotation.json | 16 ++++- .../supplier_quotation.json | 63 +++++++++++++++++++ erpnext/public/js/utils/party.js | 2 +- 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index d81ff8a768..5c18e506f5 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -31,7 +31,12 @@ from erpnext.accounts.utils import get_fiscal_year from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen from erpnext.utilities.regional import temporary_flag -PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"} +PURCHASE_TRANSACTION_TYPES = { + "Supplier Quotation", + "Purchase Order", + "Purchase Receipt", + "Purchase Invoice", +} SALES_TRANSACTION_TYPES = { "Quotation", "Sales Order", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index 06dbd86ba1..fd73f77ff8 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -9,6 +9,8 @@ "field_order": [ "naming_series", "company", + "billing_address", + "billing_address_display", "vendor", "column_break1", "transaction_date", @@ -292,13 +294,25 @@ "fieldtype": "Check", "label": "Send Document Print", "print_hide": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Company Billing Address", + "options": "Address" + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address Details", + "read_only": 1 } ], "icon": "fa fa-shopping-cart", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-08-09 12:20:26.850623", + "modified": "2023-11-06 12:45:28.898706", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 5d315ac31e..b12d3046ca 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -83,6 +83,7 @@ "pricing_rule_details", "pricing_rules", "address_and_contact_tab", + "supplier_address_section", "supplier_address", "address_display", "column_break_72", @@ -90,6 +91,14 @@ "contact_display", "contact_mobile", "contact_email", + "shipping_address_section", + "shipping_address", + "column_break_zjaq", + "shipping_address_display", + "company_billing_address_section", + "billing_address", + "column_break_gcth", + "billing_address_display", "terms_tab", "tc_name", "terms", @@ -844,6 +853,7 @@ "label": "Named Place" }, { +<<<<<<< HEAD "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", @@ -863,6 +873,55 @@ "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", "label": "Accounting Dimensions" +======= + "fieldname": "shipping_address", + "fieldtype": "Link", + "label": "Shipping Address", + "options": "Address", + "print_hide": 1 + }, + { + "fieldname": "column_break_zjaq", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_address_display", + "fieldtype": "Small Text", + "label": "Shipping Address Details", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "supplier_address_section", + "fieldtype": "Section Break", + "label": "Supplier Address" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Company Billing Address", + "options": "Address" + }, + { + "fieldname": "column_break_gcth", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address Details", + "read_only": 1 +>>>>>>> c2bda2c705 (fix: Supplier Quotation fields (#37963)) } ], "icon": "fa fa-shopping-cart", @@ -870,7 +929,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-11-17 12:34:30.083077", +======= + "modified": "2023-11-03 13:21:40.172508", +>>>>>>> c2bda2c705 (fix: Supplier Quotation fields (#37963)) "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 5c41aa0680..cba615c0d2 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -4,7 +4,7 @@ frappe.provide("erpnext.utils"); const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']; -const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']; +const PURCHASE_DOCTYPES = ['Supplier Quotation','Purchase Order', 'Purchase Receipt', 'Purchase Invoice']; erpnext.utils.get_party_details = function(frm, method, args, callback) { if (!method) { From 88b62a0a61539d3dc48f15683d47b947dae54c7b Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 20 Nov 2023 11:41:42 +0530 Subject: [PATCH 18/32] chore: resolve conflicts --- .../supplier_quotation.json | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index b12d3046ca..d3c80cc49e 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -853,27 +853,6 @@ "label": "Named Place" }, { -<<<<<<< HEAD - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" -======= "fieldname": "shipping_address", "fieldtype": "Link", "label": "Shipping Address", @@ -881,8 +860,8 @@ "print_hide": 1 }, { - "fieldname": "column_break_zjaq", - "fieldtype": "Column Break" + "fieldname": "column_break_zjaq", + "fieldtype": "Column Break" }, { "fieldname": "shipping_address_display", @@ -921,7 +900,27 @@ "fieldtype": "Small Text", "label": "Billing Address Details", "read_only": 1 ->>>>>>> c2bda2c705 (fix: Supplier Quotation fields (#37963)) + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" } ], "icon": "fa fa-shopping-cart", @@ -929,11 +928,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-11-17 12:34:30.083077", -======= - "modified": "2023-11-03 13:21:40.172508", ->>>>>>> c2bda2c705 (fix: Supplier Quotation fields (#37963)) + "modified": "2023-11-20 11:15:30.083077", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", From 3f57a7e9f06806a833fc9350a21c5d82fa525bac Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:40:41 +0530 Subject: [PATCH 19/32] fix: TypeError in Subcontracting Receipt (backport #38200) (#38208) fix: TypeError in Subcontracting Receipt (cherry picked from commit f6e93f084aadf82f444edb112cc9832b53294b15) Co-authored-by: s-aga-r --- .../doctype/serial_and_batch_bundle/serial_and_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 4436fac303..df2cfeb04f 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -401,7 +401,7 @@ class SerialandBatchBundle(Document): if abs(abs(flt(self.total_qty, precision)) - abs(flt(row.get(qty_field), precision))) > 0.01: self.throw_error_message( - f"Total quantity {abs(self.total_qty)} in the Serial and Batch Bundle {bold(self.name)} does not match with the quantity {abs(row.get(qty_field))} for the Item {bold(self.item_code)} in the {self.voucher_type} # {self.voucher_no}" + f"Total quantity {abs(flt(self.total_qty))} in the Serial and Batch Bundle {bold(self.name)} does not match with the quantity {abs(flt(row.get(qty_field)))} for the Item {bold(self.item_code)} in the {self.voucher_type} # {self.voucher_no}" ) def set_is_outward(self): From e2720418728da869c6cfc02aeca04b0e54fb0b46 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:44:26 +0530 Subject: [PATCH 20/32] fix(ux): `Task` creation from `Timesheet` (backport #38207) (#38211) * fix(ux): enable `Quick Entry` for `Task` (cherry picked from commit 331ad62f3c27a54e9d6bb49f1b70b169b633b3a6) * fix: add route options for new `Task` (cherry picked from commit 2f3fc12c08218ca06fbb911781090e619a88cf22) --------- Co-authored-by: s-aga-r --- erpnext/projects/doctype/task/task.json | 5 ++++- erpnext/projects/doctype/timesheet/timesheet.js | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 25a5455ac1..4d2d225242 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -57,6 +57,7 @@ ], "fields": [ { + "allow_in_quick_entry": 1, "fieldname": "subject", "fieldtype": "Data", "in_global_search": 1, @@ -66,6 +67,7 @@ "search_index": 1 }, { + "allow_in_quick_entry": 1, "bold": 1, "fieldname": "project", "fieldtype": "Link", @@ -396,7 +398,7 @@ "is_tree": 1, "links": [], "max_attachments": 5, - "modified": "2023-09-28 13:52:05.861175", + "modified": "2023-11-20 11:42:41.884069", "modified_by": "Administrator", "module": "Projects", "name": "Task", @@ -416,6 +418,7 @@ "write": 1 } ], + "quick_entry": 1, "search_fields": "subject", "show_name_in_global_search": 1, "show_preview_popup": 1, diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index d1d07a79d6..eb7a97e615 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -111,6 +111,7 @@ frappe.ui.form.on("Timesheet", { frm.trigger('setup_filters'); frm.trigger('set_dynamic_field_label'); + frm.trigger('set_route_options_for_new_task'); }, customer: function(frm) { @@ -172,6 +173,14 @@ frappe.ui.form.on("Timesheet", { frm.refresh_fields(); }, + set_route_options_for_new_task: (frm) => { + let task_field = frm.get_docfield('time_logs', 'task'); + + if (task_field) { + task_field.get_route_options_for_new_doc = (row) => ({'project': row.doc.project}); + } + }, + make_invoice: function(frm) { let fields = [{ "fieldtype": "Link", From 5da9a22e4ca728ec6e057130703ff05ca9b48675 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 20 Nov 2023 11:52:26 +0530 Subject: [PATCH 21/32] refactor: set default for 'update_billed_amount_in_delivery_note' (cherry picked from commit ee0c64215d160f0bd493bcefc9e94a5a90d318d2) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 4 ++-- erpnext/controllers/sales_and_purchase_return.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index cd725b9862..d1677832ed 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -2156,7 +2156,7 @@ "label": "Use Company default Cost Center for Round off" }, { - "default": "0", + "default": "1", "depends_on": "eval: doc.is_return", "fieldname": "update_billed_amount_in_delivery_note", "fieldtype": "Check", @@ -2173,7 +2173,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2023-11-03 14:39:38.012346", + "modified": "2023-11-20 11:51:43.555197", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 165e17b2d7..e91212b031 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -356,6 +356,7 @@ def make_return_doc( if doc.doctype == "Sales Invoice" or doc.doctype == "POS Invoice": doc.consolidated_invoice = "" doc.set("payments", []) + doc.update_billed_amount_in_delivery_note = True for data in source.payments: paid_amount = 0.00 base_paid_amount = 0.00 From 1ed65524e5ad89319b92c37b9e4fe79ea6fe221f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 20 Nov 2023 12:27:43 +0530 Subject: [PATCH 22/32] refactor: add flag to POS Invoice (cherry picked from commit 83a13e22b7f4aa8ba2c406389af4e7fa8aad55d0) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index f6047079ff..955b66a1b8 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -18,6 +18,7 @@ "is_pos", "is_return", "update_billed_amount_in_sales_order", + "update_billed_amount_in_delivery_note", "column_break1", "company", "posting_date", @@ -1550,12 +1551,19 @@ "fieldtype": "Currency", "label": "Amount Eligible for Commission", "read_only": 1 + }, + { + "default": "1", + "depends_on": "eval: doc.is_return && doc.return_against", + "fieldname": "update_billed_amount_in_delivery_note", + "fieldtype": "Check", + "label": "Update Billed Amount in Delivery Note" } ], "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:23:41.083409", + "modified": "2023-11-20 12:27:12.848149", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", From afaf6afd27ca166fa6c8e710fb817944dd1681fa Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 20 Nov 2023 13:26:02 +0530 Subject: [PATCH 23/32] refactor: update scheduled job for bulk transaction (cherry picked from commit fb06ad7330fd31bf1def7b87a6c3b787650f1555) --- erpnext/hooks.py | 2 +- erpnext/utilities/bulk_transaction.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 55ffaca419..c6ab6f12f6 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -421,7 +421,7 @@ scheduler_events = { "hourly_long": [ "erpnext.accounts.doctype.process_subscription.process_subscription.create_subscription_process", "erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries", - "erpnext.bulk_transaction.doctype.bulk_transaction_log.bulk_transaction_log.retry_failing_transaction", + "erpnext.utilities.bulk_transaction.retry", ], "daily": [ "erpnext.support.doctype.issue.issue.auto_close_tickets", diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 57c2f9d787..df21b61139 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -30,7 +30,10 @@ def transaction_processing(data, from_doctype, to_doctype): @frappe.whitelist() -def retry(date: str | None): +def retry(date: str | None = None): + if not date: + date = today() + if date: failed_docs = frappe.db.get_all( "Bulk Transaction Log Detail", From 99bf63ec0f856e8a74ef6ca915f581eb98583e58 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:49:38 +0530 Subject: [PATCH 24/32] fix: set default asset quantity as 1 [dev] (backport #38223) (#38226) fix: set default asset quantity as 1 [dev] (#38223) * fix: make default asset quantity as 1 * fix: get rate_of_depreciation from asset category for asset auto-creation * chore: create asset depr schedules on PR submit, not asset submit * fix: set default asset quantity as 1 * chore: move patch from v15 to v14 (cherry picked from commit 9903049c7ac7310ca9e3f69c30dc355c2e13bfd5) Co-authored-by: Anand Baburajan --- erpnext/assets/doctype/asset/asset.json | 3 +- erpnext/assets/doctype/asset/asset.py | 35 +++++++++++-------- erpnext/controllers/buying_controller.py | 2 +- erpnext/patches.txt | 1 + .../v14_0/update_zero_asset_quantity_field.py | 6 ++++ 5 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 erpnext/patches/v14_0/update_zero_asset_quantity_field.py diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index d6b9c461cb..540a4f5549 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -481,6 +481,7 @@ "read_only": 1 }, { + "default": "1", "fieldname": "asset_quantity", "fieldtype": "Int", "label": "Asset Quantity", @@ -571,7 +572,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2023-11-15 17:40:17.315203", + "modified": "2023-11-20 20:57:37.010467", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3c570d1af0..7b7953b6ce 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -46,12 +46,28 @@ class Asset(AccountsController): self.validate_item() self.validate_cost_center() self.set_missing_values() - self.validate_finance_books() - if not self.split_from: - self.prepare_depreciation_data() - update_draft_asset_depr_schedules(self) self.validate_gross_and_purchase_amount() self.validate_expected_value_after_useful_life() + self.validate_finance_books() + + if not self.split_from: + self.prepare_depreciation_data() + + if self.calculate_depreciation: + update_draft_asset_depr_schedules(self) + + if frappe.db.exists("Asset", self.name): + asset_depr_schedules_names = make_draft_asset_depr_schedules_if_not_present(self) + + if asset_depr_schedules_names: + asset_depr_schedules_links = get_comma_separated_links( + asset_depr_schedules_names, "Asset Depreciation Schedule" + ) + frappe.msgprint( + _( + "Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset." + ).format(asset_depr_schedules_links) + ) self.status = self.get_status() @@ -61,17 +77,7 @@ class Asset(AccountsController): if not self.booked_fixed_asset and self.validate_make_gl_entry(): self.make_gl_entries() if self.calculate_depreciation and not self.split_from: - asset_depr_schedules_names = make_draft_asset_depr_schedules_if_not_present(self) convert_draft_asset_depr_schedules_into_active(self) - if asset_depr_schedules_names: - asset_depr_schedules_links = get_comma_separated_links( - asset_depr_schedules_names, "Asset Depreciation Schedule" - ) - frappe.msgprint( - _( - "Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset." - ).format(asset_depr_schedules_links) - ) self.set_status() add_asset_activity(self.name, _("Asset submitted")) @@ -823,6 +829,7 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), "depreciation_start_date": d.depreciation_start_date or nowdate(), + "rate_of_depreciation": d.rate_of_depreciation, } ) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a470b47a45..68ad97d7ba 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -758,7 +758,7 @@ class BuyingController(SubcontractingController): "calculate_depreciation": 0, "purchase_receipt_amount": purchase_amount, "gross_purchase_amount": purchase_amount, - "asset_quantity": row.qty if is_grouped_asset else 0, + "asset_quantity": row.qty if is_grouped_asset else 1, "purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None, "purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None, } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 51b2a1b727..2b423a6eac 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -350,5 +350,6 @@ erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_ erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based erpnext.patches.v15_0.set_reserved_stock_in_bin erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation +erpnext.patches.v14_0.update_zero_asset_quantity_field # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/update_zero_asset_quantity_field.py b/erpnext/patches/v14_0/update_zero_asset_quantity_field.py new file mode 100644 index 0000000000..0480f9b7aa --- /dev/null +++ b/erpnext/patches/v14_0/update_zero_asset_quantity_field.py @@ -0,0 +1,6 @@ +import frappe + + +def execute(): + asset = frappe.qb.DocType("Asset") + frappe.qb.update(asset).set(asset.asset_quantity, 1).where(asset.asset_quantity == 0).run() From 15f40a7af644f695a9ba99602f69fa1ccc334f9e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 21 Nov 2023 11:46:45 +0530 Subject: [PATCH 25/32] test: prevent rounding loss based validation error (cherry picked from commit 56e991b7f49e4677605f74b1de7d00ab40bf09cc) --- .../test_payment_reconciliation.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 1d843abde1..ee860988bc 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -941,6 +941,40 @@ class TestPaymentReconciliation(FrappeTestCase): # Should not raise frappe.exceptions.ValidationError: Total Debit must be equal to Total Credit. pr.reconcile() + def test_rounding_of_unallocated_amount(self): + self.supplier = "_Test Supplier USD" + pi = self.create_purchase_invoice(qty=1, rate=10, do_not_submit=True) + pi.supplier = self.supplier + pi.currency = "USD" + pi.conversion_rate = 80 + pi.credit_to = self.creditors_usd + pi.save().submit() + + pe = get_payment_entry(pi.doctype, pi.name) + pe.target_exchange_rate = 78.726500000 + pe.received_amount = 26.75 + pe.paid_amount = 2105.93 + pe.references = [] + pe.save().submit() + + # unallocated_amount will have some rounding loss - 26.749950 + self.assertNotEqual(pe.unallocated_amount, 26.75) + + pr = frappe.get_doc("Payment Reconciliation") + pr.company = self.company + pr.party_type = "Supplier" + pr.party = self.supplier + pr.receivable_payable_account = self.creditors_usd + pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() + pr.get_unreconciled_entries() + + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + # Should not raise frappe.exceptions.ValidationError: Payment Entry has been modified after you pulled it. Please pull it again. + pr.reconcile() + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From 337707b9cc7e9b13fc1557588beb6f387c6905f3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 19 Oct 2023 11:32:21 +0530 Subject: [PATCH 26/32] fix: overallocation on Payment with PO/SO (cherry picked from commit 23df4205f8abfca6764d84665cb9877703c1a470) --- erpnext/accounts/utils.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 4abd04b6c2..3da22cea7f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -648,7 +648,7 @@ def update_reference_in_payment_entry( "outstanding_amount": d.outstanding_amount, "allocated_amount": d.allocated_amount, "exchange_rate": d.exchange_rate if d.exchange_gain_loss else payment_entry.get_exchange_rate(), - "exchange_gain_loss": d.exchange_gain_loss, # only populated from invoice in case of advance allocation + "exchange_gain_loss": d.exchange_gain_loss, "account": d.account, } @@ -661,22 +661,21 @@ def update_reference_in_payment_entry( existing_row.reference_doctype, existing_row.reference_name ).set_total_advance_paid() - original_row = existing_row.as_dict().copy() - existing_row.update(reference_details) + if d.allocated_amount <= existing_row.allocated_amount: + existing_row.allocated_amount -= d.allocated_amount - if d.allocated_amount < original_row.allocated_amount: new_row = payment_entry.append("references") new_row.docstatus = 1 for field in list(reference_details): - new_row.set(field, original_row[field]) + new_row.set(field, reference_details[field]) - new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount else: new_row = payment_entry.append("references") new_row.docstatus = 1 new_row.update(reference_details) payment_entry.flags.ignore_validate_update_after_submit = True + payment_entry.clear_unallocated_reference_document_rows() payment_entry.setup_party_account_field() payment_entry.set_missing_values() if not skip_ref_details_update_for_pe: From 9600a2cdb7c79f90509cc640de999cd5c34d03a2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 19 Oct 2023 14:03:21 +0530 Subject: [PATCH 27/32] test: overalloction on reconciliation when PO is involved (cherry picked from commit 946228d783cb58cd2809f4b0bc0a854c49cc4060) --- .../test_payment_reconciliation.py | 183 +++++++++++++++++- 1 file changed, 178 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 1d843abde1..48d1cf2cc2 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -14,6 +14,7 @@ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_pay 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.accounts.party import get_party_account +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.stock.doctype.item.test_item import create_item test_dependencies = ["Item"] @@ -151,6 +152,64 @@ class TestPaymentReconciliation(FrappeTestCase): payment.posting_date = posting_date return payment + def create_purchase_invoice( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + """ + Helper function to populate default values in sales invoice + """ + pinv = make_purchase_invoice( + qty=qty, + rate=rate, + company=self.company, + customer=self.supplier, + item_code=self.item, + item_name=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + parent_cost_center=self.cost_center, + update_stock=0, + currency="INR", + is_pos=0, + is_return=0, + return_against=None, + income_account=self.income_account, + expense_account=self.expense_account, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return pinv + + def create_purchase_order( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + """ + Helper function to populate default values in sales invoice + """ + pord = create_purchase_order( + qty=qty, + rate=rate, + company=self.company, + customer=self.supplier, + item_code=self.item, + item_name=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + parent_cost_center=self.cost_center, + update_stock=0, + currency="INR", + is_pos=0, + is_return=0, + return_against=None, + income_account=self.income_account, + expense_account=self.expense_account, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return pord + def clear_old_entries(self): doctype_list = [ "GL Entry", @@ -163,13 +222,11 @@ class TestPaymentReconciliation(FrappeTestCase): for doctype in doctype_list: qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run() - def create_payment_reconciliation(self): + def create_payment_reconciliation(self, party_is_customer=True): pr = frappe.new_doc("Payment Reconciliation") pr.company = self.company - pr.party_type = ( - self.party_type if hasattr(self, "party_type") and self.party_type else "Customer" - ) - pr.party = self.customer + pr.party_type = "Customer" if party_is_customer else "Supplier" + pr.party = self.customer if party_is_customer else self.supplier pr.receivable_payable_account = get_party_account(pr.party_type, pr.party, pr.company) pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() return pr @@ -931,6 +988,7 @@ class TestPaymentReconciliation(FrappeTestCase): if invoice.invoice_number == pi.name: invoices.append(invoice.as_dict()) break + for payment in pr.payments: if payment.reference_name == pi_return.name: payments.append(payment.as_dict()) @@ -941,6 +999,121 @@ class TestPaymentReconciliation(FrappeTestCase): # Should not raise frappe.exceptions.ValidationError: Total Debit must be equal to Total Credit. pr.reconcile() + def test_reconciliation_from_purchase_order_to_multiple_invoices(self): + """ + Reconciling advance payment from PO/SO to multiple invoices should not cause overallocation + """ + + self.supplier = "_Test Supplier" + + pi1 = self.create_purchase_invoice(qty=10, rate=100) + pi2 = self.create_purchase_invoice(qty=10, rate=100) + po = self.create_purchase_order(qty=20, rate=100) + pay = get_payment_entry(po.doctype, po.name) + # Overpay Puchase Order + pay.paid_amount = 3000 + pay.save().submit() + # assert total allocated and unallocated before reconciliation + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (po.doctype, po.name, 2000), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 2) + self.assertEqual(len(pr.payments), 2) + + for x in pr.payments: + self.assertEqual((x.reference_type, x.reference_name), (pay.doctype, pay.name)) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + # partial allocation on pi1 and full allocate on pi2 + pr.allocation[0].allocated_amount = 100 + pr.reconcile() + + # assert references and total allocated and unallocated amount + pay.reload() + self.assertEqual(len(pay.references), 3) + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (po.doctype, po.name, 900), + ) + self.assertEqual( + ( + pay.references[1].reference_doctype, + pay.references[1].reference_name, + pay.references[1].allocated_amount, + ), + (pi1.doctype, pi1.name, 100), + ) + self.assertEqual( + ( + pay.references[2].reference_doctype, + pay.references[2].reference_name, + pay.references[2].allocated_amount, + ), + (pi2.doctype, pi2.name, 1000), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 2) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + # assert references and total allocated and unallocated amount + pay.reload() + self.assertEqual(len(pay.references), 3) + # PO references should be removed now + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (pi1.doctype, pi1.name, 100), + ) + self.assertEqual( + ( + pay.references[1].reference_doctype, + pay.references[1].reference_name, + pay.references[1].allocated_amount, + ), + (pi2.doctype, pi2.name, 1000), + ) + self.assertEqual( + ( + pay.references[2].reference_doctype, + pay.references[2].reference_name, + pay.references[2].allocated_amount, + ), + (pi1.doctype, pi1.name, 900), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From 034375b4f56b8af05ca9e153b8539695e2065439 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 19 Oct 2023 14:57:05 +0530 Subject: [PATCH 28/32] refactor(test): make use of utility methods (cherry picked from commit 547993f80103fa192563a82447c39fe122918767) --- .../test_payment_reconciliation.py | 79 ++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 48d1cf2cc2..71bc498b49 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -86,26 +86,44 @@ class TestPaymentReconciliation(FrappeTestCase): self.customer5 = make_customer("_Test PR Customer 5", "EUR") def create_account(self): - account_name = "Debtors EUR" - if not frappe.db.get_value( - "Account", filters={"account_name": account_name, "company": self.company} - ): - acc = frappe.new_doc("Account") - acc.account_name = account_name - acc.parent_account = "Accounts Receivable - _PR" - acc.company = self.company - acc.account_currency = "EUR" - acc.account_type = "Receivable" - acc.insert() - else: - name = frappe.db.get_value( - "Account", - filters={"account_name": account_name, "company": self.company}, - fieldname="name", - pluck=True, - ) - acc = frappe.get_doc("Account", name) - self.debtors_eur = acc.name + accounts = [ + { + "attribute": "debtors_eur", + "account_name": "Debtors EUR", + "parent_account": "Accounts Receivable - _PR", + "account_currency": "EUR", + "account_type": "Receivable", + }, + { + "attribute": "creditors_usd", + "account_name": "Payable USD", + "parent_account": "Accounts Payable - _PR", + "account_currency": "USD", + "account_type": "Payable", + }, + ] + + for x in accounts: + x = frappe._dict(x) + if not frappe.db.get_value( + "Account", filters={"account_name": x.account_name, "company": self.company} + ): + acc = frappe.new_doc("Account") + acc.account_name = x.account_name + acc.parent_account = x.parent_account + acc.company = self.company + acc.account_currency = x.account_currency + acc.account_type = x.account_type + acc.insert() + else: + name = frappe.db.get_value( + "Account", + filters={"account_name": x.account_name, "company": self.company}, + fieldname="name", + pluck=True, + ) + acc = frappe.get_doc("Account", name) + setattr(self, x.attribute, acc.name) def create_sales_invoice( self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False @@ -963,9 +981,13 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(pr.allocation[0].difference_amount, 0) def test_reconciliation_purchase_invoice_against_return(self): - pi = make_purchase_invoice( - supplier="_Test Supplier USD", currency="USD", conversion_rate=50 - ).submit() + self.supplier = "_Test Supplier USD" + pi = self.create_purchase_invoice(qty=5, rate=50, do_not_submit=True) + pi.supplier = self.supplier + pi.currency = "USD" + pi.conversion_rate = 50 + pi.credit_to = self.creditors_usd + pi.save().submit() pi_return = frappe.get_doc(pi.as_dict()) pi_return.name = None @@ -975,11 +997,12 @@ class TestPaymentReconciliation(FrappeTestCase): pi_return.items[0].qty = -pi_return.items[0].qty pi_return.submit() - self.company = "_Test Company" - self.party_type = "Supplier" - self.customer = "_Test Supplier USD" - - pr = self.create_payment_reconciliation() + pr = frappe.get_doc("Payment Reconciliation") + pr.company = self.company + pr.party_type = "Supplier" + pr.party = self.supplier + pr.receivable_payable_account = self.creditors_usd + pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() pr.get_unreconciled_entries() invoices = [] From 2bec89a5bf339e8cd1db189cc27b556842e2def0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 24 Oct 2023 08:09:22 +0530 Subject: [PATCH 29/32] chore: fix flakiness `test_sales_order_partial_advance_payment` (cherry picked from commit 4dff2c7a0dad1de840a2b1f53d51e9fe1682fa7f) --- erpnext/selling/doctype/sales_order/test_sales_order.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 83689a2b0b..d8b5878aa3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1784,10 +1784,10 @@ class TestSalesOrder(FrappeTestCase): si.submit() pe.load_from_db() - self.assertEqual(pe.references[0].reference_name, si.name) - self.assertEqual(pe.references[0].allocated_amount, 200) - self.assertEqual(pe.references[1].reference_name, so.name) - self.assertEqual(pe.references[1].allocated_amount, 300) + self.assertEqual(pe.references[0].reference_name, so.name) + self.assertEqual(pe.references[0].allocated_amount, 300) + self.assertEqual(pe.references[1].reference_name, si.name) + self.assertEqual(pe.references[1].allocated_amount, 200) def test_delivered_item_material_request(self): "SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO." From 1f2f5d8cf6e3a1c8245af0fb95e6c0dccdbce2e8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:41:50 +0530 Subject: [PATCH 30/32] fix(Timesheet): warn user if billing hours > actual hours instead of resetting (backport #38239) (#38241) fix(Timesheet): warn user if billing hours > actual hours instead of resetting (#38239) * revert: "fix(Timesheet): reset billing hours equal to hours if they exceed actual hours" This reverts commit 0ec8034507996f06eaf8ca13a414d10b34038c6c. * fix: warn user if billing hours > actual hours (cherry picked from commit ac91030b31f1108b7e32844f12a3a6c916c0120f) Co-authored-by: Rucha Mahabal --- erpnext/projects/doctype/timesheet/timesheet.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 8e464b5c6b..b9d801ce90 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -69,8 +69,14 @@ class Timesheet(Document): def update_billing_hours(self, args): if args.is_billable: - if flt(args.billing_hours) == 0.0 or flt(args.billing_hours) > flt(args.hours): + if flt(args.billing_hours) == 0.0: args.billing_hours = args.hours + elif flt(args.billing_hours) > flt(args.hours): + frappe.msgprint( + _("Warning - Row {0}: Billing Hours are more than Actual Hours").format(args.idx), + indicator="orange", + alert=True, + ) else: args.billing_hours = 0 From ed7b845a5519087ef3aa6c9054451428b3cfa1a3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:53:03 +0530 Subject: [PATCH 31/32] fix: valuation rate for FG item for subcontracting receipt (backport #38244) (#38245) fix: valuation rate for FG item for subcontracting receipt (#38244) (cherry picked from commit 5c308a4f9aa1a27cbd216addbe495b9bf69735b1) Co-authored-by: rohitwaghchaure --- .../serial_and_batch_bundle.py | 2 +- .../test_subcontracting_receipt.py | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index df2cfeb04f..1e13eb2a8a 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -241,7 +241,7 @@ class SerialandBatchBundle(Document): valuation_field = "rate" child_table = "Subcontracting Receipt Supplied Item" else: - valuation_field = "rm_supp_cost" + valuation_field = "rate" child_table = "Subcontracting Receipt Item" precision = frappe.get_precision(child_table, valuation_field) or 2 diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 6191a8ca94..f0e4e00074 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -667,6 +667,104 @@ class TestSubcontractingReceipt(FrappeTestCase): "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0 ) + def test_subcontracting_receipt_valuation_for_fg_with_auto_created_serial_batch_bundle(self): + set_backflush_based_on("BOM") + + fg_item = make_item( + properties={ + "is_stock_item": 1, + "is_sub_contracted_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BSSNGS-.####", + } + ).name + + rm_item1 = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BNGS-.####", + } + ).name + + rm_item2 = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "has_serial_no": 1, + "create_new_batch": 1, + "batch_number_series": "BNGS-.####", + "serial_no_series": "BNSS-.####", + } + ).name + + rm_item3 = make_item( + properties={ + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "BSSSS-.####", + } + ).name + + bom = make_bom(item=fg_item, raw_materials=[rm_item1, rm_item2, rm_item3]) + + rm_batch_no = None + for row in bom.items: + make_stock_entry( + item_code=row.item_code, + qty=1, + target="_Test Warehouse 1 - _TC", + rate=300, + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 1, + "rate": 100, + "fg_item": fg_item, + "fg_item_qty": 1, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + + frappe.db.set_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1 + ) + scr = make_subcontracting_receipt(sco.name) + scr.save() + scr.submit() + scr.reload() + + for row in scr.supplied_items: + self.assertEqual(row.rate, 300.00) + self.assertTrue(row.serial_and_batch_bundle) + auto_created_serial_batch = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_no": scr.name, "voucher_detail_no": row.name}, + "auto_created_serial_and_batch_bundle", + ) + + self.assertTrue(auto_created_serial_batch) + + self.assertEqual(scr.items[0].rm_cost_per_qty, 900) + self.assertEqual(scr.items[0].service_cost_per_qty, 100) + self.assertEqual(scr.items[0].rate, 1000) + valuation_rate = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_no": scr.name, "voucher_detail_no": scr.items[0].name}, + "valuation_rate", + ) + + self.assertEqual(flt(valuation_rate), flt(1000)) + + frappe.db.set_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0 + ) + def test_subcontracting_receipt_raw_material_rate(self): # Step - 1: Set Backflush Based On as "BOM" set_backflush_based_on("BOM") From c60aaa799acb7eebedc2f8e348d4cdcd9fcb9384 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:57:34 +0530 Subject: [PATCH 32/32] fix: set asset's valuation_rate according to asset quantity (backport #38254) (#38256) fix: set asset's valuation_rate according to asset quantity (#38254) (cherry picked from commit e2bb4e2baa9059afb27f685beb88172a615097a4) Co-authored-by: Anand Baburajan --- .../doctype/purchase_receipt/purchase_receipt.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d905fe5ea6..a5940f07d6 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -731,12 +731,18 @@ class PurchaseReceipt(BuyingController): def update_assets(self, item, valuation_rate): assets = frappe.db.get_all( - "Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code} + "Asset", + filters={"purchase_receipt": self.name, "item_code": item.item_code}, + fields=["name", "asset_quantity"], ) for asset in assets: - frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(valuation_rate)) - frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(valuation_rate)) + frappe.db.set_value( + "Asset", asset.name, "gross_purchase_amount", flt(valuation_rate) * asset.asset_quantity + ) + frappe.db.set_value( + "Asset", asset.name, "purchase_receipt_amount", flt(valuation_rate) * asset.asset_quantity + ) def update_status(self, status): self.set_status(update=True, status=status)