From eea2b34f4b69cdcdff269933de4ae76c5d859156 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 11 Oct 2013 18:31:33 +0530 Subject: [PATCH] [production] update planned and produced ty --- .../production_order/production_order.py | 11 +- .../production_order/test_production_order.py | 75 +++++++++++ patches/december_2012/repost_ordered_qty.py | 20 +-- patches/february_2013/repost_reserved_qty.py | 56 +-------- patches/october_2013/repost_planned_qty.py | 11 ++ patches/patch_list.py | 1 + stock/doctype/stock_entry/stock_entry.py | 78 +++++++----- stock/doctype/warehouse/warehouse.py | 93 +------------- stock/utils.py | 11 +- utilities/repost_stock.py | 119 ++++++++++++++++++ 10 files changed, 268 insertions(+), 207 deletions(-) create mode 100644 manufacturing/doctype/production_order/test_production_order.py create mode 100644 patches/october_2013/repost_planned_qty.py create mode 100644 utilities/repost_stock.py diff --git a/manufacturing/doctype/production_order/production_order.py b/manufacturing/doctype/production_order/production_order.py index 719d149e64..b29b0f1e72 100644 --- a/manufacturing/doctype/production_order/production_order.py +++ b/manufacturing/doctype/production_order/production_order.py @@ -18,6 +18,9 @@ class DocType: self.doclist = doclist def validate(self): + if self.doc.docstatus == 0: + self.doc.status = "Draft" + import utilities utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", "In Process", "Completed", "Cancelled"]) @@ -149,12 +152,6 @@ def get_item_details(item): @webnotes.whitelist() def make_stock_entry(production_order_id, purpose): production_order = webnotes.bean("Production Order", production_order_id) - - # validate already existing - ste = webnotes.conn.get_value("Stock Entry", { - "production_order":production_order_id, - "purpose": purpose - }, "name") stock_entry = webnotes.new_bean("Stock Entry") stock_entry.doc.purpose = purpose @@ -171,4 +168,4 @@ def make_stock_entry(production_order_id, purpose): stock_entry.doc.to_warehouse = production_order.doc.fg_warehouse stock_entry.run_method("get_items") - return [d.fields for d in stock_entry.doclist] + return [d.fields for d in stock_entry.doclist] \ No newline at end of file diff --git a/manufacturing/doctype/production_order/test_production_order.py b/manufacturing/doctype/production_order/test_production_order.py new file mode 100644 index 0000000000..9a75762ba4 --- /dev/null +++ b/manufacturing/doctype/production_order/test_production_order.py @@ -0,0 +1,75 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + + +from __future__ import unicode_literals +import unittest +import webnotes +from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory +from manufacturing.doctype.production_order.production_order import make_stock_entry + + +class TestProductionOrder(unittest.TestCase): + def test_planned_qty(self): + set_perpetual_inventory(0) + webnotes.conn.sql("delete from `tabStock Ledger Entry`") + webnotes.conn.sql("""delete from `tabBin`""") + webnotes.conn.sql("""delete from `tabGL Entry`""") + + pro_bean = webnotes.bean(copy = test_records[0]) + pro_bean.insert() + pro_bean.submit() + + from stock.doctype.stock_entry.test_stock_entry import test_records as se_test_records + mr1 = webnotes.bean(copy = se_test_records[0]) + mr1.insert() + mr1.submit() + + mr2 = webnotes.bean(copy = se_test_records[0]) + mr2.doclist[1].item_code = "_Test Item Home Desktop 100" + mr2.insert() + mr2.submit() + + stock_entry = make_stock_entry(pro_bean.doc.name, "Manufacture/Repack") + stock_entry = webnotes.bean(stock_entry) + + stock_entry.doc.fg_completed_qty = 4 + stock_entry.run_method("get_items") + stock_entry.submit() + + self.assertEqual(webnotes.conn.get_value("Production Order", pro_bean.doc.name, + "produced_qty"), 4) + self.assertEqual(webnotes.conn.get_value("Bin", {"item_code": "_Test FG Item", + "warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty"), 6) + + return pro_bean.doc.name + + def test_over_production(self): + from stock.doctype.stock_entry.stock_entry import StockOverProductionError + pro_order = self.test_planned_qty() + + stock_entry = make_stock_entry(pro_order, "Manufacture/Repack") + stock_entry = webnotes.bean(stock_entry) + + stock_entry.doc.fg_completed_qty = 15 + stock_entry.run_method("get_items") + stock_entry.insert() + + self.assertRaises(StockOverProductionError, stock_entry.submit) + + + +test_records = [ + [ + { + "bom_no": "BOM/_Test FG Item/001", + "company": "_Test Company", + "doctype": "Production Order", + "production_item": "_Test FG Item", + "qty": 10.0, + "fg_warehouse": "_Test Warehouse 1 - _TC", + "wip_warehouse": "_Test Warehouse - _TC", + "stock_uom": "Nos" + } + ] +] \ No newline at end of file diff --git a/patches/december_2012/repost_ordered_qty.py b/patches/december_2012/repost_ordered_qty.py index e73b72663d..4c1d11d39b 100644 --- a/patches/december_2012/repost_ordered_qty.py +++ b/patches/december_2012/repost_ordered_qty.py @@ -3,19 +3,9 @@ def execute(): import webnotes - from webnotes.utils import flt - - for d in webnotes.conn.sql("select name, item_code, warehouse, ordered_qty from tabBin", - as_dict=1): - ordered_qty = webnotes.conn.sql(""" - select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor) - from `tabPurchase Order Item` po_item, `tabPurchase Order` po - where po_item.item_code=%s and po_item.warehouse=%s - and po_item.qty > po_item.received_qty and po_item.parent=po.name - and po.status!='Stopped' and po.docstatus=1""", (d.item_code, d.warehouse)) + from utilities.repost_stock import get_ordered_qty, update_bin - if flt(d.ordered_qty) != flt(ordered_qty[0][0]): - webnotes.conn.set_value("Bin", d.name, "ordered_qty", flt(ordered_qty[0][0])) - - webnotes.conn.sql("""update tabBin set projected_qty = actual_qty + planned_qty + - indented_qty + ordered_qty - reserved_qty where name = %s""", d.name) \ No newline at end of file + for d in webnotes.conn.sql("select item_code, warehouse from tabBin"): + update_bin(d[0], d[1], { + "ordered_qty": get_ordered_qty(d[0], d[1]) + }) \ No newline at end of file diff --git a/patches/february_2013/repost_reserved_qty.py b/patches/february_2013/repost_reserved_qty.py index 3a3353f918..5c41266c2a 100644 --- a/patches/february_2013/repost_reserved_qty.py +++ b/patches/february_2013/repost_reserved_qty.py @@ -4,54 +4,10 @@ import webnotes def execute(): webnotes.conn.auto_commit_on_many_writes = 1 - repost_reserved_qty() - webnotes.conn.auto_commit_on_many_writes = 0 + from utilities.repost_stock import get_reserved_qty, update_bin -def repost_reserved_qty(): - from webnotes.utils import flt - bins = webnotes.conn.sql("select item_code, warehouse, name, reserved_qty from `tabBin`") - i = 0 - for d in bins: - i += 1 - reserved_qty = webnotes.conn.sql(""" - select - sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty)) - from - ( - (select - qty as dnpi_qty, - ( - select qty from `tabSales Order Item` - where name = dnpi.parent_detail_docname - ) as so_item_qty, - ( - select ifnull(delivered_qty, 0) from `tabSales Order Item` - where name = dnpi.parent_detail_docname - ) as so_item_delivered_qty, - parent, name - from - ( - select qty, parent_detail_docname, parent, name - from `tabDelivery Note Packing Item` dnpi_in - where item_code = %s and warehouse = %s - and parenttype="Sales Order" - and item_code != parent_item - and exists (select * from `tabSales Order` so - where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped') - ) dnpi) - union - (select qty as dnpi_qty, qty as so_item_qty, - ifnull(delivered_qty, 0) as so_item_delivered_qty, parent, name - from `tabSales Order Item` so_item - where item_code = %s and reserved_warehouse = %s - and exists(select * from `tabSales Order` so - where so.name = so_item.parent and so.docstatus = 1 - and so.status != 'Stopped')) - ) tab - where - so_item_qty >= so_item_delivered_qty - """, (d[0], d[1], d[0], d[1])) - - if flt(d[3]) != flt(reserved_qty[0][0]): - webnotes.conn.sql("""update `tabBin` set reserved_qty = %s where name = %s""", - (reserved_qty and reserved_qty[0][0] or 0, d[2])) \ No newline at end of file + for d in webnotes.conn.sql("select item_code, warehouse from tabBin"): + update_bin(d[0], d[1], { + "reserved_qty": get_reserved_qty(d[0], d[1]) + }) + webnotes.conn.auto_commit_on_many_writes = 0 \ No newline at end of file diff --git a/patches/october_2013/repost_planned_qty.py b/patches/october_2013/repost_planned_qty.py new file mode 100644 index 0000000000..cfe47ca115 --- /dev/null +++ b/patches/october_2013/repost_planned_qty.py @@ -0,0 +1,11 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + +def execute(): + import webnotes + from utilities.repost_stock import get_planned_qty, update_bin + + for d in webnotes.conn.sql("select item_code, warehouse from tabBin"): + update_bin(d[0], d[1], { + "planned_qty": get_planned_qty(d[0], d[1]) + }) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 6a5d0f4f55..59e0b0cf87 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -221,4 +221,5 @@ patch_list = [ "patches.september_2013.p05_fix_customer_in_pos", "patches.october_2013.fix_is_cancelled_in_sle", "patches.october_2013.repost_ordered_qty", + "patches.october_2013.repost_planned_qty", ] \ No newline at end of file diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 8d2e76490c..7d8130cc85 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -6,7 +6,7 @@ import webnotes import webnotes.defaults from webnotes.utils import cstr, cint, flt, comma_or, nowdate -from webnotes.model.doc import Document, addchild +from webnotes.model.doc import addchild from webnotes.model.bean import getlist from webnotes.model.code import get_obj from webnotes import msgprint, _ @@ -21,6 +21,7 @@ class NotUpdateStockError(webnotes.ValidationError): pass class StockOverReturnError(webnotes.ValidationError): pass class IncorrectValuationRateError(webnotes.ValidationError): pass class DuplicateEntryForProductionOrderError(webnotes.ValidationError): pass +class StockOverProductionError(webnotes.ValidationError): pass from controllers.stock_controller import StockController @@ -56,12 +57,12 @@ class DocType(StockController): from stock.doctype.serial_no.serial_no import update_serial_nos_after_submit update_serial_nos_after_submit(self, "Stock Entry", "mtn_details") - self.update_production_order(1) + self.update_production_order() self.make_gl_entries() def on_cancel(self): self.update_stock_ledger() - self.update_production_order(0) + self.update_production_order() self.make_cancel_gl_entries() def validate_fiscal_year(self): @@ -326,37 +327,44 @@ class DocType(StockController): self.make_sl_entries(sl_entries, self.doc.amended_from and 'Yes' or 'No') - def update_production_order(self, is_submit): + def update_production_order(self): + def _validate_production_order(pro_bean): + if flt(pro_bean.doc.docstatus) != 1: + webnotes.throw(_("Production Order must be submitted") + ": " + + self.doc.production_order) + + if pro_bean.doc.status == 'Stopped': + msgprint(_("Transaction not allowed against stopped Production Order") + ": " + + self.doc.production_order) + if self.doc.production_order: - # first perform some validations - # (they are here coz this fn is also called during on_cancel) - pro_obj = get_obj("Production Order", self.doc.production_order) - if flt(pro_obj.doc.docstatus) != 1: - msgprint("""You cannot do any transaction against - Production Order : %s, as it's not submitted""" - % (pro_obj.doc.name), raise_exception=1) - - if pro_obj.doc.status == 'Stopped': - msgprint("""You cannot do any transaction against Production Order : %s, - as it's status is 'Stopped'"""% (pro_obj.doc.name), raise_exception=1) - - # update bin - if self.doc.purpose == "Manufacture/Repack": - from stock.utils import update_bin - pro_obj.doc.produced_qty = flt(pro_obj.doc.produced_qty) + \ - (is_submit and 1 or -1 ) * flt(self.doc.fg_completed_qty) - args = { - "item_code": pro_obj.doc.production_item, - "warehouse": pro_obj.doc.fg_warehouse, - "posting_date": self.doc.posting_date, - "planned_qty": (is_submit and -1 or 1 ) * flt(self.doc.fg_completed_qty) - } - update_bin(args) + pro_bean = webnotes.bean("Production Order", self.doc.production_order) + _validate_production_order(pro_bean) + self.update_produced_qty(pro_bean) + self.update_planned_qty(pro_bean) - # update production order status - pro_obj.doc.status = (flt(pro_obj.doc.qty)==flt(pro_obj.doc.produced_qty)) \ - and 'Completed' or 'In Process' - pro_obj.doc.save() + def update_produced_qty(self, pro_bean): + if self.doc.purpose == "Manufacture/Repack": + produced_qty = flt(pro_bean.doc.produced_qty) + \ + (self.doc.docstatus==1 and 1 or -1 ) * flt(self.doc.fg_completed_qty) + + if produced_qty > flt(pro_bean.doc.qty): + webnotes.throw(_("Production Order") + ": " + self.doc.production_order + "\n" + + _("Total Manufactured Qty can not be greater than Planned qty to manufacture") + + "(%s/%s)" % (produced_qty, flt(pro_bean.doc.qty)), StockOverProductionError) + + status = 'Completed' if flt(produced_qty) >= flt(pro_bean.doc.qty) else 'In Process' + webnotes.conn.sql("""update `tabProduction Order` set status=%s, produced_qty=%s + where name=%s""", (status, produced_qty, self.doc.production_order)) + + def update_planned_qty(self, pro_bean): + from stock.utils import update_bin + update_bin({ + "item_code": pro_bean.doc.production_item, + "warehouse": pro_bean.doc.fg_warehouse, + "posting_date": self.doc.posting_date, + "planned_qty": (self.doc.docstatus==1 and -1 or 1 ) * flt(self.doc.fg_completed_qty) + }) def get_item_details(self, arg): arg = json.loads(arg) @@ -415,7 +423,8 @@ class DocType(StockController): return ret def get_items(self): - self.doclist = self.doc.clear_table(self.doclist, 'mtn_details', 1) + self.doclist = filter(lambda d: d.parentfield!="mtn_details", self.doclist) + # self.doclist = self.doc.clear_table(self.doclist, 'mtn_details') pro_obj = None if self.doc.production_order: @@ -454,7 +463,7 @@ class DocType(StockController): "stock_uom": pro_obj.doc.stock_uom } }, bom_no=pro_obj.doc.bom_no) - + elif self.doc.purpose in ["Material Receipt", "Manufacture/Repack"]: if self.doc.purpose=="Material Receipt": self.doc.from_warehouse = "" @@ -471,6 +480,7 @@ class DocType(StockController): }, bom_no=self.doc.bom_no) self.get_stock_and_rate() + def get_bom_raw_materials(self, qty): """ diff --git a/stock/doctype/warehouse/warehouse.py b/stock/doctype/warehouse/warehouse.py index 58cf11bfdf..476a6f6478 100644 --- a/stock/doctype/warehouse/warehouse.py +++ b/stock/doctype/warehouse/warehouse.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cint, flt, validate_email_add -from webnotes.model.code import get_obj from webnotes import msgprint, _ sql = webnotes.conn.sql @@ -83,103 +82,15 @@ class DocType: webnotes.conn.delete_doc("Account", old_warehouse_account) + from utilities.repost_stock import repost for item_code in items: - self.repost(item_code[0], self.doc.merge_with) + repost(item_code[0], self.doc.merge_with) webnotes.conn.auto_commit_on_many_writes = 0 msgprint("Warehouse %s merged into %s. Now you can delete warehouse: %s" % (self.doc.name, self.doc.merge_with, self.doc.name)) - - def repost(self, item_code, warehouse=None): - from stock.utils import get_bin - self.repost_actual_qty(item_code, warehouse) - - bin = get_bin(item_code, warehouse) - self.repost_reserved_qty(bin) - self.repost_indented_qty(bin) - self.repost_ordered_qty(bin) - self.repost_planned_qty(bin) - bin.doc.projected_qty = flt(bin.doc.actual_qty) + flt(bin.doc.planned_qty) \ - + flt(bin.doc.indented_qty) + flt(bin.doc.ordered_qty) - flt(bin.doc.reserved_qty) - bin.doc.save() - - - def repost_actual_qty(self, item_code, warehouse=None): - from stock.stock_ledger import update_entries_after - if not warehouse: - warehouse = self.doc.name - - update_entries_after({ "item_code": item_code, "warehouse": warehouse }) - - def repost_reserved_qty(self, bin): - reserved_qty = webnotes.conn.sql(""" - select - sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty)) - from - ( - select - qty as dnpi_qty, - ( - select qty from `tabSales Order Item` - where name = dnpi.parent_detail_docname - ) as so_item_qty, - ( - select ifnull(delivered_qty, 0) from `tabSales Order Item` - where name = dnpi.parent_detail_docname - ) as so_item_delivered_qty - from - ( - select qty, parent_detail_docname - from `tabDelivery Note Packing Item` dnpi_in - where item_code = %s and warehouse = %s - and parenttype="Sales Order" - and exists (select * from `tabSales Order` so - where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped') - ) dnpi - ) tab - where - so_item_qty >= so_item_delivered_qty - """, (bin.doc.item_code, bin.doc.warehouse)) - - if flt(bin.doc.reserved_qty) != flt(reserved_qty[0][0]): - webnotes.conn.set_value("Bin", bin.doc.name, "reserved_qty", flt(reserved_qty[0][0])) - - - def repost_indented_qty(self, bin): - indented_qty = webnotes.conn.sql("""select sum(pr_item.qty - pr_item.ordered_qty) - from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr - where pr_item.item_code=%s and pr_item.warehouse=%s - and pr_item.qty > pr_item.ordered_qty and pr_item.parent=pr.name - and pr.status!='Stopped' and pr.docstatus=1""" - , (bin.doc.item_code, bin.doc.warehouse)) - - if flt(bin.doc.indented_qty) != flt(indented_qty[0][0]): - webnotes.conn.set_value("Bin", bin.doc.name, "indented_qty", flt(indented_qty[0][0])) - - - def repost_ordered_qty(self, bin): - ordered_qty = webnotes.conn.sql(""" - select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor) - from `tabPurchase Order Item` po_item, `tabPurchase Order` po - where po_item.item_code=%s and po_item.warehouse=%s - and po_item.qty > po_item.received_qty and po_item.parent=po.name - and po.status!='Stopped' and po.docstatus=1""" - , (bin.doc.item_code, bin.doc.warehouse)) - - if flt(bin.doc.ordered_qty) != flt(ordered_qty[0][0]): - webnotes.conn.set_value("Bin", bin.doc.name, "ordered_qty", flt(ordered_qty[0][0])) - - def repost_planned_qty(self, bin): - planned_qty = webnotes.conn.sql(""" - select sum(qty - produced_qty) from `tabProduction Order` - where production_item = %s and fg_warehouse = %s and status != "Stopped" - and docstatus=1""", (bin.doc.item_code, bin.doc.warehouse)) - - if flt(bin.doc.planned_qty) != flt(planned_qty[0][0]): - webnotes.conn.set_value("Bin", bin.doc.name, "planned_qty", flt(planned_qty[0][0])) - def on_trash(self): # delete bin bins = sql("select * from `tabBin` where warehouse = %s", self.doc.name, as_dict=1) diff --git a/stock/utils.py b/stock/utils.py index 983ee72c05..0f801c74bc 100644 --- a/stock/utils.py +++ b/stock/utils.py @@ -393,13 +393,4 @@ def notify_errors(exceptions_list): Administrator""" % ("\n\n".join(["\n".join(msg) for msg in exceptions_list]),) from webnotes.profile import get_system_managers - sendmail(get_system_managers(), subject=subject, msg=msg) - - -def repost(): - """ - Repost everything! - """ - from webnotes.model.code import get_obj - for wh in webnotes.conn.sql("select name from tabWarehouse"): - get_obj('Warehouse', wh[0]).repost_stock() + sendmail(get_system_managers(), subject=subject, msg=msg) \ No newline at end of file diff --git a/utilities/repost_stock.py b/utilities/repost_stock.py new file mode 100644 index 0000000000..b46aae608f --- /dev/null +++ b/utilities/repost_stock.py @@ -0,0 +1,119 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import webnotes + +from webnotes.utils import flt + + +def repost(): + """ + Repost everything! + """ + webnotes.conn.auto_commit_on_many_writes = 1 + + for d in webnotes.conn.sql("select item_code, warehouse from tabBin"): + repost_stock(d[0], d[1]) + + webnotes.conn.auto_commit_on_many_writes = 0 + +def repost_stock(item_code, warehouse): + repost_actual_qty(item_code, warehouse) + + if item_code and warehouse: + update_bin(item_code, warehouse, { + "reserved_qty": get_reserved_qty(item_code, warehouse), + "indented_qty": get_indented_qty(item_code, warehouse), + "ordered_qty": get_ordered_qty(item_code, warehouse), + "planned_qty": get_planned_qty(item_code, warehouse) + }) + +def repost_actual_qty(item_code, warehouse): + from stock.stock_ledger import update_entries_after + update_entries_after({ "item_code": item_code, "warehouse": warehouse }) + +def get_reserved_qty(item_code, warehouse): + reserved_qty = webnotes.conn.sql(""" + select + sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty)) + from + ( + (select + qty as dnpi_qty, + ( + select qty from `tabSales Order Item` + where name = dnpi.parent_detail_docname + ) as so_item_qty, + ( + select ifnull(delivered_qty, 0) from `tabSales Order Item` + where name = dnpi.parent_detail_docname + ) as so_item_delivered_qty, + parent, name + from + ( + select qty, parent_detail_docname, parent, name + from `tabDelivery Note Packing Item` dnpi_in + where item_code = %s and warehouse = %s + and parenttype="Sales Order" + and item_code != parent_item + and exists (select * from `tabSales Order` so + where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped') + ) dnpi) + union + (select qty as dnpi_qty, qty as so_item_qty, + ifnull(delivered_qty, 0) as so_item_delivered_qty, parent, name + from `tabSales Order Item` so_item + where item_code = %s and reserved_warehouse = %s + and exists(select * from `tabSales Order` so + where so.name = so_item.parent and so.docstatus = 1 + and so.status != 'Stopped')) + ) tab + where + so_item_qty >= so_item_delivered_qty + """, (item_code, warehouse, item_code, warehouse)) + + return flt(reserved_qty[0][0]) if reserved_qty else 0 + +def get_indented_qty(item_code, warehouse): + indented_qty = webnotes.conn.sql("""select sum(pr_item.qty - pr_item.ordered_qty) + from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr + where pr_item.item_code=%s and pr_item.warehouse=%s + and pr_item.qty > pr_item.ordered_qty and pr_item.parent=pr.name + and pr.status!='Stopped' and pr.docstatus=1""", (item_code, warehouse)) + + return flt(indented_qty[0][0]) if indented_qty else 0 + +def get_ordered_qty(item_code, warehouse): + ordered_qty = webnotes.conn.sql(""" + select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor) + from `tabPurchase Order Item` po_item, `tabPurchase Order` po + where po_item.item_code=%s and po_item.warehouse=%s + and po_item.qty > po_item.received_qty and po_item.parent=po.name + and po.status!='Stopped' and po.docstatus=1""", (item_code, warehouse)) + + return flt(ordered_qty[0][0]) if ordered_qty else 0 + +def get_planned_qty(item_code, warehouse): + planned_qty = webnotes.conn.sql(""" + select sum(qty - produced_qty) from `tabProduction Order` + where production_item = %s and fg_warehouse = %s and status != "Stopped" + and docstatus=1 and qty > produced_qty""", (item_code, warehouse)) + + return flt(planned_qty[0][0]) if planned_qty else 0 + + +def update_bin(item_code, warehouse, qty_dict=None): + from stock.utils import get_bin + bin = get_bin(item_code, warehouse) + mismatch = False + for fld, val in qty_dict.items(): + if flt(bin.doc.fields.get(fld)) != flt(val): + bin.doc.fields[fld] = flt(val) + mismatch = True + + if mismatch: + bin.doc.projected_qty = flt(bin.doc.actual_qty) + flt(bin.doc.ordered_qty) + \ + flt(bin.doc.indented_qty) + flt(bin.doc.planned_qty) - flt(bin.doc.reserved_qty) + + bin.doc.save() \ No newline at end of file