From 5b3b123fe8d4116e06f1383707166854d4d9a7f8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 May 2013 17:55:16 +0530 Subject: [PATCH 01/18] Update sales_invoice.py --- .../doctype/sales_invoice/sales_invoice.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index ab44247d5c..14a1207186 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -444,15 +444,17 @@ class DocType(SellingController): msgprint("Customer %s does not match with customer of %s: %s." %(self.doc.customer, dt, dt_no), raise_exception=1) - def validate_customer_account(self): - """Validates Debit To Account and Customer Matches""" - if self.doc.customer and self.doc.debit_to and not cint(self.doc.is_pos): - acc_head = webnotes.conn.sql("select master_name from `tabAccount` where name = %s and docstatus != 2", self.doc.debit_to) - - if (acc_head and cstr(acc_head[0][0]) != cstr(self.doc.customer)) or \ - (not acc_head and (self.doc.debit_to != cstr(self.doc.customer) + " - " + self.get_company_abbr())): - msgprint("Debit To: %s do not match with Customer: %s for Company: %s.\n If both correctly entered, please select Master Type \ - and Master Name in account master." %(self.doc.debit_to, self.doc.customer,self.doc.company), raise_exception=1) + def validate_customer(self): + """ Validate customer name with SO and DN""" + if self.doc.customer: + for d in getlist(self.doclist,'entries'): + dt = d.delivery_note and 'Delivery Note' or d.sales_order and 'Sales Order' or '' + if dt: + dt_no = d.delivery_note or d.sales_order + cust = webnotes.conn.get_value(dt, dt_no, "customer") + if cust and cstr(cust) != cstr(self.doc.customer): + msgprint("Customer %s does not match with customer of %s: %s." + %(self.doc.customer, dt, dt_no), raise_exception=1) def validate_debit_acc(self): From de79aaa13194d9c18397a28e28676ca514d05724 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 May 2013 18:28:30 +0530 Subject: [PATCH 02/18] Update sales_invoice.py --- .../doctype/sales_invoice/sales_invoice.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 14a1207186..c17654a601 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -433,15 +433,15 @@ class DocType(SellingController): from accounts.utils import reconcile_against_document reconcile_against_document(lst) - def validate_customer(self): - """ Validate customer name with SO and DN""" - for d in getlist(self.doclist,'entries'): - dt = d.delivery_note and 'Delivery Note' or d.sales_order and 'Sales Order' or '' - if dt: - dt_no = d.delivery_note or d.sales_order - cust = webnotes.conn.sql("select customer from `tab%s` where name = %s" % (dt, '%s'), dt_no) - if cust and cstr(cust[0][0]) != cstr(self.doc.customer): - msgprint("Customer %s does not match with customer of %s: %s." %(self.doc.customer, dt, dt_no), raise_exception=1) + def validate_customer_account(self): + """Validates Debit To Account and Customer Matches""" + if self.doc.customer and self.doc.debit_to and not cint(self.doc.is_pos): + acc_head = webnotes.conn.sql("select master_name from `tabAccount` where name = %s and docstatus != 2", self.doc.debit_to) + + if (acc_head and cstr(acc_head[0][0]) != cstr(self.doc.customer)) or \ + (not acc_head and (self.doc.debit_to != cstr(self.doc.customer) + " - " + self.get_company_abbr())): + msgprint("Debit To: %s do not match with Customer: %s for Company: %s.\n If both correctly entered, please select Master Type \ + and Master Name in account master." %(self.doc.debit_to, self.doc.customer,self.doc.company), raise_exception=1) def validate_customer(self): From b68fbf053212de3e3fbff645d53b01d51b8f0b58 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 30 May 2013 23:39:24 +0530 Subject: [PATCH 03/18] [update file size patch] [fix] auto commit on many writes for the duration of the patch --- patches/april_2013/p06_update_file_size.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/patches/april_2013/p06_update_file_size.py b/patches/april_2013/p06_update_file_size.py index 8709c7b642..760c3cb044 100644 --- a/patches/april_2013/p06_update_file_size.py +++ b/patches/april_2013/p06_update_file_size.py @@ -2,11 +2,13 @@ import webnotes, os, webnotes.utils def execute(): files_path = webnotes.utils.get_path("public", "files") + webnotes.conn.auto_commit_on_many_writes = 1 + for f in webnotes.conn.sql("""select name, file_name from `tabFile Data`""", as_dict=True): if f.file_name: filepath = os.path.join(files_path, f.file_name) if os.path.exists(filepath): webnotes.conn.set_value("File Data", f.name, "file_size", os.stat(filepath).st_size) - - \ No newline at end of file + + webnotes.conn.auto_commit_on_many_writes = 0 \ No newline at end of file From 74a62b17f4a5e2d26a98b2e05be35503853f4fad Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 30 May 2013 23:49:00 +0530 Subject: [PATCH 04/18] [update file data 2 patch] [fix] auto commit on many writes for the duration of the patch --- patches/april_2013/p07_update_file_data_2.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/patches/april_2013/p07_update_file_data_2.py b/patches/april_2013/p07_update_file_data_2.py index 0cb44d0c20..548ba6cdc4 100644 --- a/patches/april_2013/p07_update_file_data_2.py +++ b/patches/april_2013/p07_update_file_data_2.py @@ -2,6 +2,8 @@ import webnotes def execute(): from patches.april_2013.p05_update_file_data import update_file_list, get_single_doctypes + webnotes.conn.auto_commit_on_many_writes = 1 + singles = get_single_doctypes() for doctype in webnotes.conn.sql_list("""select table_name from `information_schema`.`columns` where table_schema=%s and column_name='file_list'""", webnotes.conn.cur_db_name): @@ -13,4 +15,5 @@ def execute(): webnotes.conn.sql("""delete from `tabCustom Field` where fieldname='file_list' and parent=%s""", doctype) - \ No newline at end of file + + webnotes.conn.auto_commit_on_many_writes = 0 \ No newline at end of file From 4631abfb71846462a1d2af2a799e3be56f5fd6ab Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 31 May 2013 14:00:07 +0530 Subject: [PATCH 05/18] Update production_planning_tool.py --- .../production_planning_tool/production_planning_tool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/manufacturing/doctype/production_planning_tool/production_planning_tool.py index 1686478f64..d4e41aca5f 100644 --- a/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -317,9 +317,9 @@ class DocType: items_to_be_requested = webnotes._dict() for item in self.item_dict: - if flt(self.item_dict[item][0]) > item_projected_qty[item]: + if flt(self.item_dict[item][0]) > item_projected_qty.get(item, 0): # shortage - requested_qty = flt(self.item_dict[item][0]) - item_projected_qty[item] + requested_qty = flt(self.item_dict[item][0]) - item_projected_qty.get(item, 0) # comsider minimum order qty requested_qty = requested_qty > flt(self.item_dict[item][3]) and \ requested_qty or flt(self.item_dict[item][3]) @@ -379,4 +379,4 @@ class DocType: webnotes.msgprint("Following Material Request created successfully: \n%s" % "\n".join(pur_req)) else: - webnotes.msgprint("Nothing to request") \ No newline at end of file + webnotes.msgprint("Nothing to request") From a0814bb96a56f6a3e35ac509be6202a29214d3d5 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 3 Jun 2013 16:10:52 +0530 Subject: [PATCH 06/18] [report] customer account head --- .../customer_account_head.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 accounts/report/customer_account_head/customer_account_head.py diff --git a/accounts/report/customer_account_head/customer_account_head.py b/accounts/report/customer_account_head/customer_account_head.py new file mode 100644 index 0000000000..61f8cb2eec --- /dev/null +++ b/accounts/report/customer_account_head/customer_account_head.py @@ -0,0 +1,49 @@ +# 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): + account_map = get_account_map() + columns = get_columns(account_map) + data = [] + customers = webnotes.conn.sql("select name from tabCustomer where docstatus < 2") + for cust in customers: + row = [cust[0]] + for company in sorted(account_map): + row.append(account_map[company].get(cust[0], '')) + data.append(row) + + return columns, data + +def get_account_map(): + accounts = webnotes.conn.sql("""select name, company, master_name + from `tabAccount` where master_type = 'Customer' + and ifnull(master_name, '') != '' and docstatus < 2""", as_dict=1) + + account_map = {} + for acc in accounts: + account_map.setdefault(acc.company, {}).setdefault(acc.master_name, {}) + account_map[acc.company][acc.master_name] = acc.name + + return account_map + +def get_columns(account_map): + columns = ["Customer:Link/Customer:120"] + \ + [(company + ":Link/Account:120") for company in sorted(account_map)] + + return columns \ No newline at end of file From df531accc9c2d13e064bcac8711172c2127b7b1a Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 3 Jun 2013 16:18:24 +0530 Subject: [PATCH 07/18] [report] customer account head --- .../report/customer_account_head/__init__.py | 0 .../customer_account_head.txt | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 accounts/report/customer_account_head/__init__.py create mode 100644 accounts/report/customer_account_head/customer_account_head.txt diff --git a/accounts/report/customer_account_head/__init__.py b/accounts/report/customer_account_head/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/customer_account_head/customer_account_head.txt b/accounts/report/customer_account_head/customer_account_head.txt new file mode 100644 index 0000000000..d258facd8a --- /dev/null +++ b/accounts/report/customer_account_head/customer_account_head.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-03 16:17:34", + "docstatus": 0, + "modified": "2013-06-03 16:17:34", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Account", + "report_name": "Customer Account Head", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Customer Account Head" + } +] \ No newline at end of file From 0feebc1a67e27f5a6cc6e162ab3e7e8ef54e5a61 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 3 Jun 2013 16:45:38 +0530 Subject: [PATCH 08/18] [status updater] managed by controllers and commonified for sales and purchase --- .../journal_voucher/journal_voucher.py | 2 +- .../purchase_invoice/purchase_invoice.py | 29 ++- .../doctype/sales_invoice/sales_invoice.py | 75 ++++++-- .../purchase_common/purchase_common.py | 9 +- .../purchase_order_item.txt | 13 +- controllers/status_updater.py | 177 ++++++++++++++++++ .../may_2013/p06_update_billed_amt_po_pr.py | 24 +++ patches/patch_list.py | 1 + .../installation_note/installation_note.py | 155 +++++++-------- stock/doctype/delivery_note/delivery_note.py | 22 ++- .../purchase_receipt/purchase_receipt.py | 17 +- .../purchase_receipt/purchase_receipt_list.js | 2 +- .../purchase_receipt_item.txt | 16 +- utilities/transaction_base.py | 4 +- 14 files changed, 406 insertions(+), 140 deletions(-) create mode 100644 controllers/status_updater.py create mode 100644 patches/may_2013/p06_update_billed_amt_po_pr.py diff --git a/accounts/doctype/journal_voucher/journal_voucher.py b/accounts/doctype/journal_voucher/journal_voucher.py index a5a4f10f34..2c5cd4fd80 100644 --- a/accounts/doctype/journal_voucher/journal_voucher.py +++ b/accounts/doctype/journal_voucher/journal_voucher.py @@ -158,7 +158,7 @@ class DocType(AccountsController): if r: self.doc.remark = ("\n").join(r) else: - webnotes.msgprint("Remarks is mandatory", raise_exception=1) + webnotes.msgprint("User Remarks is mandatory", raise_exception=1) def set_aging_date(self): if self.doc.is_opening != 'Yes': diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index 95b56dc4ff..cff7c53d21 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -33,6 +33,28 @@ class DocType(BuyingController): self.doc, self.doclist = d, dl self.tname = 'Purchase Invoice Item' self.fname = 'entries' + self.status_updater = [{ + 'source_dt': 'Purchase Invoice Item', + 'target_dt': 'Purchase Order Item', + 'join_field': 'po_detail', + 'target_field': 'billed_amt', + 'target_parent_dt': 'Purchase Order', + 'target_parent_field': 'per_billed', + 'target_ref_field': 'import_amount', + 'source_field': 'import_amount', + 'percent_join_field': 'purchase_order', + }, + { + 'source_dt': 'Purchase Invoice Item', + 'target_dt': 'Purchase Receipt Item', + 'join_field': 'pr_detail', + 'target_field': 'billed_amt', + 'target_parent_dt': 'Purchase Receipt', + 'target_parent_field': 'per_billed', + 'target_ref_field': 'import_amount', + 'source_field': 'import_amount', + 'percent_join_field': 'purchase_receipt', + }] def validate(self): super(DocType, self).validate() @@ -411,8 +433,8 @@ class DocType(BuyingController): self.make_gl_entries() self.update_against_document_in_jv() - purchase_controller.update_prevdoc_detail(self, is_submit = 1) - + + self.update_prevdoc_status() def make_gl_entries(self): from accounts.general_ledger import make_gl_entries @@ -523,7 +545,8 @@ class DocType(BuyingController): def on_cancel(self): from accounts.utils import remove_against_link_from_jv remove_against_link_from_jv(self.doc.doctype, self.doc.name, "against_voucher") - get_obj(dt = 'Purchase Common').update_prevdoc_detail(self, is_submit = 0) + + self.update_prevdoc_status() self.make_cancel_gl_entries() diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 6871b1e90f..120aba81dd 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -38,6 +38,35 @@ class DocType(SellingController): self.log = [] self.tname = 'Sales Invoice Item' self.fname = 'entries' + self.status_updater = [{ + 'source_dt': 'Sales Invoice Item', + 'target_field': 'billed_amt', + 'target_ref_field': 'export_amount', + 'target_dt': 'Sales Order Item', + 'join_field': 'so_detail', + 'target_parent_dt': 'Sales Order', + 'target_parent_field': 'per_billed', + 'source_field': 'export_amount', + 'join_field': 'so_detail', + 'percent_join_field': 'sales_order', + 'status_field': 'billing_status', + 'keyword': 'Billed' + }, + { + 'source_dt': 'Sales Invoice Item', + 'target_dt': 'Delivery Note Item', + 'join_field': 'dn_detail', + 'target_field': 'billed_amt', + 'target_parent_dt': 'Delivery Note', + 'target_parent_field': 'per_billed', + 'target_ref_field': 'export_amount', + 'source_field': 'export_amount', + 'percent_join_field': 'delivery_note', + 'status_field': 'billing_status', + 'keyword': 'Billed', + 'no_tolerance': True, + }] + def validate(self): super(DocType, self).validate() @@ -98,7 +127,9 @@ class DocType(SellingController): self.set_buying_amount() self.check_prev_docstatus() - get_obj("Sales Common").update_prevdoc_detail(1,self) + + self.update_status_updater_args() + self.update_prevdoc_status() # this sequence because outstanding may get -ve self.make_gl_entries() @@ -128,10 +159,30 @@ class DocType(SellingController): from accounts.utils import remove_against_link_from_jv remove_against_link_from_jv(self.doc.doctype, self.doc.name, "against_invoice") - sales_com_obj.update_prevdoc_detail(0, self) + self.update_status_updater_args() + self.update_prevdoc_status() self.make_cancel_gl_entries() + def update_status_updater_args(self): + if cint(self.doc.is_pos) and cint(self.doc.update_stock): + self.status_updater.append({ + 'source_dt':'Sales Invoice Item', + 'target_dt':'Sales Order Item', + 'target_parent_dt':'Sales Order', + 'target_parent_field':'per_delivered', + 'target_field':'delivered_qty', + 'target_ref_field':'qty', + 'source_field':'qty', + 'join_field':'so_detail', + 'percent_join_field':'sales_order', + 'status_field':'delivery_status', + 'keyword':'Delivered', + 'second_source_dt': 'Delivery Note Item', + 'second_source_field': 'qty', + 'second_join_field': 'prevdoc_detail_docname' + }) + def on_update_after_submit(self): self.validate_recurring_invoice() self.convert_to_recurring() @@ -351,7 +402,7 @@ class DocType(SellingController): if ret.get("warehouse"): ret["actual_qty"] = flt(webnotes.conn.get_value("Bin", - {"item_code": args.get("item_code"), "warehouse": args.get("warehouse")}, + {"item_code": args.get("item_code"), "warehouse": ret.get("warehouse")}, "actual_qty")) return ret @@ -435,13 +486,15 @@ class DocType(SellingController): def validate_customer(self): """ Validate customer name with SO and DN""" - for d in getlist(self.doclist,'entries'): - dt = d.delivery_note and 'Delivery Note' or d.sales_order and 'Sales Order' or '' - if dt: - dt_no = d.delivery_note or d.sales_order - cust = webnotes.conn.sql("select customer from `tab%s` where name = %s" % (dt, '%s'), dt_no) - if cust and cstr(cust[0][0]) != cstr(self.doc.customer): - msgprint("Customer %s does not match with customer of %s: %s." %(self.doc.customer, dt, dt_no), raise_exception=1) + if self.doc.customer: + for d in getlist(self.doclist,'entries'): + dt = d.delivery_note and 'Delivery Note' or d.sales_order and 'Sales Order' or '' + if dt: + dt_no = d.delivery_note or d.sales_order + cust = webnotes.conn.get_value(dt, dt_no, "customer") + if cust and cstr(cust) != cstr(self.doc.customer): + msgprint("Customer %s does not match with customer of %s: %s." + %(self.doc.customer, dt, dt_no), raise_exception=1) def validate_customer_account(self): @@ -1084,4 +1137,4 @@ def get_bank_cash_account(mode_of_payment): webnotes.msgprint("Default Bank / Cash Account not set in Mode of Payment: %s. Please add a Default Account in Mode of Payment master." % mode_of_payment) return { "cash_bank_account": val - } + } \ No newline at end of file diff --git a/buying/doctype/purchase_common/purchase_common.py b/buying/doctype/purchase_common/purchase_common.py index d5b563b3c5..a0761ccbbd 100644 --- a/buying/doctype/purchase_common/purchase_common.py +++ b/buying/doctype/purchase_common/purchase_common.py @@ -213,10 +213,11 @@ class DocType(BuyingController): msgprint("Please check Item %s is not present in %s %s ." % (d.item_code, d.prevdoc_doctype, d.prevdoc_docname)) raise Exception - # Check if Warehouse has been modified. - if not cstr(data[0]['warehouse']) == cstr(d.warehouse): - msgprint("Please check warehouse %s of Item %s which is not present in %s %s ." % (d.warehouse, d.item_code, d.prevdoc_doctype, d.prevdoc_docname)) - raise Exception + if cstr(data[0]['warehouse']) and \ + not cstr(data[0]['warehouse']) == cstr(d.warehouse): + msgprint("""Please check warehouse %s of Item %s + which is not present in %s %s""" % (d.warehouse, d.item_code, + d.prevdoc_doctype, d.prevdoc_docname), raise_exception=1) # Check if UOM has been modified. if not cstr(data[0]['uom']) == cstr(d.uom) and not cstr(d.prevdoc_doctype) == 'Material Request': diff --git a/buying/doctype/purchase_order_item/purchase_order_item.txt b/buying/doctype/purchase_order_item/purchase_order_item.txt index 01a144a143..d941312a0f 100755 --- a/buying/doctype/purchase_order_item/purchase_order_item.txt +++ b/buying/doctype/purchase_order_item/purchase_order_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 11:42:55", "docstatus": 0, - "modified": "2013-05-22 11:59:52", + "modified": "2013-05-31 14:26:22", "modified_by": "Administrator", "owner": "Administrator" }, @@ -377,15 +377,12 @@ "read_only": 1 }, { - "default": "0.00", "doctype": "DocField", - "fieldname": "billed_qty", - "fieldtype": "Float", - "hidden": 0, - "label": "Billed Quantity", + "fieldname": "billed_amt", + "fieldtype": "Currency", + "label": "Billed Amt", "no_copy": 1, - "oldfieldname": "billed_qty", - "oldfieldtype": "Currency", + "options": "currency", "print_hide": 1, "read_only": 1 }, diff --git a/controllers/status_updater.py b/controllers/status_updater.py new file mode 100644 index 0000000000..ac06b2e168 --- /dev/null +++ b/controllers/status_updater.py @@ -0,0 +1,177 @@ +# 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, cstr +from webnotes import msgprint + +from webnotes.model.controller import DocListController + +class StatusUpdater(DocListController): + """ + Updates the status of the calling records + Delivery Note: Update Delivered Qty, Update Percent and Validate over delivery + Sales Invoice: Update Billed Amt, Update Percent and Validate over billing + Installation Note: Update Installed Qty, Update Percent Qty and Validate over installation + """ + + def update_prevdoc_status(self): + self.update_qty() + self.validate_qty() + + def validate_qty(self): + """ + Validates qty at row level + """ + self.tolerance = {} + self.global_tolerance = None + + for args in self.status_updater: + # get unique transactions to update + for d in self.doclist: + if d.doctype == args['source_dt'] and d.fields.get(args["join_field"]): + args['name'] = d.fields[args['join_field']] + + # get all qty where qty > target_field + item = webnotes.conn.sql("""select item_code, `%(target_ref_field)s`, + `%(target_field)s`, parenttype, parent from `tab%(target_dt)s` + where `%(target_ref_field)s` < `%(target_field)s` + and name="%(name)s" and docstatus=1""" % args, as_dict=1) + if item: + item = item[0] + item['idx'] = d.idx + item['target_ref_field'] = args['target_ref_field'].replace('_', ' ') + + if not item[args['target_ref_field']]: + msgprint("""As %(target_ref_field)s for item: %(item_code)s in \ + %(parenttype)s: %(parent)s is zero, system will not check \ + over-delivery or over-billed""" % item) + elif args.get('no_tolerance'): + item['reduce_by'] = item[args['target_field']] - \ + item[args['target_ref_field']] + if item['reduce_by'] > .01: + msgprint(""" + Row #%(idx)s: Max %(target_ref_field)s allowed for Item \ + %(item_code)s against %(parenttype)s %(parent)s \ + is """ % item + cstr(item[args['target_ref_field']]) + + """.
You must reduce the %(target_ref_field)s by \ + %(reduce_by)s""" % item, raise_exception=1) + + else: + self.check_overflow_with_tolerance(item, args) + + def check_overflow_with_tolerance(self, item, args): + """ + Checks if there is overflow condering a relaxation tolerance + """ + + # check if overflow is within tolerance + tolerance = self.get_tolerance_for(item['item_code']) + overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) / + item[args['target_ref_field']]) * 100 + + if overflow_percent - tolerance > 0.01: + item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100) + item['reduce_by'] = item[args['target_field']] - item['max_allowed'] + + msgprint(""" + Row #%(idx)s: Max %(target_ref_field)s allowed for Item %(item_code)s \ + against %(parenttype)s %(parent)s is %(max_allowed)s. + + If you want to increase your overflow tolerance, please increase tolerance %% in \ + Global Defaults or Item master. + + Or, you must reduce the %(target_ref_field)s by %(reduce_by)s + + Also, please check if the order item has already been billed in the Sales Order""" % + item, raise_exception=1) + + def get_tolerance_for(self, item_code): + """ + Returns the tolerance for the item, if not set, returns global tolerance + """ + if self.tolerance.get(item_code): return self.tolerance[item_code] + + tolerance = flt(webnotes.conn.get_value('Item',item_code,'tolerance') or 0) + + if not tolerance: + if self.global_tolerance == None: + self.global_tolerance = flt(webnotes.conn.get_value('Global Defaults', None, + 'tolerance')) + tolerance = self.global_tolerance + + self.tolerance[item_code] = tolerance + return tolerance + + + def update_qty(self, change_modified=True): + """ + Updates qty at row level + """ + for args in self.status_updater: + # condition to include current record (if submit or no if cancel) + if self.doc.docstatus == 1: + args['cond'] = ' or parent="%s"' % self.doc.name + else: + args['cond'] = ' and parent!="%s"' % self.doc.name + + args['modified_cond'] = '' + if change_modified: + args['modified_cond'] = ', modified = now()' + + # update quantities in child table + for d in self.doclist: + if d.doctype == args['source_dt']: + # updates qty in the child table + args['detail_id'] = d.fields.get(args['join_field']) + + args['second_source_condition'] = "" + if args.get('second_source_dt') and args.get('second_source_field') \ + and args.get('second_join_field'): + args['second_source_condition'] = """ + (select sum(%(second_source_field)s) + from `tab%(second_source_dt)s` + where `%(second_join_field)s`="%(detail_id)s" + and (docstatus=1))""" % args + + if args['detail_id']: + webnotes.conn.sql("""update `tab%(target_dt)s` + set %(target_field)s = (select sum(%(source_field)s) + from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s" + and (docstatus=1 %(cond)s)) %(second_source_condition)s + where name='%(detail_id)s'""" % args) + + # get unique transactions to update + for name in set([d.fields.get(args['percent_join_field']) for d in self.doclist + if d.doctype == args['source_dt']]): + if name: + args['name'] = name + + # update percent complete in the parent table + webnotes.conn.sql("""update `tab%(target_parent_dt)s` + set %(target_parent_field)s = (select sum(if(%(target_ref_field)s > + ifnull(%(target_field)s, 0), %(target_field)s, + %(target_ref_field)s))/sum(%(target_ref_field)s)*100 + from `tab%(target_dt)s` where parent="%(name)s") %(modified_cond)s + where name='%(name)s'""" % args) + + # update field + if args.get('status_field'): + webnotes.conn.sql("""update `tab%(target_parent_dt)s` + set %(status_field)s = if(ifnull(%(target_parent_field)s,0)<0.001, + 'Not %(keyword)s', if(%(target_parent_field)s>=99.99, + 'Fully %(keyword)s', 'Partly %(keyword)s')) + where name='%(name)s'""" % args) \ No newline at end of file diff --git a/patches/may_2013/p06_update_billed_amt_po_pr.py b/patches/may_2013/p06_update_billed_amt_po_pr.py new file mode 100644 index 0000000000..5084e98175 --- /dev/null +++ b/patches/may_2013/p06_update_billed_amt_po_pr.py @@ -0,0 +1,24 @@ +# 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 +def execute(): + import webnotes + webnotes.reload_doc("buying", "doctype", "purchase_order_item") + webnotes.reload_doc("stock", "doctype", "purchase_receipt_item") + for pi in webnotes.conn.sql("""select name from `tabPurchase Invoice` where docstatus = 1"""): + webnotes.get_obj("Purchase Invoice", pi[0], + with_children=1).update_qty(change_modified=False) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index ea61a04dc3..d5b26a57aa 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.may_2013.p06_update_billed_amt_po_pr", ] \ No newline at end of file diff --git a/selling/doctype/installation_note/installation_note.py b/selling/doctype/installation_note/installation_note.py index ea20d51d64..9b7344bd7f 100644 --- a/selling/doctype/installation_note/installation_note.py +++ b/selling/doctype/installation_note/installation_note.py @@ -21,10 +21,7 @@ from webnotes.utils import cstr, getdate from webnotes.model.bean import getlist from webnotes.model.code import get_obj from webnotes import msgprint -from stock.utils import get_valid_serial_nos - -sql = webnotes.conn.sql - +from stock.utils import get_valid_serial_nos from utilities.transaction_base import TransactionBase @@ -34,6 +31,19 @@ class DocType(TransactionBase): self.doclist = doclist self.tname = 'Installation Note Item' self.fname = 'installed_item_details' + self.status_updater = [{ + 'source_dt': 'Installation Note Item', + 'target_dt': 'Delivery Note Item', + 'target_field': 'installed_qty', + 'target_ref_field': 'qty', + 'join_field': 'prevdoc_detail_docname', + 'target_parent_dt': 'Delivery Note', + 'target_parent_field': 'per_installed', + 'source_field': 'qty', + 'percent_join_field': 'prevdoc_docname', + 'status_field': 'installation_status', + 'keyword': 'Installed' + }] def validate(self): self.validate_fiscal_year() @@ -45,153 +55,114 @@ class DocType(TransactionBase): self.validate_mandatory() self.validate_reference_value() - - #fetch delivery note details - #==================================== def pull_delivery_note_details(self): self.validate_prev_docname() - self.doclist = get_obj('DocType Mapper', 'Delivery Note-Installation Note').dt_map('Delivery Note', 'Installation Note', self.doc.delivery_note_no, self.doc, self.doclist, "[['Delivery Note', 'Installation Note'],['Delivery Note Item', 'Installation Note Item']]") + self.doclist = get_obj('DocType Mapper', 'Delivery Note-Installation Note').dt_map( + 'Delivery Note', 'Installation Note', self.doc.delivery_note_no, + self.doc, self.doclist, "[['Delivery Note', 'Installation Note'], \ + ['Delivery Note Item', 'Installation Note Item']]") - # Validates that Delivery Note is not pulled twice - #============================================ def validate_prev_docname(self): for d in getlist(self.doclist, 'installed_item_details'): if self.doc.delivery_note_no == d.prevdoc_docname: - msgprint(cstr(self.doc.delivery_note_no) + " delivery note details have already been pulled. ") - raise Exception, "Validation Error. " - - #Fiscal Year Validation - #================================ + msgprint(cstr(self.doc.delivery_note_no) + + " delivery note details have already been pulled", raise_exception=1) + def validate_fiscal_year(self): - get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.inst_date,'Installation Date') - - # Validate Mandatory - #=============================== + get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year, self.doc.inst_date, + 'Installation Date') + def validate_mandatory(self): - # Amendment Date if self.doc.amended_from and not self.doc.amendment_date: msgprint("Please Enter Amendment Date") raise Exception, "Validation Error. " - - # Validate values with reference document - #---------------------------------------- + def validate_reference_value(self): - get_obj('DocType Mapper', 'Delivery Note-Installation Note', with_children = 1).validate_reference_value(self, self.doc.name) + mapper = get_obj('DocType Mapper', 'Delivery Note-Installation Note', with_children = 1) + mapper.validate_reference_value(self, self.doc.name) - #check if serial no added - #----------------------------- - def is_serial_no_added(self,item_code,serial_no): - ar_required = sql("select has_serial_no from tabItem where name = '%s'" % item_code) - ar_required = ar_required and ar_required[0][0] or '' + def is_serial_no_added(self, item_code, serial_no): + ar_required = webnotes.conn.get_value("Item", item_code, "has_serial_no") if ar_required == 'Yes' and not serial_no: - msgprint("Serial No is mandatory for item: "+ item_code) - raise Exception + msgprint("Serial No is mandatory for item: " + item_code, raise_exception=1) elif ar_required != 'Yes' and cstr(serial_no).strip(): - msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :"+item_code) - raise Exception + msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :" + + item_code, raise_exception=1) - #check if serial no exist in system - #------------------------------------- def is_serial_no_exist(self, item_code, serial_no): for x in serial_no: - chk = sql("select name from `tabSerial No` where name =%s", x) - if not chk: - msgprint("Serial No "+x+" does not exist in the system") - raise Exception + if not webnotes.conn.exists("Serial No", x): + msgprint("Serial No " + x + " does not exist in the system", raise_exception=1) - #check if serial no already installed - #------------------------------------------ def is_serial_no_installed(self,cur_s_no,item_code): for x in cur_s_no: - status = sql("select status from `tabSerial No` where name = %s", x) + status = webnotes.conn.sql("select status from `tabSerial No` where name = %s", x) status = status and status[0][0] or '' if status == 'Installed': - msgprint("Item "+item_code+" with serial no. "+x+" already installed") - raise Exception, "Validation Error." + msgprint("Item "+item_code+" with serial no. " + x + " already installed", + raise_exception=1) - #get list of serial no from previous_doc - #---------------------------------------------- - def get_prevdoc_serial_no(self, prevdoc_detail_docname, prevdoc_docname): - res = sql("select serial_no from `tabDelivery Note Item` where name = '%s' and parent ='%s'" % (prevdoc_detail_docname, prevdoc_docname)) - return get_valid_serial_nos(res[0][0]) + def get_prevdoc_serial_no(self, prevdoc_detail_docname): + serial_nos = webnotes.conn.get_value("Delivery Note Item", + prevdoc_detail_docname, "serial_no") + return get_valid_serial_nos(serial_nos) - #check if all serial nos from current record exist in resp delivery note - #--------------------------------------------------------------------------------- def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname): - for x in cur_s_no: - if not(x in prevdoc_s_no): - msgprint("Serial No. "+x+" not present in the Delivery Note "+prevdoc_docname, raise_exception = 1) - raise Exception, "Validation Error." - - #validate serial number - #---------------------------------------- + for sr in cur_s_no: + if sr not in prevdoc_s_no: + msgprint("Serial No. " + sr + " is not matching with the Delivery Note " + + prevdoc_docname, raise_exception = 1) + def validate_serial_no(self): cur_s_no, prevdoc_s_no, sr_list = [], [], [] for d in getlist(self.doclist, 'installed_item_details'): self.is_serial_no_added(d.item_code, d.serial_no) - if d.serial_no: - sr_list = get_valid_serial_nos(d.serial_no, d.qty, d.item_code) self.is_serial_no_exist(d.item_code, sr_list) - prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname, d.prevdoc_docname) + prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname) if prevdoc_s_no: self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname) self.is_serial_no_installed(sr_list, d.item_code) return sr_list - - #validate installation date - #------------------------------- + def validate_installation_date(self): for d in getlist(self.doclist, 'installed_item_details'): if d.prevdoc_docname: - d_date = sql("select posting_date from `tabDelivery Note` where name=%s", d.prevdoc_docname) - d_date = d_date and d_date[0][0] or '' - + d_date = webnotes.conn.get_value("Delivery Note", d.prevdoc_docname, "posting_date") if d_date > getdate(self.doc.inst_date): - msgprint("Installation Date can not be before Delivery Date "+cstr(d_date)+" for item "+d.item_code) - raise Exception + msgprint("Installation Date can not be before Delivery Date " + cstr(d_date) + + " for item "+d.item_code, raise_exception=1) def check_item_table(self): if not(getlist(self.doclist, 'installed_item_details')): - msgprint("Please fetch items from Delivery Note selected") - raise Exception + msgprint("Please fetch items from Delivery Note selected", raise_exception=1) def on_update(self): + get_obj("Stock Ledger").scrub_serial_nos(self, 'installed_item_details') webnotes.conn.set(self.doc, 'status', 'Draft') def on_submit(self): valid_lst = [] valid_lst = self.validate_serial_no() - get_obj("Sales Common").update_prevdoc_detail(1,self) - for x in valid_lst: - wp = sql("select warranty_period from `tabSerial No` where name = '%s'"% x) - wp = wp and wp[0][0] or 0 - if wp: - sql("update `tabSerial No` set maintenance_status = 'Under Warranty' where name = '%s'" % x) - - sql("update `tabSerial No` set status = 'Installed' where name = '%s'" % x) - - webnotes.conn.set(self.doc, 'status', 'Submitted') + if webnotes.conn.get_value("Serial No", x, "warranty_period"): + webnotes.conn.set_value("Serial No", x, "maintenance_status", "Under Warranty") + webnotes.conn.set_value("Serial No", x, "status", "Installed") + self.update_prevdoc_status() + webnotes.conn.set(self.doc, 'status', 'Submitted') def on_cancel(self): - cur_s_no = [] - sales_com_obj = get_obj(dt = 'Sales Common') - sales_com_obj.update_prevdoc_detail(0,self) - for d in getlist(self.doclist, 'installed_item_details'): if d.serial_no: - #get current list of serial no - cur_serial_no = d.serial_no.replace(' ', '') - cur_s_no = cur_serial_no.split(',') - - for x in cur_s_no: - sql("update `tabSerial No` set status = 'Delivered' where name = '%s'" % x) - + d.serial_no = d.serial_no.replace(",", "\n") + for sr_no in d.serial_no.split("\n"): + webnotes.conn.set_value("Serial No", sr_no, "status", "Delivered") + + self.update_prevdoc_status() webnotes.conn.set(self.doc, 'status', 'Cancelled') diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index 6ffd960000..4902680416 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cstr, flt, getdate, cint +from webnotes.utils import cstr, flt, cint from webnotes.model.bean import getlist from webnotes.model.code import get_obj from webnotes import msgprint, _ @@ -34,6 +34,19 @@ class DocType(SellingController): self.doclist = doclist self.tname = 'Delivery Note Item' self.fname = 'delivery_note_details' + self.status_updater = [{ + 'source_dt': 'Delivery Note Item', + 'target_dt': 'Sales Order Item', + 'join_field': 'prevdoc_detail_docname', + 'target_field': 'delivered_qty', + 'target_parent_dt': 'Sales Order', + 'target_parent_field': 'per_delivered', + 'target_ref_field': 'qty', + 'source_field': 'qty', + 'percent_join_field': 'prevdoc_docname', + 'status_field': 'delivery_status', + 'keyword': 'Delivered' + }] def validate_fiscal_year(self): get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date') @@ -261,8 +274,8 @@ class DocType(SellingController): sl_obj.update_serial_record(self, 'delivery_note_details', is_submit = 1, is_incoming = 0) sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0) - # update delivered qty in sales order - get_obj("Sales Common").update_prevdoc_detail(1,self) + # update delivered qty in sales order + self.update_prevdoc_status() # create stock ledger entry self.update_stock_ledger(update_stock = 1) @@ -309,7 +322,8 @@ class DocType(SellingController): sl.update_serial_record(self, 'delivery_note_details', is_submit = 0, is_incoming = 0) sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0) - sales_com_obj.update_prevdoc_detail(0,self) + self.update_prevdoc_status() + self.update_stock_ledger(update_stock = -1) webnotes.conn.set(self.doc, 'status', 'Cancelled') self.cancel_packing_slips() diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py index 8e81fa5698..bc726c9dcc 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/stock/doctype/purchase_receipt/purchase_receipt.py @@ -33,6 +33,17 @@ class DocType(BuyingController): self.tname = 'Purchase Receipt Item' self.fname = 'purchase_receipt_details' self.count = 0 + self.status_updater = [{ + 'source_dt': 'Purchase Receipt Item', + 'target_dt': 'Purchase Order Item', + 'join_field': 'prevdoc_detail_docname', + 'target_field': 'received_qty', + 'target_parent_dt': 'Purchase Order', + 'target_parent_field': 'per_received', + 'target_ref_field': 'qty', + 'source_field': 'qty', + 'percent_join_field': 'prevdoc_docname', + }] def validate_fiscal_year(self): get_obj(dt = 'Purchase Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Transaction Date') @@ -243,8 +254,7 @@ class DocType(BuyingController): # Set status as Submitted webnotes.conn.set(self.doc,'status', 'Submitted') - # Update Previous Doc i.e. update pending_qty and Status accordingly - purchase_controller.update_prevdoc_detail(self, is_submit = 1) + self.update_prevdoc_status() # Update Serial Record get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 1, is_incoming = 1) @@ -285,8 +295,7 @@ class DocType(BuyingController): # 4.Update Bin self.update_stock(is_submit = 0) - # 5.Update Material Requests Pending Qty and accordingly it's Status - pc_obj.update_prevdoc_detail(self, is_submit = 0) + self.update_prevdoc_status() # 6. Update last purchase rate pc_obj.update_last_purchase_rate(self, 0) diff --git a/stock/doctype/purchase_receipt/purchase_receipt_list.js b/stock/doctype/purchase_receipt/purchase_receipt_list.js index d20c352618..c80f6aedc7 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -6,7 +6,7 @@ wn.listview_settings['Purchase Receipt'] = { group_by: "`tabPurchase Receipt`.name", prepare_data: function(data) { if(data.purchase_order_no) { - data.purchase_order_no = data.purchase_order_no.split(","); + data.purchase_order_no = $.unique(data.purchase_order_no.split(",")); var po_list = []; $.each(data.purchase_order_no, function(i, v){ if(po_list.indexOf(v)==-1) po_list.push( diff --git a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt index 8cef6a3534..42bd929040 100755 --- a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt +++ b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 11:42:59", "docstatus": 0, - "modified": "2013-05-22 12:01:08", + "modified": "2013-05-31 14:26:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -453,18 +453,14 @@ "width": "150px" }, { - "default": "0.00", "doctype": "DocField", - "fieldname": "billed_qty", - "fieldtype": "Float", - "label": "Billed Quantity", + "fieldname": "billed_amt", + "fieldtype": "Currency", + "label": "Billed Amt", "no_copy": 1, - "oldfieldname": "billed_qty", - "oldfieldtype": "Currency", + "options": "currency", "print_hide": 1, - "print_width": "100px", - "read_only": 1, - "width": "100px" + "read_only": 1 }, { "doctype": "DocField", diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 5d7d1a84b1..46094290e2 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -19,9 +19,9 @@ import webnotes from webnotes.utils import load_json, cstr, flt, now_datetime from webnotes.model.doc import addchild -from webnotes.model.controller import DocListController +from controllers.status_updater import StatusUpdater -class TransactionBase(DocListController): +class TransactionBase(StatusUpdater): def get_default_address_and_contact(self, party_type): """get a dict of default field values of address and contact for a given party type party_type can be one of: customer, supplier""" From 7407748b29a60239643be64651559bb060d4b3e1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 10:49:08 +0530 Subject: [PATCH 09/18] customer account head report link added in accounts home page --- accounts/page/accounts_home/accounts_home.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 25ee92c397..f187a54cdc 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -232,6 +232,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Sales Partners Commission", doctype: "Sales Invoice" }, + { + "label":wn._("Customer Account Head"), + route: "query-report/Customer Account Head", + doctype: "Account" + }, ] } ] From df28eefa4bcbdde4e860cefe20bed8d3dcf17059 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 12:10:33 +0530 Subject: [PATCH 10/18] [payment reconciliation] do not allow negative outstanding in case of against_jv --- accounts/doctype/gl_entry/gl_entry.py | 25 +++++++++++++------- accounts/page/accounts_home/accounts_home.js | 5 ++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py index 429cc104a7..112e449f97 100644 --- a/accounts/doctype/gl_entry/gl_entry.py +++ b/accounts/doctype/gl_entry/gl_entry.py @@ -42,9 +42,9 @@ class DocType: self.check_negative_balance(adv_adj) # Update outstanding amt on against voucher - if self.doc.against_voucher and self.doc.against_voucher_type not in \ - ('Journal Voucher','POS') and update_outstanding == 'Yes': - self.update_outstanding_amt() + if self.doc.against_voucher and self.doc.against_voucher_type != "POS" \ + and update_outstanding == 'Yes': + self.update_outstanding_amt() def check_mandatory(self): mandatory = ['account','remarks','voucher_type','voucher_no','fiscal_year','company'] @@ -164,16 +164,25 @@ class DocType: and ifnull(is_cancelled,'No') = 'No'""", (self.doc.against_voucher, self.doc.against_voucher_type))[0][0] or 0.0) - if self.doc.against_voucher_type=='Purchase Invoice': - # amount to debit + if self.doc.against_voucher_type == 'Purchase Invoice': bal = -bal + elif self.doc.against_voucher_type == "Journal Voucher": + against_voucher_amount = flt(webnotes.conn.sql("""select sum(debit) - sum(credit) + from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s + and account = %s""", (self.doc.against_voucher, self.doc.account))[0][0]) + + bal = against_voucher_amount + bal + if against_voucher_amount < 0: + bal = -bal + # Validation : Outstanding can not be negative if bal < 0 and self.doc.is_cancelled == 'No': msgprint(_("Outstanding for Voucher ") + self.doc.against_voucher + - _(" will become ") + fmt_money(bal) + _("Outstanding cannot be less than zero. \ + _(" will become ") + fmt_money(bal) + _(". Outstanding cannot be less than zero. \ Please match exact outstanding."), raise_exception=1) # Update outstanding amt on against voucher - sql("update `tab%s` set outstanding_amount=%s where name='%s'"% - (self.doc.against_voucher_type, bal, self.doc.against_voucher)) \ No newline at end of file + if self.doc.against_voucher_type in ["Sales Invoice", "Purchase Invoice"]: + sql("update `tab%s` set outstanding_amount=%s where name='%s'"% + (self.doc.against_voucher_type, bal, self.doc.against_voucher)) \ No newline at end of file diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index f187a54cdc..10c0fc0b85 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -237,6 +237,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Customer Account Head", doctype: "Account" }, + { + "label":wn._("Item-wise Sales Register"), + route: "query-report/Item-wise Sales Register", + doctype: "Sales Invoice" + }, ] } ] From 4bf08fa1cdf8dd034497319646477888ab037351 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 4 Jun 2013 15:51:16 +0530 Subject: [PATCH 11/18] [Reports][Supplier Account Head -> Account] and [Project wise Stock Tracking -> Project] --- .../doctype/sales_invoice/sales_invoice.js | 2 +- accounts/page/accounts_home/accounts_home.js | 10 +++ .../report/supplier_account_head/__init__.py | 0 .../supplier_account_head.py | 49 ++++++++++ .../supplier_account_head.txt | 21 +++++ projects/page/projects_home/projects_home.js | 5 ++ .../project_wise_stock_tracking/__init__.py | 0 .../project_wise_stock_tracking.py | 90 +++++++++++++++++++ .../project_wise_stock_tracking.txt | 21 +++++ 9 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 accounts/report/supplier_account_head/__init__.py create mode 100644 accounts/report/supplier_account_head/supplier_account_head.py create mode 100644 accounts/report/supplier_account_head/supplier_account_head.txt create mode 100644 projects/report/project_wise_stock_tracking/__init__.py create mode 100644 projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py create mode 100644 projects/report/project_wise_stock_tracking/project_wise_stock_tracking.txt diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index be6ec3d001..212ea588ca 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -72,7 +72,7 @@ cur_frm.cscript.onload_post_render = function(doc, dt, dn) { // Hide Fields // ------------ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) { - par_flds = ['project_name', 'due_date', 'sales_order_main', + par_flds = ['due_date', 'sales_order_main', 'delivery_note_main', 'get_items', 'is_opening', 'conversion_rate', 'source', 'cancel_reason', 'total_advance', 'gross_profit', 'gross_profit_percent', 'get_advances_received', diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 25ee92c397..2f2c9ee64d 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -127,6 +127,16 @@ wn.module_page["Accounts"] = [ right: true, icon: "icon-table", items: [ + { + "label":wn._("Customer Account Head"), + route: "query-report/Customer Account Head", + doctype: "Account" + }, + { + "label":wn._("Supplier Account Head"), + route: "query-report/Supplier Account Head", + doctype: "Account" + }, { "label":wn._("General Ledger"), page: "general-ledger" diff --git a/accounts/report/supplier_account_head/__init__.py b/accounts/report/supplier_account_head/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/supplier_account_head/supplier_account_head.py b/accounts/report/supplier_account_head/supplier_account_head.py new file mode 100644 index 0000000000..8b55067a6b --- /dev/null +++ b/accounts/report/supplier_account_head/supplier_account_head.py @@ -0,0 +1,49 @@ +# 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): + account_map = get_account_map() + columns = get_columns(account_map) + data = [] + suppliers = webnotes.conn.sql("select name from tabSupplier where docstatus < 2") + for supplier in suppliers: + row = [supplier[0]] + for company in sorted(account_map): + row.append(account_map[company].get(supplier[0], '')) + data.append(row) + + return columns, data + +def get_account_map(): + accounts = webnotes.conn.sql("""select name, company, master_name + from `tabAccount` where master_type = 'Supplier' + and ifnull(master_name, '') != '' and docstatus < 2""", as_dict=1) + + account_map = {} + for acc in accounts: + account_map.setdefault(acc.company, {}).setdefault(acc.master_name, {}) + account_map[acc.company][acc.master_name] = acc.name + + return account_map + +def get_columns(account_map): + columns = ["Supplier:Link/Supplier:120"] + \ + [(company + ":Link/Account:120") for company in sorted(account_map)] + + return columns \ No newline at end of file diff --git a/accounts/report/supplier_account_head/supplier_account_head.txt b/accounts/report/supplier_account_head/supplier_account_head.txt new file mode 100644 index 0000000000..b0554e8576 --- /dev/null +++ b/accounts/report/supplier_account_head/supplier_account_head.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-04 12:56:17", + "docstatus": 0, + "modified": "2013-06-04 12:56:46", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Account", + "report_name": "Supplier Account Head", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Supplier Account Head" + } +] \ No newline at end of file diff --git a/projects/page/projects_home/projects_home.js b/projects/page/projects_home/projects_home.js index cfde6b30d2..fd13a67367 100644 --- a/projects/page/projects_home/projects_home.js +++ b/projects/page/projects_home/projects_home.js @@ -60,6 +60,11 @@ wn.module_page["Projects"] = [ route: "query-report/Daily Time Log Summary", doctype: "Time Log" }, + { + "label":wn._("Project wise Stock Tracking"), + route: "query-report/Project wise Stock Tracking", + doctype: "Project" + }, ] }] diff --git a/projects/report/project_wise_stock_tracking/__init__.py b/projects/report/project_wise_stock_tracking/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py b/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py new file mode 100644 index 0000000000..7c702e41cb --- /dev/null +++ b/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py @@ -0,0 +1,90 @@ +# 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 . + +import webnotes + +def execute(filters=None): + columns = get_columns() + proj_details = get_project_details() + pr_item_map = get_purchased_items_cost() + se_item_map = get_issued_items_cost() + dn_item_map = get_delivered_items_cost() + + data = [] + for project in proj_details: + data.append([project.name, pr_item_map.get(project.name, 0), + se_item_map.get(project.name, 0), dn_item_map.get(project.name, 0), + project.project_name, project.status, project.company, + project.customer, project.project_value, project.project_start_date, + project.completion_date]) + + return columns, data + +def get_columns(): + return ["Project Id:Link/Project:140", "Cost of Purchased Items:Currency:160", + "Cost of Issued Items:Currency:160", "Cost of Delivered Items:Currency:160", + "Project Name::120", "Project Status::120", "Company:Link/Company:100", + "Customer:Link/Customer:140", "Project Value:Currency:120", + "Project Start Date:Date:120", "Completion Date:Date:120"] + +def get_project_details(): + return webnotes.conn.sql(""" select name, project_name, status, company, customer, project_value, + project_start_date, completion_date from tabProject where docstatus < 2""", as_dict=1) + +def get_purchased_items_cost(): + pr_items = webnotes.conn.sql("""select project_name, sum(amount) as amount + from `tabPurchase Receipt Item` where ifnull(project_name, '') != '' + and docstatus = 1 group by project_name""", as_dict=1) + + pr_item_map = {} + for item in pr_items: + pr_item_map.setdefault(item.project_name, item.amount) + + return pr_item_map + +def get_issued_items_cost(): + se_items = webnotes.conn.sql("""select se.project_name, sum(se_item.amount) as amount + from `tabStock Entry` se, `tabStock Entry Detail` se_item + where se.name = se_item.parent and se.docstatus = 1 and ifnull(se_item.t_warehouse, '') = '' + and ifnull(se.project_name, '') != '' group by se.project_name""", as_dict=1) + + se_item_map = {} + for item in se_items: + se_item_map.setdefault(item.project_name, item.amount) + + return se_item_map + +def get_delivered_items_cost(): + dn_items = webnotes.conn.sql("""select dn.project_name, sum(dn_item.amount) as amount + from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item + where dn.name = dn_item.parent and dn.docstatus = 1 and ifnull(dn.project_name, '') != '' + group by dn.project_name""", as_dict=1) + + si_items = webnotes.conn.sql("""select si.project_name, sum(si_item.amount) as amount + from `tabSales Invoice` si, `tabSales Invoice Item` si_item + where si.name = si_item.parent and si.docstatus = 1 and ifnull(si.update_stock, 0) = 1 + and ifnull(si.is_pos, 0) = 1 and ifnull(si.project_name, '') != '' + group by si.project_name""", as_dict=1) + + + dn_item_map = {} + for item in dn_items: + dn_item_map.setdefault(item.project_name, item.amount) + + for item in si_items: + dn_item_map.setdefault(item.project_name, item.amount) + + return dn_item_map \ No newline at end of file diff --git a/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.txt b/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.txt new file mode 100644 index 0000000000..89f5085351 --- /dev/null +++ b/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-03 17:37:41", + "docstatus": 0, + "modified": "2013-06-03 17:37:41", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Project", + "report_name": "Project wise Stock Tracking ", + "report_type": "Report Builder" + }, + { + "doctype": "Report", + "name": "Project wise Stock Tracking" + } +] \ No newline at end of file From ad600cce2222912d22db81e2276bf729ac4079d1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 16:56:56 +0530 Subject: [PATCH 12/18] 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 c95436cf4391a8562a49c3712c92d9b3befc62e8 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 4 Jun 2013 17:01:06 +0530 Subject: [PATCH 13/18] changes in sales_invoice.js file --- accounts/doctype/sales_invoice/sales_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index 212ea588ca..be6ec3d001 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -72,7 +72,7 @@ cur_frm.cscript.onload_post_render = function(doc, dt, dn) { // Hide Fields // ------------ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) { - par_flds = ['due_date', 'sales_order_main', + par_flds = ['project_name', 'due_date', 'sales_order_main', 'delivery_note_main', 'get_items', 'is_opening', 'conversion_rate', 'source', 'cancel_reason', 'total_advance', 'gross_profit', 'gross_profit_percent', 'get_advances_received', From 89ab4719ff3648a82ea9214629f1ddea2c1c731e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 17:07:57 +0530 Subject: [PATCH 14/18] 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 15/18] 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 16/18] 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 17/18] 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 6f195270633ec4a2578caa04157b468fdb659de7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 5 Jun 2013 13:02:16 +0530 Subject: [PATCH 18/18] [report] po and pr items to be billed based on amount --- .../purchase_order_items_to_be_billed.txt | 4 ++-- .../received_items_to_be_billed.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.txt b/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.txt index 7a9ec367b1..6b3bcf73cd 100644 --- a/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.txt +++ b/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-28 15:54:16", "docstatus": 0, - "modified": "2013-05-28 16:02:57", + "modified": "2013-06-05 13:01:05", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.qty as \"Qty:Float:100\",\n\t`tabPurchase Order Item`.billed_qty as \"Billed Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.billed_qty, 0)) as \"Qty to Bill:Float:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand ifnull(`tabPurchase Order Item`.billed_qty, 0) < ifnull(`tabPurchase Order Item`.qty, 0)\norder by `tabPurchase Order`.transaction_date asc", + "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.amount as \"Amount:Currency:100\",\n\t`tabPurchase Order Item`.billed_amt as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.amount - ifnull(`tabPurchase Order Item`.billed_amt, 0)) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand ifnull(`tabPurchase Order Item`.billed_amt, 0) < ifnull(`tabPurchase Order Item`.amount, 0)\norder by `tabPurchase Order`.transaction_date asc", "ref_doctype": "Purchase Invoice", "report_name": "Purchase Order Items To Be Billed", "report_type": "Query Report" diff --git a/accounts/report/received_items_to_be_billed/received_items_to_be_billed.txt b/accounts/report/received_items_to_be_billed/received_items_to_be_billed.txt index 554e0f0c79..90617f86c4 100644 --- a/accounts/report/received_items_to_be_billed/received_items_to_be_billed.txt +++ b/accounts/report/received_items_to_be_billed/received_items_to_be_billed.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-28 15:57:59", "docstatus": 0, - "modified": "2013-05-28 17:34:05", + "modified": "2013-06-05 12:59:17", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select \n `tabPurchase Receipt`.`name` as \"Purchase Receipt:Link/Purchase Receipt:120\",\n `tabPurchase Receipt`.`posting_date` as \"Date:Date:100\",\n `tabPurchase Receipt`.`supplier` as \"Supplier:Link/Supplier:120\",\n `tabPurchase Receipt Item`.`project_name` as \"Project\",\n\t`tabPurchase Receipt Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Receipt Item`.qty as \"Qty:Float:100\",\n\t`tabPurchase Receipt Item`.billed_qty as \"Billed Qty:Float:100\", \n\t(`tabPurchase Receipt Item`.qty - ifnull(`tabPurchase Receipt Item`.billed_qty, 0)) as \"Qty to Bill:Float:100\",\n\t`tabPurchase Receipt Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Receipt Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Receipt`, `tabPurchase Receipt Item`\nwhere\n\t`tabPurchase Receipt Item`.`parent` = `tabPurchase Receipt`.`name`\n\tand `tabPurchase Receipt`.docstatus = 1\n\tand `tabPurchase Receipt`.status != \"Stopped\"\n\tand ifnull(`tabPurchase Receipt Item`.billed_qty, 0) < ifnull(`tabPurchase Receipt Item`.qty, 0)\norder by `tabPurchase Receipt`.posting_date asc", + "query": "select \n `tabPurchase Receipt`.`name` as \"Purchase Receipt:Link/Purchase Receipt:120\",\n `tabPurchase Receipt`.`posting_date` as \"Date:Date:100\",\n `tabPurchase Receipt`.`supplier` as \"Supplier:Link/Supplier:120\",\n `tabPurchase Receipt Item`.`project_name` as \"Project\",\n\t`tabPurchase Receipt Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Receipt Item`.amount as \"Amount:Currency:100\",\n\t`tabPurchase Receipt Item`.billed_amt as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Receipt Item`.amount - ifnull(`tabPurchase Receipt Item`.billed_amt, 0)) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Receipt Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Receipt Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Receipt`, `tabPurchase Receipt Item`\nwhere\n\t`tabPurchase Receipt Item`.`parent` = `tabPurchase Receipt`.`name`\n\tand `tabPurchase Receipt`.docstatus = 1\n\tand `tabPurchase Receipt`.status != \"Stopped\"\n\tand ifnull(`tabPurchase Receipt Item`.billed_amt, 0) < ifnull(`tabPurchase Receipt Item`.amount, 0)\norder by `tabPurchase Receipt`.posting_date asc", "ref_doctype": "Purchase Invoice", "report_name": "Received Items To Be Billed", "report_type": "Query Report"