From 54d209ffb0fd9721adf97683fd369d83a6b152aa Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 1 Mar 2013 18:51:10 +0530 Subject: [PATCH] sub-contracting code refactored for po --- .../doctype/purchase_order/purchase_order.py | 107 ++---------------- .../purchase_order/test_purchase_order.py | 65 +++++++++++ controllers/buying_controller.py | 61 ++++++++++ selling/doctype/customer/test_customer.py | 1 + .../purchase_receipt/purchase_receipt.py | 55 +-------- 5 files changed, 136 insertions(+), 153 deletions(-) create mode 100644 buying/doctype/purchase_order/test_purchase_order.py diff --git a/buying/doctype/purchase_order/purchase_order.py b/buying/doctype/purchase_order/purchase_order.py index 244bd7a670..438442cf24 100644 --- a/buying/doctype/purchase_order/purchase_order.py +++ b/buying/doctype/purchase_order/purchase_order.py @@ -18,12 +18,10 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cstr, flt -from webnotes.model.doc import addchild from webnotes.model.bean import getlist from webnotes.model.code import get_obj from webnotes import msgprint from buying.utils import get_last_purchase_details -from setup.utils import get_company_currency sql = webnotes.conn.sql @@ -35,7 +33,6 @@ class DocType(BuyingController): self.tname = 'Purchase Order Item' self.fname = 'po_details' - # Validate def validate(self): super(DocType, self).validate() @@ -64,6 +61,10 @@ class DocType(BuyingController): # Check for stopped status self.check_for_stopped_status(pc_obj) + # sub-contracting + self.validate_for_subcontracting() + self.update_raw_materials_supplied("po_raw_material_details") + def get_default_schedule_date(self): get_obj(dt = 'Purchase Common').get_default_schedule_date(self) @@ -79,7 +80,6 @@ class DocType(BuyingController): def get_indent_details(self): if self.doc.indent_no: get_obj('DocType Mapper','Material Request-Purchase Order').dt_map('Material Request','Purchase Order',self.doc.indent_no, self.doc, self.doclist, "[['Material Request','Purchase Order'],['Material Request Item', 'Purchase Order Item']]") - pcomm = get_obj('Purchase Common') for d in getlist(self.doclist, 'po_details'): if d.item_code and not d.purchase_rate: last_purchase_details = get_last_purchase_details(d.item_code, self.doc.name) @@ -173,8 +173,6 @@ class DocType(BuyingController): msgprint(cstr(self.doc.doctype) +" => "+ cstr(self.doc.name) +" has been modified. Please Refresh. ") raise Exception - # On Close - #------------------------------------------------------------------------------------------------- def update_status(self, status): self.check_modified_date() # step 1:=> Set Status @@ -186,8 +184,6 @@ class DocType(BuyingController): # step 3:=> Acknowledge user msgprint(self.doc.doctype + ": " + self.doc.name + " has been %s." % ((status == 'Submitted') and 'Unstopped' or cstr(status))) - - # On Submit def on_submit(self): purchase_controller = webnotes.get_obj("Purchase Common") purchase_controller.is_item_table_empty(self) @@ -207,118 +203,31 @@ class DocType(BuyingController): # Step 6 :=> Set Status webnotes.conn.set(self.doc,'status','Submitted') - # On Cancel - # ------------------------------------------------------------------------------------------------------- def on_cancel(self): pc_obj = get_obj(dt = 'Purchase Common') - # 1.Check if PO status is stopped + # Check if PO status is stopped pc_obj.check_for_stopped_status(cstr(self.doc.doctype), cstr(self.doc.name)) self.check_for_stopped_status(pc_obj) - # 2.Check if Purchase Receipt has been submitted against current Purchase Order + # Check if Purchase Receipt has been submitted against current Purchase Order pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.doc.name, detail_doctype = 'Purchase Receipt Item') - # 3.Check if Purchase Invoice has been submitted against current Purchase Order - #pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Invoice', docname = self.doc.name, detail_doctype = 'Purchase Invoice Item') - + # Check if Purchase Invoice has been submitted against current Purchase Order submitted = sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_order = '%s' and t1.docstatus = 1" % self.doc.name) if submitted: msgprint("Purchase Invoice : " + cstr(submitted[0][0]) + " has already been submitted !") raise Exception - # 4.Set Status as Cancelled webnotes.conn.set(self.doc,'status','Cancelled') - - # 5.Update Material Requests Pending Qty and accordingly it's Status pc_obj.update_prevdoc_detail(self,is_submit = 0) - - # 6.Update Bin self.update_bin( is_submit = 0, is_stopped = 0) - - # Step 7 :=> Update last purchase rate pc_obj.update_last_purchase_rate(self, is_submit = 0) - -#----------- code for Sub-contracted Items ------------------- - #--------check for sub-contracted items and accordingly update PO raw material detail table-------- - def update_rw_material_detail(self): - for d in getlist(self.doclist,'po_details'): - item_det = sql("select is_sub_contracted_item, is_purchase_item from `tabItem` where name = '%s'"%(d.item_code)) - - if item_det[0][0] == 'Yes': - if item_det[0][1] == 'Yes': - if not self.doc.is_subcontracted: - msgprint("Please enter whether purchase order to be made for subcontracting or for purchasing in 'Is Subcontracted' field .") - raise Exception - if self.doc.is_subcontracted == 'Yes': - self.add_bom(d) - else: - self.doclist = self.doc.clear_table(self.doclist,'po_raw_material_details',1) - self.doc.save() - elif item_det[0][1] == 'No': - self.add_bom(d) - self.delete_irrelevant_raw_material() - #---------------calculate amt in Purchase Order Item Supplied------------- - - def add_bom(self, d): - #----- fetching default bom from Bill of Materials instead of Item Master -- - bom_det = sql("""select t1.item, t2.item_code, t2.qty_consumed_per_unit, - t2.moving_avg_rate, t2.value_as_per_mar, t2.stock_uom, t2.name, t2.parent - from `tabBOM` t1, `tabBOM Item` t2 - where t2.parent = t1.name and t1.item = %s - and ifnull(t1.is_default,0) = 1 and t1.docstatus = 1""", (d.item_code,)) - - if not bom_det: - msgprint("No default BOM exists for item: %s" % d.item_code) - raise Exception - else: - #-------------- add child function-------------------- - chgd_rqd_qty = [] - for i in bom_det: - if i and not sql("select name from `tabPurchase Order Item Supplied` where reference_name = '%s' and bom_detail_no = '%s' and parent = '%s' " %(d.name, i[6], self.doc.name)): - - rm_child = addchild(self.doc, 'po_raw_material_details', 'Purchase Order Item Supplied', self.doclist) - - rm_child.reference_name = d.name - rm_child.bom_detail_no = i and i[6] or '' - rm_child.main_item_code = i and i[0] or '' - rm_child.rm_item_code = i and i[1] or '' - rm_child.stock_uom = i and i[5] or '' - rm_child.rate = i and flt(i[3]) or flt(i[4]) - rm_child.conversion_factor = d.conversion_factor - rm_child.required_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor) - rm_child.amount = flt(flt(rm_child.consumed_qty)*flt(rm_child.rate)) - rm_child.save() - chgd_rqd_qty.append(cstr(i[1])) - else: - act_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor) - for po_rmd in getlist(self.doclist, 'po_raw_material_details'): - if i and i[6] == po_rmd.bom_detail_no and (flt(act_qty) != flt(po_rmd.required_qty) or i[1] != po_rmd.rm_item_code): - chgd_rqd_qty.append(cstr(i[1])) - po_rmd.main_item_code = i[0] - po_rmd.rm_item_code = i[1] - po_rmd.stock_uom = i[5] - po_rmd.required_qty = flt(act_qty) - po_rmd.rate = i and flt(i[3]) or flt(i[4]) - po_rmd.amount = flt(flt(po_rmd.consumed_qty)*flt(po_rmd.rate)) - - - # Delete irrelevant raw material from PR Raw material details - #-------------------------------------------------------------- - def delete_irrelevant_raw_material(self): - for d in getlist(self.doclist,'po_raw_material_details'): - if not sql("select name from `tabPurchase Order Item` where name = '%s' and parent = '%s'and item_code = '%s'" % (d.reference_name, self.doc.name, d.main_item_code)): - d.parent = 'old_par:'+self.doc.name - d.save() - - # On Update - # ---------------------------------------------------------------------------------------------------- def on_update(self): - self.update_rw_material_detail() + pass - def get_rate(self,arg): return get_obj('Purchase Common').get_rate(arg,self) diff --git a/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py new file mode 100644 index 0000000000..bd27f07687 --- /dev/null +++ b/buying/doctype/purchase_order/test_purchase_order.py @@ -0,0 +1,65 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +from __future__ import unicode_literals +import unittest +import webnotes +import webnotes.defaults +from webnotes.utils import cint + +class TestPurchaseOrder(unittest.TestCase): + def test_subcontracting(self): + po = webnotes.bean(copy=test_records[0]) + po.insert() + self.assertEquals(len(po.doclist.get({"parentfield": "po_raw_material_details"})), 2) + + +test_dependencies = ["BOM"] + +test_records = [ + [ + { + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "doctype": "Purchase Order", + "fiscal_year": "_Test Fiscal Year 2013", + "transaction_date": "2013-02-12", + "is_subcontracted": "Yes", + "supplier": "_Test Supplier", + "supplier_name": "_Test Supplier", + "net_total": 5000.0, + "grand_total": 5000.0, + "grand_total_import": 5000.0, + + }, + { + "conversion_factor": 1.0, + "description": "_Test FG Item", + "doctype": "Purchase Order Item", + "item_code": "_Test FG Item", + "item_name": "_Test FG Item", + "parentfield": "po_details", + "qty": 10.0, + "import_rate": 500.0, + "amount": 5000.0, + "warehouse": "_Test Warehouse", + "stock_uom": "Nos", + "uom": "_Test UOM", + } + ], +] \ No newline at end of file diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 8b247ccd98..2f3128c98c 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -342,6 +342,67 @@ class BuyingController(AccountsController): ) / flt(d.conversion_factor) else: d.valuation_rate = 0.0 + + def validate_for_subcontracting(self): + if not self.doc.is_subcontracted and self.sub_contracted_items: + webnotes.msgprint(_("""Please enter whether %s is made for subcontracting or purchasing, + in 'Is Subcontracted' field""" % self.doc.doctype), raise_exception=1) + + if self.doc.doctype == "Purchase Receipt" and self.doc.is_subcontracted=="Yes" \ + and not self.doc.supplier_warehouse: + webnotes.msgprint(_("Supplier Warehouse mandatory subcontracted purchase receipt"), + raise_exception=1) + + def update_raw_materials_supplied(self, raw_material_table): + self.doclist = self.doc.clear_table(self.doclist, raw_material_table) + if self.doc.is_subcontracted=="Yes": + for item in self.doclist.get({"parentfield": self.fname}): + if item.item_code in self.sub_contracted_items: + self.add_bom_items(item, raw_material_table) + + def add_bom_items(self, d, raw_material_table): + bom_items = self.get_items_from_default_bom(d.item_code) + raw_materials_cost = 0 + for item in bom_items: + required_qty = flt(item.qty_consumed_per_unit) * flt(d.qty) * flt(d.conversion_factor) + rm_doclist = { + "parentfield": raw_material_table, + "doctype": self.doc.doctype + " Item Supplied", + "reference_name": d.name, + "bom_detail_no": item.name, + "main_item_code": d.item_code, + "rm_item_code": item.item_code, + "stock_uom": item.stock_uom, + "required_qty": required_qty, + "conversion_factor": d.conversion_factor, + "rate": item.rate, + "amount": required_qty * flt(item.rate) + } + if self.doc.doctype == "Purchase Receipt": + rm_doclist.update({ + "consumed_qty": required_qty, + "description": item.description, + }) + + self.doclist.append(rm_doclist) + + raw_materials_cost += required_qty * flt(item.rate) + + if self.doc.doctype == "Purchase Receipt": + d.rm_supp_cost = raw_materials_cost + + def get_items_from_default_bom(self, item_code): + # print webnotes.conn.sql("""select name from `tabBOM` where item = '_Test FG Item'""") + bom_items = webnotes.conn.sql("""select t2.item_code, t2.qty_consumed_per_unit, + t2.rate, t2.stock_uom, t2.name, t2.description + from `tabBOM` t1, `tabBOM Item` t2 + where t2.parent = t1.name and t1.item = %s and t1.is_default = 1 + and t1.docstatus = 1 and t1.is_active = 1""", item_code, as_dict=1) + if not bom_items: + msgprint(_("No default BOM exists for item: ") + item_code, raise_exception=1) + + return bom_items + @property def precision(self): diff --git a/selling/doctype/customer/test_customer.py b/selling/doctype/customer/test_customer.py index 09e2f5d7f5..551b03f0be 100644 --- a/selling/doctype/customer/test_customer.py +++ b/selling/doctype/customer/test_customer.py @@ -1,3 +1,4 @@ + test_records = [ [{ "doctype": "Customer", diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py index 2f8835fb16..12da0a6ee8 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/stock/doctype/purchase_receipt/purchase_receipt.py @@ -122,7 +122,7 @@ class DocType(BuyingController): # sub-contracting self.validate_for_subcontracting() - self.update_raw_materials_supplied() + self.update_raw_materials_supplied("pr_raw_material_details") self.update_valuation_rate("purchase_receipt_details") @@ -289,59 +289,6 @@ class DocType(BuyingController): self.make_gl_entries() - def validate_for_subcontracting(self): - if not self.doc.is_subcontracted and self.sub_contracted_items: - webnotes.msgprint(_("""Please enter whether Purchase Recipt is made for subcontracting - or purchasing, in 'Is Subcontracted' field"""), raise_exception=1) - - if self.doc.is_subcontracted=="Yes" and not self.doc.supplier_warehouse: - webnotes.msgprint(_("Please Enter Supplier Warehouse for subcontracted Items"), - raise_exception=1) - - def update_raw_materials_supplied(self): - self.doclist = self.doc.clear_table(self.doclist, 'pr_raw_material_details') - if self.doc.is_subcontracted=="Yes": - for item in self.doclist.get({"parentfield": "purchase_receipt_details"}): - if item.item_code in self.sub_contracted_items: - self.add_bom_items(item) - - def add_bom_items(self, d): - bom_items = self.get_items_from_default_bom(d.item_code) - raw_materials_cost = 0 - for item in bom_items: - required_qty = flt(item.qty_consumed_per_unit) * flt(d.qty) * flt(d.conversion_factor) - self.doclist.append({ - "parentfield": "pr_raw_material_details", - "doctype": "Purchase Receipt Item Supplied", - "reference_name": d.name, - "bom_detail_no": item.name, - "main_item_code": d.item_code, - "rm_item_code": item.item_code, - "description": item.description, - "stock_uom": item.stock_uom, - "required_qty": required_qty, - "consumed_qty": required_qty, - "conversion_factor": d.conversion_factor, - "rate": item.rate, - "amount": required_qty * flt(item.rate) - }) - - raw_materials_cost += required_qty * flt(item.rate) - - d.rm_supp_cost = raw_materials_cost - - def get_items_from_default_bom(self, item_code): - # print webnotes.conn.sql("""select name from `tabBOM` where item = '_Test FG Item'""") - bom_items = sql("""select t2.item_code, t2.qty_consumed_per_unit, - t2.rate, t2.stock_uom, t2.name, t2.description - from `tabBOM` t1, `tabBOM Item` t2 - where t2.parent = t1.name and t1.item = %s and t1.is_default = 1 - and t1.docstatus = 1 and t1.is_active = 1""", item_code, as_dict=1) - if not bom_items: - msgprint(_("No default BOM exists for item: ") + item_code, raise_exception=1) - - return bom_items - def bk_flush_supp_wh(self, is_submit): for d in getlist(self.doclist, 'pr_raw_material_details'): # negative quantity is passed as raw material qty has to be decreased