From d42173beb57184f7695a67a34f7e25b74a30d8ad Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 9 Jan 2025 13:52:34 +0530 Subject: [PATCH 1/5] fix: do not add ordered items from Quotation to new Sales Order (cherry picked from commit 2e930eb97b893a72c2b549ecbf0fcd525caec155) --- .../selling/doctype/quotation/quotation.py | 6 +++- .../doctype/quotation/test_quotation.py | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 48ff2756253..433f8f1562e 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -322,7 +322,11 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): 2. If selections: Is Alternative Item/Has Alternative Item: Map if selected and adequate qty 3. If selections: Simple row: Map if adequate qty """ - has_qty = item.qty > 0 + balance_qty = item.qty - ordered_items.get(item.item_code, 0.0) + if balance_qty <= 0: + return False + + has_qty = balance_qty if not selected_rows: return not item.is_alternative diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 4219d0698ca..a40b1960323 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -30,6 +30,38 @@ class TestQuotation(FrappeTestCase): self.assertTrue(sales_order.get("payment_schedule")) + def test_do_not_add_ordered_items_in_new_sales_order(self): + from erpnext.selling.doctype.quotation.quotation import make_sales_order + from erpnext.stock.doctype.item.test_item import make_item + + item = make_item("_Test Item for Quotation for SO", {"is_stock_item": 1}) + + quotation = make_quotation(qty=5, do_not_submit=True) + quotation.append( + "items", + { + "item_code": item.name, + "qty": 5, + "rate": 100, + "conversion_factor": 1, + "uom": item.stock_uom, + "warehouse": "_Test Warehouse - _TC", + "stock_uom": item.stock_uom, + }, + ) + quotation.submit() + + sales_order = make_sales_order(quotation.name) + sales_order.delivery_date = nowdate() + self.assertEqual(len(sales_order.items), 2) + sales_order.remove(sales_order.items[1]) + sales_order.submit() + + sales_order = make_sales_order(quotation.name) + self.assertEqual(len(sales_order.items), 1) + self.assertEqual(sales_order.items[0].item_code, item.name) + self.assertEqual(sales_order.items[0].qty, 5.0) + def test_gross_profit(self): from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry From f4b7fa898054eae432bf72659e684e82ed76eb06 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:51:27 +0100 Subject: [PATCH 2/5] feat: validate discount date in payment schedule (backport #44646) (#44726) Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- erpnext/controllers/accounts_controller.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 15748868510..fa26fea3bf0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2188,6 +2188,9 @@ class AccountsController(TransactionBase): return for d in self.get("payment_schedule"): + if d.due_date and d.discount_date: + d.validate_from_to_dates("discount_date", "due_date") + if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date): frappe.throw( _("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format( From 6bc210d9f4242c3e490f63f56f20fa1ae6d594b7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 Jan 2025 12:04:40 +0530 Subject: [PATCH 3/5] fix: typo (#45233) --- .../manufacturing_settings/manufacturing_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index d3ad51f7236..65ef0851d32 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -201,7 +201,7 @@ "description": "In the case of 'Use Multi-Level BOM' in a work order, if the user wishes to add sub-assembly costs to Finished Goods items without using a job card as well the scrap items, then this option needs to be enable.", "fieldname": "set_op_cost_and_scrape_from_sub_assemblies", "fieldtype": "Check", - "label": "Set Operating Cost / Scrape Items From Sub-assemblies" + "label": "Set Operating Cost / Scrap Items From Sub-assemblies" } ], "icon": "icon-wrench", @@ -226,4 +226,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From 49ffeccafa46d609fb5f744d440a54cdf297bbe3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:08:26 +0530 Subject: [PATCH 4/5] fix(Timesheet): ignore permissions when updating Task and Project (backport #45168) (#45171) * fix(Timesheet): ignore permissions when updating Task and Project (#45168) (cherry picked from commit 9e760e54a53679480982f2d2afc6638d07acf18a) # Conflicts: # erpnext/projects/doctype/timesheet/timesheet.py * chore: resolve conflicts --------- Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- 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 e67841e0358..7e237209e09 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -120,7 +120,7 @@ class Timesheet(Document): if data.task and data.task not in tasks: task = frappe.get_doc("Task", data.task) task.update_time_and_costing() - task.save() + task.save(ignore_permissions=True) tasks.append(data.task) elif data.project and data.project not in projects: From 6a52f79cceb72600cca21f8b69e7ec1cf9ce40ae Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 31 Dec 2024 13:46:50 +0530 Subject: [PATCH 5/5] fix: deduct tds on excess amount if checked (cherry picked from commit a203e3ffaf7c2c9fcc9b8b79af5a4c490b692885) --- .../tax_withholding_category.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 15287f200fb..dc8a34647e2 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -523,9 +523,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers): else: tax_withholding_net_total = inv.get("tax_withholding_net_total", 0) - if (threshold and tax_withholding_net_total >= threshold) or ( + has_cumulative_threshold_breached = ( cumulative_threshold and (supp_credit_amt + supp_inv_credit_amt) >= cumulative_threshold - ): + ) + + if (threshold and tax_withholding_net_total >= threshold) or (has_cumulative_threshold_breached): # Get net total again as TDS is calculated on net total # Grand is used to just check for threshold breach net_total = ( @@ -533,9 +535,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers): ) supp_credit_amt += net_total - if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint( - tax_details.tax_on_excess_amount - ): + if has_cumulative_threshold_breached and cint(tax_details.tax_on_excess_amount): supp_credit_amt = net_total + tax_withholding_net_total - cumulative_threshold if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0):