diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c66621856f..ccb7e8f936 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.1.15' +__version__ = '10.1.18' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index 10f326242b..3d8aedcdcc 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -4,12 +4,21 @@ .print-format { padding: 8mm; margin:4mm; - font-size:10px; + font-size: 10.0pt !important; font-family: Tahoma, sans-serif; } } {% } %} + +

{%= __(report.report_name) %}

{%= filters.customer || filters.supplier %}

@@ -91,8 +100,8 @@ {% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %} - {%= __("Date") %} - {%= __("Ref") %} + {%= __("Date") %} + {%= __("Reference") %} {% if(!filters.show_pdc_in_print) { %} {%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %} {% } %} @@ -125,10 +134,15 @@ {% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %} {% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %} - {%= dateutil.str_to_user(data[i][__("Posting Date")]) %} - {%= data[i][__("Voucher Type")] %} -
{%= data[i][__("Voucher No")] %} - {% if(!filters.show_pdc_in_print) { %} + {%= dateutil.str_to_user(data[i]["posting_date"]) %} + + {% if(!filters.show_pdc_in_print) { %} + {%= data[i]["voucher_type"] %} +
+ {% } %} + {%= data[i]["voucher_no"] %} + + {% if(!filters.show_pdc_in_print) { %} {% if(!(filters.customer || filters.supplier)) { %} {%= data[i][__("Customer")] || data[i][__("Supplier")] %} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index cf3b1bd88c..61096837f5 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -22,13 +22,35 @@ class ReceivablePayableReport(object): return columns, data, None, chart def get_columns(self, party_naming_by, args): - columns = [_("Posting Date") + ":Date:80", _(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"] + columns = [] + columns.append({ + "label": _("Posting Date"), + "fieldtype": "Date", + "fieldname": "posting_date", + "width": 90 + }) + + columns += [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"] if party_naming_by == "Naming Series": columns += [args.get("party_type") + " Name::110"] - columns += [_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/"+_("Voucher Type")+":120", - _("Due Date") + ":Date:80"] + columns.append({ + "label": _("Voucher Type"), + "fieldtype": "Data", + "fieldname": "voucher_type", + "width": 110 + }) + + columns.append({ + "label": _("Voucher No"), + "fieldtype": "Dynamic Link", + "fieldname": "voucher_no", + "width": 110, + "options": "voucher_type", + }) + + columns += [_("Due Date") + ":Date:80"] if args.get("party_type") == "Supplier": columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"] @@ -114,7 +136,7 @@ class ReceivablePayableReport(object): return_entries = self.get_return_entries(args.get("party_type")) data = [] - pdc_details = get_pdc_details(args.get("party_type")) + pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date) for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")): if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers): @@ -160,6 +182,7 @@ class ReceivablePayableReport(object): row.append(company_currency) pdc = pdc_details.get((gle.voucher_no, gle.party), {}) + remaining_balance = outstanding_amount - flt(pdc.get("pdc_amount")) row += [pdc.get("pdc_date"), pdc.get("pdc_ref"), flt(pdc.get("pdc_amount")), remaining_balance] @@ -392,7 +415,7 @@ def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_dat return [age] + outstanding_range -def get_pdc_details(party_type): +def get_pdc_details(party_type, report_date): pdc_details = frappe._dict() for pdc in frappe.db.sql(""" @@ -405,13 +428,14 @@ def get_pdc_details(party_type): on (pref.parent = pent.name) where - pent.docstatus < 2 and pent.reference_date >= pent.posting_date + pent.docstatus < 2 and pent.reference_date >= %s and pent.party_type = %s - group by pent.party, pref.reference_name""", party_type, as_dict=1): + group by pent.party, pref.reference_name""", (report_date, party_type), as_dict=1): pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc) if scrub(party_type): - amount_field = "jea.debit_in_account_currency + jea.credit_in_account_currency" + amount_field = ("jea.debit_in_account_currency" + if party_type == 'Supplier' else "jea.credit_in_account_currency") else: amount_field = "jea.debit + jea.credit" @@ -425,9 +449,9 @@ def get_pdc_details(party_type): on (jea.parent = je.name) where - je.docstatus < 2 and je.cheque_date >= je.posting_date + je.docstatus < 2 and je.cheque_date >= %s and jea.party_type = %s - group by jea.party, jea.reference_name""".format(amount_field), party_type, as_dict=1): + group by jea.party, jea.reference_name""".format(amount_field), (report_date, party_type), as_dict=1): if (pdc.invoice_no, pdc.party) in pdc_details: pdc_details[(pdc.invoice_no, pdc.party)]["pdc_amount"] += pdc.pdc_amount else: diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 1649494ce6..14fb0e0c20 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -423,10 +423,7 @@ def make_rm_stock_entry(purchase_order, rm_items): if fg_items: items = tuple(set(d["rm_item_code"] for d in rm_items_list)) - item_wh = frappe._dict(frappe.db.sql(""" - select item_code, description - from `tabItem` where name in ({0}) - """.format(", ".join(["%s"] * len(items))), items)) + item_wh = get_item_details(items) stock_entry = frappe.new_doc("Stock Entry") stock_entry.purpose = "Subcontract" @@ -441,13 +438,15 @@ def make_rm_stock_entry(purchase_order, rm_items): for item_code in fg_items: for rm_item_data in rm_items_list: if rm_item_data["item_code"] == item_code: + rm_item_code = rm_item_data["rm_item_code"] items_dict = { - rm_item_data["rm_item_code"]: { + rm_item_code: { "item_name": rm_item_data["item_name"], - "description": item_wh.get(rm_item_data["rm_item_code"]), + "description": item_wh[rm_item_code].get('description'), 'qty': rm_item_data["qty"], 'from_warehouse': rm_item_data["warehouse"], - 'stock_uom': rm_item_data["stock_uom"] + 'stock_uom': rm_item_data["stock_uom"], + 'allow_alternative_item': item_wh[rm_item_code].get('allow_alternative_item') } } stock_entry.add_to_stock_entry_detail(items_dict) @@ -456,6 +455,14 @@ def make_rm_stock_entry(purchase_order, rm_items): frappe.throw(_("No Items selected for transfer")) return purchase_order.name +def get_item_details(items): + item_details = {} + for d in frappe.db.sql("""select item_code, description, allow_alternative_item from `tabItem` + where name in ({0})""".format(", ".join(["%s"] * len(items))), items, as_dict=1): + item_details[d.item_code] = d + + return item_details + @frappe.whitelist() def update_status(status, name): po = frappe.get_doc("Purchase Order", name) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 5dacfc268f..873fc74537 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -321,6 +321,7 @@ def create_purchase_order(**args): po.is_subcontracted = args.is_subcontracted or "No" po.currency = args.currency or frappe.db.get_value("Company", po.company, "default_currency") po.conversion_factor = args.conversion_factor or 1 + po.supplier_warehouse = args.supplier_warehouse or None po.append("items", { "item_code": args.item or args.item_code or "_Test Item", diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index e3b8d82c35..fe358ed93d 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -71,6 +71,10 @@ def get_data(): "type": "doctype", "name": "Item", }, + { + "type": "doctype", + "name": "Item Alternative", + }, { "type": "doctype", "name": "Product Bundle", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index ff0f0c2b21..de6ed79351 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -10,6 +10,7 @@ from erpnext.accounts.party import get_party_details from erpnext.stock.get_item_details import get_conversion_factor from erpnext.buying.utils import validate_for_items, update_last_purchase_rate from erpnext.stock.stock_ledger import get_valuation_rate +from erpnext.stock.doctype.stock_entry.stock_entry import get_used_alternative_items from erpnext.controllers.stock_controller import StockController @@ -200,6 +201,11 @@ class BuyingController(StockController): exploded_item = item.get('include_exploded_items') bom_items = get_items_from_bom(item.item_code, item.bom, exploded_item) + + used_alternative_items = [] + if self.doctype == 'Purchase Receipt' and item.purchase_order: + used_alternative_items = get_used_alternative_items(purchase_order = item.purchase_order) + raw_materials_cost = 0 items = list(set([d.item_code for d in bom_items])) item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse @@ -211,6 +217,16 @@ class BuyingController(StockController): if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company: reserve_warehouse = None + conversion_factor = item.conversion_factor + if (self.doctype == 'Purchase Receipt' and item.purchase_order and + bom_item.item_code in used_alternative_items): + alternative_item_data = used_alternative_items.get(bom_item.item_code) + bom_item.item_code = alternative_item_data.item_code + bom_item.item_name = alternative_item_data.item_name + bom_item.stock_uom = alternative_item_data.stock_uom + conversion_factor = alternative_item_data.conversion_factor + bom_item.description = alternative_item_data.description + # check if exists exists = 0 for d in self.get(raw_material_table): @@ -223,7 +239,7 @@ class BuyingController(StockController): rm = self.append(raw_material_table, {}) required_qty = flt(flt(bom_item.qty_consumed_per_unit) * (flt(item.qty) + getattr(item, 'rejected_qty', 0)) * - flt(item.conversion_factor), rm.precision("required_qty")) + flt(conversion_factor), rm.precision("required_qty")) rm.reference_name = item.name rm.bom_detail_no = bom_item.name rm.main_item_code = item.item_code @@ -233,7 +249,7 @@ class BuyingController(StockController): if self.doctype == "Purchase Order" and not rm.reserve_warehouse: rm.reserve_warehouse = reserve_warehouse - rm.conversion_factor = item.conversion_factor + rm.conversion_factor = conversion_factor if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: rm.consumed_qty = required_qty @@ -463,4 +479,4 @@ def get_items_from_bom(item_code, bom, exploded_item=1): if not bom_items: msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1) - return bom_items \ No newline at end of file + return bom_items diff --git a/erpnext/docs/assets/img/manufacturing/allow-alternative-item-bom.png b/erpnext/docs/assets/img/manufacturing/allow-alternative-item-bom.png new file mode 100644 index 0000000000..41208817c3 Binary files /dev/null and b/erpnext/docs/assets/img/manufacturing/allow-alternative-item-bom.png differ diff --git a/erpnext/docs/assets/img/manufacturing/allow-alternative-item-wo.png b/erpnext/docs/assets/img/manufacturing/allow-alternative-item-wo.png new file mode 100644 index 0000000000..baa732fb7e Binary files /dev/null and b/erpnext/docs/assets/img/manufacturing/allow-alternative-item-wo.png differ diff --git a/erpnext/docs/assets/img/manufacturing/allow-alternative-item.png b/erpnext/docs/assets/img/manufacturing/allow-alternative-item.png new file mode 100644 index 0000000000..17719803c5 Binary files /dev/null and b/erpnext/docs/assets/img/manufacturing/allow-alternative-item.png differ diff --git a/erpnext/docs/assets/img/manufacturing/item-alternative.png b/erpnext/docs/assets/img/manufacturing/item-alternative.png new file mode 100644 index 0000000000..ff7c66c3a9 Binary files /dev/null and b/erpnext/docs/assets/img/manufacturing/item-alternative.png differ diff --git a/erpnext/docs/assets/img/manufacturing/purchase_order_item_alternative.gif b/erpnext/docs/assets/img/manufacturing/purchase_order_item_alternative.gif new file mode 100644 index 0000000000..4907e3ecd7 Binary files /dev/null and b/erpnext/docs/assets/img/manufacturing/purchase_order_item_alternative.gif differ diff --git a/erpnext/docs/assets/img/manufacturing/work_order_item_alternative.gif b/erpnext/docs/assets/img/manufacturing/work_order_item_alternative.gif new file mode 100644 index 0000000000..076f0b19de Binary files /dev/null and b/erpnext/docs/assets/img/manufacturing/work_order_item_alternative.gif differ diff --git a/erpnext/docs/user/manual/en/manufacturing/index.txt b/erpnext/docs/user/manual/en/manufacturing/index.txt index 13f07010d0..9befc66395 100644 --- a/erpnext/docs/user/manual/en/manufacturing/index.txt +++ b/erpnext/docs/user/manual/en/manufacturing/index.txt @@ -4,6 +4,7 @@ work-order workstation operation subcontracting +item-alternative tools setup articles diff --git a/erpnext/docs/user/manual/en/manufacturing/item-alternative.md b/erpnext/docs/user/manual/en/manufacturing/item-alternative.md new file mode 100644 index 0000000000..69bc825b64 --- /dev/null +++ b/erpnext/docs/user/manual/en/manufacturing/item-alternative.md @@ -0,0 +1,32 @@ +# Item Alternative + +Item alternative feature is very useful in manufacturing industries, if the raw material defined in the BOM is not available during the production process then their respective available alternative item used to complete the production process. + +To make item alaternative for an item, kindly enable the "Allow Alternative Item" in the item. +Item + +* To make item alternative, goto module Stock > Items and Pricing > Item Alternative +Item Alternative + +The user can enable Two-Way between an item and their alternative item if both can be used as an alternative to each other + + +### Item Alternative for work order + +To allow to use alternative items in the manufacturing process user can configure allow an alternative item in the BOM/Work Order + +##### Provision to allow alternative item in the bom +Item + +##### Provision to allow alternative item in the work order +User can also enable/disable allow alternative item in the work order +Item + +##### How it works for work order +Item + +### Item Alternative for subcontract +In subcontract, the user has to transfer raw materials to the subcontracted supplier to get finished good from them. If the raw material is not available in the stock, with this feature, the user can transfer the alternate item of the subcontracted raw material to the supplier. + +##### How it works for subcontract +Item \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 30b9683638..b2f7bb3524 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -42,6 +42,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -73,6 +74,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -106,6 +108,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -136,6 +139,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -168,6 +172,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -196,6 +201,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -228,6 +234,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -260,6 +267,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -290,6 +298,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -320,6 +329,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -351,6 +361,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -382,6 +393,38 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Alternative Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -412,6 +455,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -443,6 +487,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -472,6 +517,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -503,6 +549,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -533,6 +580,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -565,6 +613,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -597,6 +646,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -627,6 +677,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -659,6 +710,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -689,6 +741,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -720,6 +773,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -750,6 +804,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -780,6 +835,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -810,6 +866,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -841,6 +898,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -869,6 +927,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -900,6 +959,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -931,6 +991,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -962,6 +1023,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -991,6 +1053,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1021,6 +1084,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1050,6 +1114,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1081,6 +1146,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1110,6 +1176,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1142,6 +1209,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1172,6 +1240,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1200,6 +1269,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1230,6 +1300,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1259,6 +1330,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1288,6 +1360,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1317,6 +1390,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1347,6 +1421,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1378,6 +1453,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1408,6 +1484,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1440,6 +1517,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1471,6 +1549,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1502,6 +1581,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1532,6 +1612,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1564,6 +1645,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1594,6 +1676,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1626,6 +1709,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1657,6 +1741,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1688,6 +1773,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1719,6 +1805,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1733,7 +1820,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-02-16 13:43:55.485813", + "modified": "2018-02-26 22:51:40.232456", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index ea33f4ed02..007299a798 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -543,6 +543,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite item.description, item.image, item.stock_uom, + item.allow_alternative_item, item.default_warehouse, item.expense_account as expense_account, item.buying_cost_center as cost_center @@ -606,6 +607,9 @@ def validate_bom_no(item, bom_no): for d in bom.items: if (d.item_code.lower() == item.lower()): rm_item_exists = True + for d in bom.scrap_items: + if (d.item_code.lower() == item.lower()): + rm_item_exists = True if bom.item.lower() == item.lower() or \ bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower(): rm_item_exists = True diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 7d50416d20..a1ebca05ce 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -171,7 +171,7 @@ frappe.ui.form.on("Work Order", { frm.set_value('sales_order', ""); frm.trigger('set_sales_order'); erpnext.in_production_item_onchange = true; - $.each(["description", "stock_uom", "project", "bom_no"], function(i, field) { + $.each(["description", "stock_uom", "project", "bom_no", "allow_alternative_item"], function(i, field) { frm.set_value(field, r.message[field]); }); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 6f6c4e42fb..e6bdc26a78 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -40,6 +40,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -71,6 +72,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -105,6 +107,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -137,6 +140,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -171,6 +175,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -202,6 +207,38 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Alternative Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -231,6 +268,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -264,6 +302,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -297,6 +336,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -331,6 +371,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -362,6 +403,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -394,6 +436,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -425,6 +468,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -455,6 +499,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -485,6 +530,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -517,6 +563,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -545,6 +592,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -576,6 +624,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -606,6 +655,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -637,6 +687,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -668,6 +719,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -699,6 +751,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -729,6 +782,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -758,6 +812,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -788,6 +843,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -818,6 +874,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -848,6 +905,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -880,6 +938,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -912,6 +971,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -944,6 +1004,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -975,6 +1036,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1006,6 +1068,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1037,6 +1100,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1066,6 +1130,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1097,6 +1162,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1127,6 +1193,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1156,6 +1223,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1189,6 +1257,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1221,6 +1290,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1249,6 +1319,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -1282,6 +1353,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1312,6 +1384,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1342,6 +1415,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1373,6 +1447,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1403,6 +1478,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1435,6 +1511,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1449,7 +1526,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-02-13 02:58:11.328693", + "modified": "2018-03-05 12:43:10.442928", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 6c632a5a03..bb773ab01a 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -489,6 +489,7 @@ class WorkOrder(Document): 'item_code': item.item_code, 'item_name': item.item_name, 'description': item.description, + 'allow_alternative_item': item.allow_alternative_item, 'required_qty': item.qty, 'source_warehouse': item.source_warehouse or item.default_warehouse }) @@ -503,15 +504,17 @@ class WorkOrder(Document): transferred_qty = frappe.db.sql('''select sum(qty) from `tabStock Entry` entry, `tabStock Entry Detail` detail where - entry.work_order = %s + entry.work_order = %(name)s and entry.purpose = "Material Transfer for Manufacture" and entry.docstatus = 1 and detail.parent = entry.name - and detail.item_code = %s''', (self.name, d.item_code))[0][0] + and (detail.item_code = %(item)s or detail.original_item = %(item)s)''', { + 'name': self.name, + 'item': d.item_code + })[0][0] d.db_set('transferred_qty', flt(transferred_qty), update_modified = False) - @frappe.whitelist() def get_item_details(item, project = None): res = frappe.db.sql(""" @@ -548,6 +551,7 @@ def get_item_details(item, project = None): frappe.throw(_("Default BOM for {0} not found").format(item)) res['project'] = project or frappe.db.get_value('BOM', res['bom_no'], 'project') + res['allow_alternative_item'] = frappe.db.get_value('BOM', res['bom_no'], 'allow_alternative_item') res.update(check_if_scrap_warehouse_mandatory(res["bom_no"])) return res diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json index 1ac6b66047..2f89a3ded2 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -41,6 +41,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -72,6 +73,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -101,6 +103,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -131,6 +134,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -161,6 +165,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -191,6 +196,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -221,6 +227,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -252,6 +259,38 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Alternative Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -281,6 +320,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -311,6 +351,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -341,6 +382,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -354,7 +396,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-02-13 02:58:11.328693", + "modified": "2018-03-05 13:07:07.530725", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Item", diff --git a/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py b/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py index c49752213c..c0a9e5eb5b 100644 --- a/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py +++ b/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py @@ -6,6 +6,8 @@ import frappe from erpnext.stock.utils import get_bin def execute(): + frappe.reload_doc("stock", "doctype", "bin") + frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied") for d in frappe.db.sql(""" select distinct rm_item_code, reserve_warehouse from `tabPurchase Order Item Supplied` @@ -26,4 +28,4 @@ def execute(): bin_doc = get_bin(d[0], d[1]) bin_doc.update_reserved_qty_for_production() except: - pass \ No newline at end of file + pass diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 8b41adc3ce..43cee681c6 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -42,6 +42,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -59,7 +60,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Status", "length": 0, "no_copy": 1, @@ -75,6 +76,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -91,7 +93,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Project Type", "length": 0, "no_copy": 0, @@ -107,6 +109,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -139,6 +142,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -171,6 +175,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -200,6 +205,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -216,7 +222,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Priority", "length": 0, "no_copy": 0, @@ -232,6 +238,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -263,6 +270,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -294,6 +302,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -323,6 +332,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -354,6 +364,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -386,6 +397,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -415,6 +427,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -446,6 +459,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -476,6 +490,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -508,6 +523,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -539,6 +555,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -570,6 +587,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -600,6 +618,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -631,6 +650,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -662,6 +682,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -692,6 +713,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -722,6 +744,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -752,6 +775,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -781,6 +805,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -812,6 +837,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -843,6 +869,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -875,6 +902,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -906,6 +934,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -937,6 +966,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -967,6 +997,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -997,6 +1028,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1026,6 +1058,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1056,6 +1089,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1087,6 +1121,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1117,6 +1152,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1147,6 +1183,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1177,6 +1214,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1207,6 +1245,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -1240,6 +1279,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1269,6 +1309,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1301,6 +1342,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1332,6 +1374,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1363,6 +1406,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1395,6 +1439,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1424,6 +1469,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1455,6 +1501,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1486,6 +1533,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1517,6 +1565,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1548,6 +1597,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1579,6 +1629,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1611,6 +1662,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1642,6 +1694,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1656,7 +1709,7 @@ "issingle": 0, "istable": 0, "max_attachments": 4, - "modified": "2018-03-22 11:44:38.723507", + "modified": "2018-03-28 10:19:32.743900", "modified_by": "Administrator", "module": "Projects", "name": "Project", diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 37291d7500..0e6017987a 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -184,6 +184,142 @@ $.extend(erpnext.utils, { }, }); +erpnext.utils.select_alternate_items = function(opts) { + const frm = opts.frm; + const warehouse_field = opts.warehouse_field || 'warehouse'; + const item_field = opts.item_field || 'item_code'; + + this.data = []; + const dialog = new frappe.ui.Dialog({ + title: __("Select Alternate Item"), + fields: [ + {fieldtype:'Section Break', label: __('Items')}, + { + fieldname: "alternative_items", fieldtype: "Table", cannot_add_rows: true, + in_place_edit: true, data: this.data, + get_data: () => { + return this.data; + }, + fields: [{ + fieldtype:'Data', + fieldname:"docname", + hidden: 1 + }, { + fieldtype:'Link', + fieldname:"item_code", + options: 'Item', + in_list_view: 1, + read_only: 1, + label: __('Item Code') + }, { + fieldtype:'Link', + fieldname:"alternate_item", + options: 'Item', + default: "", + in_list_view: 1, + label: __('Alternate Item'), + onchange: function() { + const item_code = this.get_value(); + const warehouse = this.grid_row.on_grid_fields_dict.warehouse.get_value(); + if (item_code && warehouse) { + frappe.call({ + method: "erpnext.stock.utils.get_latest_stock_qty", + args: { + item_code: item_code, + warehouse: warehouse + }, + callback: (r) => { + this.grid_row.on_grid_fields_dict + .actual_qty.set_value(r.message || 0); + } + }) + } + }, + get_query: (e) => { + return { + query: "erpnext.stock.doctype.item_alternative.item_alternative.get_alternative_items", + filters: { + item_code: e.item_code + } + }; + } + }, { + fieldtype:'Link', + fieldname:"warehouse", + options: 'Warehouse', + default: "", + in_list_view: 1, + label: __('Warehouse'), + onchange: function() { + const warehouse = this.get_value(); + const item_code = this.grid_row.on_grid_fields_dict.item_code.get_value(); + if (item_code && warehouse) { + frappe.call({ + method: "erpnext.stock.utils.get_latest_stock_qty", + args: { + item_code: item_code, + warehouse: warehouse + }, + callback: (r) => { + this.grid_row.on_grid_fields_dict + .actual_qty.set_value(r.message || 0); + } + }) + } + }, + }, { + fieldtype:'Float', + fieldname:"actual_qty", + default: 0, + read_only: 1, + in_list_view: 1, + label: __('Available Qty') + }] + }, + ], + primary_action: function() { + const args = this.get_values()["alternative_items"]; + const alternative_items = args.filter(d => { + if (d.alternate_item && d.item_code != d.alternate_item) { + return true; + } + }); + + alternative_items.forEach(d => { + let row = frappe.get_doc(opts.child_doctype, d.docname); + let qty = row.qty; + row[item_field] = d.alternate_item; + + frm.script_manager.trigger(item_field, row.doctype, row.name) + .then(() => { + frappe.model.set_value(row.doctype, row.name, 'qty', qty); + frappe.model.set_value(row.doctype, row.name, + opts.original_item_field, d.item_code); + }); + }); + + refresh_field(opts.child_docname); + this.hide(); + }, + primary_action_label: __('Update') + }); + + frm.doc[opts.child_docname].forEach(d => { + if (!opts.condition || opts.condition(d)) { + dialog.fields_dict.alternative_items.df.data.push({ + "docname": d.name, + "item_code": d[item_field], + "warehouse": d[warehouse_field], + "actual_qty": d.actual_qty + }); + } + }) + + this.data = dialog.fields_dict.alternative_items.df.data; + dialog.fields_dict.alternative_items.grid.refresh(); + dialog.show(); +} + erpnext.utils.map_current_doc = function(opts) { if(opts.get_query_filters) { opts.get_query = function() { diff --git a/erpnext/public/js/utils/item_quick_entry.js b/erpnext/public/js/utils/item_quick_entry.js index ebb92dac8f..b2e0b85556 100644 --- a/erpnext/public/js/utils/item_quick_entry.js +++ b/erpnext/public/js/utils/item_quick_entry.js @@ -319,7 +319,8 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({ ["parent", "=", $(e.target).attr("data-fieldname")], ["attribute_value", "like", e.target.value + "%"] ], - fields: ["attribute_value"] + fields: ["attribute_value"], + parent: "Item" }, callback: function(r) { if (r.message) { diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 0fb87300bb..78def269df 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -85,8 +85,6 @@ erpnext.SerialNoBatchSelector = Class.extend({ fields: fields }); - this.bind_qty(); - this.dialog.set_primary_action(__('Insert'), function() { me.values = me.dialog.get_values(); if(me.validate()) { @@ -102,17 +100,24 @@ erpnext.SerialNoBatchSelector = Class.extend({ } if (d.batch_no) { - this.dialog.fields_dict.batches.df.data.push({ - 'batch_no': d.batch_no, - 'actual_qty': d.actual_qty, - 'selected_qty': d.qty, - 'available_qty': d.actual_batch_qty + this.frm.doc.items.forEach(data => { + if(data.item_code == d.item_code) { + this.dialog.fields_dict.batches.df.data.push({ + 'batch_no': data.batch_no, + 'actual_qty': data.actual_qty, + 'selected_qty': data.qty, + 'available_qty': data.actual_batch_qty + }); + } }); - this.dialog.fields_dict.batches.grid.refresh(); } } + if (this.has_batch) { + this.update_total_qty(); + } + this.dialog.show(); }, @@ -161,18 +166,44 @@ erpnext.SerialNoBatchSelector = Class.extend({ var me = this; if(this.has_batch) { this.values.batches.map((batch, i) => { - let item_code_field = {}; - let row = (i !== 0) ? this.frm.add_child("items", this.item) : this.item; + let batch_no = batch.batch_no; + let row = ''; + + if (i !== 0 && !this.batch_exists(batch_no)) { + row = this.frm.add_child("items", { + 'item_code': this.item.item_code, + 'item_name': this.item.item_name, + 'price_list_rate': this.item.price_list_rate, + 'rate': this.item.rate, + 'qty': batch.selected_qty, + 'batch_no': batch_no, + 'actual_qty': this.item.actual_qty, + 'discount_percentage': this.item.discount_percentage + }); + } else { + row = this.frm.doc.items.find(i => i.batch_no === batch_no); + } + + if (!row) { + row = this.item; + } + this.map_row_values(row, batch, 'batch_no', 'selected_qty', this.values.warehouse); }); } else { this.map_row_values(this.item, this.values, 'serial_no', 'qty'); } + refresh_field("items"); this.callback && this.callback(this.item); }, + batch_exists: function(batch) { + const batches = this.frm.doc.items.map(data => data.batch_no); + return (batches && in_list(batches, batch)) ? true : false; + }, + map_row_values: function(row, values, number, qty_field, warehouse) { row.qty = values[qty_field]; row[number] = values[number]; @@ -185,20 +216,15 @@ erpnext.SerialNoBatchSelector = Class.extend({ } }, - bind_qty: function() { - let batches_field = this.dialog.fields_dict.batches; + update_total_qty: function() { let qty_field = this.dialog.fields_dict.qty; - if(batches_field) { - batches_field.grid.wrapper.on('change', function() { - let total_qty = 0; - batches_field.grid.wrapper.find( - 'input[data-fieldname="selected_qty"]').each(function() { + let total_qty = 0; - total_qty += Number($(this).val()); - }); - qty_field.set_input(total_qty); - }); - } + this.dialog.fields_dict.batches.df.data.forEach(data => { + total_qty += flt(data.selected_qty); + }); + + qty_field.set_input(total_qty); }, get_batch_fields: function() { @@ -230,7 +256,10 @@ erpnext.SerialNoBatchSelector = Class.extend({ if(row === this.grid_row) { return ""; } - return row.on_grid_fields_dict.batch_no.get_value(); + + if (row.on_grid_fields_dict.batch_no) { + return row.on_grid_fields_dict.batch_no.get_value(); + } }); if(selected_batches.includes(val)) { this.set_value(""); @@ -293,6 +322,8 @@ erpnext.SerialNoBatchSelector = Class.extend({ } else { this.grid.refresh(); } + + me.update_total_qty(); } }, ], diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index a027a98656..6d8d1b74c4 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -9,7 +9,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) { }); frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => { - if (r && r.use_pos_in_offline_mode && !cint(r.use_pos_in_offline_mode)) { + if (r && !cint(r.use_pos_in_offline_mode)) { // online wrapper.pos = new erpnext.pos.PointOfSale(wrapper); window.cur_pos = wrapper.pos; @@ -23,7 +23,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) { frappe.pages['point-of-sale'].refresh = function(wrapper) { if (wrapper.pos) { - cur_frm = wrapper.pos.frm; + wrapper.pos.make_new_invoice(); } if (frappe.flags.is_offline) { @@ -96,8 +96,8 @@ erpnext.pos.PointOfSale = class PointOfSale { wrapper: this.wrapper.find('.cart-container'), events: { on_customer_change: (customer) => this.frm.set_value('customer', customer), - on_field_change: (item_code, field, value) => { - this.update_item_in_cart(item_code, field, value); + on_field_change: (item_code, field, value, batch_no) => { + this.update_item_in_cart(item_code, field, value, batch_no); }, on_numpad: (value) => { if (value == 'Pay') { @@ -158,10 +158,12 @@ erpnext.pos.PointOfSale = class PointOfSale { }); } - update_item_in_cart(item_code, field='qty', value=1) { + update_item_in_cart(item_code, field='qty', value=1, batch_no) { frappe.dom.freeze(); - if(this.cart.exists(item_code)) { - const item = this.frm.doc.items.find(i => i.item_code === item_code); + if(this.cart.exists(item_code, batch_no)) { + const search_field = batch_no ? 'batch_no' : 'item_code'; + const search_value = batch_no || item_code; + const item = this.frm.doc.items.find(i => i[search_field] === search_value); frappe.flags.hide_serial_batch_dialog = false; if (typeof value === 'string' && !in_list(['serial_no', 'batch_no'], field)) { @@ -219,29 +221,31 @@ erpnext.pos.PointOfSale = class PointOfSale { ]); } - select_batch_and_serial_no(item) { + select_batch_and_serial_no(row) { frappe.dom.unfreeze(); - erpnext.show_serial_batch_selector(this.frm, item, () => { - this.update_item_in_frm(item, 'qty', item.qty) - .then(() => { - // update cart - frappe.run_serially([ - () => { - if (item.qty === 0) { - frappe.model.clear_doc(item.doctype, item.name); - } - }, - () => this.update_cart_data(item) - ]); - }); + erpnext.show_serial_batch_selector(this.frm, row, () => { + this.frm.doc.items.forEach(item => { + this.update_item_in_frm(item, 'qty', item.qty) + .then(() => { + // update cart + frappe.run_serially([ + () => { + if (item.qty === 0) { + frappe.model.clear_doc(item.doctype, item.name); + } + }, + () => this.update_cart_data(item) + ]); + }); + }) }, () => { - this.on_close(item); + this.on_close(row); }, true); } on_close(item) { - if (!this.cart.exists(item.item_code) && item.qty) { + if (!this.cart.exists(item.item_code, item.batch_no) && item.qty) { frappe.model.clear_doc(item.doctype, item.name); } } @@ -492,6 +496,11 @@ erpnext.pos.PointOfSale = class PointOfSale { // // }).addClass('visible-xs'); + this.page.add_menu_item(__("Form View"), function () { + frappe.model.sync(me.frm.doc); + frappe.set_route("Form", me.frm.doc.doctype, me.frm.doc.name); + }); + this.page.add_menu_item(__("POS Profile"), function () { frappe.set_route('List', 'POS Profile'); }); @@ -602,11 +611,15 @@ class POSCart { this.customer_field.set_value(""); this.frm.msgbox = ""; + let total_item_qty = 0.0; + this.frm.set_value("pos_total_qty",total_item_qty); + this.$discount_amount.find('input:text').val(''); this.wrapper.find('.grand-total-value').text( format_currency(this.frm.doc.grand_total, this.frm.currency)); this.wrapper.find('.rounded-total-value').text( format_currency(this.frm.doc.rounded_total, this.frm.currency)); + this.$qty_total.find(".quantity-total").text(total_item_qty); const customer = this.frm.doc.customer; this.customer_field.set_value(customer); @@ -721,7 +734,7 @@ class POSCart { total_item_qty += d.qty; } }); - this.$qty_total.find('.quantity-total').text(total_item_qty) + this.$qty_total.find('.quantity-total').text(total_item_qty); this.frm.set_value("pos_total_qty",total_item_qty); } @@ -804,10 +817,11 @@ class POSCart { this.numpad.reset_value(); } else { const item_code = this.selected_item.attr('data-item-code'); + const batch_no = this.selected_item.attr('data-batch-no'); const field = this.selected_item.active_field; const value = this.numpad.get_value(); - this.events.on_field_change(item_code, field, value); + this.events.on_field_change(item_code, field, value, batch_no); } } @@ -835,7 +849,7 @@ class POSCart { add_item(item) { this.$empty_state.hide(); - if (this.exists(item.item_code)) { + if (this.exists(item.item_code, item.batch_no)) { // update quantity this.update_item(item); } else if (flt(item.qty) > 0.0) { @@ -848,7 +862,10 @@ class POSCart { } update_item(item) { - const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`); + const item_selector = item.batch_no ? + `[data-batch-no="${item.batch_no}"]` : `[data-item-code="${item.item_code}"]`; + + const $item = this.$cart_items.find(item_selector); if(item.qty > 0) { const is_stock_item = this.get_item_details(item.item_code).is_stock_item; @@ -870,7 +887,8 @@ class POSCart { const rate = format_currency(item.rate, this.frm.doc.currency); const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red'; return ` -
+
${item.item_name}
@@ -911,8 +929,11 @@ class POSCart { return this.item_data[item_code]; } - exists(item_code) { - let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`); + exists(item_code, batch_no) { + const is_exists = batch_no ? + `[data-batch-no="${batch_no}"]` : `[data-item-code="${item_code}"]`; + + let $item = this.$cart_items.find(is_exists); return $item.length > 0; } diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index dff65afca3..edd594afb8 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -88,7 +88,7 @@ erpnext.stock.ItemDashboard = Class.extend({ if(!data) data = []; data.forEach(function(d) { - d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production; + d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract; d.pending_qty = 0; d.total_reserved = d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract; if(d.actual_or_pending > d.actual_qty) { diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index acff76dc95..efa6c14a00 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -116,14 +116,14 @@ class Bin(Document): se.docstatus=1 and se.purpose='Subcontract' and ifnull(se.purchase_order, '') !='' - and sed.item_code = %s + and (sed.item_code = %(item)s or sed.original_item = %(item)s) and se.name = sed.parent and se.purchase_order = po.name and po.docstatus = 1 and po.is_subcontracted = 'Yes' and po.status != 'Closed' and po.per_received < 100 - """, (self.item_code))[0][0] + """, {'item': self.item_code})[0][0] if reserved_qty_for_sub_contract > materials_transferred: reserved_qty_for_sub_contract = reserved_qty_for_sub_contract - materials_transferred diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index cd343fbbdf..1a57a099a3 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -456,7 +456,8 @@ $.extend(erpnext.item, { ], fields: ["attribute_value"], limit_start: 0, - limit_page_length: 500 + limit_page_length: 500, + parent: "Item" } }).then((r) => { if(r.message) { @@ -598,7 +599,8 @@ $.extend(erpnext.item, { ["parent","=", i], ["attribute_value", "like", term + "%"] ], - fields: ["attribute_value"] + fields: ["attribute_value"], + parent: "Item" }, callback: function(r) { if (r.message) { diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 1866d8f223..a6137f9dbd 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -366,6 +366,37 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Alternative Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3686,7 +3717,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2018-03-09 03:13:18.516087", + "modified": "2018-03-12 03:13:18.516087", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py index 21608a6d97..c571355cad 100644 --- a/erpnext/stock/doctype/item/item_dashboard.py +++ b/erpnext/stock/doctype/item/item_dashboard.py @@ -15,7 +15,7 @@ def get_data(): 'transactions': [ { 'label': _('Groups'), - 'items': ['BOM', 'Product Bundle'] + 'items': ['BOM', 'Product Bundle', 'Item Alternative'] }, { 'label': _('Pricing'), diff --git a/erpnext/stock/doctype/item_alternative/__init__.py b/erpnext/stock/doctype/item_alternative/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.js b/erpnext/stock/doctype/item_alternative/item_alternative.js new file mode 100644 index 0000000000..ef0a88b9c8 --- /dev/null +++ b/erpnext/stock/doctype/item_alternative/item_alternative.js @@ -0,0 +1,14 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Item Alternative', { + setup: function(frm) { + frm.fields_dict.item_code.get_query = () => { + return { + filters: { + 'allow_alternative_item': 1 + } + }; + }; + } +}); diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.json b/erpnext/stock/doctype/item_alternative/item_alternative.json new file mode 100644 index 0000000000..a13e307281 --- /dev/null +++ b/erpnext/stock/doctype/item_alternative/item_alternative.json @@ -0,0 +1,292 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-02-26 17:39:11.249778", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "item_code", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Item Code", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "alternative_item_code", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Alternative Item Code", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "two_way", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Two-way", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "item_name", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Item Name", + "length": 0, + "no_copy": 0, + "options": "item_code.item_name", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "alternative_item_name", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Alternative Item Name", + "length": 0, + "no_copy": 0, + "options": "alternative_item_code.item_name", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-03-07 16:08:08.097107", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Alternative", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Item Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "item_code", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py new file mode 100644 index 0000000000..6f9c5de3ca --- /dev/null +++ b/erpnext/stock/doctype/item_alternative/item_alternative.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document + +class ItemAlternative(Document): + def validate(self): + self.has_alternative_item() + self.validate_alternative_item() + self.validate_duplicate() + + def has_alternative_item(self): + if (self.item_code and + not frappe.db.get_value('Item', self.item_code, 'allow_alternative_item')): + frappe.throw(_("Not allow to set alternative item for the item {0}").format(self.item_code)) + + def validate_alternative_item(self): + if self.item_code == self.alternative_item_code: + frappe.throw(_("Alternative item must not be same as item code")) + + def validate_duplicate(self): + if frappe.db.get_value("Item Alternative", {'item_code': self.item_code, + 'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}): + frappe.throw(_("Already record exists for the item {0}".format(self.item_code))) + +def get_alternative_items(doctype, txt, searchfield, start, page_len, filters): + return frappe.db.sql(""" (select alternative_item_code from `tabItem Alternative` + where item_code = %(item_code)s and alternative_item_code like %(txt)s) + union + (select item_code from `tabItem Alternative` + where alternative_item_code = %(item_code)s and item_code like %(txt)s + and two_way = 1) limit {0}, {1} + """.format(start, page_len), { + "item_code": frappe.db.escape(filters.get('item_code')), + "txt": "%%%s%%" % frappe.db.escape(txt) + }) \ No newline at end of file diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.js b/erpnext/stock/doctype/item_alternative/test_item_alternative.js new file mode 100644 index 0000000000..87318499fe --- /dev/null +++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Item Alternative", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Item Alternative + () => frappe.tests.make('Item Alternative', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py new file mode 100644 index 0000000000..d5700fe514 --- /dev/null +++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import frappe, json +from frappe.utils import flt +from erpnext.stock.doctype.item.test_item import create_item +from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order +from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry +from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation +from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record +from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_rm_stock_entry +import unittest + +class TestItemAlternative(unittest.TestCase): + def setUp(self): + make_items() + + def test_alternative_item_for_subcontract_rm(self): + create_stock_reconciliation(item_code='Alternate Item For A RW 1', warehouse='_Test Warehouse - _TC', + qty=5, rate=2000) + create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC', + qty=5, rate=2000) + + supplier_warehouse = "Test Supplier Warehouse - _TC" + po = create_purchase_order(item = "Test Finished Goods - A", + is_subcontracted='Yes', qty=5, rate=3000, supplier_warehouse=supplier_warehouse) + + rm_item = [{"item_code": "Test Finished Goods - A", "rm_item_code": "Test FG A RW 1", "item_name":"Test FG A RW 1", + "qty":5, "warehouse":"_Test Warehouse - _TC", "rate":2000, "amount":10000, "stock_uom":"Nos"}, + {"item_code": "Test Finished Goods - A", "rm_item_code": "Test FG A RW 2", "item_name":"Test FG A RW 2", + "qty":5, "warehouse":"_Test Warehouse - _TC", "rate":2000, "amount":10000, "stock_uom":"Nos"}] + + rm_item_string = json.dumps(rm_item) + reserved_qty_for_sub_contract = frappe.db.get_value('Bin', + {'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_sub_contract') + + se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string)) + se.to_warehouse = supplier_warehouse + se.insert() + + doc = frappe.get_doc('Stock Entry', se.name) + for item in doc.items: + if item.item_code == 'Test FG A RW 1': + item.item_code = 'Alternate Item For A RW 1' + item.item_name = 'Alternate Item For A RW 1' + item.description = 'Alternate Item For A RW 1' + item.original_item = 'Test FG A RW 1' + + doc.save() + doc.submit() + after_transfer_reserved_qty_for_sub_contract = frappe.db.get_value('Bin', + {'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_sub_contract') + + self.assertEqual(after_transfer_reserved_qty_for_sub_contract, flt(reserved_qty_for_sub_contract - 5)) + + pr = make_purchase_receipt(po.name) + pr.save() + + pr = frappe.get_doc('Purchase Receipt', pr.name) + status = False + for d in pr.supplied_items: + if d.rm_item_code == 'Alternate Item For A RW 1': + status = True + + self.assertEqual(status, True) + + def test_alternative_item_for_production_rm(self): + create_stock_reconciliation(item_code='Alternate Item For A RW 1', + warehouse='_Test Warehouse - _TC',qty=5, rate=2000) + create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC', + qty=5, rate=2000) + pro_order = make_wo_order_test_record(production_item='Test Finished Goods - A', + qty=5, source_warehouse='_Test Warehouse - _TC', wip_warehouse='Test Supplier Warehouse - _TC') + + reserved_qty_for_production = frappe.db.get_value('Bin', + {'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_production') + + ste = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 5)) + ste.insert() + + for item in ste.items: + if item.item_code == 'Test FG A RW 1': + item.item_code = 'Alternate Item For A RW 1' + item.item_name = 'Alternate Item For A RW 1' + item.description = 'Alternate Item For A RW 1' + item.original_item = 'Test FG A RW 1' + + ste.submit() + reserved_qty_for_production_after_transfer = frappe.db.get_value('Bin', + {'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_production') + + self.assertEqual(reserved_qty_for_production_after_transfer, flt(reserved_qty_for_production - 5)) + ste1 = frappe.get_doc(make_stock_entry(pro_order.name, "Manufacture", 5)) + + status = False + for d in ste1.items: + if d.item_code == 'Alternate Item For A RW 1': + status = True + + self.assertEqual(status, True) + ste1.submit() + +def make_items(): + items = ['Test Finished Goods - A', 'Test FG A RW 1', 'Test FG A RW 2', 'Alternate Item For A RW 1'] + for item_code in items: + if not frappe.db.exists('Item', item_code): + create_item(item_code) + + create_stock_reconciliation(item_code="Test FG A RW 1", + warehouse='_Test Warehouse - _TC', qty=10, rate=2000) + + if frappe.db.exists('Item', 'Test FG A RW 1'): + doc = frappe.get_doc('Item', 'Test FG A RW 1') + doc.allow_alternative_item = 1 + doc.save() + + if frappe.db.exists('Item', 'Test Finished Goods - A'): + doc = frappe.get_doc('Item', 'Test Finished Goods - A') + doc.is_sub_contracted_item = 1 + doc.save() + + if not frappe.db.get_value('BOM', + {'item': 'Test Finished Goods - A', 'docstatus': 1}): + make_bom(item = 'Test Finished Goods - A', raw_materials = ['Test FG A RW 1', 'Test FG A RW 2']) + + if not frappe.db.get_value('Warehouse', {'warehouse_name': 'Test Supplier Warehouse'}): + frappe.get_doc({ + 'doctype': 'Warehouse', + 'warehouse_name': 'Test Supplier Warehouse', + 'company': '_Test Company' + }).insert(ignore_permissions=True) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index a14948142f..0979fe7d09 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -120,6 +120,25 @@ frappe.ui.form.on('Stock Entry', { }); } + if(frm.doc.items) { + const has_alternative = frm.doc.items.find(i => i.allow_alternative_item === 1); + + if (frm.doc.docstatus == 0 && has_alternative) { + frm.add_custom_button(__('Alternate Item'), () => { + erpnext.utils.select_alternate_items({ + frm: frm, + child_docname: "items", + warehouse_field: "s_warehouse", + child_doctype: "Stock Entry Detail", + original_item_field: "original_item", + condition: (d) => { + if (d.s_warehouse && d.allow_alternative_item) {return true;} + } + }) + }); + } + } + if (frm.doc.docstatus===0) { frm.add_custom_button(__('Purchase Invoice'), function() { erpnext.utils.map_current_doc({ diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4f9cefe5d1..7ef8ab0a00 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -400,9 +400,10 @@ class StockEntry(StockController): if self.purpose == "Subcontract" and self.purchase_order: purchase_order = frappe.get_doc("Purchase Order", self.purchase_order) for se_item in self.items: + item_code = se_item.original_item or se_item.item_code precision = cint(frappe.db.get_default("float_precision")) or 3 total_allowed = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ - if d.rm_item_code == se_item.item_code]) + if d.rm_item_code == item_code]) if not total_allowed: frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}") .format(se_item.item_code, self.purchase_order)) @@ -421,7 +422,8 @@ class StockEntry(StockController): def validate_bom(self): for d in self.get('items'): if d.bom_no and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse): - validate_bom_no(d.item_code, d.bom_no) + item_code = d.original_item or d.item_code + validate_bom_no(item_code, d.bom_no) def validate_finished_goods(self): """validation: finished good quantity should be same as manufacturing quantity""" @@ -608,7 +610,6 @@ class StockEntry(StockController): item_dict = self.get_bom_raw_materials(self.fg_completed_qty) #Get PO Supplied Items Details - print('Purchase Order', self.purchase_order, self.purpose) if self.purchase_order and self.purpose == "Subcontract": #Get PO Supplied Items Details item_wh = frappe._dict(frappe.db.sql(""" @@ -695,9 +696,23 @@ class StockEntry(StockController): item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded = self.use_multi_level_bom) + used_alternative_items = get_used_alternative_items(work_order = self.work_order) for item in item_dict.values(): # if source warehouse presents in BOM set from_warehouse as bom source_warehouse + if item["allow_alternative_item"]: + item["allow_alternative_item"] = frappe.db.get_value('Work Order', + self.work_order, "allow_alternative_item") + item.from_warehouse = self.from_warehouse or item.source_warehouse or item.default_warehouse + if item.item_code in used_alternative_items: + alternative_item_data = used_alternative_items.get(item.item_code) + item.item_code = alternative_item_data.item_code + item.item_name = alternative_item_data.item_name + item.stock_uom = alternative_item_data.stock_uom + item.uom = alternative_item_data.uom + item.conversion_factor = alternative_item_data.conversion_factor + item.description = alternative_item_data.description + return item_dict def get_bom_scrap_material(self, qty): @@ -805,16 +820,19 @@ class StockEntry(StockController): wip_warehouse = pro_order.wip_warehouse else: wip_warehouse = None - + for d in pro_order.get("required_items"): if flt(d.required_qty) > flt(d.transferred_qty): item_row = d.as_dict() if d.source_warehouse and not frappe.db.get_value("Warehouse", d.source_warehouse, "is_group"): item_row["from_warehouse"] = d.source_warehouse - + item_row["to_warehouse"] = wip_warehouse + if item_row["allow_alternative_item"]: + item_row["allow_alternative_item"] = pro_order.allow_alternative_item + item_dict.setdefault(d.item_code, item_row) - + return item_dict def add_to_stock_entry_detail(self, item_dict, bom_no=None): @@ -827,7 +845,7 @@ class StockEntry(StockController): se_child = self.append('items') se_child.s_warehouse = item_dict[d].get("from_warehouse") se_child.t_warehouse = item_dict[d].get("to_warehouse") - se_child.item_code = cstr(d) + se_child.item_code = item_dict[d].get('item_code') or cstr(d) se_child.item_name = item_dict[d]["item_name"] se_child.description = item_dict[d]["description"] se_child.uom = stock_uom @@ -835,6 +853,7 @@ class StockEntry(StockController): se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty")) se_child.expense_account = item_dict[d].get("expense_account") or expense_account se_child.cost_center = item_dict[d].get("cost_center") or cost_center + se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0) if item_dict[d].get("idx"): se_child.idx = item_dict[d].get("idx") @@ -882,8 +901,9 @@ class StockEntry(StockController): #Update reserved sub contracted quantity in bin based on Supplied Item Details for d in self.get("items"): - reserve_warehouse = item_wh.get(d.item_code) - stock_bin = get_bin(d.item_code, reserve_warehouse) + item_code = d.get('original_item') or d.get('item_code') + reserve_warehouse = item_wh.get(item_code) + stock_bin = get_bin(item_code, reserve_warehouse) stock_bin.update_reserved_qty_for_sub_contracting() @frappe.whitelist() @@ -976,6 +996,30 @@ def get_operating_cost_per_unit(work_order=None, bom_no=None): return operating_cost_per_unit +def get_used_alternative_items(purchase_order=None, work_order=None): + cond = "" + + if purchase_order: + cond = "and ste.purpose = 'Subcontract' and ste.purchase_order = '{0}'".format(purchase_order) + elif work_order: + cond = "and ste.purpose = 'Material Transfer for Manufacture' and ste.work_order = '{0}'".format(work_order) + + if not cond: return {} + + used_alternative_items = {} + data = frappe.db.sql(""" select sted.original_item, sted.uom, sted.conversion_factor, + sted.item_code, sted.item_name, sted.conversion_factor,sted.stock_uom, sted.description + from + `tabStock Entry` ste, `tabStock Entry Detail` sted + where + sted.parent = ste.name and ste.docstatus = 1 and sted.original_item != sted.item_code + {0} """.format(cond), as_dict=1) + + for d in data: + used_alternative_items[d.original_item] = d + + return used_alternative_items + @frappe.whitelist() def get_uom_details(item_code, uom, qty): """Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}` diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 0f8bd17b10..5fdab6f8f3 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -41,6 +41,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -70,6 +71,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -102,6 +104,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -130,6 +133,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -162,6 +166,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -190,6 +195,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -222,6 +228,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -250,6 +257,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -279,6 +287,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -309,6 +318,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -341,6 +351,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "300px" }, @@ -371,6 +382,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -401,6 +413,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -432,6 +445,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -461,6 +475,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -492,6 +507,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -524,6 +540,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -555,6 +572,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -586,6 +604,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -618,6 +637,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -649,6 +669,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -677,6 +698,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -709,6 +731,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -740,6 +763,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -772,6 +796,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -803,6 +828,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -834,6 +860,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -866,6 +893,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -895,6 +923,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -926,6 +955,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -954,6 +984,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -986,6 +1017,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1018,6 +1050,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1047,6 +1080,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1078,6 +1112,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1106,6 +1141,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1138,6 +1174,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1167,6 +1204,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1197,6 +1235,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1228,6 +1267,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1259,6 +1299,39 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "s_warehouse", + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Alternative Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1287,6 +1360,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1318,6 +1392,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1348,6 +1423,39 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "original_item", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Original Item", + "length": 0, + "no_copy": 1, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1361,7 +1469,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-02-16 20:19:57.471380", + "modified": "2018-03-05 13:09:25.849700", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 067acc79c4..552d284b23 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -320,7 +320,7 @@ def get_price_list_rate(args, item_doc, out): out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \ / flt(args.conversion_rate) if not args.price_list_uom_dependant: - out.price_list_rate = flt(out.price_list_rate * (args.conversion_factor or 1.0)) + out.price_list_rate = flt(out.price_list_rate * (flt(args.conversion_factor) or 1.0)) if not out.price_list_rate and args.transaction_type=="buying": from erpnext.stock.doctype.item.item import get_last_purchase_details diff --git a/erpnext/stock/report/item_variant_details/item_variant_details.py b/erpnext/stock/report/item_variant_details/item_variant_details.py index f1488f8eff..7379eb4e50 100644 --- a/erpnext/stock/report/item_variant_details/item_variant_details.py +++ b/erpnext/stock/report/item_variant_details/item_variant_details.py @@ -14,10 +14,15 @@ def get_data(item): if not item: return [] item_dicts = [] + variants = None variant_results = frappe.db.sql("""select name from `tabItem` where variant_of = %s""", item, as_dict=1) - variants = ",".join(['"' + frappe.db.escape(variant['name']) + '"' for variant in variant_results]) + if not variant_results: + frappe.msgprint(_("There isn't any item variant for the selected item")) + return [] + else: + variants = ",".join(['"' + frappe.db.escape(variant['name']) + '"' for variant in variant_results]) order_count_map = get_open_sales_orders_map(variants) stock_details_map = get_stock_details_map(variants)