diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 19c6c8f3472..bff85872781 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -622,6 +622,7 @@ "read_only": 1 }, { + "depends_on": "packed_items", "fieldname": "packing_list", "fieldtype": "Section Break", "label": "Packing List", @@ -629,6 +630,7 @@ "print_hide": 1 }, { + "depends_on": "packed_items", "fieldname": "packed_items", "fieldtype": "Table", "label": "Packed Items", @@ -1564,7 +1566,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-08-24 18:19:20.728433", + "modified": "2021-08-27 20:12:57.306772", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", 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 a7618e2c747..d39a9fc058d 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -8,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "item_code", + "product_bundle", "col_break1", "item_name", "description_section", @@ -857,12 +858,19 @@ "fieldtype": "Link", "label": "Discount Account", "options": "Account" + }, + { + "fieldname": "product_bundle", + "fieldtype": "Link", + "label": "Product Bundle", + "options": "Product Bundle", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-08-12 20:14:48.506639", + "modified": "2021-09-01 16:04:03.538643", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 2dd3d690e93..2cb9acfa2a9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -578,6 +578,9 @@ frappe.ui.form.on('Sales Invoice', { frm.add_fetch('payment_term', 'invoice_portion', 'invoice_portion'); frm.add_fetch('payment_term', 'description', 'description'); + frm.set_df_property('packed_items', 'cannot_add_rows', true); + frm.set_df_property('packed_items', 'cannot_delete_rows', true); + frm.set_query("account_for_change_amount", function() { return { filters: { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index d8aa32e224b..b5620ae6a96 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -247,7 +247,7 @@ "depends_on": "customer", "fetch_from": "customer.customer_name", "fieldname": "customer_name", - "fieldtype": "Small Text", + "fieldtype": "Data", "hide_days": 1, "hide_seconds": 1, "in_global_search": 1, @@ -695,7 +695,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Scan Barcode", - "length": 1, "options": "Barcode" }, { @@ -727,6 +726,7 @@ "read_only": 1 }, { + "depends_on": "packed_items", "fieldname": "packing_list", "fieldtype": "Section Break", "hide_days": 1, @@ -736,6 +736,7 @@ "print_hide": 1 }, { + "depends_on": "packed_items", "fieldname": "packed_items", "fieldtype": "Table", "hide_days": 1, @@ -1060,7 +1061,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Apply Additional Discount On", - "length": 15, "options": "\nGrand Total\nNet Total", "print_hide": 1 }, @@ -1147,7 +1147,7 @@ { "description": "In Words will be visible once you save the Sales Invoice.", "fieldname": "base_in_words", - "fieldtype": "Small Text", + "fieldtype": "Data", "hide_days": 1, "hide_seconds": 1, "label": "In Words (Company Currency)", @@ -1207,7 +1207,7 @@ }, { "fieldname": "in_words", - "fieldtype": "Small Text", + "fieldtype": "Data", "hide_days": 1, "hide_seconds": 1, "label": "In Words", @@ -1560,7 +1560,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Print Language", - "length": 6, "print_hide": 1, "read_only": 1 }, @@ -1648,7 +1647,6 @@ "hide_seconds": 1, "in_standard_filter": 1, "label": "Status", - "length": 30, "no_copy": 1, "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer", "print_hide": 1, @@ -1708,7 +1706,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Is Opening Entry", - "length": 4, "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", @@ -1720,7 +1717,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "C-Form Applicable", - "length": 4, "no_copy": 1, "options": "No\nYes", "print_hide": 1 @@ -2021,7 +2017,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-08-25 14:46:05.279588", + "modified": "2021-08-27 20:13:40.456462", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index ba17a94e8d3..856b97d1645 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -36,5 +36,20 @@ frappe.query_reports["Gross Profit"] = { "options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject", "default": "Invoice" }, - ] + ], + "tree": true, + "name_field": "parent", + "parent_field": "parent_invoice", + "initial_depth": 3, + "formatter": function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (data && data.indent == 0.0) { + value = $(`${value}`); + var $value = $(value).css("font-weight", "bold"); + value = $value.wrap("

").parent().html(); + } + + return value; + }, } diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index c949d9b74e5..8ea5e07aef8 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -41,6 +41,34 @@ def execute(filters=None): columns = get_columns(group_wise_columns, filters) + if filters.group_by == 'Invoice': + get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data) + + else: + get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data) + + return columns, data + +def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data): + column_names = get_column_names() + + # to display item as Item Code: Item Name + columns[0] = 'Sales Invoice:Link/Item:300' + # removing Item Code and Item Name columns + del columns[4:6] + + for src in gross_profit_data.si_list: + row = frappe._dict() + row.indent = src.indent + row.parent_invoice = src.parent_invoice + row.currency = filters.currency + + for col in group_wise_columns.get(scrub(filters.group_by)): + row[column_names[col]] = src.get(col) + + data.append(row) + +def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data): for idx, src in enumerate(gross_profit_data.grouped_data): row = [] for col in group_wise_columns.get(scrub(filters.group_by)): @@ -51,8 +79,6 @@ def execute(filters=None): row[0] = frappe.bold("Total") data.append(row) - return columns, data - def get_columns(group_wise_columns, filters): columns = [] column_map = frappe._dict({ @@ -93,12 +119,38 @@ def get_columns(group_wise_columns, filters): return columns +def get_column_names(): + return frappe._dict({ + 'parent': 'sales_invoice', + 'customer': 'customer', + 'customer_group': 'customer_group', + 'posting_date': 'posting_date', + 'item_code': 'item_code', + 'item_name': 'item_name', + 'item_group': 'item_group', + 'brand': 'brand', + 'description': 'description', + 'warehouse': 'warehouse', + 'qty': 'qty', + 'base_rate': 'avg._selling_rate', + 'buying_rate': 'valuation_rate', + 'base_amount': 'selling_amount', + 'buying_amount': 'buying_amount', + 'gross_profit': 'gross_profit', + 'gross_profit_percent': 'gross_profit_%', + 'project': 'project' + }) + class GrossProfitGenerator(object): def __init__(self, filters=None): self.data = [] self.average_buying_rate = {} self.filters = frappe._dict(filters) self.load_invoice_items() + + if filters.group_by == 'Invoice': + self.group_items_by_invoice() + self.load_stock_ledger_entries() self.load_product_bundle() self.load_non_stock_items() @@ -112,7 +164,12 @@ class GrossProfitGenerator(object): self.currency_precision = cint(frappe.db.get_default("currency_precision")) or 3 self.float_precision = cint(frappe.db.get_default("float_precision")) or 2 - for row in self.si_list: + grouped_by_invoice = True if self.filters.get("group_by") == "Invoice" else False + + if grouped_by_invoice: + buying_amount = 0 + + for row in reversed(self.si_list): if self.skip_row(row, self.product_bundles): continue @@ -134,12 +191,20 @@ class GrossProfitGenerator(object): row.buying_amount = flt(self.get_buying_amount(row, row.item_code), self.currency_precision) + if grouped_by_invoice: + if row.indent == 1.0: + buying_amount += row.buying_amount + elif row.indent == 0.0: + row.buying_amount = buying_amount + buying_amount = 0 + # get buying rate - if row.qty: - row.buying_rate = flt(row.buying_amount / row.qty, self.float_precision) - row.base_rate = flt(row.base_amount / row.qty, self.float_precision) + if flt(row.qty): + row.buying_rate = flt(row.buying_amount / flt(row.qty), self.float_precision) + row.base_rate = flt(row.base_amount / flt(row.qty), self.float_precision) else: - row.buying_rate, row.base_rate = 0.0, 0.0 + if self.is_not_invoice_row(row): + row.buying_rate, row.base_rate = 0.0, 0.0 # calculate gross profit row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision) @@ -171,7 +236,7 @@ class GrossProfitGenerator(object): if i==0: new_row = row else: - new_row.qty += row.qty + new_row.qty += flt(row.qty) new_row.buying_amount += flt(row.buying_amount, self.currency_precision) new_row.base_amount += flt(row.base_amount, self.currency_precision) new_row = self.set_average_rate(new_row) @@ -183,16 +248,19 @@ class GrossProfitGenerator(object): and row.item_code in self.returned_invoices[row.parent]: returned_item_rows = self.returned_invoices[row.parent][row.item_code] for returned_item_row in returned_item_rows: - row.qty += returned_item_row.qty + row.qty += flt(returned_item_row.qty) row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) - row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision) - if row.qty or row.base_amount: + row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision) + if (flt(row.qty) or row.base_amount) and self.is_not_invoice_row(row): row = self.set_average_rate(row) self.grouped_data.append(row) self.add_to_totals(row) self.set_average_gross_profit(self.totals) self.grouped_data.append(self.totals) + def is_not_invoice_row(self, row): + return (self.filters.get("group_by") == "Invoice" and row.indent != 0.0) or self.filters.get("group_by") != "Invoice" + def set_average_rate(self, new_row): self.set_average_gross_profit(new_row) new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0 @@ -354,6 +422,109 @@ class GrossProfitGenerator(object): .format(conditions=conditions, sales_person_cols=sales_person_cols, sales_team_table=sales_team_table, match_cond = get_match_cond('Sales Invoice')), self.filters, as_dict=1) + def group_items_by_invoice(self): + """ + Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children. + """ + + parents = [] + + for row in self.si_list: + if row.parent not in parents: + parents.append(row.parent) + + parents_index = 0 + for index, row in enumerate(self.si_list): + if parents_index < len(parents) and row.parent == parents[parents_index]: + invoice = self.get_invoice_row(row) + self.si_list.insert(index, invoice) + parents_index += 1 + + else: + # skipping the bundle items rows + if not row.indent: + row.indent = 1.0 + row.parent_invoice = row.parent + row.parent = row.item_code + + if frappe.db.exists('Product Bundle', row.item_code): + self.add_bundle_items(row, index) + + def get_invoice_row(self, row): + return frappe._dict({ + 'parent_invoice': "", + 'indent': 0.0, + 'parent': row.parent, + 'posting_date': row.posting_date, + 'posting_time': row.posting_time, + 'project': row.project, + 'update_stock': row.update_stock, + 'customer': row.customer, + 'customer_group': row.customer_group, + 'item_code': None, + 'item_name': None, + 'description': None, + 'warehouse': None, + 'item_group': None, + 'brand': None, + 'dn_detail': None, + 'delivery_note': None, + 'qty': None, + 'item_row': None, + 'is_return': row.is_return, + 'cost_center': row.cost_center, + 'base_net_amount': frappe.db.get_value('Sales Invoice', row.parent, 'base_net_total') + }) + + def add_bundle_items(self, product_bundle, index): + bundle_items = self.get_bundle_items(product_bundle) + + for i, item in enumerate(bundle_items): + bundle_item = self.get_bundle_item_row(product_bundle, item) + self.si_list.insert((index+i+1), bundle_item) + + def get_bundle_items(self, product_bundle): + return frappe.get_all( + 'Product Bundle Item', + filters = { + 'parent': product_bundle.item_code + }, + fields = ['item_code', 'qty'] + ) + + def get_bundle_item_row(self, product_bundle, item): + item_name, description, item_group, brand = self.get_bundle_item_details(item.item_code) + + return frappe._dict({ + 'parent_invoice': product_bundle.item_code, + 'indent': product_bundle.indent + 1, + 'parent': item.item_code, + 'posting_date': product_bundle.posting_date, + 'posting_time': product_bundle.posting_time, + 'project': product_bundle.project, + 'customer': product_bundle.customer, + 'customer_group': product_bundle.customer_group, + 'item_code': item.item_code, + 'item_name': item_name, + 'description': description, + 'warehouse': product_bundle.warehouse, + 'item_group': item_group, + 'brand': brand, + 'dn_detail': product_bundle.dn_detail, + 'delivery_note': product_bundle.delivery_note, + 'qty': (flt(product_bundle.qty) * flt(item.qty)), + 'item_row': None, + 'is_return': product_bundle.is_return, + 'cost_center': product_bundle.cost_center + }) + + def get_bundle_item_details(self, item_code): + return frappe.db.get_value( + 'Item', + item_code, + ['item_name', 'description', 'item_group', 'brand'] + ) + def load_stock_ledger_entries(self): res = frappe.db.sql("""select item_code, voucher_type, voucher_no, voucher_detail_no, stock_value, warehouse, actual_qty as qty diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a55a0b7f9fc..ef54538fcd4 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -144,9 +144,7 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-user" }, { "allow_on_submit": 1, @@ -156,9 +154,7 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "naming_series", @@ -170,9 +166,7 @@ "options": "PUR-ORD-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1, - "show_days": 1, - "show_seconds": 1 + "set_only_once": 1 }, { "bold": 1, @@ -186,18 +180,14 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", - "label": "Get Items from Open Material Requests", - "show_days": 1, - "show_seconds": 1 + "label": "Get Items from Open Material Requests" }, { "bold": 1, @@ -206,9 +196,7 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "company", @@ -220,17 +208,13 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -242,35 +226,27 @@ "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "allow_on_submit": 1, "fieldname": "schedule_date", "fieldtype": "Date", - "label": "Required By", - "show_days": 1, - "show_seconds": 1 + "label": "Required By" }, { "allow_on_submit": 1, "depends_on": "eval:doc.docstatus===1", "fieldname": "order_confirmation_no", "fieldtype": "Data", - "label": "Order Confirmation No", - "show_days": 1, - "show_seconds": 1 + "label": "Order Confirmation No" }, { "allow_on_submit": 1, "depends_on": "eval:doc.order_confirmation_no", "fieldname": "order_confirmation_date", "fieldtype": "Date", - "label": "Order Confirmation Date", - "show_days": 1, - "show_seconds": 1 + "label": "Order Confirmation Date" }, { "fieldname": "amended_from", @@ -282,25 +258,19 @@ "oldfieldtype": "Data", "options": "Purchase Order", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "drop_ship", "fieldtype": "Section Break", - "label": "Drop Ship", - "show_days": 1, - "show_seconds": 1 + "label": "Drop Ship" }, { "fieldname": "customer", "fieldtype": "Link", "label": "Customer", "options": "Customer", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "bold": 1, @@ -308,41 +278,31 @@ "fieldtype": "Data", "label": "Customer Name", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_19", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "customer_contact_person", "fieldtype": "Link", "label": "Customer Contact", - "options": "Contact", - "show_days": 1, - "show_seconds": 1 + "options": "Contact" }, { "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Contact", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Mobile No", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "customer_contact_email", @@ -350,35 +310,27 @@ "hidden": 1, "label": "Customer Contact Email", "options": "Email", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact", - "show_days": 1, - "show_seconds": 1 + "label": "Address and Contact" }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Supplier Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Supplier Contact", "options": "Contact", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "address_display", @@ -405,42 +357,32 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Company Shipping Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address Details", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-tag" }, { "fieldname": "currency", @@ -450,9 +392,7 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "conversion_rate", @@ -462,24 +402,18 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "cb_price_list", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "price_list_currency", @@ -487,18 +421,14 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -507,9 +437,7 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "sec_warehouse", @@ -522,15 +450,11 @@ "fieldtype": "Link", "label": "Set Target Warehouse", "options": "Warehouse", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "No", @@ -539,35 +463,27 @@ "in_standard_filter": 1, "label": "Supply Raw Materials", "options": "No\nYes", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "fieldname": "items_section", "fieldtype": "Section Break", "hide_border": 1, "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-shopping-cart" }, { "fieldname": "scan_barcode", "fieldtype": "Data", "label": "Scan Barcode", - "options": "Barcode", - "show_days": 1, - "show_seconds": 1 + "options": "Barcode" }, { "allow_bulk_edit": 1, @@ -577,34 +493,26 @@ "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "collapsible": 1, "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Pricing Rules", - "show_days": 1, - "show_seconds": 1 + "label": "Pricing Rules" }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Purchase Order Pricing Rule", "options": "Pricing Rule Detail", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_material_details", "fieldtype": "Section Break", - "label": "Raw Materials Supplied", - "show_days": 1, - "show_seconds": 1 + "label": "Raw Materials Supplied" }, { "fieldname": "supplied_items", @@ -615,23 +523,17 @@ "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "sb_last_purchase", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total", @@ -639,9 +541,7 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_net_total", @@ -652,24 +552,18 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "net_total", @@ -679,26 +573,20 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "taxes_and_charges", @@ -707,24 +595,18 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "section_break_52", @@ -737,17 +619,13 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges", - "show_days": 1, - "show_seconds": 1 + "options": "Purchase Taxes and Charges" }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup", - "show_days": 1, - "show_seconds": 1 + "label": "Tax Breakup" }, { "fieldname": "other_charges_calculation", @@ -756,18 +634,14 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "label": "Taxes and Charges", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "depends_on": "base_taxes_and_charges_added", @@ -778,9 +652,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "base_taxes_and_charges_deducted", @@ -791,9 +663,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "base_total_taxes_and_charges", @@ -805,15 +675,11 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_39", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "taxes_and_charges_added", @@ -824,9 +690,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "taxes_and_charges_deducted", @@ -837,9 +701,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "total_taxes_and_charges", @@ -848,18 +710,14 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "collapsible_depends_on": "apply_discount_on", "fieldname": "discount_section", "fieldtype": "Section Break", - "label": "Additional Discount", - "show_days": 1, - "show_seconds": 1 + "label": "Additional Discount" }, { "default": "Grand Total", @@ -867,9 +725,7 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_discount_amount", @@ -877,32 +733,24 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "totals_section", @@ -918,9 +766,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -930,9 +776,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "description": "In Words will be visible once you save the Purchase Order.", @@ -943,9 +787,7 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_rounded_total", @@ -955,16 +797,12 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break4", "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Column Break" }, { "fieldname": "grand_total", @@ -974,9 +812,7 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -986,26 +822,20 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total", - "show_days": 1, - "show_seconds": 1 + "label": "Disable Rounded Total" }, { "fieldname": "in_words", @@ -1015,9 +845,7 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "advance_paid", @@ -1026,25 +854,19 @@ "no_copy": 1, "options": "party_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms", - "show_days": 1, - "show_seconds": 1 + "label": "Payment Terms" }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template", - "show_days": 1, - "show_seconds": 1 + "options": "Payment Terms Template" }, { "fieldname": "payment_schedule", @@ -1052,9 +874,7 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1063,9 +883,7 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-legal" }, { "fieldname": "tc_name", @@ -1074,27 +892,21 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Text Editor" }, { "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information", - "oldfieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Section Break" }, { "default": "Draft", @@ -1109,9 +921,7 @@ "print_hide": 1, "read_only": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "ref_sq", @@ -1122,9 +932,7 @@ "oldfieldtype": "Data", "options": "Supplier Quotation", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "party_account_currency", @@ -1134,24 +942,18 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "inter_company_order_reference", "fieldtype": "Link", "label": "Inter Company Order Reference", "options": "Sales Order", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval:!doc.__islocal", @@ -1161,9 +963,7 @@ "label": "% Received", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -1173,9 +973,7 @@ "label": "% Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, @@ -1185,8 +983,6 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1197,9 +993,7 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1211,15 +1005,11 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1, - "show_days": 1, - "show_seconds": 1 + "report_hide": 1 }, { "fieldname": "column_break_86", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "allow_on_submit": 1, @@ -1227,25 +1017,19 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section", - "show_days": 1, - "show_seconds": 1 + "label": "Subscription Section" }, { "allow_on_submit": 1, @@ -1253,9 +1037,7 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1263,15 +1045,11 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "auto_repeat", @@ -1280,35 +1058,27 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference", - "show_days": 1, - "show_seconds": 1 + "label": "Update Auto Repeat Reference" }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", - "options": "Tax Category", - "show_days": 1, - "show_seconds": 1 + "options": "Tax Category" }, { "depends_on": "supplied_items", "fieldname": "set_reserve_warehouse", "fieldtype": "Link", "label": "Set Reserve Warehouse", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "collapsible": 1, @@ -1318,9 +1088,7 @@ }, { "fieldname": "column_break_75", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "billing_address", @@ -1361,25 +1129,21 @@ "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", - "label": "Apply Tax Withholding Amount", - "show_days": 1, - "show_seconds": 1 + "label": "Apply Tax Withholding Amount" }, { "depends_on": "eval: doc.apply_tds", "fieldname": "tax_withholding_category", "fieldtype": "Link", "label": "Tax Withholding Category", - "options": "Tax Withholding Category", - "show_days": 1, - "show_seconds": 1 + "options": "Tax Withholding Category" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2021-08-30 20:03:14.008804", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", 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 132dd1769c5..87cd57517e2 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -10,6 +10,7 @@ "item_code", "supplier_part_no", "item_name", + "product_bundle", "column_break_4", "schedule_date", "expected_delivery_date", @@ -488,7 +489,6 @@ "no_copy": 1, "options": "Sales Order", "print_hide": 1, - "read_only": 1, "search_index": 1 }, { @@ -830,13 +830,20 @@ "label": "Production Plan Sub Assembly Item", "no_copy": 1, "read_only": 1 + }, + { + "fieldname": "product_bundle", + "fieldtype": "Link", + "label": "Product Bundle", + "options": "Product Bundle", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-28 19:22:22.715365", + "modified": "2021-08-30 20:06:26.712097", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index e82ae8f3726..7610a24648c 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -174,6 +174,7 @@ class Lead(SellingController): "salutation": self.salutation, "gender": self.gender, "designation": self.designation, + "company_name": self.company_name, }) if self.email_id: diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 81162a4c9a8..b0e41e00905 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -15,6 +15,8 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { } else { organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method); } + + frappe.breadcrumbs.add('HR'); organizational_chart.show(); }); }); diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js index 5258f3aff9e..3ef9762a839 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -135,15 +135,15 @@ frappe.ui.form.on("Salary Slip", { change_form_labels: function(frm, company_currency) { frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction", - "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"], + "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "gross_base_year_to_date"], company_currency); - frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date"], + frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date", "gross_year_to_date"], frm.doc.currency); // toggle fields frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction", - "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"], + "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "base_gross_year_to_date"], frm.doc.currency != company_currency); }, diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 42a0f290cb4..19744037a54 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -56,6 +56,8 @@ "totals", "gross_pay", "base_gross_pay", + "gross_year_to_date", + "base_gross_year_to_date", "column_break_25", "total_deduction", "base_total_deduction", @@ -625,13 +627,27 @@ "label": "Leave Details", "options": "Salary Slip Leave", "read_only": 1 + }, + { + "fieldname": "gross_year_to_date", + "fieldtype": "Currency", + "label": "Gross Year To Date", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "base_gross_year_to_date", + "fieldtype": "Currency", + "label": "Gross Year To Date(Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2021-03-31 22:44:09.772331", + "modified": "2021-09-01 10:35:52.374549", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", @@ -672,4 +688,4 @@ "sort_order": "DESC", "timeline_field": "employee", "title_field": "employee_name" -} \ No newline at end of file +} diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 6325351debc..37a0b60052c 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -1214,7 +1214,7 @@ class SalarySlip(TransactionBase): period_start_date, period_end_date = self.get_year_to_date_period() salary_slip_sum = frappe.get_list('Salary Slip', - fields = ['sum(net_pay) as sum'], + fields = ['sum(net_pay) as net_sum', 'sum(gross_pay) as gross_sum'], filters = {'employee_name' : self.employee_name, 'start_date' : ['>=', period_start_date], 'end_date' : ['<', period_end_date], @@ -1222,10 +1222,13 @@ class SalarySlip(TransactionBase): 'docstatus': 1 }) - year_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0 + year_to_date = flt(salary_slip_sum[0].net_sum) if salary_slip_sum else 0.0 + gross_year_to_date = flt(salary_slip_sum[0].gross_sum) if salary_slip_sum else 0.0 year_to_date += self.net_pay + gross_year_to_date += self.gross_pay self.year_to_date = year_to_date + self.gross_year_to_date = gross_year_to_date def compute_month_to_date(self): month_to_date = 0 diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 23ec2fdb849..62867327537 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -67,8 +67,6 @@ erpnext.HierarchyChart = class { } show() { - frappe.breadcrumbs.add('HR'); - this.setup_actions(); if ($(`[data-fieldname="company"]`).length) return; let me = this; @@ -83,8 +81,9 @@ erpnext.HierarchyChart = class { reqd: 1, change: () => { me.company = undefined; + $('#hierarchy-chart-wrapper').remove(); - if (company.get_value() && me.company != company.get_value()) { + if (company.get_value()) { me.company = company.get_value(); // svg for connectors @@ -92,6 +91,8 @@ erpnext.HierarchyChart = class { me.setup_hierarchy(); me.render_root_nodes(); me.all_nodes_expanded = false; + } else { + frappe.throw(__('Please select a company first.')); } } }); @@ -172,11 +173,11 @@ erpnext.HierarchyChart = class { `); this.page.main - .find('#hierarchy-chart-wrapper') + .find('#hierarchy-chart') + .empty() .append(this.$hierarchy); this.nodes = {}; - this.all_nodes_expanded = false; } make_svg_markers() { @@ -203,6 +204,8 @@ erpnext.HierarchyChart = class { +
+
`); } @@ -219,7 +222,10 @@ erpnext.HierarchyChart = class { let expand_node = undefined; let node = undefined; - $.each(r.message, (i, data) => { + $.each(r.message, (_i, data) => { + if ($(`#${data.id}`).length) + return; + node = new me.Node({ id: data.id, parent: $('
  • ').appendTo(me.$hierarchy.find('.node-children')), @@ -290,7 +296,7 @@ erpnext.HierarchyChart = class { () => frappe.dom.freeze(), () => this.setup_hierarchy(), () => this.render_root_nodes(true), - () => this.get_all_nodes(node.id, node.name), + () => this.get_all_nodes(), (data_list) => this.render_children_of_all_nodes(data_list), () => frappe.dom.unfreeze() ]); @@ -341,15 +347,13 @@ erpnext.HierarchyChart = class { node.expanded = true; } - get_all_nodes(node_id, node_name) { + get_all_nodes() { return new Promise(resolve => { frappe.call({ method: 'erpnext.utilities.hierarchy_chart.get_all_nodes', args: { method: this.method, - company: this.company, - parent: node_id, - parent_name: node_name + company: this.company }, callback: (r) => { resolve(r.message); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index b1b78c05174..b1a88795572 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -59,8 +59,6 @@ erpnext.HierarchyChartMobile = class { } show() { - frappe.breadcrumbs.add('HR'); - let me = this; if ($(`[data-fieldname="company"]`).length) return; diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 12234495493..0e1a915deb7 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -18,6 +18,8 @@ frappe.ui.form.on('Quotation', { } }); + frm.set_df_property('packed_items', 'cannot_add_rows', true); + frm.set_df_property('packed_items', 'cannot_delete_rows', true); }, refresh: function(frm) { diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 3eba62bc193..43a44900fcb 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -43,6 +43,8 @@ "ignore_pricing_rule", "items_section", "items", + "bundle_items_section", + "packed_items", "pricing_rule_details", "pricing_rules", "sec_break23", @@ -926,6 +928,24 @@ "label": "Lost Reasons", "options": "Quotation Lost Reason Detail", "read_only": 1 + }, + { + "depends_on": "packed_items", + "fieldname": "packed_items", + "fieldtype": "Table", + "label": "Bundle Items", + "options": "Packed Item", + "print_hide": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "packed_items", + "depends_on": "packed_items", + "fieldname": "bundle_items_section", + "fieldtype": "Section Break", + "label": "Bundle Items", + "options": "fa fa-suitcase", + "print_hide": 1 } ], "icon": "fa fa-shopping-cart", @@ -933,7 +953,7 @@ "is_submittable": 1, "links": [], "max_attachments": 1, - "modified": "2020-10-30 13:58:59.212060", + "modified": "2021-08-27 20:10:07.864951", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index e4f8a475816..6cd03dd2791 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -31,6 +31,9 @@ class Quotation(SellingController): if self.items: self.with_items = 1 + from erpnext.stock.doctype.packed_item.packed_item import make_packing_list + make_packing_list(self) + def validate_valid_till(self): if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date): frappe.throw(_("Valid till date cannot be before transaction date")) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 527a999aefa..854260059c4 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -226,9 +226,87 @@ class TestQuotation(unittest.TestCase): expired_quotation.reload() self.assertEqual(expired_quotation.status, "Expired") + def test_product_bundle_mapping_on_creating_so(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + from erpnext.selling.doctype.quotation.quotation import make_sales_order + + make_item("_Test Product Bundle", {"is_stock_item": 0}) + make_item("_Test Bundle Item 1", {"is_stock_item": 1}) + make_item("_Test Bundle Item 2", {"is_stock_item": 1}) + + make_product_bundle("_Test Product Bundle", + ["_Test Bundle Item 1", "_Test Bundle Item 2"]) + + quotation = make_quotation(item_code="_Test Product Bundle", qty=1, rate=100) + sales_order = make_sales_order(quotation.name) + + quotation_item = [quotation.items[0].item_code, quotation.items[0].rate, quotation.items[0].qty, quotation.items[0].amount] + so_item = [sales_order.items[0].item_code, sales_order.items[0].rate, sales_order.items[0].qty, sales_order.items[0].amount] + + self.assertEqual(quotation_item, so_item) + + quotation_packed_items = [ + [quotation.packed_items[0].parent_item, quotation.packed_items[0].item_code, quotation.packed_items[0].qty], + [quotation.packed_items[1].parent_item, quotation.packed_items[1].item_code, quotation.packed_items[1].qty] + ] + so_packed_items = [ + [sales_order.packed_items[0].parent_item, sales_order.packed_items[0].item_code, sales_order.packed_items[0].qty], + [sales_order.packed_items[1].parent_item, sales_order.packed_items[1].item_code, sales_order.packed_items[1].qty] + ] + + self.assertEqual(quotation_packed_items, so_packed_items) + + def test_product_bundle_price_calculation_when_calculate_bundle_price_is_unchecked(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + + make_item("_Test Product Bundle", {"is_stock_item": 0}) + bundle_item1 = make_item("_Test Bundle Item 1", {"is_stock_item": 1}) + bundle_item2 = make_item("_Test Bundle Item 2", {"is_stock_item": 1}) + + make_product_bundle("_Test Product Bundle", + ["_Test Bundle Item 1", "_Test Bundle Item 2"]) + + bundle_item1.valuation_rate = 100 + bundle_item1.save() + + bundle_item2.valuation_rate = 200 + bundle_item2.save() + + quotation = make_quotation(item_code="_Test Product Bundle", qty=2, rate=100) + self.assertEqual(quotation.items[0].amount, 200) + + def test_product_bundle_price_calculation_when_calculate_bundle_price_is_checked(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + + make_item("_Test Product Bundle", {"is_stock_item": 0}) + make_item("_Test Bundle Item 1", {"is_stock_item": 1}) + make_item("_Test Bundle Item 2", {"is_stock_item": 1}) + + make_product_bundle("_Test Product Bundle", + ["_Test Bundle Item 1", "_Test Bundle Item 2"]) + + enable_calculate_bundle_price() + + quotation = make_quotation(item_code="_Test Product Bundle", qty=2, rate=100, do_not_submit=1) + quotation.packed_items[0].rate = 100 + quotation.packed_items[1].rate = 200 + quotation.save() + + self.assertEqual(quotation.items[0].amount, 600) + self.assertEqual(quotation.items[0].rate, 300) + + enable_calculate_bundle_price(enable=0) test_records = frappe.get_test_records('Quotation') +def enable_calculate_bundle_price(enable=1): + selling_settings = frappe.get_doc("Selling Settings") + selling_settings.editable_bundle_item_rates = enable + selling_settings.save() + def get_quotation_dict(party_name=None, item_code=None): if not party_name: party_name = '_Test Customer' diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index b42c6153129..f6926906b82 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -43,6 +43,9 @@ frappe.ui.form.on("Sales Order", { } } }); + + frm.set_df_property('packed_items', 'cannot_add_rows', true); + frm.set_df_property('packed_items', 'cannot_delete_rows', true); }, refresh: function(frm) { if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed' diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 38ea5c81d49..85282ca1a07 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -55,6 +55,8 @@ "items_section", "scan_barcode", "items", + "packing_list", + "packed_items", "pricing_rule_details", "pricing_rules", "section_break_31", @@ -101,8 +103,6 @@ "in_words", "advance_paid", "disable_rounded_total", - "packing_list", - "packed_items", "payment_schedule_section", "payment_terms_template", "payment_schedule", @@ -1019,6 +1019,7 @@ { "collapsible": 1, "collapsible_depends_on": "packed_items", + "depends_on": "packed_items", "fieldname": "packing_list", "fieldtype": "Section Break", "hide_days": 1, @@ -1029,14 +1030,14 @@ "print_hide": 1 }, { + "depends_on": "packed_items", "fieldname": "packed_items", "fieldtype": "Table", "hide_days": 1, "hide_seconds": 1, "label": "Packed Items", "options": "Packed Item", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "payment_schedule_section", @@ -1511,7 +1512,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:15:26.531553", + "modified": "2021-09-01 15:12:24.115483", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 5d44582aab4..47ebfe90d31 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -947,11 +947,52 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): "pricing_rules" ], "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map + "condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map and not is_product_bundle(doc.item_code) + }, + "Packed Item": { + "doctype": "Purchase Order Item", + "field_map": [ + ["parent", "sales_order"], + ["uom", "uom"], + ["conversion_factor", "conversion_factor"], + ["parent_item", "product_bundle"], + ["rate", "rate"] + ], + "field_no_map": [ + "price_list_rate", + "item_tax_template", + "discount_percentage", + "discount_amount", + "supplier", + "pricing_rules" + ], } }, target_doc, set_missing_values) + + set_delivery_date(doc.items, source_name) + return doc +def set_delivery_date(items, sales_order): + delivery_dates = frappe.get_all( + 'Sales Order Item', + filters = { + 'parent': sales_order + }, + fields = ['delivery_date', 'item_code'] + ) + + delivery_by_item = frappe._dict() + for date in delivery_dates: + delivery_by_item[date.item_code] = date.delivery_date + + for item in items: + if item.product_bundle: + item.schedule_date = delivery_by_item[item.product_bundle] + +def is_product_bundle(item_code): + return frappe.db.exists('Product Bundle', item_code) + @frappe.whitelist() def make_work_orders(items, sales_order, company, project=None): '''Make Work Orders against the given Sales Order for the given `items`''' diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index d685fbff82b..054791e9e4f 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -906,6 +906,38 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(purchase_orders[0].supplier, '_Test Supplier') self.assertEqual(purchase_orders[1].supplier, '_Test Supplier 1') + def test_product_bundles_in_so_are_replaced_with_bundle_items_in_po(self): + """ + Tests if the the Product Bundles in the Items table of Sales Orders are replaced with + their child items(from the Packed Items table) on creating a Purchase Order from it. + """ + from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order + + product_bundle = make_item("_Test Product Bundle", {"is_stock_item": 0}) + make_item("_Test Bundle Item 1", {"is_stock_item": 1}) + make_item("_Test Bundle Item 2", {"is_stock_item": 1}) + + make_product_bundle("_Test Product Bundle", + ["_Test Bundle Item 1", "_Test Bundle Item 2"]) + + so_items = [ + { + "item_code": product_bundle.item_code, + "warehouse": "", + "qty": 2, + "rate": 400, + "delivered_by_supplier": 1, + "supplier": '_Test Supplier' + } + ] + + so = make_sales_order(item_list=so_items) + + purchase_order = make_purchase_order(so.name, selected_items=so_items) + + self.assertEqual(purchase_order.items[0].item_code, "_Test Bundle Item 1") + self.assertEqual(purchase_order.items[1].item_code, "_Test Bundle Item 2") + def test_reserved_qty_for_closing_so(self): bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, fields=["reserved_qty"]) diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.py b/erpnext/selling/doctype/selling_settings/selling_settings.py index b219e7ecce0..b54559ad288 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.py +++ b/erpnext/selling/doctype/selling_settings/selling_settings.py @@ -15,6 +15,7 @@ from frappe.model.document import Document class SellingSettings(Document): def on_update(self): self.toggle_hide_tax_id() + self.toggle_editable_rate_for_bundle_items() def validate(self): for key in ["cust_master_name", "campaign_naming_by", "customer_group", "territory", @@ -33,6 +34,11 @@ class SellingSettings(Document): make_property_setter(doctype, "tax_id", "hidden", self.hide_tax_id, "Check", validate_fields_for_doctype=False) make_property_setter(doctype, "tax_id", "print_hide", self.hide_tax_id, "Check", validate_fields_for_doctype=False) + def toggle_editable_rate_for_bundle_items(self): + editable_bundle_item_rates = cint(self.editable_bundle_item_rates) + + make_property_setter("Packed Item", "rate", "read_only", not(editable_bundle_item_rates), "Check", validate_fields_for_doctype=False) + def set_default_customer_group_and_territory(self): if not self.customer_group: self.customer_group = get_root_of('Customer Group') diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 2de57c87f18..e440f33020d 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -90,10 +90,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran this.frm.toggle_display("customer_name", (this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer)); - if(this.frm.fields_dict.packed_items) { - var packing_list_exists = (this.frm.doc.packed_items || []).length; - this.frm.toggle_display("packing_list", packing_list_exists ? true : false); - } + this.toggle_editable_price_list_rate(); } diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 958189614fd..fdc8763baa6 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -543,6 +543,7 @@ { "collapsible": 1, "collapsible_depends_on": "packed_items", + "depends_on": "packed_items", "fieldname": "packing_list", "fieldtype": "Section Break", "label": "Packing List", @@ -551,6 +552,7 @@ "print_hide": 1 }, { + "depends_on": "packed_items", "fieldname": "packed_items", "fieldtype": "Table", "label": "Packed Items", @@ -1306,7 +1308,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:15:50.574966", + "modified": "2021-08-27 20:14:40.215231", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json index bb396e806f6..830d5469bf0 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.json +++ b/erpnext/stock/doctype/packed_item/packed_item.json @@ -16,6 +16,7 @@ "conversion_factor", "column_break_9", "qty", + "rate", "uom", "section_break_9", "serial_no", @@ -215,13 +216,23 @@ "fieldname": "conversion_factor", "fieldtype": "Float", "label": "Conversion Factor" + }, + { + "fetch_from": "item_code.valuation_rate", + "fetch_if_empty": 1, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-05-26 07:08:05.111385", + "modified": "2021-09-01 15:10:29.646399", "modified_by": "Administrator", "module": "Stock", "name": "Packed Item", diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index 4ab71bdf629..eea00ec06cc 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -39,8 +39,10 @@ def update_packing_list_item(doc, packing_item_code, qty, main_item_row, descrip # check if exists exists = 0 for d in doc.get("packed_items"): - if d.parent_item == main_item_row.item_code and d.item_code == packing_item_code and\ - d.parent_detail_docname == main_item_row.name: + if d.parent_item == main_item_row.item_code and d.item_code == packing_item_code: + if d.parent_detail_docname != main_item_row.name: + d.parent_detail_docname = main_item_row.name + pi, exists = d, 1 break @@ -86,6 +88,9 @@ def make_packing_list(doc): cleanup_packing_list(doc, parent_items) + if frappe.db.get_single_value("Selling Settings", "editable_bundle_item_rates"): + update_product_bundle_price(doc, parent_items) + def cleanup_packing_list(doc, parent_items): """Remove all those child items which are no longer present in main item table""" delete_list = [] @@ -103,6 +108,40 @@ def cleanup_packing_list(doc, parent_items): if d not in delete_list: doc.append("packed_items", d) +def update_product_bundle_price(doc, parent_items): + """Updates the prices of Product Bundles based on the rates of the Items in the bundle.""" + + if not doc.get('items'): + return + + parent_items_index = 0 + bundle_price = 0 + + for bundle_item in doc.get("packed_items"): + if parent_items[parent_items_index][0] == bundle_item.parent_item: + bundle_item_rate = bundle_item.rate if bundle_item.rate else 0 + bundle_price += bundle_item.qty * bundle_item_rate + else: + update_parent_item_price(doc, parent_items[parent_items_index][0], bundle_price) + + bundle_price = 0 + parent_items_index += 1 + + # for the last product bundle + if doc.get("packed_items"): + update_parent_item_price(doc, parent_items[parent_items_index][0], bundle_price) + +def update_parent_item_price(doc, parent_item_code, bundle_price): + parent_item_doc = doc.get('items', {'item_code': parent_item_code})[0] + + current_parent_item_price = parent_item_doc.amount + if current_parent_item_price != bundle_price: + parent_item_doc.amount = bundle_price + update_parent_item_rate(parent_item_doc, bundle_price) + +def update_parent_item_rate(parent_item_doc, bundle_price): + parent_item_doc.rate = bundle_price/parent_item_doc.qty + @frappe.whitelist() def get_items_from_product_bundle(args): args = json.loads(args) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 82cc98e7f75..3efa66e02ed 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -10,6 +10,7 @@ "barcode", "section_break_2", "item_code", + "product_bundle", "supplier_part_no", "column_break_2", "item_name", @@ -956,12 +957,19 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "product_bundle", + "fieldtype": "Link", + "label": "Product Bundle", + "options": "Product Bundle", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-03-29 04:17:00.336298", + "modified": "2021-09-01 16:02:40.338597", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py index 384d84194bb..cc506e5cfda 100644 --- a/erpnext/utilities/hierarchy_chart.py +++ b/erpnext/utilities/hierarchy_chart.py @@ -6,17 +6,21 @@ import frappe from frappe import _ @frappe.whitelist() -def get_all_nodes(parent, parent_name, method, company): +def get_all_nodes(method, company): '''Recursively gets all data from nodes''' method = frappe.get_attr(method) if method not in frappe.whitelisted: frappe.throw(_('Not Permitted'), frappe.PermissionError) - data = method(parent, company) - result = [dict(parent=parent, parent_name=parent_name, data=data)] + root_nodes = method(company=company) + result = [] + nodes_to_expand = [] - nodes_to_expand = [{'id': d.get('id'), 'name': d.get('name')} for d in data if d.get('expandable')] + for root in root_nodes: + data = method(root.id, company) + result.append(dict(parent=root.id, parent_name=root.name, data=data)) + nodes_to_expand.extend([{'id': d.get('id'), 'name': d.get('name')} for d in data if d.get('expandable')]) while nodes_to_expand: parent = nodes_to_expand.pop(0)