diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d70977c07e2..fba2f74a5b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.3.0 hooks: - id: trailing-whitespace files: "erpnext.*" @@ -15,6 +15,10 @@ repos: args: ['--branch', 'develop'] - id: check-merge-conflict - id: check-ast + - id: check-json + - id: check-toml + - id: check-yaml + - id: debug-statements - repo: https://github.com/PyCQA/flake8 rev: 5.0.4 diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json index 741d4283e2f..daf2e21d78c 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json @@ -53,8 +53,13 @@ }, "II. Forderungen und sonstige Vermögensgegenstände": { "is_group": 1, - "Ford. a. Lieferungen und Leistungen": { + "Forderungen aus Lieferungen und Leistungen mit Kontokorrent": { "account_number": "1400", + "account_type": "Receivable", + "is_group": 1 + }, + "Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": { + "account_number": "1410", "account_type": "Receivable" }, "Durchlaufende Posten": { @@ -180,8 +185,13 @@ }, "IV. Verbindlichkeiten aus Lieferungen und Leistungen": { "is_group": 1, - "Verbindlichkeiten aus Lieferungen u. Leistungen": { + "Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": { "account_number": "1600", + "account_type": "Payable", + "is_group": 1 + }, + "Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": { + "account_number": "1610", "account_type": "Payable" } }, diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json index 2bf55cfcd04..000ef80ee38 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json @@ -407,13 +407,10 @@ "Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": { "account_number": "9960" }, - "Debitoren": { - "is_group": 1, - "account_number": "10000" - }, - "Forderungen aus Lieferungen und Leistungen": { + "Forderungen aus Lieferungen und Leistungen mit Kontokorrent": { "account_number": "1200", - "account_type": "Receivable" + "account_type": "Receivable", + "is_group": 1 }, "Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": { "account_number": "1210" @@ -1138,18 +1135,15 @@ "Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": { "account_number": "9964" }, - "Kreditoren": { - "account_number": "70000", + "Verb. aus Lieferungen und Leistungen mit Kontokorrent": { + "account_number": "3300", + "account_type": "Payable", "is_group": 1, - "Wareneingangs-Verrechnungskonto" : { + "Wareneingangs-Verrechnungskonto" : { "account_number": "70001", "account_type": "Stock Received But Not Billed" } }, - "Verb. aus Lieferungen und Leistungen": { - "account_number": "3300", - "account_type": "Payable" - }, "Verb. aus Lieferungen und Leistungen ohne Kontokorrent": { "account_number": "3310" }, diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index e6d97a1fb26..592eaecc1c5 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -138,8 +138,7 @@ "label": "Against Voucher Type", "oldfieldname": "against_voucher_type", "oldfieldtype": "Data", - "options": "DocType", - "search_index": 1 + "options": "DocType" }, { "fieldname": "against_voucher", @@ -158,8 +157,7 @@ "label": "Voucher Type", "oldfieldname": "voucher_type", "oldfieldtype": "Select", - "options": "DocType", - "search_index": 1 + "options": "DocType" }, { "fieldname": "voucher_no", @@ -291,4 +289,4 @@ "search_fields": "voucher_no,account,posting_date,against_voucher", "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c2aa1d936e5..3b77614607a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -164,6 +164,18 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. } }) }, __("Get Items From")); + + if (!this.frm.doc.is_return) { + frappe.db.get_single_value("Buying Settings", "maintain_same_rate").then((value) => { + if (value) { + this.frm.doc.items.forEach((item) => { + this.frm.fields_dict.items.grid.update_docfield_property( + "rate", "read_only", (item.purchase_receipt && item.pr_detail) + ); + }); + } + }); + } } this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 944a8fb2364..7171bddac4b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1660,10 +1660,6 @@ def make_inter_company_sales_invoice(source_name, target_doc=None): return make_inter_company_transaction("Purchase Invoice", source_name, target_doc) -def on_doctype_update(): - frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"]) - - @frappe.whitelist() def make_purchase_receipt(source_name, target_doc=None): def update_item(obj, target, source_parent): diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index bc56132c60f..3d59d288e4d 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -286,7 +286,6 @@ "oldfieldname": "import_rate", "oldfieldtype": "Currency", "options": "currency", - "read_only_depends_on": "eval: (!parent.is_return && doc.purchase_receipt && doc.pr_detail)", "reqd": 1 }, { @@ -894,7 +893,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-30 16:26:05.629780", + "modified": "2023-12-25 22:00:28.043555", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 3d18a860361..63a576b5ba8 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2388,10 +2388,6 @@ def get_loyalty_programs(customer): return lp_details -def on_doctype_update(): - frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"]) - - @frappe.whitelist() def create_invoice_discounting(source_name, target_doc=None): invoice = frappe.get_doc("Sales Invoice", source_name) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 0979cffbf3c..dc2e8a8518b 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -149,11 +149,16 @@ frappe.query_reports["Accounts Payable"] = { "label": __("Revaluation Journals"), "fieldtype": "Check", }, + { + "fieldname": "in_party_currency", + "label": __("In Party Currency"), + "fieldtype": "Check", + }, { "fieldname": "ignore_accounts", "label": __("Group by Voucher"), "fieldtype": "Check", - } + }, ], diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index 9f03d92cd50..b4cb25ff1b8 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -40,6 +40,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "range2": 60, "range3": 90, "range4": 120, + "in_party_currency": 1, } data = execute(filters) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index d6e3098e171..28aa7bf9ecf 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -181,13 +181,17 @@ frappe.query_reports["Accounts Receivable"] = { "label": __("Revaluation Journals"), "fieldtype": "Check", }, + { + "fieldname": "in_party_currency", + "label": __("In Party Currency"), + "fieldtype": "Check", + }, { "fieldname": "ignore_accounts", "label": __("Group by Voucher"), "fieldtype": "Check", } - ], "formatter": function(value, row, column, data, default_formatter) { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py old mode 100755 new mode 100644 index e4d5938c0b5..8f7b99aef16 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -28,8 +28,8 @@ from erpnext.accounts.utils import get_currency_precision # 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters # 7. For overpayment against an invoice with payment terms, there will be an additional row # 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated -# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party -# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable" +# 9. Report amounts are in party currency if in_party_currency is selected, otherwise company currency +# 10. This report is based on Payment Ledger Entries def execute(filters=None): @@ -84,6 +84,9 @@ class ReceivablePayableReport(object): self.total_row_map = {} self.skip_total_row = 1 + if self.filters.get("in_party_currency"): + self.skip_total_row = 1 + def get_data(self): self.get_ple_entries() self.get_sales_invoices_or_customers_based_on_sales_person() @@ -145,7 +148,7 @@ class ReceivablePayableReport(object): if self.filters.get("group_by_party"): self.init_subtotal_row(ple.party) - if self.filters.get("group_by_party"): + if self.filters.get("group_by_party") and not self.filters.get("in_party_currency"): self.init_subtotal_row("Total") def get_invoices(self, ple): @@ -224,8 +227,7 @@ class ReceivablePayableReport(object): if not row: return - # amount in "Party Currency", if its supplied. If not, amount in company currency - if self.filters.get("party_type") and self.filters.get("party"): + if self.filters.get("in_party_currency") or self.filters.get("party_account"): amount = ple.amount_in_account_currency else: amount = ple.amount @@ -260,8 +262,10 @@ class ReceivablePayableReport(object): def update_sub_total_row(self, row, party): total_row = self.total_row_map.get(party) - for field in self.get_currency_fields(): - total_row[field] += row.get(field, 0.0) + if total_row: + for field in self.get_currency_fields(): + total_row[field] += row.get(field, 0.0) + total_row["currency"] = row.get("currency", "") def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) @@ -322,7 +326,7 @@ class ReceivablePayableReport(object): if self.filters.get("group_by_party"): self.append_subtotal_row(self.previous_party) if self.data: - self.data.append(self.total_row_map.get("Total")) + self.data.append(self.total_row_map.get("Total", {})) def append_row(self, row): self.allocate_future_payments(row) @@ -453,7 +457,7 @@ class ReceivablePayableReport(object): party_details = self.get_party_details(row.party) or {} row.update(party_details) - if self.filters.get("party_type") and self.filters.get("party"): + if self.filters.get("in_party_currency") or self.filters.get("party_account"): row.currency = row.account_currency else: row.currency = self.company_currency diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 77f8c6eaaa9..976935b99f6 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -579,7 +579,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters.update({"party_account": self.debtors_usd}) report = execute(filters)[1] self.assertEqual(len(report), 1) - expected_data = [8000.0, 8000.0, self.debtors_usd, si2.currency] + expected_data = [100.0, 100.0, self.debtors_usd, si2.currency] row = report[0] self.assertEqual( expected_data, [row.invoiced, row.outstanding, row.party_account, row.account_currency] @@ -616,6 +616,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "range2": 60, "range3": 90, "range4": 120, + "in_party_currency": 1, } si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index f6c7bd3db70..e4953bb1815 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -345,21 +345,16 @@ def get_tds_docs_query(filters, bank_accounts, tds_accounts): if filters.get("party"): party = [filters.get("party")] - query = query.where( - ((gle.account.isin(tds_accounts) & gle.against.isin(party))) - | ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party"))) - | gle.party.isin(party) + jv_condition = gle.against.isin(party) | ( + (gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party")) ) else: party = frappe.get_all(filters.get("party_type"), pluck="name") - query = query.where( - ((gle.account.isin(tds_accounts) & gle.against.isin(party))) - | ( - (gle.voucher_type == "Journal Entry") - & ((gle.party_type == filters.get("party_type")) | (gle.party_type == "")) - ) - | gle.party.isin(party) + jv_condition = gle.against.isin(party) | ( + (gle.voucher_type == "Journal Entry") + & ((gle.party_type == filters.get("party_type")) | (gle.party_type == "")) ) + query = query.where((gle.account.isin(tds_accounts) & jv_condition) | gle.party.isin(party)) return query diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 45790b80fcb..1a9035c3327 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -123,8 +123,7 @@ "oldfieldname": "item_code", "oldfieldtype": "Link", "options": "Item", - "reqd": 1, - "search_index": 1 + "reqd": 1 }, { "fieldname": "supplier_part_no", diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 34d3e700ccc..772fbd54512 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -433,8 +433,11 @@ class SubcontractingController(StockController): self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty) self.available_materials[key]["batch_no"][batch_no] = 0 - if abs(qty) > 0 and not new_rm_obj: + if new_rm_obj: + self.remove(rm_obj) + elif abs(qty) > 0: self.__set_consumed_qty(rm_obj, qty) + else: self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty) self.__set_serial_nos(item_row, rm_obj) @@ -525,6 +528,10 @@ class SubcontractingController(StockController): (row.item_code, row.get(self.subcontract_data.order_field)) ] -= row.qty + def __reset_idx(self): + for idx, item in enumerate(self.get(self.raw_material_table)): + item.idx = idx + 1 + def __prepare_supplied_items(self): self.initialized_fields() self.__get_subcontract_orders() @@ -532,6 +539,7 @@ class SubcontractingController(StockController): self.get_available_materials() self.__remove_changed_rows() self.__set_supplied_items() + self.__reset_idx() def __validate_batch_no(self, row, key): if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get( diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index d02402299e3..28c33e47fb9 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -214,6 +214,7 @@ "options": "\nWork Order\nJob Card" }, { + "default": "1", "fieldname": "conversion_rate", "fieldtype": "Float", "label": "Conversion Rate", @@ -606,7 +607,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2023-04-06 12:47:58.514795", + "modified": "2023-12-26 19:34:08.159312", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", @@ -645,4 +646,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index dd102b0fae0..cd92263543b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -305,6 +305,8 @@ frappe.ui.form.on('Production Plan', { frappe.throw(__("Select the Warehouse")); } + frm.set_value("consider_minimum_order_qty", 0); + if (frm.doc.ignore_existing_ordered_qty) { frm.events.get_items_for_material_requests(frm); } else { diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 49386c4ebc4..257b60c4869 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -48,6 +48,7 @@ "material_request_planning", "include_non_stock_items", "include_subcontracted_items", + "consider_minimum_order_qty", "include_safety_stock", "ignore_existing_ordered_qty", "column_break_25", @@ -423,13 +424,19 @@ "fieldtype": "Link", "label": "Sub Assembly Warehouse", "options": "Warehouse" + }, + { + "default": "0", + "fieldname": "consider_minimum_order_qty", + "fieldtype": "Check", + "label": "Consider Minimum Order Qty" } ], "icon": "fa fa-calendar", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-11-03 14:08:11.928027", + "modified": "2023-12-26 16:31:13.740777", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index b546594a4ed..ee2e58bfb46 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1135,7 +1135,14 @@ def get_subitems( def get_material_request_items( - row, sales_order, company, ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict + doc, + row, + sales_order, + company, + ignore_existing_ordered_qty, + include_safety_stock, + warehouse, + bin_dict, ): total_qty = row["qty"] @@ -1144,8 +1151,14 @@ def get_material_request_items( required_qty = total_qty elif total_qty > bin_dict.get("projected_qty", 0): required_qty = total_qty - bin_dict.get("projected_qty", 0) - if required_qty > 0 and required_qty < row["min_order_qty"]: + + if ( + doc.get("consider_minimum_order_qty") + and required_qty > 0 + and required_qty < row["min_order_qty"] + ): required_qty = row["min_order_qty"] + item_group_defaults = get_item_group_defaults(row.item_code, company) if not row["purchase_uom"]: @@ -1483,6 +1496,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d if details.qty > 0: items = get_material_request_items( + doc, details, sales_order, company, diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index d546306f080..8c8e6efd92d 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1488,6 +1488,29 @@ class TestProductionPlan(FrappeTestCase): after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) self.assertAlmostEqual(after_qty, before_qty) + def test_min_order_qty_in_pp(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + from erpnext.stock.utils import get_or_make_bin + + fg_item = make_item(properties={"is_stock_item": 1}).name + rm_item = make_item(properties={"is_stock_item": 1, "min_order_qty": 1000}).name + + rm_warehouse = create_warehouse("RM Warehouse", company="_Test Company") + + make_bom(item=fg_item, raw_materials=[rm_item], source_warehouse="_Test Warehouse - _TC") + + pln = create_production_plan(item_code=fg_item, planned_qty=10, do_not_submit=1) + + pln.for_warehouse = rm_warehouse + mr_items = get_items_for_material_requests(pln.as_dict()) + for d in mr_items: + self.assertEqual(d.get("quantity"), 10.0) + + pln.consider_minimum_order_qty = 1 + mr_items = get_items_for_material_requests(pln.as_dict()) + for d in mr_items: + self.assertEqual(d.get("quantity"), 1000.0) + def create_production_plan(**args): """ diff --git a/erpnext/patches.txt b/erpnext/patches.txt index c755a90818d..e464552fa0c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -355,5 +355,5 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles erpnext.patches.v14_0.update_total_asset_cost_field # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger -erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index +erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.patches.v14_0.set_maintain_stock_for_bom_item diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js deleted file mode 100644 index 5514963c966..00000000000 --- a/erpnext/public/js/utils/sales_common.js +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.provide("erpnext.selling"); - -erpnext.sales_common = { - setup_selling_controller:function() { - erpnext.selling.SellingController = class SellingController extends erpnext.TransactionController { - setup() { - super.setup(); - this.toggle_enable_for_stock_uom("allow_to_edit_stock_uom_qty_for_sales"); - this.frm.email_field = "contact_email"; - } - - onload() { - super.onload(); - this.setup_queries(); - this.frm.set_query('shipping_rule', function() { - return { - filters: { - "shipping_rule_type": "Selling" - } - }; - }); - } - - setup_queries() { - var me = this; - - $.each([["customer", "customer"], - ["lead", "lead"]], - function(i, opts) { - if(me.frm.fields_dict[opts[0]]) - me.frm.set_query(opts[0], erpnext.queries[opts[1]]); - }); - - me.frm.set_query('contact_person', erpnext.queries.contact_query); - me.frm.set_query('customer_address', erpnext.queries.address_query); - me.frm.set_query('shipping_address_name', erpnext.queries.address_query); - me.frm.set_query('dispatch_address_name', erpnext.queries.dispatch_address_query); - - erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype); - - if(this.frm.fields_dict.selling_price_list) { - this.frm.set_query("selling_price_list", function() { - return { filters: { selling: 1 } }; - }); - } - - if(this.frm.fields_dict.tc_name) { - this.frm.set_query("tc_name", function() { - return { filters: { selling: 1 } }; - }); - } - - if(!this.frm.fields_dict["items"]) { - return; - } - - if(this.frm.fields_dict["items"].grid.get_field('item_code')) { - this.frm.set_query("item_code", "items", function() { - return { - query: "erpnext.controllers.queries.item_query", - filters: {'is_sales_item': 1, 'customer': me.frm.doc.customer, 'has_variants': 0} - } - }); - } - - if(this.frm.fields_dict["packed_items"] && - this.frm.fields_dict["packed_items"].grid.get_field('batch_no')) { - this.frm.set_query("batch_no", "packed_items", function(doc, cdt, cdn) { - return me.set_query_for_batch(doc, cdt, cdn) - }); - } - - if(this.frm.fields_dict["items"].grid.get_field('item_code')) { - this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) { - return me.set_query_for_item_tax_template(doc, cdt, cdn) - }); - } - - } - - refresh() { - super.refresh(); - - frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} - - this.frm.toggle_display("customer_name", - (this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer)); - - this.toggle_editable_price_list_rate(); - } - - customer() { - var me = this; - erpnext.utils.get_party_details(this.frm, null, null, function() { - me.apply_price_list(); - }); - } - - customer_address() { - erpnext.utils.get_address_display(this.frm, "customer_address"); - erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name"); - } - - shipping_address_name() { - erpnext.utils.get_address_display(this.frm, "shipping_address_name", "shipping_address"); - erpnext.utils.set_taxes_from_address(this.frm, "shipping_address_name", "customer_address", "shipping_address_name"); - } - - dispatch_address_name() { - erpnext.utils.get_address_display(this.frm, "dispatch_address_name", "dispatch_address"); - } - - sales_partner() { - this.apply_pricing_rule(); - } - - campaign() { - this.apply_pricing_rule(); - } - - selling_price_list() { - this.apply_price_list(); - this.set_dynamic_labels(); - } - - discount_percentage(doc, cdt, cdn) { - var item = frappe.get_doc(cdt, cdn); - item.discount_amount = 0.0; - this.apply_discount_on_item(doc, cdt, cdn, 'discount_percentage'); - } - - discount_amount(doc, cdt, cdn) { - - if(doc.name === cdn) { - return; - } - - var item = frappe.get_doc(cdt, cdn); - item.discount_percentage = 0.0; - this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount'); - } - - commission_rate() { - this.calculate_commission(); - } - - total_commission() { - frappe.model.round_floats_in(this.frm.doc, ["amount_eligible_for_commission", "total_commission"]); - - const { amount_eligible_for_commission } = this.frm.doc; - if(!amount_eligible_for_commission) return; - - this.frm.set_value( - "commission_rate", flt( - this.frm.doc.total_commission * 100.0 / amount_eligible_for_commission - ) - ); - } - - allocated_percentage(doc, cdt, cdn) { - var sales_person = frappe.get_doc(cdt, cdn); - if(sales_person.allocated_percentage) { - - sales_person.allocated_percentage = flt(sales_person.allocated_percentage, - precision("allocated_percentage", sales_person)); - - sales_person.allocated_amount = flt(this.frm.doc.amount_eligible_for_commission * - sales_person.allocated_percentage / 100.0, - precision("allocated_amount", sales_person)); - refresh_field(["allocated_amount"], sales_person); - - this.calculate_incentive(sales_person); - refresh_field(["allocated_percentage", "allocated_amount", "commission_rate","incentives"], sales_person.name, - sales_person.parentfield); - } - } - - sales_person(doc, cdt, cdn) { - var row = frappe.get_doc(cdt, cdn); - this.calculate_incentive(row); - refresh_field("incentives",row.name,row.parentfield); - } - - toggle_editable_price_list_rate() { - var df = frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "price_list_rate", this.frm.doc.name); - var editable_price_list_rate = cint(frappe.defaults.get_default("editable_price_list_rate")); - - if(df && editable_price_list_rate) { - const parent_field = frappe.meta.get_parentfield(this.frm.doc.doctype, this.frm.doc.doctype + " Item"); - if (!this.frm.fields_dict[parent_field]) return; - - this.frm.fields_dict[parent_field].grid.update_docfield_property( - 'price_list_rate', 'read_only', 0 - ); - } - } - - calculate_commission() { - if(!this.frm.fields_dict.commission_rate || this.frm.doc.docstatus === 1) return; - - if(this.frm.doc.commission_rate > 100) { - this.frm.set_value("commission_rate", 100); - frappe.throw(`${__(frappe.meta.get_label( - this.frm.doc.doctype, "commission_rate", this.frm.doc.name - ))} ${__("cannot be greater than 100")}`); - } - - this.frm.doc.amount_eligible_for_commission = this.frm.doc.items.reduce( - (sum, item) => item.grant_commission ? sum + item.base_net_amount : sum, 0 - ) - - this.frm.doc.total_commission = flt( - this.frm.doc.amount_eligible_for_commission * this.frm.doc.commission_rate / 100.0, - precision("total_commission") - ); - - refresh_field(["amount_eligible_for_commission", "total_commission"]); - } - - calculate_contribution() { - var me = this; - $.each(this.frm.doc.doctype.sales_team || [], function(i, sales_person) { - frappe.model.round_floats_in(sales_person); - if (!sales_person.allocated_percentage) return; - - sales_person.allocated_amount = flt( - me.frm.doc.amount_eligible_for_commission - * sales_person.allocated_percentage - / 100.0, - precision("allocated_amount", sales_person) - ); - }); - } - - calculate_incentive(row) { - if(row.allocated_amount) - { - row.incentives = flt( - row.allocated_amount * row.commission_rate / 100.0, - precision("incentives", row)); - } - } - - set_dynamic_labels() { - super.set_dynamic_labels(); - this.set_product_bundle_help(this.frm.doc); - } - - set_product_bundle_help(doc) { - if(!this.frm.fields_dict.packing_list) return; - if ((doc.packed_items || []).length) { - $(this.frm.fields_dict.packing_list.row.wrapper).toggle(true); - - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { - var help_msg = "