diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 3bad2f081ef..9883ba30f6d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -147,7 +147,7 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) { cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) { return{ query: "erpnext.controllers.queries.item_query", - filters: [["Item", "name", "!=", doc.item]] + filters: [["Item", "name", "!=", cur_frm.doc.item]] } } diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5f85ef91a8c..68274d1b425 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -49,7 +49,7 @@ class StockEntry(StockController): self.validate_finished_goods() self.validate_with_material_request() self.validate_batch() - + self.set_actual_qty() self.calculate_rate_and_amount() @@ -212,10 +212,10 @@ class StockEntry(StockController): if fg_qty_already_entered >= qty: frappe.throw(_("Stock Entries already created for Production Order ") + self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError) - + def set_actual_qty(self): allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) - + for d in self.get('items'): previous_sle = get_previous_sle({ "item_code": d.item_code, @@ -232,7 +232,7 @@ class StockEntry(StockController): frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}. Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError) - + def get_stock_and_rate(self): self.set_actual_qty() self.calculate_rate_and_amount() @@ -244,7 +244,7 @@ class StockEntry(StockController): self.validate_valuation_rate() self.set_total_incoming_outgoing_value() self.set_total_amount() - + def set_basic_rate(self, force=False): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 @@ -269,9 +269,9 @@ class StockEntry(StockController): d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: raw_material_cost += flt(d.basic_amount) - + self.set_basic_rate_for_finished_goods(raw_material_cost) - + def set_basic_rate_for_finished_goods(self, raw_material_cost): if self.purpose in ["Manufacture", "Repack"]: number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) @@ -279,11 +279,11 @@ class StockEntry(StockController): if d.bom_no or (d.t_warehouse and number_of_fg_items == 1): d.basic_rate = flt(raw_material_cost / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt(raw_material_cost, d.precision("basic_amount")) - + def distribute_additional_costs(self): if self.purpose == "Material Issue": self.additional_costs = [] - + self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")]) total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse]) @@ -292,11 +292,11 @@ class StockEntry(StockController): d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs else: d.additional_cost = 0 - + def update_valuation_rate(self): for d in self.get("items"): d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount")) - d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty), + d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty), d.precision("valuation_rate")) def validate_valuation_rate(self): @@ -373,7 +373,7 @@ class StockEntry(StockController): def update_stock_ledger(self): sl_entries = [] - for d in self.get('items'): + for d in self.get('items'): if cstr(d.s_warehouse) and self.docstatus == 1: sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.s_warehouse), @@ -399,15 +399,15 @@ class StockEntry(StockController): })) self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No') - + def get_gl_entries(self, warehouse_account): expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - + gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account) - + for d in self.get("items"): additional_cost = flt(d.additional_cost, d.precision("additional_cost")) - if additional_cost: + if additional_cost: gl_entries.append(self.get_gl_dict({ "account": expenses_included_in_valuation, "against": d.expense_account, @@ -415,7 +415,7 @@ class StockEntry(StockController): "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": additional_cost })) - + gl_entries.append(self.get_gl_dict({ "account": d.expense_account, "against": expenses_included_in_valuation, @@ -521,7 +521,7 @@ class StockEntry(StockController): def get_items(self): self.set('items', []) self.validate_production_order() - + if not self.posting_date or not self.posting_time: frappe.throw(_("Posting date and posting time is mandatory")) @@ -548,7 +548,7 @@ class StockEntry(StockController): for item in item_dict.values(): item["to_warehouse"] = self.pro_doc.wip_warehouse self.add_to_stock_entry_detail(item_dict) - + elif self.production_order and self.purpose == "Manufacture" and \ frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")== "Material Transferred for Manufacture": self.get_transfered_raw_materials() @@ -578,10 +578,14 @@ class StockEntry(StockController): to_warehouse = self.pro_doc.fg_warehouse else: item_code = frappe.db.get_value("BOM", self.bom_no, "item") - to_warehouse = "" + to_warehouse = self.to_warehouse item = frappe.db.get_value("Item", item_code, ["item_name", - "description", "stock_uom", "expense_account", "buying_cost_center", "name"], as_dict=1) + "description", "stock_uom", "expense_account", "buying_cost_center", "name", "default_warehouse"], as_dict=1) + + if not self.production_order and not to_warehouse: + # in case of BOM + to_warehouse = item.default_warehouse self.add_to_stock_entry_detail({ item.name: { @@ -600,57 +604,57 @@ class StockEntry(StockController): from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict # item dict = { item_code: {qty, description, stock_uom} } - item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, + item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded = self.use_multi_level_bom) for item in item_dict.values(): item.from_warehouse = self.from_warehouse or item.default_warehouse return item_dict - + def get_transfered_raw_materials(self): transferred_materials = frappe.db.sql(""" - select - item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse, - description, stock_uom, expense_account, cost_center - from `tabStock Entry` se,`tabStock Entry Detail` sed - where - se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' - and se.production_order= %s and ifnull(sed.t_warehouse, '') != '' + select + item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse, + description, stock_uom, expense_account, cost_center + from `tabStock Entry` se,`tabStock Entry Detail` sed + where + se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' + and se.production_order= %s and ifnull(sed.t_warehouse, '') != '' group by sed.item_code, sed.t_warehouse """, self.production_order, as_dict=1) - + materials_already_backflushed = frappe.db.sql(""" - select - item_code, sed.s_warehouse as warehouse, sum(qty) as qty - from - `tabStock Entry` se, `tabStock Entry Detail` sed - where - se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture' - and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' + select + item_code, sed.s_warehouse as warehouse, sum(qty) as qty + from + `tabStock Entry` se, `tabStock Entry Detail` sed + where + se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture' + and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' group by sed.item_code, sed.s_warehouse """, self.production_order, as_dict=1) - + backflushed_materials= {} for d in materials_already_backflushed: backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty}) - + po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from `tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0] manufacturing_qty = flt(po_qty.qty) produced_qty = flt(po_qty.produced_qty) trans_qty = flt(po_qty.material_transferred_for_manufacturing) - + for item in transferred_materials: qty= item.qty - + if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)): qty = (qty/trans_qty) * flt(self.fg_completed_qty) - + elif backflushed_materials.get(item.item_code): for d in backflushed_materials.get(item.item_code): if d.get(item.warehouse): qty-= d.get(item.warehouse) - + if qty > 0: self.add_to_stock_entry_detail({ item.item_code: { @@ -729,7 +733,7 @@ class StockEntry(StockController): se_child.s_warehouse = self.from_warehouse if se_child.t_warehouse==None: se_child.t_warehouse = self.to_warehouse - + # in stock uom se_child.transfer_qty = flt(item_dict[d]["qty"]) se_child.conversion_factor = 1.00 @@ -761,7 +765,7 @@ class StockEntry(StockController): def get_production_order_details(production_order): production_order = frappe.get_doc("Production Order", production_order) pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_qty) - + return { "from_bom": 1, "bom_no": production_order.bom_no, @@ -771,7 +775,7 @@ def get_production_order_details(production_order): "fg_completed_qty": pending_qty_to_produce, "additional_costs": get_additional_costs(production_order, fg_qty=pending_qty_to_produce) } - + def get_additional_costs(production_order=None, bom_no=None, fg_qty=None): additional_costs = [] operating_cost_per_unit = get_operating_cost_per_unit(production_order, bom_no) @@ -780,33 +784,33 @@ def get_additional_costs(production_order=None, bom_no=None, fg_qty=None): "description": "Operating Cost as per Production Order / BOM", "amount": operating_cost_per_unit * flt(fg_qty) }) - + if production_order and production_order.additional_operating_cost: additional_operating_cost_per_unit = \ flt(production_order.additional_operating_cost) / flt(production_order.qty) - + additional_costs.append({ "description": "Additional Operating Cost", "amount": additional_operating_cost_per_unit * flt(fg_qty) }) - + return additional_costs - + def get_operating_cost_per_unit(production_order=None, bom_no=None): operating_cost_per_unit = 0 if production_order: if not bom_no: bom_no = production_order.bom_no - + for d in production_order.get("operations"): if flt(d.completed_qty): operating_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty) else: operating_cost_per_unit += flt(d.planned_operating_cost) / flt(production_order.qty) - + # Get operating cost from BOM if not found in production_order. if not operating_cost_per_unit and bom_no: bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1) operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity) - - return operating_cost_per_unit \ No newline at end of file + + return operating_cost_per_unit