From f5279a0d6753257d17b80640224cac169ecf950f Mon Sep 17 00:00:00 2001 From: pawan <pawan@erpnext.com> Date: Fri, 24 Nov 2017 11:21:47 +0530 Subject: [PATCH] [fix] #8427 --- .../doctype/purchase_order/purchase_order.py | 15 ++++++++- erpnext/stock/dashboard/item_dashboard.js | 2 +- erpnext/stock/dashboard/item_dashboard.py | 5 +-- erpnext/stock/doctype/bin/bin.json | 32 ++++++++++++++++++- erpnext/stock/doctype/bin/bin.py | 22 +++++++++++++ .../stock/doctype/stock_entry/stock_entry.py | 27 ++++++++++++++++ .../stock/page/stock_balance/stock_balance.js | 1 + erpnext/stock/stock_balance.py | 2 +- 8 files changed, 100 insertions(+), 6 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index bbaa043208..038683090b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -13,7 +13,7 @@ from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty from frappe.desk.notifications import clear_doctype_notifications from erpnext.buying.utils import (validate_for_items, check_for_closed_status, update_last_purchase_rate) - +from erpnext.stock.utils import get_bin form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -187,6 +187,8 @@ class PurchaseOrder(BuyingController): self.update_prevdoc_status() self.update_requested_qty() self.update_ordered_qty() + if self.is_subcontracted == "Yes": + self.update_reserved_qty_for_subcontract() frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total) @@ -249,6 +251,17 @@ class PurchaseOrder(BuyingController): if item.delivered_by_supplier == 1: item.received_qty = item.qty + def update_reserved_qty_for_subcontract(self): + items = list(set([d.rm_item_code for d in self.get("supplied_items")])) + item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse + from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items)) + + for d in self.supplied_items: + if d.rm_item_code: + warehouse = item_wh.get(d.rm_item_code) + stock_bin = get_bin(d.rm_item_code, warehouse) + stock_bin.update_reserved_qty_for_sub_contracting(self.name, transferred_qty=0, transaction_type = "Reserve") + @frappe.whitelist() def close_or_unclose_purchase_orders(names, status): if not frappe.has_permission("Purchase Order", "write"): diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 3c334c46b6..23820d32b2 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -89,7 +89,7 @@ erpnext.stock.ItemDashboard = Class.extend({ data.forEach(function(d) { d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production; d.pending_qty = 0; - d.total_reserved = d.reserved_qty + d.reserved_qty_for_production; + 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) { d.pending_qty = d.actual_or_pending - d.actual_qty; } diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py index 0d75a9a544..f95daafd38 100644 --- a/erpnext/stock/dashboard/item_dashboard.py +++ b/erpnext/stock/dashboard/item_dashboard.py @@ -26,13 +26,14 @@ def get_data(item_code=None, warehouse=None, item_group=None, return frappe.db.sql(''' select b.item_code, b.warehouse, b.projected_qty, b.reserved_qty, - b.reserved_qty_for_production, b.actual_qty, b.valuation_rate, i.item_name + b.reserved_qty_for_production, b.reserved_qty_for_sub_contract, b.actual_qty, b.valuation_rate, i.item_name from tabBin b, tabItem i where b.item_code = i.name and - (b.projected_qty != 0 or b.reserved_qty != 0 or b.reserved_qty_for_production != 0 or b.actual_qty != 0) + (b.projected_qty != 0 or b.reserved_qty != 0 or b.reserved_qty_for_production != 0 + or b.reserved_qty_for_sub_contract != 0 or b.actual_qty != 0) {conditions} order by {sort_by} {sort_order} diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json index 1f6e9e1785..def817b65d 100644 --- a/erpnext/stock/doctype/bin/bin.json +++ b/erpnext/stock/doctype/bin/bin.json @@ -296,6 +296,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reserved_qty_for_sub_contract", + "fieldtype": "Float", + "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": "Reserved Qty for sub contract", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -463,7 +493,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-13 13:06:32.601505", + "modified": "2017-11-22 08:14:30.615638", "modified_by": "Administrator", "module": "Stock", "name": "Bin", diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 626a9db97f..b3e89dc78d 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -94,6 +94,28 @@ class Bin(Document): self.db_set('reserved_qty_for_production', self.reserved_qty_for_production) self.db_set('projected_qty', self.projected_qty) + def update_reserved_qty_for_sub_contracting(self, po_name, transferred_qty, transaction_type): + #Update Reserved Quantity for Sub Contracting in Bin + if transaction_type == "Reserve": + required_qty = frappe.db.sql('''select sum(itemsup.required_qty) + from `tabItem` item, `tabPurchase Order` po, `tabPurchase Order Item Supplied` itemsup + where + item.name = itemsup.rm_item_code + and po.name = %s + and itemsup.rm_item_code = %s + and itemsup.parent = po.name + and po.docstatus = 1 + and po.is_subcontracted = 'Yes' + and item.default_warehouse = %s''', (po_name, self.item_code, self.warehouse))[0][0] + elif transaction_type == "Transfer": + required_qty = 0 + + reserved_qty_bin = self.reserved_qty_for_sub_contract + reserved_qty_for_sub_contract = reserved_qty_bin + required_qty - transferred_qty + + self.set_projected_qty() + self.db_set('reserved_qty_for_sub_contract', reserved_qty_for_sub_contract) + self.db_set('projected_qty', self.projected_qty) def update_item_projected_qty(item_code): '''Set total_projected_qty in Item as sum of projected qty in all warehouses''' diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4d79e13d74..40e386ea67 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -11,6 +11,7 @@ from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor from erpnext.stock.doctype.batch.batch import get_batch_no, set_batch_nos from erpnext.manufacturing.doctype.bom.bom import validate_bom_no +from erpnext.stock.utils import get_bin import json class IncorrectValuationRateError(frappe.ValidationError): pass @@ -64,6 +65,8 @@ class StockEntry(StockController): update_serial_nos_after_submit(self, "items") self.update_production_order() self.validate_purchase_order() + if self.purchase_order and self.purpose == "Subcontract": + self.update_purchase_order_supplied_items() self.make_gl_entries() def on_cancel(self): @@ -803,6 +806,30 @@ class StockEntry(StockController): if getdate(self.posting_date) > getdate(expiry_date): frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code)) + def update_purchase_order_supplied_items(self): + materials_transferred = frappe._dict(frappe.db.sql(""" + select + concat(item_code, sed.s_warehouse), sum(qty) + from + `tabStock Entry` se, `tabStock Entry Detail` sed + where + se.name = sed.parent and se.docstatus=1 and se.purpose='Subcontract' + and se.purchase_order= %s and ifnull(sed.s_warehouse, '') != '' + group by sed.item_code, sed.s_warehouse + """, self.purchase_order)) + #Get PO Supplied Items Details + po_doc = frappe.get_doc("Purchase Order",self.purchase_order) + po_supplied_items = po_doc.get("supplied_items") + items = list(set([d.rm_item_code for d in po_supplied_items])) + item_wh = frappe._dict(frappe.db.sql("""select item_code as "item_code", default_warehouse as "warehouse" + from tabItem where name in ({0})""".format(", ".join(["%s"] * len(items))), items)) + #Update reserved sub contracted quantity in bin based on Supplied Item Details + for d in po_supplied_items: + warehouse = item_wh.get(d.rm_item_code) + transferred_qty = materials_transferred.get(d.rm_item_code + warehouse) + stock_bin = get_bin(d.rm_item_code, warehouse) + stock_bin.update_reserved_qty_for_sub_contracting(self.purchase_order, transferred_qty, transaction_type = "Transfer") + @frappe.whitelist() def get_production_order_details(production_order): production_order = frappe.get_doc("Production Order", production_order) diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index 16a85fa922..85ea5b19be 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -48,6 +48,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { {fieldname: 'projected_qty', label: __('Projected qty')}, {fieldname: 'reserved_qty', label: __('Reserved for sale')}, {fieldname: 'reserved_qty_for_production', label: __('Reserved for manufacturing')}, + {fieldname: 'reserved_qty_for_sub_contract', label: __('Reserved for sub contracting')}, {fieldname: 'actual_qty', label: __('Actual qty in stock')}, ] }, diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index 6a4ac439ee..49909d9c56 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -150,7 +150,7 @@ def update_bin_qty(item_code, warehouse, qty_dict=None): if mismatch: bin.projected_qty = (flt(bin.actual_qty) + flt(bin.ordered_qty) + flt(bin.indented_qty) + flt(bin.planned_qty) - flt(bin.reserved_qty) - - flt(bin.reserved_qty_for_production)) + - flt(bin.reserved_qty_for_production)) - flt(bin.reserved_qty_for_sub_contract) bin.save()