From 4c17f9420e61901c26243bbf5bea47c580834b3c Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 12 Aug 2013 14:18:09 +0530 Subject: [PATCH 1/4] [manufacturing] Added New Reports --- .../production_order/production_order.js | 33 ++++---- .../production_order/production_order.py | 21 ++++- .../manufacturing_home/manufacturing_home.js | 15 ++++ .../completed_production_orders/__init__.py | 0 .../completed_production_orders.txt | 22 +++++ .../report/open_production_orders/__init__.py | 0 .../open_production_orders.txt | 22 +++++ .../production_orders_in_progress/__init__.py | 0 .../production_orders_in_progress.txt | 22 +++++ stock/doctype/stock_entry/stock_entry.js | 81 +++++++++---------- stock/doctype/stock_entry/stock_entry.py | 3 +- stock/stock_ledger.py | 5 +- stock/utils.py | 2 +- utilities/make_demo.py | 63 +++++++++++++-- 14 files changed, 219 insertions(+), 70 deletions(-) create mode 100644 manufacturing/report/completed_production_orders/__init__.py create mode 100644 manufacturing/report/completed_production_orders/completed_production_orders.txt create mode 100644 manufacturing/report/open_production_orders/__init__.py create mode 100644 manufacturing/report/open_production_orders/open_production_orders.txt create mode 100644 manufacturing/report/production_orders_in_progress/__init__.py create mode 100644 manufacturing/report/production_orders_in_progress/production_orders_in_progress.txt diff --git a/manufacturing/doctype/production_order/production_order.js b/manufacturing/doctype/production_order/production_order.js index 012c27980d7..f680776a565 100644 --- a/manufacturing/doctype/production_order/production_order.js +++ b/manufacturing/doctype/production_order/production_order.js @@ -61,30 +61,25 @@ cur_frm.cscript['Unstop Production Order'] = function() { } cur_frm.cscript['Transfer Raw Materials'] = function() { - var doc = cur_frm.doc; - cur_frm.cscript.make_se(doc, 'Material Transfer'); + cur_frm.cscript.make_se('Material Transfer'); } cur_frm.cscript['Update Finished Goods'] = function() { - var doc = cur_frm.doc; - cur_frm.cscript.make_se(doc, 'Manufacture/Repack'); + cur_frm.cscript.make_se('Manufacture/Repack'); } -cur_frm.cscript.make_se = function(doc, purpose) { - var se = wn.model.get_new_doc("Stock Entry"); - se.purpose = purpose; - se.production_order = doc.name; - if(purpose==="Material Transfer") { - se.to_warehouse = doc.wip_warehouse; - } else { - se.from_warehouse = doc.wip_warehouse; - se.to_warehouse = doc.fg_warehouse; - } - se.company = doc.company; - se.fg_completed_qty = doc.qty - doc.produced_qty; - se.bom_no = doc.bom_no; - se.use_multi_level_bom = doc.use_multi_level_bom; - loaddoc('Stock Entry', se.name); +cur_frm.cscript.make_se = function(purpose) { + wn.call({ + method:"manufacturing.doctype.production_order.production_order.make_stock_entry", + args: { + "production_order_id": cur_frm.doc.name, + "purpose": purpose + }, + callback: function(r) { + var doclist = wn.model.sync(r.message); + wn.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }) } cur_frm.fields_dict['production_item'].get_query = function(doc) { diff --git a/manufacturing/doctype/production_order/production_order.py b/manufacturing/doctype/production_order/production_order.py index 2f311808a32..90a74e9110c 100644 --- a/manufacturing/doctype/production_order/production_order.py +++ b/manufacturing/doctype/production_order/production_order.py @@ -137,4 +137,23 @@ def get_item_details(item): if bom: res.bom_no = bom[0][0] - return res \ No newline at end of file + return res + +@webnotes.whitelist() +def make_stock_entry(production_order_id, purpose): + production_order = webnotes.bean("Production Order", production_order_id) + + stock_entry = webnotes.new_bean("Stock Entry") + stock_entry.doc.purpose = purpose + stock_entry.doc.production_order = production_order_id + stock_entry.doc.company = production_order.doc.company + stock_entry.doc.bom_no = production_order.doc.bom_no + stock_entry.doc.fg_completed_qty = flt(production_order.doc.qty) - flt(production_order.doc.produced_qty) + + if purpose=="Material Transfer": + stock_entry.doc.to_warehouse = production_order.doc.wip_warehouse + else: + stock_entry.doc.from_warehouse = production_order.doc.wip_warehouse + stock_entry.doc.to_warehouse = production_order.doc.fg_warehouse + + return [d.fields for d in stock_entry.doclist] diff --git a/manufacturing/page/manufacturing_home/manufacturing_home.js b/manufacturing/page/manufacturing_home/manufacturing_home.js index b29bbbbeed6..a2a4eaa2612 100644 --- a/manufacturing/page/manufacturing_home/manufacturing_home.js +++ b/manufacturing/page/manufacturing_home/manufacturing_home.js @@ -64,11 +64,26 @@ wn.module_page["Manufacturing"] = [ right: true, icon: "icon-list", items: [ + { + "label":wn._("Open Production Orders"), + route: "query-report/Open Production Orders", + doctype:"Production Order" + }, + { + "label":wn._("Production Orders in Progress"), + route: "query-report/Production Orders in Progress", + doctype:"Production Order" + }, { "label":wn._("Issued Items Against Production Order"), route: "query-report/Issued Items Against Production Order", doctype:"Production Order" }, + { + "label":wn._("Completed Production Orders"), + route: "query-report/Completed Production Orders", + doctype:"Production Order" + }, ] } ] diff --git a/manufacturing/report/completed_production_orders/__init__.py b/manufacturing/report/completed_production_orders/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/manufacturing/report/completed_production_orders/completed_production_orders.txt b/manufacturing/report/completed_production_orders/completed_production_orders.txt new file mode 100644 index 00000000000..facda7e9039 --- /dev/null +++ b/manufacturing/report/completed_production_orders/completed_production_orders.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-08-12 12:44:27", + "docstatus": 0, + "modified": "2013-08-12 12:44:27", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) = `tabProduction Order`.qty", + "ref_doctype": "Production Order", + "report_name": "Completed Production Orders", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Completed Production Orders" + } +] \ No newline at end of file diff --git a/manufacturing/report/open_production_orders/__init__.py b/manufacturing/report/open_production_orders/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/manufacturing/report/open_production_orders/open_production_orders.txt b/manufacturing/report/open_production_orders/open_production_orders.txt new file mode 100644 index 00000000000..f92bdd32d3e --- /dev/null +++ b/manufacturing/report/open_production_orders/open_production_orders.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-08-12 12:32:30", + "docstatus": 0, + "modified": "2013-08-12 12:42:29", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) < `tabProduction Order`.qty\n AND NOT EXISTS (SELECT name from `tabStock Entry` where production_order =`tabProduction Order`.name) ", + "ref_doctype": "Production Order", + "report_name": "Open Production Orders", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Open Production Orders" + } +] \ No newline at end of file diff --git a/manufacturing/report/production_orders_in_progress/__init__.py b/manufacturing/report/production_orders_in_progress/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/manufacturing/report/production_orders_in_progress/production_orders_in_progress.txt b/manufacturing/report/production_orders_in_progress/production_orders_in_progress.txt new file mode 100644 index 00000000000..3d3493f8a16 --- /dev/null +++ b/manufacturing/report/production_orders_in_progress/production_orders_in_progress.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-08-12 12:43:47", + "docstatus": 0, + "modified": "2013-08-12 12:43:47", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) < `tabProduction Order`.qty\n AND EXISTS (SELECT name from `tabStock Entry` where production_order =`tabProduction Order`.name) ", + "ref_doctype": "Production Order", + "report_name": "Production Orders in Progress", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Production Orders in Progress" + } +] \ No newline at end of file diff --git a/stock/doctype/stock_entry/stock_entry.js b/stock/doctype/stock_entry/stock_entry.js index 4fa6c6c55a0..53998f83e8c 100644 --- a/stock/doctype/stock_entry/stock_entry.js +++ b/stock/doctype/stock_entry/stock_entry.js @@ -4,34 +4,7 @@ wn.require("public/app/js/controllers/stock_controller.js"); wn.provide("erpnext.stock"); -erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ - onload: function() { - this.set_default_account(); - }, - - set_default_account: function() { - var me = this; - - if (cint(wn.defaults.get_default("auto_inventory_accounting")) && !this.frm.doc.expense_adjustment_account) { - if (this.frm.doc.purpose == "Sales Return") - account_for = "stock_in_hand_account"; - else if (this.frm.doc.purpose == "Purchase Return") - account_for = "stock_received_but_not_billed"; - else account_for = "stock_adjustment_account"; - - return this.frm.call({ - method: "accounts.utils.get_company_default", - args: { - "fieldname": account_for, - "company": this.frm.doc.company - }, - callback: function(r) { - if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message); - } - }); - } - }, - +erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ setup: function() { var me = this; @@ -80,11 +53,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ }, onload_post_render: function() { - if(this.frm.doc.__islocal && (this.frm.doc.production_order || this.frm.doc.bom_no) - && !getchildren('Stock Entry Detail', this.frm.doc.name, 'mtn_details').length) { - // if production order / bom is mentioned, get items - this.get_items(); - } + this.set_default_account(); }, refresh: function() { @@ -115,6 +84,33 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ after_cancel: function() { this.clean_up(); }, + + set_default_account: function() { + var me = this; + + if (cint(wn.defaults.get_default("auto_inventory_accounting")) && !this.frm.doc.expense_adjustment_account) { + var account_for = "stock_adjustment_account"; + if (this.frm.doc.purpose == "Sales Return") + account_for = "stock_in_hand_account"; + else if (this.frm.doc.purpose == "Purchase Return") + account_for = "stock_received_but_not_billed"; + + return this.frm.call({ + method: "accounts.utils.get_company_default", + args: { + "fieldname": account_for, + "company": this.frm.doc.company + }, + callback: function(r) { + if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message); + + me.get_items(); + } + }); + } else { + me.get_items(); + } + }, clean_up: function() { // Clear Production Order record from locals, because it is updated via Stock Entry @@ -126,13 +122,17 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ }, get_items: function() { - return this.frm.call({ - doc: this.frm.doc, - method: "get_items", - callback: function(r) { - if(!r.exc) refresh_field("mtn_details"); - } - }); + if(this.frm.doc.__islocal && (this.frm.doc.production_order || this.frm.doc.bom_no) + && !getchildren('Stock Entry Detail', this.frm.doc.name, 'mtn_details').length) { + // if production order / bom is mentioned, get items + return this.frm.call({ + doc: this.frm.doc, + method: "get_items", + callback: function(r) { + if(!r.exc) refresh_field("mtn_details"); + } + }); + } }, qty: function(doc, cdt, cdn) { @@ -212,7 +212,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ }); loaddoc("Journal Voucher", jv_name); } - } }); } diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 66d1dbf7c2a..b702316d9b5 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -19,6 +19,7 @@ sql = webnotes.conn.sql class NotUpdateStockError(webnotes.ValidationError): pass class StockOverReturnError(webnotes.ValidationError): pass +class IncorrectValuationRateError(webnotes.ValidationError): pass from controllers.stock_controller import StockController @@ -245,7 +246,7 @@ class DocType(StockController): def validate_incoming_rate(self): for d in getlist(self.doclist, 'mtn_details'): if d.t_warehouse: - self.validate_value("incoming_rate", ">", 0, d) + self.validate_value("incoming_rate", ">", 0, d, raise_exception=IncorrectValuationRateError) def validate_bom(self): for d in getlist(self.doclist, 'mtn_details'): diff --git a/stock/stock_ledger.py b/stock/stock_ledger.py index 4dcca6708e7..f0619c7384d 100644 --- a/stock/stock_ledger.py +++ b/stock/stock_ledger.py @@ -8,6 +8,7 @@ from stock.utils import get_valuation_method import json # future reposting +class NegativeStockError(webnotes.ValidationError): pass _exceptions = [] def update_entries_after(args, verbose=1): @@ -253,9 +254,9 @@ def _raise_exceptions(args, verbose=1): _exceptions[0]["voucher_type"], _exceptions[0]["voucher_no"], abs(deficiency)) if verbose: - msgprint(msg, raise_exception=1) + msgprint(msg, raise_exception=NegativeStockError) else: - raise webnotes.ValidationError, msg + raise NegativeStockError, msg def get_previous_sle(args, for_update=False): """ diff --git a/stock/utils.py b/stock/utils.py index 96eeef644ef..5c53d849452 100644 --- a/stock/utils.py +++ b/stock/utils.py @@ -69,7 +69,7 @@ def get_incoming_rate(args): if valuation_method == 'FIFO': if not previous_sle: return 0.0 - previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]')) + previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]') or '[]') in_rate = previous_stock_queue and \ get_fifo_rate(previous_stock_queue, args.get("qty") or 0) or 0 elif valuation_method == 'Moving Average': diff --git a/utilities/make_demo.py b/utilities/make_demo.py index 124259fe4e1..a5228620f25 100644 --- a/utilities/make_demo.py +++ b/utilities/make_demo.py @@ -19,15 +19,18 @@ runs_for = 20 prob = { "Quotation": { "make": 0.5, "qty": (1,5) }, "Sales Order": { "make": 0.5, "qty": (1,4) }, + "Purchase Order": { "make": 0.7, "qty": (1,4) }, + "Purchase Receipt": { "make": 0.7, "qty": (1,4) }, "Supplier Quotation": { "make": 0.5, "qty": (1, 3) } } -def make(): +def make(reset=False): webnotes.connect() webnotes.print_messages = True webnotes.mute_emails = True - #setup() + if reset: + setup() simulate() def setup(): @@ -68,13 +71,29 @@ def run_sales(current_date): make_sales_order(current_date) def run_stock(current_date): - pass # make purchase requests + if can_make("Purchase Receipt"): + from buying.doctype.purchase_order.purchase_order import make_purchase_receipt + report = "Purchase Order Items To Be Received" + for po in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Purchase Receipt")]: + pr = webnotes.bean(make_purchase_receipt(po)) + pr.doc.posting_date = current_date + pr.doc.fiscal_year = "2010" + pr.insert() + pr.submit() # make delivery notes (if possible) + if can_make("Delivery Note"): + from selling.doctype.sales_order.sales_order import make_delivery_note + report = "Ordered Items To Be Delivered" + for so in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Delivery Note")]: + dn = webnotes.bean(make_delivery_note(so)) + dn.doc.posting_date = current_date + dn.doc.fiscal_year = "2010" + dn.insert() + dn.submit() + - # make stock entry (from production order) - def run_purchase(current_date): # make supplier quotations if can_make("Supplier Quotation"): @@ -121,6 +140,40 @@ def run_manufacturing(current_date): b = webnotes.bean("Material Request", pro[0]) b.submit() + # stores -> wip + if can_make("Stock Entry for WIP"): + for pro in query_report.run("Open Production Orders")["result"][:how_many("Stock Entry for WIP")]: + make_stock_entry_from_pro(pro[0], "Material Transfer", current_date) + + # wip -> fg + if can_make("Stock Entry for FG"): + for pro in query_report.run("Production Orders in Progress")["result"][:how_many("Stock Entry for FG")]: + make_stock_entry_from_pro(pro[0], "Manufacture/Repack", current_date) + + # try posting older drafts (if exists) + for st in webnotes.conn.get_values("Stock Entry", {"docstatus":0}): + try: + webnotes.bean("Stock Entry", st[0]).submit() + except NegativeStockError: pass + except IncorrectValuationRateError: pass + + +def make_stock_entry_from_pro(pro_id, purpose, current_date): + from manufacturing.doctype.production_order.production_order import make_stock_entry + from stock.stock_ledger import NegativeStockError + from stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError + + st = webnotes.bean(make_stock_entry(pro_id, purpose)) + st.run_method("get_items") + st.doc.posting_date = current_date + st.doc.fiscal_year = "2010" + st.doc.expense_adjustment_account = "Stock in Hand - WP" + try: + st.insert() + st.submit() + except NegativeStockError: pass + except IncorrectValuationRateError: pass + def make_quotation(current_date): b = webnotes.bean([{ "creation": current_date, From d1055ce1bc06f62378017f5991c4f24175eec1f8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Aug 2013 15:15:19 +0530 Subject: [PATCH 2/4] [fix] [minor] Target variance report if distribution id is not specified --- .../budget_variance_report.py | 38 +++++++------- ..._person_target_variance_item_group_wise.py | 49 +++++++++---------- ...rritory_target_variance_item_group_wise.py | 39 ++++++++------- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/accounts/report/budget_variance_report/budget_variance_report.py b/accounts/report/budget_variance_report/budget_variance_report.py index 158ff3a818e..015e2c01b31 100644 --- a/accounts/report/budget_variance_report/budget_variance_report.py +++ b/accounts/report/budget_variance_report/budget_variance_report.py @@ -47,18 +47,20 @@ def get_columns(filters): msgprint(_("Please specify") + ": " + label, raise_exception=True) - columns = ["Cost Center:Link/Cost Center:100", "Account:Link/Account:100"] + columns = ["Cost Center:Link/Cost Center:120", "Account:Link/Account:120"] group_months = False if filters["period"] == "Monthly" else True for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]): for label in ["Target (%s)", "Actual (%s)", "Variance (%s)"]: if group_months: - columns.append(label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))) + label = label % (from_date.strftime("%b") + " - " + to_date.strftime("%b")) else: - columns.append(label % from_date.strftime("%b")) + label = label % from_date.strftime("%b") + + columns.append(label+":Float:120") - return columns + ["Total Target::80", "Total Actual::80", "Total Variance::80"] + return columns + ["Total Target::120", "Total Actual::120", "Total Variance::120"] #Get cost center & target details def get_costcenter_target_details(filters): @@ -66,19 +68,17 @@ def get_costcenter_target_details(filters): cc.parent_cost_center, bd.account, bd.budget_allocated from `tabCost Center` cc, `tabBudget Detail` bd where bd.parent=cc.name and bd.fiscal_year=%s and - cc.company=%s and ifnull(cc.distribution_id, '')!='' - order by cc.name""" % ('%s', '%s'), + cc.company=%s order by cc.name""" % ('%s', '%s'), (filters.get("fiscal_year"), filters.get("company")), as_dict=1) #Get target distribution details of accounts of cost center def get_target_distribution_details(filters): target_details = {} - for d in webnotes.conn.sql("""select bdd.month, bdd.percentage_allocation \ - from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd, \ - `tabCost Center` cc where bdd.parent=bd.name and cc.distribution_id=bd.name and \ - bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): - target_details.setdefault(d.month, d) + for d in webnotes.conn.sql("""select bd.name, bdd.month, bdd.percentage_allocation \ + from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd + where bdd.parent=bd.name and bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): + target_details.setdefault(d.name, {}).setdefault(d.month, d.percentage_allocation) return target_details @@ -99,15 +99,19 @@ def get_costcenter_account_month_map(filters): cam_map = {} for ccd in costcenter_target_details: - for month in tdd: + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime('%B') + cam_map.setdefault(ccd.name, {}).setdefault(ccd.account, {})\ - .setdefault(month, webnotes._dict({ - "target": 0.0, "actual": 0.0 - })) + .setdefault(month, webnotes._dict({ + "target": 0.0, "actual": 0.0 + })) tav_dict = cam_map[ccd.name][ccd.account][month] - tav_dict.target = flt(ccd.budget_allocated) * \ - (tdd[month]["percentage_allocation"]/100) + month_percentage = ccd.distribution_id and \ + tdd.get(ccd.distribution_id, {}).get(month, 0) or 100.0/12 + + tav_dict.target = flt(flt(ccd.budget_allocated) * month_percentage /100) for ad in actual_details: if ad.month_name == month and ad.account == ccd.account \ diff --git a/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py index 96c147fd644..6fdafe083d7 100644 --- a/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py +++ b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py @@ -8,6 +8,7 @@ from webnotes.utils import flt import time from accounts.utils import get_fiscal_year from controllers.trends import get_period_date_ranges, get_period_month_ranges +from webnotes.model.meta import get_field_precision def execute(filters=None): if not filters: filters = {} @@ -16,10 +17,7 @@ def execute(filters=None): period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) sim_map = get_salesperson_item_month_map(filters) - precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2 - data = [] - for salesperson, salesperson_items in sim_map.items(): for item_group, monthwise_data in salesperson_items.items(): row = [salesperson, item_group] @@ -29,7 +27,7 @@ def execute(filters=None): for month in relevant_months: month_data = monthwise_data.get(month, {}) for i, fieldname in enumerate(["target", "achieved", "variance"]): - value = flt(month_data.get(fieldname), precision) + value = flt(month_data.get(fieldname)) period_data[i] += value totals[i] += value period_data[2] = period_data[0] - period_data[1] @@ -47,37 +45,37 @@ def get_columns(filters): msgprint(_("Please specify") + ": " + label, raise_exception=True) - columns = ["Sales Person:Link/Sales Person:80", "Item Group:Link/Item Group:80"] + columns = ["Sales Person:Link/Sales Person:120", "Item Group:Link/Item Group:120"] group_months = False if filters["period"] == "Monthly" else True for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]): for label in ["Target (%s)", "Achieved (%s)", "Variance (%s)"]: if group_months: - columns.append(label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))) + label = label % (from_date.strftime("%b") + " - " + to_date.strftime("%b")) else: - columns.append(label % from_date.strftime("%b")) + label = label % from_date.strftime("%b") + + columns.append(label+":Float:120") - return columns + ["Total Target::80", "Total Achieved::80", "Total Variance::80"] + return columns + ["Total Target::120", "Total Achieved::120", "Total Variance::120"] #Get sales person & item group details def get_salesperson_details(filters): return webnotes.conn.sql("""select sp.name, td.item_group, td.target_qty, td.target_amount, sp.distribution_id from `tabSales Person` sp, `tabTarget Detail` td - where td.parent=sp.name and td.fiscal_year=%s and - ifnull(sp.distribution_id, '')!='' order by sp.name""", + where td.parent=sp.name and td.fiscal_year=%s order by sp.name""", (filters["fiscal_year"]), as_dict=1) #Get target distribution details of item group def get_target_distribution_details(filters): target_details = {} - for d in webnotes.conn.sql("""select bdd.month, bdd.percentage_allocation \ - from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd, \ - `tabTerritory` t where bdd.parent=bd.name and t.distribution_id=bd.name and \ - bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): - target_details.setdefault(d.month, d) + for d in webnotes.conn.sql("""select bd.name, bdd.month, bdd.percentage_allocation + from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd + where bdd.parent=bd.name and bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): + target_details.setdefault(d.name, {}).setdefault(d.month, d.percentage_allocation) return target_details @@ -94,32 +92,33 @@ def get_achieved_details(filters): (start_date, end_date), as_dict=1) def get_salesperson_item_month_map(filters): + import datetime salesperson_details = get_salesperson_details(filters) tdd = get_target_distribution_details(filters) achieved_details = get_achieved_details(filters) sim_map = {} - for sd in salesperson_details: - for month in tdd: + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime('%B') sim_map.setdefault(sd.name, {}).setdefault(sd.item_group, {})\ - .setdefault(month, webnotes._dict({ - "target": 0.0, "achieved": 0.0 - })) + .setdefault(month, webnotes._dict({ + "target": 0.0, "achieved": 0.0 + })) tav_dict = sim_map[sd.name][sd.item_group][month] - + month_percentage = sd.distribution_id and \ + tdd.get(sd.distribution_id, {}).get(month, 0) or 100.0/12 + for ad in achieved_details: if (filters["target_on"] == "Quantity"): - tav_dict.target = flt(sd.target_qty) * \ - (tdd[month]["percentage_allocation"]/100) + tav_dict.target = flt(flt(sd.target_qty) * month_percentage/100, 2) if ad.month_name == month and get_item_group(ad.item_code) == sd.item_group \ and ad.sales_person == sd.name: tav_dict.achieved += ad.qty if (filters["target_on"] == "Amount"): - tav_dict.target = flt(sd.target_amount) * \ - (tdd[month]["percentage_allocation"]/100) + tav_dict.target = flt(flt(sd.target_amount) * month_percentage/100, 2) if ad.month_name == month and get_item_group(ad.item_code) == sd.item_group \ and ad.sales_person == sd.name: tav_dict.achieved += ad.amount diff --git a/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py index 4a8cc7c8889..829f7818e32 100644 --- a/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py +++ b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py @@ -46,37 +46,36 @@ def get_columns(filters): label = (" ".join(fieldname.split("_"))).title() msgprint(_("Please specify") + ": " + label, raise_exception=True) - columns = ["Territory:Link/Territory:80", "Item Group:Link/Item Group:80"] + columns = ["Territory:Link/Territory:120", "Item Group:Link/Item Group:120"] group_months = False if filters["period"] == "Monthly" else True for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]): for label in ["Target (%s)", "Achieved (%s)", "Variance (%s)"]: if group_months: - columns.append(label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))) + label = label % (from_date.strftime("%b") + " - " + to_date.strftime("%b")) else: - columns.append(label % from_date.strftime("%b")) + label = label % from_date.strftime("%b") + columns.append(label+":Float:120") - return columns + ["Total Target::80", "Total Achieved::80", "Total Variance::80"] + return columns + ["Total Target::120", "Total Achieved::120", "Total Variance::120"] #Get territory & item group details def get_territory_details(filters): return webnotes.conn.sql("""select t.name, td.item_group, td.target_qty, td.target_amount, t.distribution_id from `tabTerritory` t, `tabTarget Detail` td - where td.parent=t.name and td.fiscal_year=%s and - ifnull(t.distribution_id, '')!='' order by t.name""", + where td.parent=t.name and td.fiscal_year=%s order by t.name""", (filters["fiscal_year"]), as_dict=1) #Get target distribution details of item group def get_target_distribution_details(filters): target_details = {} - for d in webnotes.conn.sql("""select bdd.month, bdd.percentage_allocation \ - from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd, \ - `tabTerritory` t where bdd.parent=bd.name and t.distribution_id=bd.name and \ - bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): - target_details.setdefault(d.month, d) + for d in webnotes.conn.sql("""select bd.name, bdd.month, bdd.percentage_allocation + from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd + where bdd.parent=bd.name and bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): + target_details.setdefault(d.name, {}).setdefault(d.month, d.percentage_allocation) return target_details @@ -99,25 +98,27 @@ def get_territory_item_month_map(filters): tim_map = {} for td in territory_details: - for month in tdd: + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime('%B') + tim_map.setdefault(td.name, {}).setdefault(td.item_group, {})\ - .setdefault(month, webnotes._dict({ - "target": 0.0, "achieved": 0.0 - })) + .setdefault(month, webnotes._dict({ + "target": 0.0, "achieved": 0.0 + })) tav_dict = tim_map[td.name][td.item_group][month] + month_percentage = td.distribution_id and \ + tdd.get(td.distribution_id, {}).get(month, 0) or 100.0/12 for ad in achieved_details: if (filters["target_on"] == "Quantity"): - tav_dict.target = flt(td.target_qty) * \ - (tdd[month]["percentage_allocation"]/100) + tav_dict.target = flt(flt(td.target_qty) * month_percentage /100) if ad.month_name == month and get_item_group(ad.item_code) == td.item_group \ and ad.territory == td.name: tav_dict.achieved += ad.qty if (filters["target_on"] == "Amount"): - tav_dict.target = flt(td.target_amount) * \ - (tdd[month]["percentage_allocation"]/100) + tav_dict.target = flt(flt(td.target_amount) * month_percentage / 100) if ad.month_name == month and get_item_group(ad.item_code) == td.item_group \ and ad.territory == td.name: tav_dict.achieved += ad.amount From 37fe72e4e96229df8c7678df1f8d13150635a5f1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Aug 2013 15:29:50 +0530 Subject: [PATCH 3/4] [fix] [minor] Do not validate leave balance if status is Rejected --- hr/doctype/leave_application/leave_application.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hr/doctype/leave_application/leave_application.py b/hr/doctype/leave_application/leave_application.py index 14c8b1f2121..8a2df1e9c4c 100755 --- a/hr/doctype/leave_application/leave_application.py +++ b/hr/doctype/leave_application/leave_application.py @@ -115,7 +115,8 @@ class DocType(DocListController): self.doc.leave_balance = get_leave_balance(self.doc.employee, self.doc.leave_type, self.doc.fiscal_year)["leave_balance"] - if self.doc.leave_balance - self.doc.total_leave_days < 0: + if self.doc.status != "Rejected" \ + and self.doc.leave_balance - self.doc.total_leave_days < 0: #check if this leave type allow the remaining balance to be in negative. If yes then warn the user and continue to save else warn the user and don't save. msgprint("There is not enough leave balance for Leave Type: %s" % \ (self.doc.leave_type,), From bf5d44c804fd116451a5075f2dc679fcdc9a9ca5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Aug 2013 16:30:48 +0530 Subject: [PATCH 4/4] [fix] [minor] reorder level bug through scheduler --- stock/utils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stock/utils.py b/stock/utils.py index 5c53d849452..848783b2c62 100644 --- a/stock/utils.py +++ b/stock/utils.py @@ -184,8 +184,8 @@ def _get_buying_amount(voucher_type, voucher_no, item_row, stock_ledger_entries) def reorder_item(): """ Reorder item if stock reaches reorder level""" if not hasattr(webnotes, "auto_indent"): - webnotes.auto_indent = webnotes.conn.get_value('Stock Settings', None, 'auto_indent') - + webnotes.auto_indent = cint(webnotes.conn.get_value('Stock Settings', None, 'auto_indent')) + if webnotes.auto_indent: material_requests = {} bin_list = webnotes.conn.sql("""select item_code, warehouse, projected_qty @@ -280,8 +280,8 @@ def create_material_request(material_requests): if mr_list: if not hasattr(webnotes, "reorder_email_notify"): - webnotes.reorder_email_notify = webnotes.conn.get_value('Stock Settings', None, - 'reorder_email_notify') + webnotes.reorder_email_notify = cint(webnotes.conn.get_value('Stock Settings', None, + 'reorder_email_notify')) if(webnotes.reorder_email_notify): send_email_notification(mr_list) @@ -307,7 +307,6 @@ def send_email_notification(mr_list): msg += "" + item.item_code + "" + item.warehouse + "" + \ cstr(item.qty) + "" + cstr(item.uom) + "" msg += "" - sendmail(email_list, subject='Auto Material Request Generation Notification', msg = msg) def notify_errors(exceptions_list):