From f2ceb003797332cb6c1fee7a5c3e67a2b2b8e3d1 Mon Sep 17 00:00:00 2001 From: vishnu Date: Sun, 14 May 2023 09:35:38 +0000 Subject: [PATCH 1/9] fix: Creating landed cost voucher from connections --- erpnext/public/js/controllers/accounts.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index d943126018..47b88a002b 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -91,6 +91,12 @@ frappe.ui.form.on("Sales Invoice", { }); frappe.ui.form.on('Purchase Invoice', { + setup: (frm) => { + frm.make_methods = { + 'Landed Cost Voucher': function () { frm.trigger('create_landedcost_voucher') }, + } + }, + mode_of_payment: function(frm) { get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){ frm.set_value('cash_bank_account', account); @@ -99,6 +105,20 @@ frappe.ui.form.on('Purchase Invoice', { payment_terms_template: function() { cur_frm.trigger("disable_due_date"); + }, + + create_landedcost_voucher: function (frm) { + let lcv = frappe.model.get_new_doc('Landed Cost Voucher'); + lcv.company = frm.doc.company; + + let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Invoice'); + lcv_receipt.receipt_document_type = 'Purchase Invoice'; + lcv_receipt.receipt_document = frm.doc.name; + lcv_receipt.supplier = frm.doc.supplier; + lcv_receipt.grand_total = frm.doc.grand_total; + lcv.purchase_receipts = [lcv_receipt]; + + frappe.set_route("Form", lcv.doctype, lcv.name); } }); From 84b7c1bba09f89a390a17898b4d8fa665adcbc8f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 17 May 2023 13:40:33 +0530 Subject: [PATCH 2/9] fix: tds incorrectly calculated for invoice that are below threshold Two purchase invoices for the same supplier, using different tax withholding categories have this issue. | Category | single | cumulative | |----------+--------+------------| | cat1 | 100 | 500 | | cat2 | 1000 | 5000 | 1. PINV1 of net total: 105/- uses cat1. TDS is calculated as it breached single threshold 2. PINV2 of net total: 200/- uses cat2. TDS incorrectly calculated as PINV1 already has TDS calculated and 'consider_party_ledger_amount' is enabled. --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ad3477ef3d..1f2d980373 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -302,7 +302,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): "docstatus": 1, } - if not tax_details.get("consider_party_ledger_amount") and doctype != "Sales Invoice": + if doctype != "Sales Invoice": filters.update( {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) From adf2474d9ded01ed89aedddd1fd3da3514cd68b0 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 16 May 2023 15:12:47 +0530 Subject: [PATCH 3/9] fix(ux): SCR consumed-qty read-only property --- .../subcontracting_receipt.js | 18 ++++-------------- .../subcontracting_receipt.py | 8 ++++++++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 45289b1dab..4bf008ac40 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -76,26 +76,14 @@ frappe.ui.form.on('Subcontracting Receipt', { } }); - let batch_no_field = frm.get_docfield("items", "batch_no"); + let batch_no_field = frm.get_docfield('items', 'batch_no'); if (batch_no_field) { batch_no_field.get_route_options_for_new_doc = function(row) { return { - "item": row.doc.item_code + 'item': row.doc.item_code } }; } - - frappe.db.get_single_value('Buying Settings', 'backflush_raw_materials_of_subcontract_based_on').then(val => { - if (val == 'Material Transferred for Subcontract') { - frm.fields_dict['supplied_items'].grid.grid_rows.forEach((grid_row) => { - grid_row.docfields.forEach((df) => { - if (df.fieldname == 'consumed_qty') { - df.read_only = 0; - } - }); - }); - } - }); }, refresh: (frm) => { @@ -157,6 +145,8 @@ frappe.ui.form.on('Subcontracting Receipt', { } }); }, __('Get Items From')); + + frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', 'read_only', frm.doc.__onload && frm.doc.__onload.backflush_based_on === 'BOM'); } }, diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 2c84262273..416f4f80a2 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -28,6 +28,14 @@ class SubcontractingReceipt(SubcontractingController): }, ] + def onload(self): + self.set_onload( + "backflush_based_on", + frappe.db.get_single_value( + "Buying Settings", "backflush_raw_materials_of_subcontract_based_on" + ), + ) + def update_status_updater_args(self): if cint(self.is_return): self.status_updater.extend( From e5c86bc2e82202522bd803f66dc1bdc00f8e4432 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 17 May 2023 16:04:49 +0530 Subject: [PATCH 4/9] fix: consider 0 if rate/qty are null (#35338) [skip ci] --- .../patches/v13_0/update_amt_in_work_order_required_items.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py index e37f291233..64170edb71 100644 --- a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py +++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py @@ -7,4 +7,6 @@ def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order") frappe.reload_doc("manufacturing", "doctype", "work_order_item") - frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""") + frappe.db.sql( + """UPDATE `tabWork Order Item` SET amount = ifnull(rate, 0.0) * ifnull(required_qty, 0.0)""" + ) From 9fb8b1827daa126087d3e237d135a96e73841e70 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 11 May 2023 12:47:47 +0530 Subject: [PATCH 5/9] fix: Pick List Status --- erpnext/stock/doctype/pick_list/pick_list.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 46d6e9e757..74927c779c 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -29,6 +29,7 @@ class PickList(Document): self.validate_for_qty() def before_save(self): + self.update_status() self.set_item_locations() # set percentage picked in SO @@ -89,20 +90,20 @@ class PickList(Document): self.update_reference_qty() self.update_sales_order_picking_status() - def update_status(self, status=None, update_modified=True): + def update_status(self, status=None): if not status: if self.docstatus == 0: status = "Draft" elif self.docstatus == 1: - if self.status == "Draft": - status = "Open" - elif target_document_exists(self.name, self.purpose): + if target_document_exists(self.name, self.purpose): status = "Completed" + else: + status = "Open" elif self.docstatus == 2: status = "Cancelled" if status: - frappe.db.set_value("Pick List", self.name, "status", status, update_modified=update_modified) + self.db_set("status", status) def update_reference_qty(self): packed_items = [] From 8c53e0f0045cc8ecdcd61dff3b4dc651b2c34e0c Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Wed, 17 May 2023 22:29:09 +0530 Subject: [PATCH 6/9] fix: depreciation schedule for existing assets [dev] (#35256) * fix: depreciation schedule for existing assets * chore: correct logic for existing assets and fix test * chore: properly check if depr amount is non zero --- erpnext/assets/doctype/asset/test_asset.py | 2 +- .../asset_depreciation_schedule.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 203612ff1b..f3a9ba0948 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -691,7 +691,7 @@ class TestDepreciationMethods(AssetSetup): ) self.assertEqual(asset.status, "Draft") - expected_schedules = [["2032-12-31", 30000.0, 77095.89], ["2033-06-06", 12904.11, 90000.0]] + expected_schedules = [["2032-12-31", 42904.11, 90000.0]] schedules = [ [cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount] for d in get_depr_schedule(asset.name, "Draft") diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 116593ad9e..ad5ec3d6ed 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -337,7 +337,7 @@ class AssetDepreciationSchedule(Document): depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life skip_row = True - if depreciation_amount > 0: + if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0: self.add_depr_schedule_row( schedule_date, depreciation_amount, @@ -521,9 +521,11 @@ def get_straight_line_or_manual_depr_amount(asset, row): ) # if the Depreciation Schedule is being prepared for the first time else: - return (flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)) / flt( - row.total_number_of_depreciations - ) + return ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) def get_wdv_or_dd_depr_amount( From 90ddf1c0f0bd7e7bd8a932d3e21c0829cf60ff28 Mon Sep 17 00:00:00 2001 From: MOHAMMED NIYAS <76736615+niyazrazak@users.noreply.github.com> Date: Thu, 18 May 2023 10:23:58 +0530 Subject: [PATCH 7/9] fix: create sales order Getting error while clicking create sales order button from quotation Taceback (most recent call last): File "apps/frappe/frappe/app.py", line 66, in application response = frappe.api.handle() File "apps/frappe/frappe/api.py", line 54, in handle return frappe.handler.handle() File "apps/frappe/frappe/handler.py", line 45, in handle data = execute_cmd(cmd) File "apps/frappe/frappe/handler.py", line 83, in execute_cmd return frappe.call(method, **frappe.form_dict) File "apps/frappe/frappe/__init__.py", line 1607, in call return fn(*args, **newargs) File "apps/frappe/frappe/model/mapper.py", line 36, in make_mapped_doc return method(source_name) File "apps/erpnext/erpnext/selling/doctype/quotation/quotation.py", line 263, in make_sales_order return _make_sales_order(source_name, target_doc) File "apps/erpnext/erpnext/selling/doctype/quotation/quotation.py", line 333, in _make_sales_order doclist = get_mapped_doc( File "apps/frappe/frappe/model/mapper.py", line 144, in get_mapped_doc postprocess(source_doc, target_doc) File "apps/erpnext/erpnext/selling/doctype/quotation/quotation.py", line 291, in set_missing_values for d in customer.get("sales_team"): TypeError: 'NoneType' object is not iterable --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 693fc92ce9..0cab21fa30 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -288,7 +288,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): ) # sales team - for d in customer.get("sales_team"): + for d in customer.get("sales_team", []): target.append( "sales_team", { From 132846bbd107a921dfbfde9240f368bea9382cf7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 May 2023 12:00:59 +0530 Subject: [PATCH 8/9] fix(test): cumulative threshold checks --- .../test_tax_withholding_category.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 1e86cf5d2e..bc4f6709fc 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -110,9 +110,9 @@ class TestTaxWithholdingCategory(unittest.TestCase): invoices.append(pi1) # Cumulative threshold is 30000 - # Threshold calculation should be on both the invoices - # TDS should be applied only on 1000 - self.assertEqual(pi1.taxes[0].tax_amount, 1000) + # Threshold calculation should be only on the Second invoice + # Second didn't breach, no TDS should be applied + self.assertEqual(pi1.taxes, []) for d in reversed(invoices): d.cancel() From b2290c6f57cfe8a9ddade44f673039a4600313c9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 May 2023 12:47:38 +0530 Subject: [PATCH 9/9] fix: possible type error on quotation -> sales order creation --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 0cab21fa30..61969fe8a9 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -288,7 +288,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): ) # sales team - for d in customer.get("sales_team", []): + for d in customer.get("sales_team") or []: target.append( "sales_team", {