From ad600cce2222912d22db81e2276bf729ac4079d1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 16:56:56 +0530 Subject: [PATCH 01/12] bom exploded items grouped by items --- manufacturing/doctype/bom/bom.py | 57 ++++---- manufacturing/doctype/bom/test_bom.py | 132 +----------------- .../bom_explosion_item/bom_explosion_item.txt | 23 +-- .../bom_replace_tool/bom_replace_tool.py | 3 +- .../production_planning_tool.py | 34 ++--- patches/june_2013/__init__.py | 0 .../p01_update_bom_exploded_items.py | 28 ++++ patches/patch_list.py | 1 + stock/doctype/stock_entry/stock_entry.py | 24 ++-- 9 files changed, 86 insertions(+), 216 deletions(-) create mode 100644 patches/june_2013/__init__.py create mode 100644 patches/june_2013/p01_update_bom_exploded_items.py diff --git a/manufacturing/doctype/bom/bom.py b/manufacturing/doctype/bom/bom.py index 5a1d47fd4e..4ce4feb156 100644 --- a/manufacturing/doctype/bom/bom.py +++ b/manufacturing/doctype/bom/bom.py @@ -270,18 +270,23 @@ class DocType: if b[0]: bom_list.append(b[0]) - def update_cost_and_exploded_items(self): - bom_list = self.traverse_tree() + def update_cost_and_exploded_items(self, bom_list=[]): + bom_list = self.traverse_tree(bom_list) for bom in bom_list: bom_obj = get_obj("BOM", bom, with_children=1) bom_obj.on_update() - def traverse_tree(self): + return bom_list + + def traverse_tree(self, bom_list=[]): def _get_children(bom_no): return [cstr(d[0]) for d in webnotes.conn.sql("""select bom_no from `tabBOM Item` where parent = %s and ifnull(bom_no, '') != ''""", bom_no)] - bom_list, count = [self.doc.name], 0 + count = 0 + if self.doc.name not in bom_list: + bom_list.append(self.doc.name) + while(count < len(bom_list)): for child_bom in _get_children(bom_list[count]): if child_bom not in bom_list: @@ -325,52 +330,50 @@ class DocType: def get_exploded_items(self): """ Get all raw materials including items from child bom""" - self.cur_exploded_items = [] + self.cur_exploded_items = {} for d in getlist(self.doclist, 'bom_materials'): if d.bom_no: self.get_child_exploded_items(d.bom_no, d.qty) else: - self.cur_exploded_items.append({ + self.add_to_cur_exploded_items(webnotes._dict({ 'item_code' : d.item_code, 'description' : d.description, 'stock_uom' : d.stock_uom, 'qty' : flt(d.qty), - 'rate' : flt(d.rate), - 'amount' : flt(d.amount), - 'parent_bom' : d.parent, - 'mat_detail_no' : d.name, - 'qty_consumed_per_unit' : flt(d.qty_consumed_per_unit) - }) + 'rate' : flt(d.rate), + })) + + def add_to_cur_exploded_items(self, args): + if self.cur_exploded_items.get(args.item_code): + self.cur_exploded_items[args.item_code]["qty"] += args.qty + else: + self.cur_exploded_items[args.item_code] = args def get_child_exploded_items(self, bom_no, qty): """ Add all items from Flat BOM of child BOM""" child_fb_items = sql("""select item_code, description, stock_uom, qty, rate, - amount, parent_bom, mat_detail_no, qty_consumed_per_unit - from `tabBOM Explosion Item` where parent = '%s' and docstatus = 1""" % - bom_no, as_dict = 1) + qty_consumed_per_unit from `tabBOM Explosion Item` + where parent = %s and docstatus = 1""", bom_no, as_dict = 1) + for d in child_fb_items: - self.cur_exploded_items.append({ + self.add_to_cur_exploded_items(webnotes._dict({ 'item_code' : d['item_code'], 'description' : d['description'], 'stock_uom' : d['stock_uom'], 'qty' : flt(d['qty_consumed_per_unit'])*qty, - 'rate' : flt(d['rate']), - 'amount' : flt(d['amount']), - 'parent_bom' : d['parent_bom'], - 'mat_detail_no' : d['mat_detail_no'], - 'qty_consumed_per_unit' : flt(d['qty_consumed_per_unit'])*qty/flt(self.doc.quantity) - - }) + 'rate' : flt(d['rate']), + })) def add_exploded_items(self): "Add items to Flat BOM table" self.doclist = self.doc.clear_table(self.doclist, 'flat_bom_details', 1) for d in self.cur_exploded_items: - ch = addchild(self.doc, 'flat_bom_details', 'BOM Explosion Item', - self.doclist) - for i in d.keys(): - ch.fields[i] = d[i] + ch = addchild(self.doc, 'flat_bom_details', 'BOM Explosion Item', self.doclist) + for i in self.cur_exploded_items[d].keys(): + ch.fields[i] = self.cur_exploded_items[d][i] + ch.amount = flt(ch.qty) * flt(ch.rate) + ch.qty_consumed_per_unit = flt(ch.qty) / flt(self.doc.quantity) ch.docstatus = self.doc.docstatus ch.save(1) diff --git a/manufacturing/doctype/bom/test_bom.py b/manufacturing/doctype/bom/test_bom.py index e742c0c8ac..cb91e78cc5 100644 --- a/manufacturing/doctype/bom/test_bom.py +++ b/manufacturing/doctype/bom/test_bom.py @@ -48,134 +48,4 @@ test_records = [ "stock_uom": "No." } ] -] - - - -# import webnotes.model -# from webnotes.utils import nowdate, flt -# from accounts.utils import get_fiscal_year -# from webnotes.model.doclist import DocList -# import copy -# -# company = webnotes.conn.get_default("company") -# -# -# def load_data(): -# -# # create default warehouse -# if not webnotes.conn.exists("Warehouse", "Default Warehouse"): -# webnotes.insert({"doctype": "Warehouse", -# "warehouse_name": "Default Warehouse", -# "warehouse_type": "Stores"}) -# -# # create UOM: Nos. -# if not webnotes.conn.exists("UOM", "Nos"): -# webnotes.insert({"doctype": "UOM", "uom_name": "Nos"}) -# -# from webnotes.tests import insert_test_data -# # create item groups and items -# insert_test_data("Item Group", -# sort_fn=lambda ig: (ig[0].get('parent_item_group'), ig[0].get('name'))) -# insert_test_data("Item") -# -# base_bom_fg = [ -# {"doctype": "BOM", "item": "Android Jack D", "quantity": 1, -# "is_active": "Yes", "is_default": 1, "uom": "Nos"}, -# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations", -# "opn_description": "Development", "hour_rate": 10, "time_in_mins": 90}, -# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1, -# "qty": 2, "rate": 20, "stock_uom": "Nos", "parentfield": "bom_materials"}, -# {"doctype": "BOM Item", "item_code": "Home Desktop 100", "operation_no": 1, -# "qty": 1, "rate": 300, "stock_uom": "Nos", "parentfield": "bom_materials"}, -# {"doctype": "BOM Item", "item_code": "Nebula 7", "operation_no": 1, -# "qty": 5, "stock_uom": "Nos", "parentfield": "bom_materials"}, -# ] -# -# base_bom_child = [ -# {"doctype": "BOM", "item": "Nebula 7", "quantity": 5, -# "is_active": "Yes", "is_default": 1, "uom": "Nos"}, -# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations", -# "opn_description": "Development"}, -# {"doctype": "BOM Item", "item_code": "Android Jack S", "operation_no": 1, -# "qty": 10, "stock_uom": "Nos", "parentfield": "bom_materials"} -# ] -# -# base_bom_grandchild = [ -# {"doctype": "BOM", "item": "Android Jack S", "quantity": 1, -# "is_active": "Yes", "is_default": 1, "uom": "Nos"}, -# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations", -# "opn_description": "Development"}, -# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1, -# "qty": 3, "rate": 10, "stock_uom": "Nos", "parentfield": "bom_materials"} -# ] -# -# -# class TestPurchaseReceipt(unittest.TestCase): -# def setUp(self): -# webnotes.conn.begin() -# load_data() -# -# def test_bom_validation(self): -# # show throw error bacause bom no missing for sub-assembly item -# bom_fg = copy.deepcopy(base_bom_fg) -# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg)) -# -# # main item is not a manufacturing item -# bom_fg = copy.deepcopy(base_bom_fg) -# bom_fg[0]["item"] = "Home Desktop 200" -# bom_fg.pop(4) -# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg)) -# -# # operation no mentioed in material table not matching with operation table -# bom_fg = copy.deepcopy(base_bom_fg) -# bom_fg.pop(4) -# bom_fg[2]["operation_no"] = 2 -# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg)) -# -# -# def test_bom(self): -# gc_wrapper = webnotes.insert(DocList(base_bom_grandchild)) -# gc_wrapper.submit() -# -# bom_child = copy.deepcopy(base_bom_child) -# bom_child[2]["bom_no"] = gc_wrapper.doc.name -# child_wrapper = webnotes.insert(DocList(bom_child)) -# child_wrapper.submit() -# -# bom_fg = copy.deepcopy(base_bom_fg) -# bom_fg[4]["bom_no"] = child_wrapper.doc.name -# fg_wrapper = webnotes.insert(DocList(bom_fg)) -# fg_wrapper.load_from_db() -# -# self.check_bom_cost(fg_wrapper) -# -# self.check_flat_bom(fg_wrapper, child_wrapper, gc_wrapper) -# -# def check_bom_cost(self, fg_wrapper): -# expected_values = { -# "operating_cost": 15, -# "raw_material_cost": 640, -# "total_cost": 655 -# } -# -# for key in expected_values: -# self.assertEqual(flt(expected_values[key]), flt(fg_wrapper.doc.fields.get(key))) -# -# def check_flat_bom(self, fg_wrapper, child_wrapper, gc_wrapper): -# expected_flat_bom_items = { -# ("Home Desktop 300", fg_wrapper.doc.name): (2, 20), -# ("Home Desktop 100", fg_wrapper.doc.name): (1, 300), -# ("Home Desktop 300", gc_wrapper.doc.name): (30, 10) -# } -# -# self.assertEqual(len(fg_wrapper.doclist.get({"parentfield": "flat_bom_details"})), 3) -# -# for key, val in expected_flat_bom_items.items(): -# flat_bom = fg_wrapper.doclist.get({"parentfield": "flat_bom_details", -# "item_code": key[0], "parent_bom": key[1]})[0] -# self.assertEqual(val, (flat_bom.qty, flat_bom.rate)) -# -# -# def tearDown(self): -# webnotes.conn.rollback() \ No newline at end of file +] \ No newline at end of file diff --git a/manufacturing/doctype/bom_explosion_item/bom_explosion_item.txt b/manufacturing/doctype/bom_explosion_item/bom_explosion_item.txt index 07aad7dc66..3808cdfb00 100644 --- a/manufacturing/doctype/bom_explosion_item/bom_explosion_item.txt +++ b/manufacturing/doctype/bom_explosion_item/bom_explosion_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:48", + "creation": "2013-03-07 11:42:57", "docstatus": 0, - "modified": "2013-03-07 07:03:18", + "modified": "2013-06-04 13:13:28", "modified_by": "Administrator", "owner": "Administrator" }, @@ -80,25 +80,6 @@ "oldfieldtype": "Link", "options": "UOM" }, - { - "doctype": "DocField", - "fieldname": "parent_bom", - "fieldtype": "Link", - "hidden": 0, - "label": "Parent BOM", - "oldfieldname": "parent_bom", - "oldfieldtype": "Link", - "options": "BOM", - "print_width": "250px", - "width": "250px" - }, - { - "doctype": "DocField", - "fieldname": "mat_detail_no", - "fieldtype": "Data", - "hidden": 1, - "label": "Mat Detail No" - }, { "doctype": "DocField", "fieldname": "qty_consumed_per_unit", diff --git a/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py b/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py index 4c9c42da2e..e69c48723d 100644 --- a/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py +++ b/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py @@ -29,9 +29,10 @@ class DocType: self.validate_bom() self.update_new_bom() bom_list = self.get_parent_boms() + updated_bom = [] for bom in bom_list: bom_obj = get_obj("BOM", bom, with_children=1) - bom_obj.update_cost_and_exploded_items() + updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) webnotes.msgprint(_("BOM replaced")) diff --git a/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/manufacturing/doctype/production_planning_tool/production_planning_tool.py index d4e41aca5f..ed7f7bfb3f 100644 --- a/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -241,40 +241,30 @@ class DocType: def get_raw_materials(self, bom_dict): """ Get raw materials considering sub-assembly items { - "item_code": [qty_required, description, stock_uom] + "item_code": [qty_required, description, stock_uom, min_order_qty] } """ for bom in bom_dict: if self.doc.use_multi_level_bom: # get all raw materials with sub assembly childs - fl_bom_items = sql(""" - select - item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty, - description, stock_uom, min_order_qty - from - ( - select distinct fb.name, fb.description, fb.item_code, - fb.qty_consumed_per_unit, fb.stock_uom, it.min_order_qty - from `tabBOM Explosion Item` fb,`tabItem` it - where it.name = fb.item_code - and ifnull(it.is_pro_applicable, 'No') = 'No' - and ifnull(it.is_sub_contracted_item, 'No') = 'No' - and fb.docstatus<2 and fb.parent=%s - ) a - group by item_code,stock_uom - """ , (flt(bom_dict[bom]), bom)) + fl_bom_items = sql("""select fb.item_code, + ifnull(sum(fb.qty_consumed_per_unit), 0)*%s as qty, + fb.description, fb.stock_uom, it.min_order_qty + from `tabBOM Explosion Item` fb,`tabItem` it + where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No' + and ifnull(it.is_sub_contracted_item, 'No') = 'No' + and fb.docstatus<2 and fb.parent=%s + group by item_code, stock_uom""", (flt(bom_dict[bom]), bom)) else: # Get all raw materials considering SA items as raw materials, # so no childs of SA items - fl_bom_items = sql(""" - select bom_item.item_code, + fl_bom_items = sql("""select bom_item.item_code, ifnull(sum(bom_item.qty_consumed_per_unit), 0) * %s, bom_item.description, bom_item.stock_uom, item.min_order_qty from `tabBOM Item` bom_item, tabItem item where bom_item.parent = %s and bom_item.docstatus < 2 - and bom_item.item_code = item.name - group by item_code - """, (flt(bom_dict[bom]), bom)) + and bom_item.item_code = item.name + group by item_code""", (flt(bom_dict[bom]), bom)) self.make_items_dict(fl_bom_items) def make_items_dict(self, item_list): diff --git a/patches/june_2013/__init__.py b/patches/june_2013/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/patches/june_2013/p01_update_bom_exploded_items.py b/patches/june_2013/p01_update_bom_exploded_items.py new file mode 100644 index 0000000000..dff70229bb --- /dev/null +++ b/patches/june_2013/p01_update_bom_exploded_items.py @@ -0,0 +1,28 @@ +# 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 webnotes + +def execute(): + updated_bom = [] + for bom in webnotes.conn.sql("select name from tabBOM where docstatus < 2"): + webnotes.errprint(bom[0]) + if bom[0] not in updated_bom: + bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) + updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) + + webnotes.errprint(updated_bom) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index ea61a04dc3..6a396725dd 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -252,4 +252,5 @@ patch_list = [ "patches.may_2013.p03_update_support_ticket", "patches.may_2013.p04_reorder_level", "patches.may_2013.p05_update_cancelled_gl_entries", + "patches.june_2013.p01_update_bom_exploded_items", ] \ 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 bce0f620d4..522a14af6b 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -506,17 +506,13 @@ class DocType(StockController): if self.doc.use_multi_level_bom: # get all raw materials with sub assembly childs - fl_bom_sa_child_item = sql("""select - item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty, - description,stock_uom - from ( select distinct fb.name, fb.description, fb.item_code, - fb.qty_consumed_per_unit, fb.stock_uom - from `tabBOM Explosion Item` fb,`tabItem` it - where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No' - and ifnull(it.is_sub_contracted_item, 'No') = 'No' and fb.docstatus<2 - and fb.parent=%s - ) a - group by item_code, stock_uom""" , (qty, self.doc.bom_no), as_dict=1) + fl_bom_sa_child_item = sql("""select fb.item_code, + ifnull(sum(fb.qty_consumed_per_unit),0)*%s as qty, fb.description, fb.stock_uom + from `tabBOM Explosion Item` fb,`tabItem` it + where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No' + and ifnull(it.is_sub_contracted_item, 'No') = 'No' and fb.docstatus < 2 + and fb.parent=%s group by item_code, stock_uom""", + (qty, self.doc.bom_no), as_dict=1) if fl_bom_sa_child_item: _make_items_dict(fl_bom_sa_child_item) @@ -524,10 +520,10 @@ class DocType(StockController): # Get all raw materials considering multi level BOM, # if multi level bom consider childs of Sub-Assembly items fl_bom_sa_items = sql("""select item_code, - ifnull(sum(qty_consumed_per_unit), 0) * '%s' as qty, + ifnull(sum(qty_consumed_per_unit), 0) *%s as qty, description, stock_uom from `tabBOM Item` - where parent = '%s' and docstatus < 2 - group by item_code""" % (qty, self.doc.bom_no), as_dict=1) + where parent = %s and docstatus < 2 + group by item_code""", (qty, self.doc.bom_no), as_dict=1) if fl_bom_sa_items: _make_items_dict(fl_bom_sa_items) From 89ab4719ff3648a82ea9214629f1ddea2c1c731e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 17:07:57 +0530 Subject: [PATCH 02/12] bom exploded items grouped by items --- patches/june_2013/p01_update_bom_exploded_items.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/patches/june_2013/p01_update_bom_exploded_items.py b/patches/june_2013/p01_update_bom_exploded_items.py index dff70229bb..6a6169d0cc 100644 --- a/patches/june_2013/p01_update_bom_exploded_items.py +++ b/patches/june_2013/p01_update_bom_exploded_items.py @@ -20,9 +20,6 @@ import webnotes def execute(): updated_bom = [] for bom in webnotes.conn.sql("select name from tabBOM where docstatus < 2"): - webnotes.errprint(bom[0]) if bom[0] not in updated_bom: bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) - updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) - - webnotes.errprint(updated_bom) \ No newline at end of file + updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) \ No newline at end of file From 8d7310ac5fd9b11f1054532f25ed0b5bee5534e2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 17:11:43 +0530 Subject: [PATCH 03/12] bom exploded items grouped by items --- patches/june_2013/p01_update_bom_exploded_items.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/patches/june_2013/p01_update_bom_exploded_items.py b/patches/june_2013/p01_update_bom_exploded_items.py index 6a6169d0cc..657c2c036a 100644 --- a/patches/june_2013/p01_update_bom_exploded_items.py +++ b/patches/june_2013/p01_update_bom_exploded_items.py @@ -22,4 +22,5 @@ def execute(): for bom in webnotes.conn.sql("select name from tabBOM where docstatus < 2"): if bom[0] not in updated_bom: bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) - updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) \ No newline at end of file + updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) + webnotes.conn.commit() \ No newline at end of file From 0323e0048d06518342ca10c5f894ba9bcc9da71f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 17:14:17 +0530 Subject: [PATCH 04/12] bom exploded items grouped by items --- manufacturing/doctype/bom/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manufacturing/doctype/bom/bom.py b/manufacturing/doctype/bom/bom.py index 4ce4feb156..5f641486b5 100644 --- a/manufacturing/doctype/bom/bom.py +++ b/manufacturing/doctype/bom/bom.py @@ -266,7 +266,7 @@ class DocType: for b in boms: if b[0] == self.doc.name: msgprint("""Recursion Occured => '%s' cannot be '%s' of '%s'. - """ % (cstr(b), cstr(d[2]), self.doc.name), raise_exception = 1) + """ % (cstr(b[0]), cstr(d[2]), self.doc.name), raise_exception = 1) if b[0]: bom_list.append(b[0]) From aec5553814d276110962d4f8e0b94a0a76c3ef4f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 17:20:47 +0530 Subject: [PATCH 05/12] bom exploded items grouped by items --- patches/june_2013/p01_update_bom_exploded_items.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/patches/june_2013/p01_update_bom_exploded_items.py b/patches/june_2013/p01_update_bom_exploded_items.py index 657c2c036a..eff0931e4d 100644 --- a/patches/june_2013/p01_update_bom_exploded_items.py +++ b/patches/june_2013/p01_update_bom_exploded_items.py @@ -21,6 +21,9 @@ def execute(): updated_bom = [] for bom in webnotes.conn.sql("select name from tabBOM where docstatus < 2"): if bom[0] not in updated_bom: - bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) - updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) - webnotes.conn.commit() \ No newline at end of file + try: + bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) + updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) + webnotes.conn.commit() + except: + pass \ No newline at end of file From 1725315d105b32eda172cbe7761bf17ea5ad4067 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 4 Jun 2013 19:35:28 +0530 Subject: [PATCH 06/12] Finalized batch-wise balance history --- .../batch_wise_balance_history/__init__.py | 0 .../batch_wise_balance_history.js | 39 +++++++ .../batch_wise_balance_history.py | 109 ++++++++++++++++++ .../batch_wise_balance_history.txt | 21 ++++ 4 files changed, 169 insertions(+) create mode 100644 stock/report/batch_wise_balance_history/__init__.py create mode 100644 stock/report/batch_wise_balance_history/batch_wise_balance_history.js create mode 100644 stock/report/batch_wise_balance_history/batch_wise_balance_history.py create mode 100644 stock/report/batch_wise_balance_history/batch_wise_balance_history.txt diff --git a/stock/report/batch_wise_balance_history/__init__.py b/stock/report/batch_wise_balance_history/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.js b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js new file mode 100644 index 0000000000..0ba1938a59 --- /dev/null +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js @@ -0,0 +1,39 @@ +wn.query_reports["Batch-Wise Balance History"] = { + "filters": [ + { + "fieldname":"item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + "width": "80" + }, + { + "fieldname":"warehouse", + "label": "Warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": "80" + }, + { + "fieldname":"batch_no", + "label": "Batch", + "fieldtype": "Link", + "options": "Batch", + "width": "80" + }, + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "width": "80", + "default": sys_defaults.year_start_date, + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "width": "80", + "default": wn.datetime.get_today() + } + ] +} \ No newline at end of file diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py new file mode 100644 index 0000000000..ca3e775f72 --- /dev/null +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -0,0 +1,109 @@ +# 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 webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + item_map = get_item_details(filters) + iwb_map = get_item_warehouse_batch_map(filters) + + data = [] + for item in sorted(iwb_map): + for wh in sorted(iwb_map[item]): + for batch in sorted(iwb_map[item][wh]): + qty_dict = iwb_map[item][wh][batch] + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], wh, batch, + qty_dict.opening_qty, qty_dict.in_qty, + qty_dict.out_qty, qty_dict.bal_qty + ]) + + return columns, data + +def get_columns(filters): + """return columns based on filters""" + + columns = ["Item:Link/Item:100"] + ["Item Name::150"] + ["Description::150"] + \ + ["Warehouse:Link/Warehouse:100"] + ["Batch:Link/Batch:100"] + ["Opening Qty::90"] + \ + ["In Qty::80"] + ["Out Qty::80"] + ["Balance Qty::90"] + + return columns + +def get_conditions(filters): + conditions = "" + if filters.get("item_code"): + conditions += " and item_code='%s'" % filters["item_code"] + + if filters.get("warehouse"): + conditions += " and warehouse='%s'" % filters["warehouse"] + + if filters.get("batch_no"): + conditions += " and batch_no='%s'" % filters["batch_no"] + + if not filters.get("from_date"): + webnotes.msgprint("Please enter From Date", raise_exception=1) + + if filters.get("to_date"): + conditions += " and posting_date <= '%s'" % filters["to_date"] + else: + webnotes.msgprint("Please enter To Date", raise_exception=1) + + return conditions + +#get all details +def get_stock_ledger_entries(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select item_code, batch_no, warehouse, + posting_date, actual_qty + from `tabStock Ledger Entry` + where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % + conditions, as_dict=1) + +def get_item_warehouse_batch_map(filters): + sle = get_stock_ledger_entries(filters) + iwb_map = {} + + for d in sle: + iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {})\ + .setdefault(d.batch_no, webnotes._dict({ + "opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0 + })) + qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no] + if d.posting_date < filters["from_date"]: + qty_dict.opening_qty += flt(d.actual_qty) + elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]: + if flt(d.actual_qty) > 0: + qty_dict.in_qty += flt(d.actual_qty) + else: + qty_dict.out_qty += abs(flt(d.actual_qty)) + + qty_dict.bal_qty += flt(d.actual_qty) + + return iwb_map + +def get_item_details(filters): + if filters.get("item_code"): + conditions = " and name = '%s'" % filters["item_code"] + item_map = {} + for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): + item_map.setdefault(d.name, d) + + return item_map \ No newline at end of file diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.txt b/stock/report/batch_wise_balance_history/batch_wise_balance_history.txt new file mode 100644 index 0000000000..9e795b9f31 --- /dev/null +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-04 11:03:47", + "docstatus": 0, + "modified": "2013-06-04 19:32:27", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Stock Ledger Entry", + "report_name": "Batch-Wise Balance History", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Batch-Wise Balance History" + } +] \ No newline at end of file From 6653a957676f609b9b1d78739b78a68c231f9278 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Wed, 5 Jun 2013 11:36:24 +0530 Subject: [PATCH 07/12] Completed Warehouse-Wise Stock Balance --- stock/page/stock_home/stock_home.js | 8 ++ .../warehouse_wise_stock_balance/__init__.py | 0 .../warehouse_wise_stock_balance.js | 32 ++++++ .../warehouse_wise_stock_balance.py | 104 ++++++++++++++++++ .../warehouse_wise_stock_balance.txt | 21 ++++ 5 files changed, 165 insertions(+) create mode 100644 stock/report/warehouse_wise_stock_balance/__init__.py create mode 100644 stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js create mode 100644 stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py create mode 100644 stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index 75e4ee15b0..bc71a4faf5 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -205,6 +205,14 @@ wn.module_page["Stock"] = [ "label":wn._("Requested Items To Be Transferred"), route: "query-report/Requested Items To Be Transferred", }, + { + "label":wn._("Batch-Wise Balance History"), + route: "query-report/Batch-Wise Balance History", + }, + { + "label":wn._("Warehouse-Wise Stock Balance"), + route: "query-report/Warehouse-Wise Stock Balance", + }, ] } ] diff --git a/stock/report/warehouse_wise_stock_balance/__init__.py b/stock/report/warehouse_wise_stock_balance/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js new file mode 100644 index 0000000000..5e1eb3a7b2 --- /dev/null +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js @@ -0,0 +1,32 @@ +wn.query_reports["Warehouse-Wise Stock Balance"] = { + "filters": [ + { + "fieldname":"item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + "width": "80" + }, + { + "fieldname":"warehouse", + "label": "Warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": "80" + }, + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "width": "80", + "default": sys_defaults.year_start_date, + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "width": "80", + "default": wn.datetime.get_today() + } + ] +} \ No newline at end of file diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py new file mode 100644 index 0000000000..324bbe3e8c --- /dev/null +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py @@ -0,0 +1,104 @@ +# 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 webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + item_map = get_item_details(filters) + iwb_map = get_item_warehouse_map(filters) + + data = [] + for item in sorted(iwb_map): + for wh in sorted(iwb_map[item]): + qty_dict = iwb_map[item][wh] + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], wh, + qty_dict.opening_qty, qty_dict.in_qty, + qty_dict.out_qty, qty_dict.bal_qty + ]) + + return columns, data + +def get_columns(filters): + """return columns based on filters""" + + columns = ["Item:Link/Item:100"] + ["Item Name::150"] + ["Description::150"] + \ + ["Warehouse:Link/Warehouse:100"] + ["Opening Qty::90"] + \ + ["In Qty::80"] + ["Out Qty::80"] + ["Balance Qty::90"] + + return columns + +def get_conditions(filters): + conditions = "" + if filters.get("item_code"): + conditions += " and item_code='%s'" % filters["item_code"] + + if filters.get("warehouse"): + conditions += " and warehouse='%s'" % filters["warehouse"] + + if not filters.get("from_date"): + webnotes.msgprint("Please enter From Date", raise_exception=1) + + if filters.get("to_date"): + conditions += " and posting_date <= '%s'" % filters["to_date"] + else: + webnotes.msgprint("Please enter To Date", raise_exception=1) + + return conditions + +#get all details +def get_stock_ledger_entries(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select item_code, warehouse, + posting_date, actual_qty + from `tabStock Ledger Entry` + where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % + conditions, as_dict=1) + +def get_item_warehouse_map(filters): + sle = get_stock_ledger_entries(filters) + iwb_map = {} + + for d in sle: + iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, webnotes._dict({\ + "opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0 + })) + qty_dict = iwb_map[d.item_code][d.warehouse] + if d.posting_date < filters["from_date"]: + qty_dict.opening_qty += flt(d.actual_qty) + elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]: + if flt(d.actual_qty) > 0: + qty_dict.in_qty += flt(d.actual_qty) + else: + qty_dict.out_qty += abs(flt(d.actual_qty)) + + qty_dict.bal_qty += flt(d.actual_qty) + + return iwb_map + +def get_item_details(filters): + if filters.get("item_code"): + conditions = " and name = '%s'" % filters["item_code"] + item_map = {} + for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): + item_map.setdefault(d.name, d) + + return item_map \ No newline at end of file diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt new file mode 100644 index 0000000000..2513587632 --- /dev/null +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-05 11:00:31", + "docstatus": 0, + "modified": "2013-06-05 11:00:31", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Stock Ledger Entry", + "report_name": "Warehouse-Wise Stock Balance", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Warehouse-Wise Stock Balance" + } +] \ No newline at end of file From 196b6b8b68df735321abea0b80abb3f0a1836f45 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 5 Jun 2013 16:12:42 +0530 Subject: [PATCH 08/12] [report] itemwise sales register --- accounts/page/accounts_home/accounts_home.js | 5 ++ .../item_wise_sales_register/__init__.py | 0 .../item_wise_sales_register.js | 39 +++++++++++ .../item_wise_sales_register.py | 67 +++++++++++++++++++ .../item_wise_sales_register.txt | 22 ++++++ 5 files changed, 133 insertions(+) create mode 100644 accounts/report/item_wise_sales_register/__init__.py create mode 100644 accounts/report/item_wise_sales_register/item_wise_sales_register.js create mode 100644 accounts/report/item_wise_sales_register/item_wise_sales_register.py create mode 100644 accounts/report/item_wise_sales_register/item_wise_sales_register.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 03009023c1..7f623d7115 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -252,6 +252,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Item-wise Sales Register", doctype: "Sales Invoice" }, + { + "label":wn._("Item-wise Purchase Register"), + route: "query-report/Item-wise Purchase Register", + doctype: "Purchase Invoice" + }, ] } ] diff --git a/accounts/report/item_wise_sales_register/__init__.py b/accounts/report/item_wise_sales_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/accounts/report/item_wise_sales_register/item_wise_sales_register.js new file mode 100644 index 0000000000..b9ce9595fe --- /dev/null +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -0,0 +1,39 @@ +wn.query_reports["Item-wise Sales Register"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname": "item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Debit", + "master_type": "Customer" + } + } + } + } + ] +} \ No newline at end of file diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/accounts/report/item_wise_sales_register/item_wise_sales_register.py new file mode 100644 index 0000000000..f6e26af350 --- /dev/null +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -0,0 +1,67 @@ +# 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 webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + item_list = get_items(filters) + + data = [] + for d in item_list: + data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, d.customer, + d.debit_to, d.territory, d.project_name, d.company, d.sales_order, d.delivery_note, + d.income_account, d.qty, d.basic_rate, d.amount]) + + return columns, data + + +def get_columns(): + return [ + "Item Code:Link/Item:120", "Item Name::120", "Item Group:Link/Item Group:100", + "Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer:Link/Customer:120", + "Customer Account:Link/Account:120", "Territory:Link/Territory:80", + "Project:Link/Project:80", "Company:Link/Company:100", "Sales Order:Link/Sales Order:100", + "Delivery Note:Link/Delivery Note:100", "Income Account:Link/Account:140", + "Qty:Float:120", "Rate:Currency:120", "Amount:Currency:120" + ] + + +def get_conditions(filters): + conditions = "" + + if filters.get("account"): conditions += " and si.debit_to = %(account)s" + + if filters.get("item_code"): conditions += " and si_item.item_code = %(item_code)s" + + if filters.get("from_date"): conditions += " and si.posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and si.posting_date<=%(to_date)s" + + return conditions + +def get_items(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select si.name, si.posting_date, si.debit_to, si.project_name, + si.customer, si.remarks, si.territory, si_item.item_code, si_item.item_name, + si_item.item_group, si_item.sales_order, si_item.delivery_note, si_item.income_account, + si_item.qty, si_item.basic_rate, si_item.amount + from `tabSales Invoice` si, `tabSales Invoice Item` si_item + where si.name = si_item.parent and si.docstatus = 1 %s + order by si.posting_date desc, si_item.item_code desc""" % conditions, filters, as_dict=1) \ No newline at end of file diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.txt b/accounts/report/item_wise_sales_register/item_wise_sales_register.txt new file mode 100644 index 0000000000..fb0555d459 --- /dev/null +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-13 17:50:55", + "docstatus": 0, + "modified": "2013-05-13 17:50:55", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Invoice", + "report_name": "Item-wise Sales Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Item-wise Sales Register" + } +] \ No newline at end of file From c36e2653f9b29de938dc943a60d908023c73d535 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 5 Jun 2013 16:13:05 +0530 Subject: [PATCH 09/12] [report] itemwise purchase register --- .../item_wise_purchase_register/__init__.py | 0 .../item_wise_purchase_register.js | 39 ++++++++++ .../item_wise_purchase_register.py | 74 +++++++++++++++++++ .../item_wise_purchase_register.txt | 22 ++++++ 4 files changed, 135 insertions(+) create mode 100644 accounts/report/item_wise_purchase_register/__init__.py create mode 100644 accounts/report/item_wise_purchase_register/item_wise_purchase_register.js create mode 100644 accounts/report/item_wise_purchase_register/item_wise_purchase_register.py create mode 100644 accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt diff --git a/accounts/report/item_wise_purchase_register/__init__.py b/accounts/report/item_wise_purchase_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js new file mode 100644 index 0000000000..8323a1af78 --- /dev/null +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js @@ -0,0 +1,39 @@ +wn.query_reports["Item-wise Purchase Register"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname": "item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Credit", + "master_type": "Supplier" + } + } + } + } + ] +} \ No newline at end of file diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py new file mode 100644 index 0000000000..ad9d79504b --- /dev/null +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -0,0 +1,74 @@ +# 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 webnotes + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + item_list = get_items(filters) + aii_account_map = get_aii_accounts() + webnotes.errprint(aii_account_map) + data = [] + for d in item_list: + expense_head = d.expense_head or aii_account_map.get(d.company) + data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, d.supplier, + d.credit_to, d.project_name, d.company, d.purchase_order, d.purchase_receipt, + expense_head, d.qty, d.rate, d.amount]) + + return columns, data + + +def get_columns(): + return ["Item Code:Link/Item:120", "Item Name::120", "Item Group:Link/Item Group:100", + "Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier:Link/Customer:120", + "Supplier Account:Link/Account:120", "Project:Link/Project:80", "Company:Link/Company:100", + "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100", + "Expense Account:Link/Account:140", "Qty:Float:120", "Rate:Currency:120", + "Amount:Currency:120"] + + +def get_conditions(filters): + conditions = "" + + if filters.get("account"): conditions += " and pi.credit_to = %(account)s" + + if filters.get("item_code"): conditions += " and pi_item.item_code = %(item_code)s" + + if filters.get("from_date"): conditions += " and pi.posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and pi.posting_date<=%(to_date)s" + + return conditions + +def get_items(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select pi.name, pi.posting_date, pi.credit_to, pi.company, + pi.supplier, pi.remarks, pi_item.item_code, pi_item.item_name, pi_item.item_group, + pi_item.project_name, pi_item.purchase_order, pi_item.purchase_receipt, + pi_item.expense_head, pi_item.qty, pi_item.rate, pi_item.amount + from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item + where pi.name = pi_item.parent and pi.docstatus = 1 %s + order by pi.posting_date desc, pi_item.item_code desc""" % conditions, filters, as_dict=1) + +def get_aii_accounts(): + aii_account_map = {} + for d in webnotes.conn.sql("select name, stock_received_but_not_billed from tabCompany", + as_dict=1): + aii_account_map.setdefault(d.name, d.stock_received_but_not_billed) + + return aii_account_map \ No newline at end of file diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt new file mode 100644 index 0000000000..7ded5ff583 --- /dev/null +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-06-05 15:37:30", + "docstatus": 0, + "modified": "2013-06-05 15:37:30", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Invoice", + "report_name": "Item-wise Purchase Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Item-wise Purchase Register" + } +] \ No newline at end of file From e3e15a94c3bdf8a11905355c2499187ef906d2a4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 6 Jun 2013 11:35:49 +0530 Subject: [PATCH 10/12] [system console] [security fix] removed system console --- patches/patch_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/patches/patch_list.py b/patches/patch_list.py index 6a396725dd..d8b84da8f8 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -253,4 +253,5 @@ patch_list = [ "patches.may_2013.p04_reorder_level", "patches.may_2013.p05_update_cancelled_gl_entries", "patches.june_2013.p01_update_bom_exploded_items", + "execute:webnotes.delete_doc('DocType', 'System Console')", ] \ No newline at end of file From 657f8a667737e8a693db113f744a463c6e56e933 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Thu, 6 Jun 2013 18:33:26 +0530 Subject: [PATCH 11/12] Completed 'Item Prices' Report --- stock/page/stock_home/stock_home.js | 4 + stock/report/item_prices/__init__.py | 0 stock/report/item_prices/item_prices.js | 3 + stock/report/item_prices/item_prices.py | 106 +++++++++++++++++++++++ stock/report/item_prices/item_prices.txt | 21 +++++ 5 files changed, 134 insertions(+) create mode 100644 stock/report/item_prices/__init__.py create mode 100644 stock/report/item_prices/item_prices.js create mode 100644 stock/report/item_prices/item_prices.py create mode 100644 stock/report/item_prices/item_prices.txt diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index bc71a4faf5..bd28b94291 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -213,6 +213,10 @@ wn.module_page["Stock"] = [ "label":wn._("Warehouse-Wise Stock Balance"), route: "query-report/Warehouse-Wise Stock Balance", }, + { + "label":wn._("Item Prices"), + route: "query-report/Item Prices", + }, ] } ] diff --git a/stock/report/item_prices/__init__.py b/stock/report/item_prices/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/item_prices/item_prices.js b/stock/report/item_prices/item_prices.js new file mode 100644 index 0000000000..3dfa7cad33 --- /dev/null +++ b/stock/report/item_prices/item_prices.js @@ -0,0 +1,3 @@ +wn.query_reports["Item Prices"] = { + "filters": [] +} \ No newline at end of file diff --git a/stock/report/item_prices/item_prices.py b/stock/report/item_prices/item_prices.py new file mode 100644 index 0000000000..ea0be477df --- /dev/null +++ b/stock/report/item_prices/item_prices.py @@ -0,0 +1,106 @@ +# 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 webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + item_map = get_item_details() + pl = get_price_list() + bom_rate = get_item_bom_rate() + val_rate_map = get_valuation_rate() + + data = [] + for item in sorted(item_map): + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], item_map[item]["stock_uom"], + flt(item_map[item]["last_purchase_rate"]), val_rate_map.get(item, 0), + pl.get(item, {}).get("selling"), pl.get(item, {}).get("buying"), + bom_rate.get(item, 0), flt(item_map[item]["standard_rate"]) + ]) + + return columns, data + +def get_columns(filters): + """return columns based on filters""" + + columns = ["Item:Link/Item:100", "Item Name::150", "Description::150", "UOM:Link/UOM:80", + "Last Purchase Rate:Currency:90", "Valuation Rate:Currency:80", "Sales Price List::80", + "Purchase Price List::80", "BOM Rate:Currency:90", "Standard Rate:Currency:100"] + + return columns + +def get_item_details(): + """returns all items details""" + + item_map = {} + + for i in webnotes.conn.sql("select name, item_name, description, \ + stock_uom, standard_rate, last_purchase_rate from tabItem \ + order by item_code", as_dict=1): + item_map.setdefault(i.name, i) + + return item_map + +def get_price_list(): + """Get selling & buying price list of every item""" + + rate = {} + + price_list = webnotes.conn.sql("""select parent, selling, buying, + concat(price_list_name, " - ", ref_currency, " ", ref_rate) as price + from `tabItem Price` where docstatus<2""", as_dict=1) + + for j in price_list: + if j.selling: + rate.setdefault(j.parent, {}).setdefault("selling", []).append(j.price) + if j.buying: + rate.setdefault(j.parent, {}).setdefault("buying", []).append(j.price) + + item_rate_map = {} + + for item in rate: + item_rate_map.setdefault(item, {}).setdefault("selling", + ", ".join(rate[item].get("selling", []))) + item_rate_map[item]["buying"] = ", ".join(rate[item].get("buying", [])) + + return item_rate_map + +def get_item_bom_rate(): + """Get BOM rate of an item from BOM""" + + bom_map = {} + + for b in webnotes.conn.sql("""select item, (total_cost/quantity) as bom_rate + from `tabBOM` where is_active=1 and is_default=1""", as_dict=1): + bom_map.setdefault(b.item, flt(b.bom_rate)) + + return bom_map + +def get_valuation_rate(): + """Get an average valuation rate of an item from all warehouses""" + + val_rate_map = {} + + for d in webnotes.conn.sql("""select item_code, avg(valuation_rate) as val_rate + from tabBin group by item_code""", as_dict=1): + val_rate_map.setdefault(d.item_code, d.val_rate) + + return val_rate_map \ No newline at end of file diff --git a/stock/report/item_prices/item_prices.txt b/stock/report/item_prices/item_prices.txt new file mode 100644 index 0000000000..4c49ca1748 --- /dev/null +++ b/stock/report/item_prices/item_prices.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-05 11:43:30", + "docstatus": 0, + "modified": "2013-06-05 11:43:30", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Stock Ledger Entry", + "report_name": "Item Prices", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Item Prices" + } +] \ No newline at end of file From 426b57e1b41feb6f273838097e35e524907364ac Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Fri, 7 Jun 2013 11:52:15 +0530 Subject: [PATCH 12/12] Deleted item_prices.js --- stock/report/item_prices/item_prices.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 stock/report/item_prices/item_prices.js diff --git a/stock/report/item_prices/item_prices.js b/stock/report/item_prices/item_prices.js deleted file mode 100644 index 3dfa7cad33..0000000000 --- a/stock/report/item_prices/item_prices.js +++ /dev/null @@ -1,3 +0,0 @@ -wn.query_reports["Item Prices"] = { - "filters": [] -} \ No newline at end of file