diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index 8961167f018..3003c68196e 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -25,7 +25,8 @@ "in_list_view": 1, "label": "Type", "options": "DocType", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "columns": 2, @@ -35,7 +36,8 @@ "in_list_view": 1, "label": "Name", "options": "reference_doctype", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "due_date", @@ -104,7 +106,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-26 17:06:55.597389", + "modified": "2022-12-12 12:31:44.919895", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", @@ -113,5 +115,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index ed46d85e3a4..e2b015bf021 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -256,7 +256,7 @@ def apply_pricing_rule(args, doc=None): for item in item_list: args_copy = copy.deepcopy(args) args_copy.update(item) - data = get_pricing_rule_for_item(args_copy, item.get("price_list_rate"), doc=doc) + data = get_pricing_rule_for_item(args_copy, doc=doc) out.append(data) if ( @@ -293,7 +293,7 @@ def update_pricing_rule_uom(pricing_rule, args): pricing_rule.uom = row.uom -def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False): +def get_pricing_rule_for_item(args, doc=None, for_validate=False): from erpnext.accounts.doctype.pricing_rule.utils import ( get_applied_pricing_rules, get_pricing_rule_items, diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 79c7c12b413..7f8cc4c63e0 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -997,7 +997,7 @@ def make_pricing_rule(**args): "apply_on": args.apply_on or "Item Code", "applicable_for": args.applicable_for, "selling": args.selling or 0, - "currency": "USD", + "currency": "INR", "apply_discount_on_rate": args.apply_discount_on_rate or 0, "buying": args.buying or 0, "min_qty": args.min_qty or 0.0, diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 35eed49da70..ab1d7385f78 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -244,6 +244,17 @@ def get_other_conditions(conditions, values, args): and ifnull(`tabPricing Rule`.valid_upto, '2500-12-31')""" values["transaction_date"] = args.get("transaction_date") + if args.get("doctype") in [ + "Quotation", + "Sales Order", + "Delivery Note", + "Sales Invoice", + "POS Invoice", + ]: + conditions += """ and ifnull(`tabPricing Rule`.selling, 0) = 1""" + else: + conditions += """ and ifnull(`tabPricing Rule`.buying, 0) = 1""" + return conditions @@ -663,7 +674,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): item_details.free_item_data.append(free_item_data_args) -def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False): +def apply_pricing_rule_for_free_items(doc, pricing_rule_args): if pricing_rule_args: items = tuple((d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 330e442a808..6c8f4bb6fe9 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -533,12 +533,13 @@ def get_accounts(root_type, companies): ], filters={"company": company, "root_type": root_type}, ): - if account.account_name not in added_accounts: + if account.account_number: + account_key = account.account_number + "-" + account.account_name + else: + account_key = account.account_name + + if account_key not in added_accounts: accounts.append(account) - if account.account_number: - account_key = account.account_number + "-" + account.account_name - else: - account_key = account.account_name added_accounts.append(account_key) return accounts diff --git a/erpnext/accounts/report/tax_detail/tax_detail.py b/erpnext/accounts/report/tax_detail/tax_detail.py index ba8d3072283..ba733c2d185 100644 --- a/erpnext/accounts/report/tax_detail/tax_detail.py +++ b/erpnext/accounts/report/tax_detail/tax_detail.py @@ -234,8 +234,11 @@ def modify_report_columns(doctype, field, column): if field in ["item_tax_rate", "base_net_amount"]: return None - if doctype == "GL Entry" and field in ["debit", "credit"]: - column.update({"label": _("Amount"), "fieldname": "amount"}) + if doctype == "GL Entry": + if field in ["debit", "credit"]: + column.update({"label": _("Amount"), "fieldname": "amount"}) + elif field == "voucher_type": + column.update({"fieldtype": "Data", "options": ""}) if field == "taxes_and_charges": column.update({"label": _("Taxes and Charges Template")}) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 7e542197407..7791b117444 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -136,6 +136,10 @@ frappe.ui.form.on('Asset', { }, __("Manage")); } + if (frm.doc.depr_entry_posting_status === "Failed") { + frm.trigger("set_depr_posting_failure_alert"); + } + frm.trigger("setup_chart"); } @@ -146,6 +150,19 @@ frappe.ui.form.on('Asset', { } }, + set_depr_posting_failure_alert: function (frm) { + const alert = ` +
+
+ + Failed to post depreciation entries + +
+
`; + + frm.dashboard.set_headline_alert(alert); + }, + toggle_reference_doc: function(frm) { if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) { frm.set_df_property('purchase_invoice', 'read_only', 1); diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index f0505ff9835..8b6af47b049 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -70,6 +70,7 @@ "column_break_51", "purchase_receipt_amount", "default_finance_book", + "depr_entry_posting_status", "amended_from" ], "fields": [ @@ -488,6 +489,16 @@ "fieldtype": "Int", "label": "Asset Quantity", "read_only_depends_on": "eval:!doc.is_existing_asset" + }, + { + "fieldname": "depr_entry_posting_status", + "fieldtype": "Select", + "hidden": 1, + "label": "Depreciation Entry Posting Status", + "no_copy": 1, + "options": "\nSuccessful\nFailed", + "print_hide": 1, + "read_only": 1 } ], "idx": 72, @@ -510,7 +521,7 @@ "link_fieldname": "asset" } ], - "modified": "2022-07-20 10:15:12.887372", + "modified": "2022-12-05 16:21:30.024060", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 97941706aa8..c6daccc9728 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -5,6 +5,8 @@ import frappe from frappe import _ from frappe.utils import add_months, cint, flt, getdate, nowdate, today +from frappe.utils.data import get_link_to_form +from frappe.utils.user import get_users_with_role from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_checks_for_pl_and_bs_accounts, @@ -12,7 +14,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry -def post_depreciation_entries(date=None, commit=True): +def post_depreciation_entries(date=None): # Return if automatic booking of asset depreciation is disabled if not cint( frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically") @@ -21,10 +23,22 @@ def post_depreciation_entries(date=None, commit=True): if not date: date = today() - for asset in get_depreciable_assets(date): - make_depreciation_entry(asset, date) - if commit: + + failed_asset_names = [] + + for asset_name in get_depreciable_assets(date): + try: + make_depreciation_entry(asset_name, date) frappe.db.commit() + except Exception as e: + frappe.db.rollback() + failed_asset_names.append(asset_name) + + if failed_asset_names: + set_depr_entry_posting_status_for_failed_assets(failed_asset_names) + notify_depr_entry_posting_error(failed_asset_names) + + frappe.db.commit() def get_depreciable_assets(date): @@ -123,6 +137,8 @@ def make_depreciation_entry(asset_name, date=None): finance_books.value_after_depreciation -= d.depreciation_amount finance_books.db_update() + frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful") + asset.set_status() return asset @@ -186,6 +202,42 @@ def get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation return credit_account, debit_account +def set_depr_entry_posting_status_for_failed_assets(failed_asset_names): + for asset_name in failed_asset_names: + frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed") + + +def notify_depr_entry_posting_error(failed_asset_names): + recipients = get_users_with_role("Accounts Manager") + + if not recipients: + recipients = get_users_with_role("System Manager") + + subject = _("Error while posting depreciation entries") + + asset_links = get_comma_separated_asset_links(failed_asset_names) + + message = ( + _("Hi,") + + "
" + + _("The following assets have failed to post depreciation entries: {0}").format(asset_links) + + "." + ) + + frappe.sendmail(recipients=recipients, subject=subject, message=message) + + +def get_comma_separated_asset_links(asset_names): + asset_links = [] + + for asset_name in asset_names: + asset_links.append(get_link_to_form("Asset", asset_name)) + + asset_links = ", ".join(asset_links) + + return asset_links + + @frappe.whitelist() def scrap_asset(asset_name): asset = frappe.get_doc("Asset", asset_name) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index baed310adb9..5a31ca0e2d7 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1478,6 +1478,7 @@ def create_asset(**args): "asset_owner": args.asset_owner or "Company", "is_existing_asset": args.is_existing_asset or 1, "asset_quantity": args.get("asset_quantity") or 1, + "depr_entry_posting_status": args.depr_entry_posting_status or "", } ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 5a051e3bafc..334a2d806d6 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -197,7 +197,7 @@ class AccountsController(TransactionBase): validate_einvoice_fields(self) - if self.doctype != "Material Request": + if self.doctype != "Material Request" and not self.ignore_pricing_rule: apply_pricing_rule_on_transaction(self) def before_cancel(self): diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 6e7d2b33c28..bf077282bf8 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -349,7 +349,7 @@ class StatusUpdater(Document): def warn_about_bypassing_with_role(self, item, qty_or_amount, role): action = _("Over Receipt/Delivery") if qty_or_amount == "qty" else _("Overbilling") - msg = _("{} of {} {} ignored for item {} because you have {} role.").format( + msg = _("{0} of {1} {2} ignored for item {3} because you have {4} role.").format( action, _(item["target_ref_field"].title()), frappe.bold(item["reduce_by"]), diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py index 1e1b4356008..cdf1541f888 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.query_builder.functions import Floor, Sum +from frappe.query_builder.functions import Sum from pypika.terms import ExistsCriterion @@ -58,9 +58,9 @@ def get_bom_stock(filters): bom_item.description, bom_item.stock_qty, bom_item.stock_uom, - bom_item.stock_qty * qty_to_produce / bom.quantity, - Sum(bin.actual_qty).as_("actual_qty"), - Sum(Floor(bin.actual_qty / (bom_item.stock_qty * qty_to_produce / bom.quantity))), + (bom_item.stock_qty / bom.quantity) * qty_to_produce, + Sum(bin.actual_qty), + Sum(bin.actual_qty) / (bom_item.stock_qty / bom.quantity), ) .where((bom_item.parent == filters.get("bom")) & (bom_item.parenttype == "BOM")) .groupby(bom_item.item_code) diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py index 16c25ce7e6d..109d9ab656b 100644 --- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py @@ -49,7 +49,7 @@ class ProductionPlanReport(object): parent.bom_no, parent.fg_warehouse.as_("warehouse"), ) - .where(parent.status.notin(["Completed", "Stopped"])) + .where(parent.status.notin(["Completed", "Stopped", "Closed"])) ) if order_by == "Planned Start Date": @@ -79,10 +79,11 @@ class ProductionPlanReport(object): query = query.where(child.parent.isin(self.filters.docnames)) if doctype == "Sales Order": - query = query.select( - child.delivery_date, - parent.base_grand_total, - ).where((child.stock_qty > child.produced_qty) & (parent.per_delivered < 100.0)) + query = query.select(child.delivery_date, parent.base_grand_total,).where( + (child.stock_qty > child.produced_qty) + & (parent.per_delivered < 100.0) + & (parent.status.notin(["Completed", "Closed"])) + ) if order_by == "Delivery Date": query = query.orderby(child.delivery_date, order=Order.asc) @@ -91,7 +92,9 @@ class ProductionPlanReport(object): elif doctype == "Material Request": query = query.select(child.schedule_date,).where( - (parent.per_ordered < 100) & (parent.material_request_type == "Manufacture") + (parent.per_ordered < 100) + & (parent.material_request_type == "Manufacture") + & (parent.status != "Stopped") ) if order_by == "Required Date": diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 74810005ed9..1f8a5e39f25 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -58,7 +58,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if ( in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) - && this.frm.doc.s_pos + && this.frm.doc.is_pos && this.frm.doc.is_return ) { this.set_total_amount_to_default_mop(); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 46ac80895cf..58d8de24993 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1130,10 +1130,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe qty(doc, cdt, cdn) { let item = frappe.get_doc(cdt, cdn); - item.pricing_rules = '' - this.conversion_factor(doc, cdt, cdn, true); - this.calculate_stock_uom_rate(doc, cdt, cdn); - this.apply_pricing_rule(item, true); + // item.pricing_rules = '' + frappe.run_serially([ + () => this.remove_pricing_rule(item), + () => this.conversion_factor(doc, cdt, cdn, true), + () => this.calculate_stock_uom_rate(doc, cdt, cdn), + () => this.apply_pricing_rule(item, true) + ]); } calculate_stock_uom_rate(doc, cdt, cdn) { @@ -1357,16 +1360,21 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe var item_list = []; $.each(this.frm.doc["items"] || [], function(i, d) { - if (d.item_code && !d.is_free_item) { - item_list.push({ - "doctype": d.doctype, - "name": d.name, - "item_code": d.item_code, - "pricing_rules": d.pricing_rules, - "parenttype": d.parenttype, - "parent": d.parent, - "price_list_rate": d.price_list_rate - }) + if (d.item_code) { + if (d.is_free_item) { + // Simply remove free items + me.frm.get_field("items").grid.grid_rows[i].remove(); + } else { + item_list.push({ + "doctype": d.doctype, + "name": d.name, + "item_code": d.item_code, + "pricing_rules": d.pricing_rules, + "parenttype": d.parenttype, + "parent": d.parent, + "price_list_rate": d.price_list_rate + }) + } } }); return this.frm.call({ diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index d0eb3774e26..60c33567bef 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -6,7 +6,7 @@ import json import frappe import frappe.defaults -from frappe import _, msgprint +from frappe import _, msgprint, qb from frappe.contacts.address_and_contact import ( delete_contact_and_address, load_address_and_contact, @@ -732,12 +732,15 @@ def make_address(args, is_primary_address=1): @frappe.validate_and_sanitize_search_inputs def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, filters): customer = filters.get("customer") - return frappe.db.sql( - """ - select `tabContact`.name from `tabContact`, `tabDynamic Link` - where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s - and `tabDynamic Link`.link_doctype = 'Customer' - and `tabContact`.name like %(txt)s - """, - {"customer": customer, "txt": "%%%s%%" % txt}, + + con = qb.DocType("Contact") + dlink = qb.DocType("Dynamic Link") + + return ( + qb.from_(con) + .join(dlink) + .on(con.name == dlink.parent) + .select(con.name, con.full_name, con.email_id) + .where((dlink.link_name == customer) & (con.name.like(f"%{txt}%"))) + .run() ) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 6f0b381fc16..b151dd5e79c 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -30,6 +30,24 @@ class TestQuotation(FrappeTestCase): self.assertTrue(sales_order.get("payment_schedule")) + def test_maintain_rate_in_sales_cycle_is_enforced(self): + from erpnext.selling.doctype.quotation.quotation import make_sales_order + + maintain_rate = frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate") + frappe.db.set_single_value("Selling Settings", "maintain_same_sales_rate", 1) + + quotation = frappe.copy_doc(test_records[0]) + quotation.transaction_date = nowdate() + quotation.valid_till = add_months(quotation.transaction_date, 1) + quotation.insert() + quotation.submit() + + sales_order = make_sales_order(quotation.name) + sales_order.items[0].rate = 1 + self.assertRaises(frappe.ValidationError, sales_order.save) + + frappe.db.set_single_value("Selling Settings", "maintain_same_sales_rate", maintain_rate) + def test_make_sales_order_with_different_currency(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 78e2370878f..0013c95032f 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -194,7 +194,7 @@ class SalesOrder(SellingController): ) if cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate")): - self.validate_rate_with_reference_doc([["Quotation", "prev_docname", "quotation_item"]]) + self.validate_rate_with_reference_doc([["Quotation", "prevdoc_docname", "quotation_item"]]) def update_enquiry_status(self, prevdoc, flag): enq = frappe.db.sql( diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index ace2e29c2b4..5c4b57813d3 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -12,7 +12,10 @@ def get_data(): "Auto Repeat": "reference_document", "Maintenance Visit": "prevdoc_docname", }, - "internal_links": {"Quotation": ["items", "prevdoc_docname"]}, + "internal_links": { + "Quotation": ["items", "prevdoc_docname"], + "Material Request": ["items", "material_request"], + }, "transactions": [ { "label": _("Fulfillment"), diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 411176b70af..95bbf84616b 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -152,7 +152,7 @@ def get_parent_item_groups(item_group_name, from_item=False): if from_item and frappe.request.environ.get("HTTP_REFERER"): # base page after 'Home' will vary on Item page - last_page = frappe.request.environ["HTTP_REFERER"].split("/")[-1] + last_page = frappe.request.environ["HTTP_REFERER"].split("/")[-1].split("?")[0] if last_page and last_page in ("shop-by-category", "all-products"): base_nav_page_title = " ".join(last_page.split("-")).title() base_nav_page = {"name": _(base_nav_page_title), "route": "/" + last_page} diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 108611c09bf..31dccf6944d 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -113,7 +113,7 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru if args.get(key) is None: args[key] = value - data = get_pricing_rule_for_item(args, out.price_list_rate, doc, for_validate=for_validate) + data = get_pricing_rule_for_item(args, doc=doc, for_validate=for_validate) out.update(data) @@ -1305,7 +1305,7 @@ def apply_price_list_on_item(args): item_doc = frappe.db.get_value("Item", args.item_code, ["name", "variant_of"], as_dict=1) item_details = get_price_list_rate(args, item_doc) - item_details.update(get_pricing_rule_for_item(args, item_details.price_list_rate)) + item_details.update(get_pricing_rule_for_item(args)) return item_details diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py index 81700099fa4..d364b577a26 100644 --- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py +++ b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py @@ -45,7 +45,7 @@ def get_warehouses(report_filters: StockBalanceFilter): return frappe.get_all( "Warehouse", fields=["name", "parent_warehouse", "is_group"], - filters={"company": report_filters.company, "disabled": 0}, + filters={"company": report_filters.company}, order_by="lft", ) @@ -55,9 +55,10 @@ def get_data(filters: StockBalanceFilter): warehouses = get_warehouses(filters) for warehouse in warehouses: - warehouse["stock_balance"] = warehouse_balance.get(warehouse.name, 0) + warehouse.stock_balance = warehouse_balance.get(warehouse.name, 0) or 0.0 update_indent(warehouses) + set_balance_in_parent(warehouses) return warehouses @@ -69,13 +70,26 @@ def update_indent(warehouses): warehouse.indent = indent for child in warehouses: if child.parent_warehouse == warehouse.name: - warehouse.stock_balance += child.stock_balance add_indent(child, indent + 1) if warehouse.is_group: add_indent(warehouse, warehouse.indent or 0) +def set_balance_in_parent(warehouses): + # sort warehouses by indent in descending order + warehouses = sorted(warehouses, key=lambda x: x.get("indent", 0), reverse=1) + + for warehouse in warehouses: + + def update_balance(warehouse, balance): + for parent in warehouses: + if warehouse.parent_warehouse == parent.name: + parent.stock_balance += balance + + update_balance(warehouse, warehouse.stock_balance) + + def get_columns(): return [ { diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index ec9c50247fa..a401983d2ad 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -9916,3 +9916,4 @@ Cost and Freight,Kosten und Fracht, Delivered at Place,Geliefert benannter Ort, Delivered at Place Unloaded,Geliefert benannter Ort entladen, Delivered Duty Paid,Geliefert verzollt, +{0} of {1} {2} ignored for item {3} because you have {4} role,"{0} von Artikel {3} mit {1} {2} wurde ignoriert, weil Sie die Rolle {4} haben." diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 04ee0b3b1eb..afe9654e8ea 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -110,6 +110,7 @@ def get_price(item_code, price_list, customer_group, company, qty=1): "conversion_rate": 1, "for_shopping_cart": True, "currency": frappe.db.get_value("Price List", price_list, "currency"), + "doctype": "Quotation", } )