From 4b9a3ed8a0600e3fd7db4588db400311744db019 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 9 Apr 2013 10:27:06 +0530 Subject: [PATCH 001/295] [knowledge base] added all permissions --- utilities/page/questions/questions.txt | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/utilities/page/questions/questions.txt b/utilities/page/questions/questions.txt index 64aaa514d3..804e2a6ce9 100644 --- a/utilities/page/questions/questions.txt +++ b/utilities/page/questions/questions.txt @@ -1,20 +1,31 @@ [ { - "owner": "Administrator", + "creation": "2012-06-14 18:44:56", "docstatus": 0, - "creation": "2011-05-04 11:09:49", + "modified": "2013-04-09 10:25:31", "modified_by": "Administrator", - "modified": "2011-03-29 13:53:57" + "owner": "Administrator" }, { - "name": "__common__", + "doctype": "Page", "module": "Utilities", - "standard": "Yes", + "name": "__common__", "page_name": "Questions", - "doctype": "Page" + "standard": "Yes" }, { - "name": "questions", - "doctype": "Page" + "doctype": "Page Role", + "name": "__common__", + "parent": "questions", + "parentfield": "roles", + "parenttype": "Page", + "role": "All" + }, + { + "doctype": "Page", + "name": "questions" + }, + { + "doctype": "Page Role" } ] \ No newline at end of file From 0c1611219ed7f325bb997caf1d0510d1507af0a6 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 13:54:13 +0530 Subject: [PATCH 002/295] [files] [cleanup/redesign] removed the old file_list structure and also added file listing in setup --- hr/doctype/employee/employee.py | 26 ++++++++++--------- patches/april_2013/p05_update_file_data.py | 4 ++- patches/april_2013/p06_update_file_size.py | 11 ++++++++ patches/patch_list.py | 2 ++ setup/page/setup/setup.js | 6 +++++ .../stock_reconciliation.txt | 7 +++-- 6 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 patches/april_2013/p06_update_file_size.py diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py index 16ecb6d2ed..4100e2ee81 100644 --- a/hr/doctype/employee/employee.py +++ b/hr/doctype/employee/employee.py @@ -100,18 +100,20 @@ class DocType: if self.doc.gender: profile_wrapper.doc.gender = self.doc.gender - if self.doc.image and self.doc.file_list: - # add to file list and user_image - for file_args in self.doc.file_list.split("\n"): - fname, fid = file_args.split(",") - if self.doc.image == fname: - new_file_args = fname + "," + fid - file_list = cstr(profile_wrapper.doc.file_list).split("\n") - if new_file_args not in file_list: - file_list += [new_file_args] - profile_wrapper.doc.file_list = "\n".join(file_list) - profile_wrapper.doc.user_image = fname - break + if self.doc.image: + if not profile_wrapper.doc.user_image == self.doc.image: + profile_wrapper.doc.user_image = self.doc.image + try: + webnotes.doc({ + "doctype": "File Data", + "file_name": self.doc.image, + "attached_to_doctype": "Profile", + "attached_to_name": self.doc.user_id + }).insert() + except webnotes.DuplicateEntryError, e: + # already exists + pass + break profile_wrapper.save() diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index 1f288c1aac..a9a3cf9629 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -2,11 +2,13 @@ import webnotes, webnotes.utils, os from webnotes.modules.export_file import export_to_files def execute(): + webnotes.reload_doc("core", "doctype", "file_data") + webontes.reset_perms("File Data") + singles = webnotes.conn.sql_list("""select name from tabDocType where ifnull(issingle,0)=1""") for doctype in webnotes.conn.sql_list("""select parent from tabDocField where fieldname='file_list' and fieldtype='Text'"""): - print doctype if doctype in singles: doc = webnotes.doc(doctype, doctype) update_for_doc(doctype, doc) diff --git a/patches/april_2013/p06_update_file_size.py b/patches/april_2013/p06_update_file_size.py new file mode 100644 index 0000000000..e61c060d52 --- /dev/null +++ b/patches/april_2013/p06_update_file_size.py @@ -0,0 +1,11 @@ +import webnotes, os, webnotes.utils + +def execute(): + files_path = webnotes.utils.get_path("public", "files") + for f in webnotes.conn.sql("""select name, file_name from + `tabFile Data`""", as_dict=True): + 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 diff --git a/patches/patch_list.py b/patches/patch_list.py index b827c5b482..d5b62e37fc 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -241,4 +241,6 @@ patch_list = [ "patches.april_2013.p04_reverse_modules_list", "execute:webnotes.delete_doc('Search Criteria', 'time_log_summary')", "patches.april_2013.p04_update_role_in_pages", + "patches.april_2013.p05_update_file_data", + "patches.april_2013.p06_update_file_size", ] \ No newline at end of file diff --git a/setup/page/setup/setup.js b/setup/page/setup/setup.js index 4ab7578850..b47f6f28d1 100644 --- a/setup/page/setup/setup.js +++ b/setup/page/setup/setup.js @@ -82,6 +82,12 @@ wn.module_page["Setup"] = [ label: wn._("Rename Tool"), "description":wn._("Rename multiple items in one go") }, + { + "route":"List/File Data", + doctype: "File Data", + label: wn._("File Manager"), + "description":wn._("List, delete uploaded files.") + }, ] }, { diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.txt b/stock/doctype/stock_reconciliation/stock_reconciliation.txt index 760620bb73..9b5b2a128a 100644 --- a/stock/doctype/stock_reconciliation/stock_reconciliation.txt +++ b/stock/doctype/stock_reconciliation/stock_reconciliation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 06:51:17", + "creation": "2013-03-28 10:35:31", "docstatus": 0, - "modified": "2013-03-26 08:32:03", + "modified": "2013-04-10 13:44:19", "modified_by": "Administrator", "owner": "Administrator" }, @@ -52,6 +52,7 @@ "fieldname": "posting_date", "fieldtype": "Date", "in_filter": 0, + "in_list_view": 1, "label": "Posting Date", "oldfieldname": "reconciliation_date", "oldfieldtype": "Date", @@ -63,6 +64,7 @@ "fieldname": "posting_time", "fieldtype": "Time", "in_filter": 0, + "in_list_view": 1, "label": "Posting Time", "oldfieldname": "reconciliation_time", "oldfieldtype": "Time", @@ -148,6 +150,7 @@ "fieldname": "stock_value_difference", "fieldtype": "Currency", "hidden": 1, + "in_list_view": 1, "label": "Stock Value Difference", "print_hide": 1 }, From 44323693f4abd96e8ef4445ffcd1ea9d4825c512 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 13:54:44 +0530 Subject: [PATCH 003/295] [files] [cleanup/redesign] removed the old file_list structure and also added file listing in setup --- home/page/latest_updates/latest_updates.js | 1 + 1 file changed, 1 insertion(+) diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index c0bad9405b..6f8a05b81c 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,4 +1,5 @@ erpnext.updates = [ + ["10th April", ["Redesigned File Uploads and added File Manager in Setup"]], ["27th March", ["Rename multiple items together. Go to Setup > Rename Tool"]], ["26th March", ["Added project to Stock Ledger and Balance", "Added Default Cash Account in Company."]], From 04a1916bbdb6e96fc4e13502fa9ded43dc860b2a Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 13:57:08 +0530 Subject: [PATCH 004/295] [files] [cleanup/redesign] removed the old file_list structure and also added file listing in setup --- patches/april_2013/p05_update_file_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index a9a3cf9629..dc3b9a882c 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -36,7 +36,7 @@ def update_for_doc(doctype, doc): exists = True if not (filename.startswith("http://") or filename.startswith("https://")): - if not os.path.exists(os.path.join(webnotes.utils.get_base_path(), "public", "files", filename)): + if not os.path.exists(webnotes.utils.get_path("public", "files", filename): exists = False if exists: From 753ae05ba90d212bddd0e46446d856d906bb1b82 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 14:14:48 +0530 Subject: [PATCH 005/295] [files] [cleanup/redesign] removed the old file_list structure and also added file listing in setup --- patches/april_2013/p05_update_file_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index dc3b9a882c..a1d2f63b62 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -36,7 +36,7 @@ def update_for_doc(doctype, doc): exists = True if not (filename.startswith("http://") or filename.startswith("https://")): - if not os.path.exists(webnotes.utils.get_path("public", "files", filename): + if not os.path.exists(webnotes.utils.get_path("public", "files", filename)): exists = False if exists: From 9e93037119cd179efd452d3050ac8595798ad426 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 14:17:45 +0530 Subject: [PATCH 006/295] [files] [cleanup/redesign] removed the old file_list structure and also added file listing in setup --- patches/april_2013/p05_update_file_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index a1d2f63b62..8fd38ede88 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -3,7 +3,7 @@ from webnotes.modules.export_file import export_to_files def execute(): webnotes.reload_doc("core", "doctype", "file_data") - webontes.reset_perms("File Data") + webnotes.reset_perms("File Data") singles = webnotes.conn.sql_list("""select name from tabDocType where ifnull(issingle,0)=1""") From cc887a83208f7ca6f4a4b3640484ea56b8376179 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 14:20:56 +0530 Subject: [PATCH 007/295] [files] [cleanup/redesign] removed the old file_list structure and also added file listing in setup --- patches/april_2013/p05_update_file_data.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index 8fd38ede88..9fed8a495b 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -32,7 +32,11 @@ def update_for_doc(doctype, doc): if not filedata: continue - filename, fileid = filedata.split(",") + filedata = filedata.split(",") + if len(filedata)==2: + filename, fileid = filedata[0], filedata[1] + else: + continue exists = True if not (filename.startswith("http://") or filename.startswith("https://")): From 7c179bd00c35024ceed699009f6584726962617b Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 14:22:06 +0530 Subject: [PATCH 008/295] [files] [cleanup/redesign] removed the old file_list structure and also added file listing in setup --- patches/april_2013/p06_update_file_size.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/patches/april_2013/p06_update_file_size.py b/patches/april_2013/p06_update_file_size.py index e61c060d52..8709c7b642 100644 --- a/patches/april_2013/p06_update_file_size.py +++ b/patches/april_2013/p06_update_file_size.py @@ -4,8 +4,9 @@ def execute(): files_path = webnotes.utils.get_path("public", "files") for f in webnotes.conn.sql("""select name, file_name from `tabFile Data`""", as_dict=True): - 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) + 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 From 1b35b1aa62522a80edd3f2db2294b27a22149e7a Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 10 Apr 2013 16:16:59 +0530 Subject: [PATCH 009/295] [file] [patch fix] --- patches/april_2013/p05_update_file_data.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index 9fed8a495b..a5540cc3cd 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -11,8 +11,9 @@ def execute(): fieldname='file_list' and fieldtype='Text'"""): if doctype in singles: doc = webnotes.doc(doctype, doctype) - update_for_doc(doctype, doc) - webnotes.conn.set_value(doctype, None, "file_list", None) + if doc.file_list: + update_for_doc(doctype, doc) + webnotes.conn.set_value(doctype, None, "file_list", None) else: try: for doc in webnotes.conn.sql("""select name, file_list from `tab%s` where From 5ed6676193b4d9781e84584cc82f0a48945e06c4 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 15 Apr 2013 10:49:17 +0530 Subject: [PATCH 010/295] [editor] [stylefix] --- website/doctype/website_settings/website_settings.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/doctype/website_settings/website_settings.txt b/website/doctype/website_settings/website_settings.txt index 442663aae4..4b66e098a4 100644 --- a/website/doctype/website_settings/website_settings.txt +++ b/website/doctype/website_settings/website_settings.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-25 16:01:34", "docstatus": 0, - "modified": "2013-03-13 16:25:22", + "modified": "2013-04-12 10:30:18", "modified_by": "Administrator", "owner": "Administrator" }, @@ -50,7 +50,7 @@ "fieldtype": "Link", "label": "Home Page", "options": "Web Page", - "reqd": 1 + "reqd": 0 }, { "description": "The name of your company / website as you want to appear on browser title bar. All pages will have this as the prefix to the title.", From a1924eda90379c769453581c0944de96900e6677 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 17 Apr 2013 18:40:40 +0530 Subject: [PATCH 011/295] [employee] [fix] removed erroneous break statement --- hr/doctype/employee/employee.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py index bef3274c22..87fe9a45e9 100644 --- a/hr/doctype/employee/employee.py +++ b/hr/doctype/employee/employee.py @@ -114,7 +114,6 @@ class DocType: except webnotes.DuplicateEntryError, e: # already exists pass - break profile_wrapper.save() From 9beae1e5c7c4493cf651ee2308d41d783cc51087 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 18 Apr 2013 19:41:28 +0530 Subject: [PATCH 012/295] [attributions] [updates] added GeoLite attribution --- home/page/attributions/attributions.html | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/home/page/attributions/attributions.html b/home/page/attributions/attributions.html index 2d08c79dd0..99deea4687 100644 --- a/home/page/attributions/attributions.html +++ b/home/page/attributions/attributions.html @@ -41,6 +41,11 @@
  • dateutil
  • termcolor
  • python-memcached +
  • requests +
  • chardet +
  • pygeoip +
  • dropbox +
  • google-api-python-client @@ -100,7 +105,12 @@ Downloadify - Flash Download Widget A tiny javascript + Flash library that enables the creation and download of text files without server interaction. - + + + GeoLite data by MaxMind + GeoLite data created by MaxMind, available from + https://www.maxmind.com + From bbd8dddd566325978e5f8281cd0e185a37bcd510 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 19 Apr 2013 11:01:03 +0530 Subject: [PATCH 013/295] allowed rename for mode of payment --- .../mode_of_payment/mode_of_payment.txt | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/accounts/doctype/mode_of_payment/mode_of_payment.txt b/accounts/doctype/mode_of_payment/mode_of_payment.txt index 2d019964fc..0841af0dae 100644 --- a/accounts/doctype/mode_of_payment/mode_of_payment.txt +++ b/accounts/doctype/mode_of_payment/mode_of_payment.txt @@ -1,68 +1,68 @@ [ { - "owner": "harshada@webnotestech.com", + "creation": "2012-12-04 17:49:20", "docstatus": 0, - "creation": "2012-07-03 13:30:49", + "modified": "2013-04-19 10:59:05", "modified_by": "Administrator", - "modified": "2012-12-04 16:33:37" + "owner": "harshada@webnotestech.com" }, { - "name": "__common__", + "allow_rename": 1, "autoname": "field:mode_of_payment", - "module": "Accounts", "doctype": "DocType", - "document_type": "Master" + "document_type": "Master", + "module": "Accounts", + "name": "__common__" }, { + "doctype": "DocField", "name": "__common__", "parent": "Mode of Payment", - "doctype": "DocField", + "parentfield": "fields", "parenttype": "DocType", "permlevel": 0, - "parentfield": "fields" + "read_only": 0 }, { - "parent": "Mode of Payment", - "read": 1, - "doctype": "DocPerm", - "cancel": 1, - "name": "__common__", - "amend": 0, "create": 1, - "submit": 0, - "write": 1, + "doctype": "DocPerm", + "name": "__common__", + "parent": "Mode of Payment", + "parentfield": "permissions", "parenttype": "DocType", - "role": "Accounts Manager", - "report": 1, "permlevel": 0, - "parentfield": "permissions" + "read": 1, + "report": 1, + "role": "Accounts Manager", + "submit": 0, + "write": 1 }, { - "name": "Mode of Payment", - "doctype": "DocType" + "doctype": "DocType", + "name": "Mode of Payment" }, { - "oldfieldtype": "Data", "doctype": "DocField", - "label": "Mode of Payment", - "oldfieldname": "mode_of_payment", "fieldname": "mode_of_payment", "fieldtype": "Data", + "label": "Mode of Payment", + "oldfieldname": "mode_of_payment", + "oldfieldtype": "Data", "reqd": 1 }, { "doctype": "DocField", - "label": "Company", "fieldname": "company", "fieldtype": "Link", + "label": "Company", "options": "Company" }, { "description": "Default Bank / Cash account will be automatically updated in POS Invoice when this mode is selected.", "doctype": "DocField", - "label": "Default Account", "fieldname": "default_account", "fieldtype": "Link", + "label": "Default Account", "options": "Account" }, { From c54bbc039fbc61831cc5610bb9fced90d03e607c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 19 Apr 2013 16:41:57 +0530 Subject: [PATCH 014/295] [patch] [fix] reload Delivery Note Item before default cost center patch --- patches/patch_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/patches/patch_list.py b/patches/patch_list.py index f6a599694d..e0493dc6eb 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -242,5 +242,6 @@ patch_list = [ "patches.april_2013.p04_update_role_in_pages", "patches.april_2013.p05_fixes_in_reverse_modules", "execute:webnotes.delete_doc('DocType Mapper', 'Delivery Note-Packing Slip')", + "execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')", "patches.april_2013.p06_default_cost_center", ] \ No newline at end of file From f19822b041308ab19c8b01ce1425857f04a35ccc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Apr 2013 12:01:01 +0530 Subject: [PATCH 015/295] fixes in sales/purchase invoice list view --- .../purchase_invoice/purchase_invoice_list.js | 6 +- .../doctype/sales_invoice/sales_invoice.js | 1 - .../doctype/sales_invoice/sales_invoice.txt | 234 +++++++++++++----- .../sales_invoice/sales_invoice_list.js | 6 +- 4 files changed, 173 insertions(+), 74 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/accounts/doctype/purchase_invoice/purchase_invoice_list.js index a3f667f212..7b410a3085 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -1,9 +1,9 @@ // render wn.listview_settings['Purchase Invoice'] = { add_fields: ["`tabPurchase Invoice`.grand_total", "`tabPurchase Invoice`.outstanding_amount"], - add_columns: [{"content":"outstanding_amount", width:"10%", type:"bar-graph", label: "Paid"}], + add_columns: [{"content":"paid_amount", width:"10%", type:"bar-graph", label: "Paid"}], prepare_data: function(data) { - data.outstanding_amount = ((flt(data.grand_total) - - flt(data.outstanding_amount)) / flt(data.grand_total)) * 100; + data.paid_amount = flt(data.grand_total) ? (((flt(data.grand_total) - + flt(data.outstanding_amount)) / flt(data.grand_total)) * 100) : 0; } }; diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index 7863306de1..be6ec3d001 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -159,7 +159,6 @@ cur_frm.cscript.update_stock = function(doc, dt, dn) { cur_frm.cscript.hide_fields(doc, dt, dn); } - cur_frm.cscript.warehouse = function(doc, cdt , cdn) { var d = locals[cdt][cdn]; if (!d.item_code) { msgprint("please enter item code first"); return }; diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index cf6e994c11..2464cb6333 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-22 18:37:25", + "creation": "2013-04-19 11:00:14", "docstatus": 0, - "modified": "2013-03-22 18:38:13", + "modified": "2013-04-22 11:59:28", "modified_by": "Administrator", "owner": "Administrator" }, @@ -44,7 +44,8 @@ "fieldtype": "Section Break", "label": "Basic Info", "oldfieldtype": "Section Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -52,6 +53,7 @@ "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 0, + "read_only": 0, "width": "50%" }, { @@ -65,6 +67,7 @@ "oldfieldtype": "Select", "options": "INV\nINV/10-11/", "print_hide": 1, + "read_only": 0, "reqd": 1 }, { @@ -74,7 +77,8 @@ "label": "Is POS", "oldfieldname": "is_pos", "oldfieldtype": "Check", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "eval:doc.is_pos==1", @@ -84,7 +88,8 @@ "label": "Update Stock", "oldfieldname": "update_stock", "oldfieldtype": "Check", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "description": "The account to which you will pay (have paid) the money to.", @@ -97,6 +102,7 @@ "oldfieldtype": "Link", "options": "Account", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -110,7 +116,8 @@ "oldfieldname": "customer", "oldfieldtype": "Link", "options": "Customer", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -160,7 +167,8 @@ "doctype": "DocField", "fieldname": "column_break1", "fieldtype": "Column Break", - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "read_only": 0 }, { "default": "Today", @@ -174,6 +182,7 @@ "oldfieldname": "posting_date", "oldfieldtype": "Date", "print_hide": 0, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -188,6 +197,7 @@ "no_copy": 1, "oldfieldname": "due_date", "oldfieldtype": "Date", + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -198,14 +208,16 @@ "label": "Mode of Payment", "oldfieldname": "mode_of_payment", "oldfieldtype": "Select", - "options": "link:Mode of Payment" + "options": "link:Mode of Payment", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "items", "fieldtype": "Section Break", "label": "Items", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "allow_on_submit": 1, @@ -215,25 +227,29 @@ "label": "Entries", "oldfieldname": "entries", "oldfieldtype": "Table", - "options": "Sales Invoice Item" + "options": "Sales Invoice Item", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "sales_bom_help", "fieldtype": "HTML", "label": "Sales BOM Help", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "section_break0", "fieldtype": "Section Break", - "options": "Simple" + "options": "Simple", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "col_break26", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -255,12 +271,14 @@ "fieldtype": "Button", "label": "Re-Calculate Values", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "col_break25", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -272,7 +290,8 @@ "oldfieldname": "sales_order_main", "oldfieldtype": "Link", "options": "Sales Order", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "description": "Select Items from Delivery Note", @@ -283,7 +302,8 @@ "oldfieldname": "delivery_note_main", "oldfieldtype": "Link", "options": "Delivery Note", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -291,18 +311,21 @@ "fieldtype": "Button", "label": "Get Items", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "currency_section", "fieldtype": "Section Break", - "label": "Price List and Currency" + "label": "Price List and Currency", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "col_break27", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -314,6 +337,7 @@ "oldfieldtype": "Select", "options": "link:Price List", "print_hide": 1, + "read_only": 0, "reqd": 1 }, { @@ -324,6 +348,7 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, + "read_only": 0, "reqd": 1 }, { @@ -333,12 +358,14 @@ "fieldtype": "Float", "label": "Price List Currency Conversion Rate", "print_hide": 1, + "read_only": 0, "reqd": 1 }, { "doctype": "DocField", "fieldname": "column_break2", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -350,6 +377,7 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, + "read_only": 0, "reqd": 1 }, { @@ -362,6 +390,7 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "print_hide": 1, + "read_only": 0, "reqd": 1 }, { @@ -369,7 +398,8 @@ "fieldname": "taxes", "fieldtype": "Section Break", "label": "Taxes and Charges", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", @@ -379,7 +409,8 @@ "oldfieldname": "charge", "oldfieldtype": "Link", "options": "Sales Taxes and Charges Master", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -387,7 +418,8 @@ "fieldtype": "Button", "label": "Get Taxes and Charges", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "allow_on_submit": 1, @@ -397,7 +429,8 @@ "label": "Taxes and Charges1", "oldfieldname": "other_charges", "oldfieldtype": "Table", - "options": "Sales Taxes and Charges" + "options": "Sales Taxes and Charges", + "read_only": 0 }, { "doctype": "DocField", @@ -405,7 +438,8 @@ "fieldtype": "Button", "label": "Calculate Taxes and Charges", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -424,7 +458,8 @@ "fieldtype": "HTML", "label": "Taxes and Charges Calculation", "oldfieldtype": "HTML", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -432,7 +467,8 @@ "fieldtype": "Section Break", "label": "Totals", "oldfieldtype": "Section Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -440,6 +476,7 @@ "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -508,6 +545,7 @@ "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -569,12 +607,14 @@ "doctype": "DocField", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments" + "label": "Payments", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break3", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -585,7 +625,8 @@ "oldfieldname": "paid_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -595,12 +636,14 @@ "oldfieldname": "cash_bank_account", "oldfieldtype": "Link", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break4", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -608,7 +651,8 @@ "fieldname": "write_off_outstanding_amount_automatically", "fieldtype": "Check", "label": "Write Off Outstanding Amount", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -616,7 +660,8 @@ "fieldtype": "Currency", "label": "Write Off Amount", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -624,7 +669,8 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -632,14 +678,16 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", @@ -649,7 +697,8 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -658,7 +707,8 @@ "label": "Get Terms and Conditions", "oldfieldtype": "Button", "options": "get_tc_details", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -667,7 +717,8 @@ "label": "Terms and Conditions HTML", "oldfieldtype": "HTML", "options": "You can add Terms and Notes that will be printed in the Transaction", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -675,18 +726,21 @@ "fieldtype": "Text Editor", "label": "Terms and Conditions Details", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", - "label": "Contact Info" + "label": "Contact Info", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "col_break23", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -695,7 +749,8 @@ "fieldtype": "Link", "label": "Customer Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -703,12 +758,14 @@ "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "col_break24", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -719,6 +776,7 @@ "label": "Territory", "options": "Territory", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -730,6 +788,7 @@ "label": "Customer Group", "options": "Customer Group", "print_hide": 1, + "read_only": 0, "search_index": 0 }, { @@ -738,7 +797,8 @@ "fieldtype": "Section Break", "label": "More Info", "oldfieldtype": "Section Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -746,6 +806,7 @@ "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -760,6 +821,7 @@ "oldfieldtype": "Select", "options": "No\nYes", "print_hide": 1, + "read_only": 0, "search_index": 0 }, { @@ -770,7 +832,8 @@ "label": "Aging Date", "oldfieldname": "aging_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -780,7 +843,8 @@ "no_copy": 1, "oldfieldname": "posting_time", "oldfieldtype": "Time", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "allow_on_submit": 1, @@ -791,7 +855,8 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "link:Letter Head", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -801,6 +866,7 @@ "no_copy": 1, "options": "No\nYes", "print_hide": 1, + "read_only": 0, "report_hide": 0 }, { @@ -822,7 +888,8 @@ "oldfieldname": "campaign", "oldfieldtype": "Link", "options": "Campaign", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "description": "Track this Sales Invoice against any Project", @@ -834,6 +901,7 @@ "oldfieldname": "project_name", "oldfieldtype": "Link", "options": "Project", + "read_only": 0, "search_index": 1 }, { @@ -847,6 +915,7 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, + "read_only": 0, "report_hide": 1 }, { @@ -854,7 +923,8 @@ "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -864,7 +934,8 @@ "oldfieldname": "source", "oldfieldtype": "Select", "options": "\nExisting Customer\nReference\nAdvertisement\nCold Calling\nExhibition\nSupplier Reference\nMass Mailing\nCustomer's Vendor\nCampaign", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -876,6 +947,7 @@ "oldfieldtype": "Link", "options": "Company", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -890,6 +962,7 @@ "oldfieldtype": "Select", "options": "link:Fiscal Year", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -914,7 +987,8 @@ "no_copy": 1, "oldfieldname": "amendment_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "eval:!doc.__islocal", @@ -936,6 +1010,7 @@ "oldfieldname": "remarks", "oldfieldtype": "Text", "print_hide": 1, + "read_only": 0, "reqd": 0 }, { @@ -944,7 +1019,8 @@ "fieldtype": "Section Break", "label": "Advances", "oldfieldtype": "Section Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -953,7 +1029,8 @@ "label": "Get Advances Received", "oldfieldtype": "Button", "options": "get_advances", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -963,14 +1040,16 @@ "oldfieldname": "advance_adjustment_details", "oldfieldtype": "Table", "options": "Sales Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "packing_list", "fieldtype": "Section Break", "label": "Packing List", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -978,7 +1057,8 @@ "fieldtype": "Table", "label": "Packing Details", "options": "Delivery Note Packing Item", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -986,7 +1066,8 @@ "fieldtype": "Section Break", "label": "Sales Team", "oldfieldtype": "Section Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -994,6 +1075,7 @@ "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -1005,7 +1087,8 @@ "oldfieldname": "sales_partner", "oldfieldtype": "Link", "options": "Sales Partner", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -1013,6 +1096,7 @@ "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -1022,7 +1106,8 @@ "label": "Commission Rate (%)", "oldfieldname": "commission_rate", "oldfieldtype": "Currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -1032,14 +1117,16 @@ "oldfieldname": "total_commission", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "section_break2", "fieldtype": "Section Break", "options": "Simple", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -1049,7 +1136,8 @@ "oldfieldname": "sales_team", "oldfieldtype": "Table", "options": "Sales Team", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "eval:doc.docstatus<2", @@ -1057,13 +1145,15 @@ "fieldname": "recurring_invoice", "fieldtype": "Section Break", "label": "Recurring Invoice", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break11", "fieldtype": "Column Break", "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -1075,7 +1165,8 @@ "fieldtype": "Check", "label": "Convert into Recurring Invoice", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "allow_on_submit": 1, @@ -1087,7 +1178,8 @@ "label": "Recurring Type", "no_copy": 1, "options": "Monthly\nQuarterly\nHalf-yearly\nYearly", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "allow_on_submit": 1, @@ -1098,7 +1190,8 @@ "fieldtype": "Int", "label": "Repeat on Day of Month", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "allow_on_submit": 1, @@ -1109,7 +1202,8 @@ "fieldtype": "Date", "label": "Invoice Period From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "allow_on_submit": 1, @@ -1120,7 +1214,8 @@ "fieldtype": "Date", "label": "Invoice Period To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -1128,6 +1223,7 @@ "fieldtype": "Column Break", "no_copy": 0, "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -1139,7 +1235,8 @@ "fieldtype": "Small Text", "label": "Notification Email Address", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "eval:doc.convert_into_recurring_invoice==1", @@ -1172,7 +1269,8 @@ "fieldtype": "Date", "label": "End Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -1184,6 +1282,7 @@ "oldfieldname": "against_income_account", "oldfieldtype": "Small Text", "print_hide": 1, + "read_only": 0, "report_hide": 1 }, { @@ -1193,7 +1292,8 @@ "hidden": 1, "label": "File List", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "amend": 1, diff --git a/accounts/doctype/sales_invoice/sales_invoice_list.js b/accounts/doctype/sales_invoice/sales_invoice_list.js index c1f8061665..fcd6df14c3 100644 --- a/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -1,9 +1,9 @@ // render wn.listview_settings['Sales Invoice'] = { add_fields: ["`tabSales Invoice`.grand_total", "`tabSales Invoice`.outstanding_amount"], - add_columns: [{"content":"outstanding_amount", width:"10%", type:"bar-graph"}], + add_columns: [{"content":"paid_amount", width:"10%", type:"bar-graph", "label": "Paid"}], prepare_data: function(data) { - data.outstanding_amount = (flt(data.grand_total) - - flt(data.outstanding_amount)) / flt(data.grand_total) * 100; + data.paid_amount = flt(data.grand_total) ? (((flt(data.grand_total) - + flt(data.outstanding_amount)) / flt(data.grand_total)) * 100) : 0; } }; From faab3c4cf2d1927788cd06df57a2a6d51070cc41 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 22 Apr 2013 12:20:09 +0530 Subject: [PATCH 016/295] [listview] [label] show appropriate tooltip for bar graph --- accounts/doctype/sales_invoice/sales_invoice_list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice_list.js b/accounts/doctype/sales_invoice/sales_invoice_list.js index c1f8061665..14d92f2f38 100644 --- a/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -1,7 +1,8 @@ // render wn.listview_settings['Sales Invoice'] = { add_fields: ["`tabSales Invoice`.grand_total", "`tabSales Invoice`.outstanding_amount"], - add_columns: [{"content":"outstanding_amount", width:"10%", type:"bar-graph"}], + add_columns: [{"content":"outstanding_amount", width:"10%", type:"bar-graph", + label: "Payment Received"}], prepare_data: function(data) { data.outstanding_amount = (flt(data.grand_total) - flt(data.outstanding_amount)) / flt(data.grand_total) * 100; From e7366f763bebd653e5593c62ee45b50766425356 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Apr 2013 13:14:52 +0530 Subject: [PATCH 017/295] [report] accounts receivable migrated into new style --- accounts/page/accounts_home/accounts_home.js | 5 + .../report/accounts_receivable/__init__.py | 0 .../accounts_receivable.js | 42 ++++++ .../accounts_receivable.py | 141 ++++++++++++++++++ .../accounts_receivable.txt | 21 +++ .../accounts_receivable.sql | 2 +- 6 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 accounts/report/accounts_receivable/__init__.py create mode 100644 accounts/report/accounts_receivable/accounts_receivable.js create mode 100644 accounts/report/accounts_receivable/accounts_receivable.py create mode 100644 accounts/report/accounts_receivable/accounts_receivable.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 9981ee9a97..0dd00ed659 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -139,6 +139,11 @@ wn.module_page["Accounts"] = [ "page":"Financial Statements", "label": wn._("Financial Statements") }, + { + "label":wn._("Accounts Receivable"), + route: "query-report/Accounts Receivable", + doctype: "Sales Invoice" + }, ] }, { diff --git a/accounts/report/accounts_receivable/__init__.py b/accounts/report/accounts_receivable/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/accounts_receivable/accounts_receivable.js b/accounts/report/accounts_receivable/accounts_receivable.js new file mode 100644 index 0000000000..68c8593dd6 --- /dev/null +++ b/accounts/report/accounts_receivable/accounts_receivable.js @@ -0,0 +1,42 @@ +wn.query_reports["Accounts Receivable"] = { + "filters": [ + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = wn.query_report.filters_by_name.company.get_value(); + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Debit", + "company": company, + "master_type": "Customer" + } + } + } + }, + { + "fieldname":"report_date", + "label": "Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname":"ageing_based_on", + "label": "Ageing Based On", + "fieldtype": "Select", + "options": 'Posting Date' + NEWLINE + 'Due Date', + "default": "Posting Date" + } + ] +} \ No newline at end of file diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py new file mode 100644 index 0000000000..4c0d4e11d3 --- /dev/null +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -0,0 +1,141 @@ +from __future__ import unicode_literals +import webnotes +from webnotes.utils import getdate, nowdate, flt, cstr + +def execute(filters=None): + if not filters: filters = {} + columns = get_columns() + entries = get_gl_entries(filters) + + entries_after_report_date = [[gle.voucher_type, gle.voucher_no] + for gle in get_gl_entries(filters, upto_report_date=False)] + + account_territory_map = get_account_territory_map() + si_due_date_map = get_si_due_date_map() + + # Age of the invoice on this date + age_on = getdate(filters.get("report_date")) > getdate(nowdate()) \ + and nowdate() or filters.get("report_date") + + data = [] + total_invoiced_amount = total_payment = total_outstanding = 0 + for gle in entries: + if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ + or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: + + due_date = (gle.voucher_type == "Sales Invoice") \ + and si_due_date_map.get(gle.voucher_no) or "" + + invoiced_amount = gle.debit > 0 and gle.debit or 0 + payment_amount = get_payment_amount(gle, filters.get("report_date") or nowdate(), + entries_after_report_date) + outstanding_amount = invoiced_amount - payment_amount + + if abs(flt(outstanding_amount)) > 0.01: + row = [gle.posting_date, gle.account, gle.voucher_type, gle.voucher_no, + gle.remarks, due_date, account_territory_map.get(gle.account), + invoiced_amount, payment_amount, outstanding_amount] + # Ageing + if filters.get("ageing_based_on") == "Due Date": + ageing_based_on_date = due_date + else: + ageing_based_on_date = gle.posting_date + row += get_ageing_data(ageing_based_on_date, age_on, outstanding_amount) + + # Add to total + total_invoiced_amount += flt(invoiced_amount) + total_payment += flt(payment_amount) + total_outstanding += flt(outstanding_amount) + + data.append(row) + if data: + data.append(["", "", "", "", "", "", "Total", total_invoiced_amount, total_payment, + total_outstanding, "", "", "", ""]) + + return columns, data + +def get_columns(): + return [ + "Posting Date:Date:80", "Account:Link/Account:150", "Voucher Type::110", + "Voucher No::120", "Remarks::150", "Due Date:Date:80", "Territory:Link/Territory:80", + "Invoiced Amount:Currency:100", "Payment Amount:Currency:100", + "Outstanding Amount:Currency:100", "Age:Int:50", "0-30:Currency:100", + "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" + ] + +def get_gl_entries(filters, upto_report_date=True): + conditions, customer_accounts = get_conditions(filters, upto_report_date) + return webnotes.conn.sql("""select * from `tabGL Entry` + where ifnull(is_cancelled, 'No') = 'No' %s order by posting_date, account""" % + (conditions) % (", ".join(['%s']*len(customer_accounts))), + tuple(customer_accounts), as_dict=1) + +def get_conditions(filters, upto_report_date=True): + conditions = "" + if filters.get("company"): + conditions += " and company='%s'" % filters["company"] + + customer_accounts = [] + if filters.get("account"): + customer_accounts = [filters["account"]] + elif filters.get("company"): + customer_accounts = webnotes.conn.sql_list("""select name from `tabAccount` + where ifnull(master_type, '') = 'Customer' and docstatus < 2 %s""" % + conditions, filters) + + if customer_accounts: + conditions += " and account in (%s)" + + if filters.get("report_date"): + if upto_report_date: + conditions += " and posting_date<='%s'" % filters["report_date"] + else: + conditions += " and posting_date>'%s'" % filters["report_date"] + + return conditions, customer_accounts + +def get_account_territory_map(): + account_territory_map = {} + for each in webnotes.conn.sql("""select t2.name, t1.territory from `tabCustomer` t1, + `tabAccount` t2 where t1.name = t2.master_name group by t2.name"""): + account_territory_map[each[0]] = each[1] + + return account_territory_map + +def get_si_due_date_map(): + """ get due_date from sales invoice """ + si_due_date_map = {} + for t in webnotes.conn.sql("""select name, due_date from `tabSales Invoice` group by name"""): + si_due_date_map[t[0]] = t[1] + + return si_due_date_map + +def get_payment_amount(gle, report_date, entries_after_report_date): + payment_amount = 0 + if flt(gle.credit) > 0 and (not gle.against_voucher or + [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date): + payment_amount = gle.credit + elif flt(gle.debit) > 0: + payment_amount = webnotes.conn.sql(""" + select sum(ifnull(credit, 0)) - sum(ifnull(debit, 0)) from `tabGL Entry` + where account = %s and posting_date <= %s and against_voucher_type = %s + and against_voucher = %s and name != %s and ifnull(is_cancelled, 'No') = 'No'""", + (gle.account, report_date, gle.voucher_type, gle.voucher_no, gle.name))[0][0] + + return flt(payment_amount) + +def get_ageing_data(ageing_based_on_date, age_on, outstanding_amount): + val1 = val2 = val3 = val4 = diff = 0 + diff = age_on and ageing_based_on_date \ + and (getdate(age_on) - getdate(ageing_based_on_date)).days or 0 + + if diff <= 30: + val1 = outstanding_amount + elif 30 < diff <= 60: + val2 = outstanding_amount + elif 60 < diff <= 90: + val3 = outstanding_amount + elif diff > 90: + val4 = outstanding_amount + + return [diff, val1, val2, val3, val4] \ No newline at end of file diff --git a/accounts/report/accounts_receivable/accounts_receivable.txt b/accounts/report/accounts_receivable/accounts_receivable.txt new file mode 100644 index 0000000000..28f7e4f44d --- /dev/null +++ b/accounts/report/accounts_receivable/accounts_receivable.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-04-16 11:31:13", + "docstatus": 0, + "modified": "2013-04-16 11:31:13", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Invoice", + "report_name": "Accounts Receivable", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Accounts Receivable" + } +] \ No newline at end of file diff --git a/accounts/search_criteria/accounts_receivable/accounts_receivable.sql b/accounts/search_criteria/accounts_receivable/accounts_receivable.sql index e9f392200d..6f1c459fdb 100644 --- a/accounts/search_criteria/accounts_receivable/accounts_receivable.sql +++ b/accounts/search_criteria/accounts_receivable/accounts_receivable.sql @@ -13,4 +13,4 @@ WHERE AND `tabGL Entry`.`is_cancelled` = 'No' AND `tabAccount`.`master_type` = 'Customer' AND `tabAccount`.`name` = `tabGL Entry`.`account` -ORDER BY `tabGL Entry`.`posting_date` +ORDER BY `tabGL Entry`.`posting_date`, `tabGL Entry`.`account` From 2f829ecbdf9e3489bb66e6bd2d032bd89c6fd6d9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Apr 2013 14:10:36 +0530 Subject: [PATCH 018/295] field label changed in salary structure --- .../salary_structure/salary_structure.txt | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/hr/doctype/salary_structure/salary_structure.txt b/hr/doctype/salary_structure/salary_structure.txt index c2efac6a8d..43b36626f6 100644 --- a/hr/doctype/salary_structure/salary_structure.txt +++ b/hr/doctype/salary_structure/salary_structure.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-23 19:57:18", + "creation": "2013-03-07 18:50:29", "docstatus": 0, - "modified": "2013-01-29 17:35:34", + "modified": "2013-04-22 14:09:04", "modified_by": "Administrator", "owner": "Administrator" }, @@ -41,6 +41,7 @@ "doctype": "DocField", "fieldname": "column_break0", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -52,6 +53,7 @@ "oldfieldname": "employee", "oldfieldtype": "Link", "options": "Employee", + "read_only": 0, "reqd": 1 }, { @@ -116,6 +118,7 @@ "doctype": "DocField", "fieldname": "column_break1", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -129,6 +132,7 @@ "oldfieldname": "is_active", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -139,6 +143,7 @@ "label": "From Date", "oldfieldname": "from_date", "oldfieldtype": "Date", + "read_only": 0, "reqd": 1 }, { @@ -148,7 +153,8 @@ "in_list_view": 1, "label": "To Date", "oldfieldname": "to_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "read_only": 0 }, { "description": "Cost to Company", @@ -156,10 +162,11 @@ "fieldname": "ctc", "fieldtype": "Currency", "in_filter": 1, - "label": "CTC", + "label": "Annual Cost To Company", "oldfieldname": "ctc", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "read_only": 0, "reqd": 1 }, { @@ -169,6 +176,7 @@ "in_filter": 1, "label": "Company", "options": "link:Company", + "read_only": 0, "reqd": 1 }, { @@ -178,7 +186,8 @@ "fieldtype": "Section Break", "label": "Earning & Deduction", "oldfieldname": "earning_deduction", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", @@ -188,6 +197,7 @@ "label": "Earning", "oldfieldname": "col_brk2", "oldfieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -198,7 +208,8 @@ "label": "Earning1", "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Structure Earning" + "options": "Salary Structure Earning", + "read_only": 0 }, { "doctype": "DocField", @@ -208,6 +219,7 @@ "label": "Deduction", "oldfieldname": "col_brk3", "oldfieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -218,18 +230,21 @@ "label": "Deduction1", "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Structure Deduction" + "options": "Salary Structure Deduction", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "section_break0", "fieldtype": "Section Break", - "options": "Simple" + "options": "Simple", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break2", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -256,6 +271,7 @@ "doctype": "DocField", "fieldname": "column_break3", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { From 978dc8b74dddcd70a18bd1aa77d8ef198045a9df Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 22 Apr 2013 14:25:06 +0530 Subject: [PATCH 019/295] [patches] [fix] fix in update_account_property_patch --- patches/october_2012/update_account_property.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/october_2012/update_account_property.py b/patches/october_2012/update_account_property.py index efe0649d22..a9d365faf8 100644 --- a/patches/october_2012/update_account_property.py +++ b/patches/october_2012/update_account_property.py @@ -11,4 +11,4 @@ def execute(): for acc in roots: webnotes.conn.sql("""update tabAccount set debit_or_credit = %(debit_or_credit)s, is_pl_account = %(is_pl_account)s, company = %(company)s - where lft > %(lft)s and rgt < %(rgt)s""", acc, debug=1) \ No newline at end of file + where lft > %(lft)s and rgt < %(rgt)s""", acc) \ No newline at end of file From a3054062670ca04877e8313cb7253fd807801785 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Apr 2013 14:30:30 +0530 Subject: [PATCH 020/295] [salary structure] added a validation --- .../salary_structure/salary_structure.py | 67 +++++++------------ 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/hr/doctype/salary_structure/salary_structure.py b/hr/doctype/salary_structure/salary_structure.py index 22789e3f49..e70dc05568 100644 --- a/hr/doctype/salary_structure/salary_structure.py +++ b/hr/doctype/salary_structure/salary_structure.py @@ -18,31 +18,25 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cstr, flt -from webnotes.model import db_exists from webnotes.model.doc import addchild, make_autoname -from webnotes.model.bean import copy_doclist -from webnotes import msgprint +from webnotes import msgprint, _ sql = webnotes.conn.sql class DocType: - #init function def __init__(self,doc,doclist=[]): self.doc = doc self.doclist = doclist - - #autoname function - #--------------------------------------------------------- + def autoname(self): self.doc.name = make_autoname(self.doc.employee + '/.SST' + '/.#####') - - #get employee details - #--------------------------------------------------------- + def get_employee_details(self): ret = {} - det = sql("select employee_name, branch, designation, department, grade from `tabEmployee` where name = '%s'" %self.doc.employee) + det = sql("""select employee_name, branch, designation, department, grade + from `tabEmployee` where name = '%s'""", self.doc.employee) if det: ret = { 'employee_name': cstr(det[0][0]), @@ -53,20 +47,16 @@ class DocType: 'backup_employee': cstr(self.doc.employee) } return ret - - # Set Salary structure field values - #--------------------------------------------------------- def get_ss_values(self,employee): - basic_info = sql("select bank_name, bank_ac_no, esic_card_no, pf_number from `tabEmployee` where name ='%s'" % employee) + basic_info = sql("""select bank_name, bank_ac_no, esic_card_no, pf_number + from `tabEmployee` where name ='%s'""", employee) ret = {'bank_name': basic_info and basic_info[0][0] or '', 'bank_ac_no': basic_info and basic_info[0][1] or '', 'esic_no': basic_info and basic_info[0][2] or '', 'pf_no': basic_info and basic_info[0][3] or ''} return ret - - # Make earning and deduction table - #--------------------------------------------------------- + def make_table(self, doct_name, tab_fname, tab_name): list1 = sql("select name from `tab%s` where docstatus != 2" % doct_name) for li in list1: @@ -77,37 +67,30 @@ class DocType: elif(tab_fname == 'deduction_details'): child.d_type = cstr(li[0]) child.d_modified_amt = 0 - - # add earning & deduction types to table - #--------------------------------------------------------- + def make_earn_ded_table(self): - #Earning List self.make_table('Earning Type','earning_details','Salary Structure Earning') - - #Deduction List - self.make_table('Deduction Type','deduction_details', - 'Salary Structure Deduction') - + self.make_table('Deduction Type','deduction_details', 'Salary Structure Deduction') - # Check if another active ss exists - #--------------------------------------------------------- def check_existing(self): - ret = sql("select name from `tabSalary Structure` where is_active = 'Yes' and employee = '%s' and name!='%s'" %(self.doc.employee,self.doc.name)) + ret = sql("""select name from `tabSalary Structure` where is_active = 'Yes' + and employee = '%s' and name!='%s'""", (self.doc.employee,self.doc.name)) if ret and self.doc.is_active=='Yes': - msgprint("Another Salary Structure '%s' is active for employee '%s'. Please make its status 'Inactive' to proceed."%(cstr(ret), self.doc.employee)) - raise Exception + msgprint(_("""Another Salary Structure '%s' is active for employee '%s'. + Please make its status 'Inactive' to proceed.""") % + (cstr(ret), self.doc.employee), raise_exception=1) - # Validate net pay - #--------------------------------------------------------- - def validate_net_pay(self): + def validate_amount(self): if flt(self.doc.net_pay) < 0: - msgprint("Net pay can not be negative") - raise Exception - elif flt(self.doc.net_pay) > flt(self.doc.ctc): - msgprint("Net pay can not be greater than CTC") - raise Exception + msgprint(_("Net pay can not be negative"), raise_exception=1) + elif flt(self.doc.net_pay)*12 > flt(self.doc.ctc): + msgprint(_("Net pay can not be greater than 1/12th of Annual Cost To Company"), + raise_exception=1) + + if flt(self.doc.ctc) < 12*flt(self.doc.total_earning): + msgprint(_("Annual Cost To Company can not be less than 12 months of Total Earning"), + raise_exception=1) def validate(self): self.check_existing() - self.validate_net_pay() - + self.validate_amount() \ No newline at end of file From d16cae113aa41787e068abdcbf317016e6475d03 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Apr 2013 15:15:47 +0530 Subject: [PATCH 021/295] [salary structure] added a validation --- hr/doctype/salary_structure/salary_structure.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hr/doctype/salary_structure/salary_structure.py b/hr/doctype/salary_structure/salary_structure.py index e70dc05568..018aab7e7c 100644 --- a/hr/doctype/salary_structure/salary_structure.py +++ b/hr/doctype/salary_structure/salary_structure.py @@ -36,7 +36,7 @@ class DocType: def get_employee_details(self): ret = {} det = sql("""select employee_name, branch, designation, department, grade - from `tabEmployee` where name = '%s'""", self.doc.employee) + from `tabEmployee` where name = %s""", self.doc.employee) if det: ret = { 'employee_name': cstr(det[0][0]), @@ -50,7 +50,7 @@ class DocType: def get_ss_values(self,employee): basic_info = sql("""select bank_name, bank_ac_no, esic_card_no, pf_number - from `tabEmployee` where name ='%s'""", employee) + from `tabEmployee` where name =%s""", employee) ret = {'bank_name': basic_info and basic_info[0][0] or '', 'bank_ac_no': basic_info and basic_info[0][1] or '', 'esic_no': basic_info and basic_info[0][2] or '', @@ -74,22 +74,23 @@ class DocType: def check_existing(self): ret = sql("""select name from `tabSalary Structure` where is_active = 'Yes' - and employee = '%s' and name!='%s'""", (self.doc.employee,self.doc.name)) + and employee = %s and name!=%s""", (self.doc.employee,self.doc.name)) if ret and self.doc.is_active=='Yes': msgprint(_("""Another Salary Structure '%s' is active for employee '%s'. Please make its status 'Inactive' to proceed.""") % (cstr(ret), self.doc.employee), raise_exception=1) def validate_amount(self): + if flt(self.doc.ctc) < 12*flt(self.doc.total_earning): + msgprint(_("Annual Cost To Company can not be less than 12 months of Total Earning"), + raise_exception=1) + if flt(self.doc.net_pay) < 0: msgprint(_("Net pay can not be negative"), raise_exception=1) elif flt(self.doc.net_pay)*12 > flt(self.doc.ctc): msgprint(_("Net pay can not be greater than 1/12th of Annual Cost To Company"), raise_exception=1) - - if flt(self.doc.ctc) < 12*flt(self.doc.total_earning): - msgprint(_("Annual Cost To Company can not be less than 12 months of Total Earning"), - raise_exception=1) + def validate(self): self.check_existing() From a7b8f3553945e99e9180fd410c5b74b6a61a5136 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 22 Apr 2013 18:59:04 +0530 Subject: [PATCH 022/295] [leave application] [employee query] allow employee lookup for which the current user is leave approver or thhe employee has no leave approvers mentioned or the user is the employee --- .../leave_application/leave_application.js | 7 +++++-- .../leave_application/leave_application.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/hr/doctype/leave_application/leave_application.js b/hr/doctype/leave_application/leave_application.js index 7f8948a674..2715a6c5db 100755 --- a/hr/doctype/leave_application/leave_application.js +++ b/hr/doctype/leave_application/leave_application.js @@ -16,7 +16,6 @@ cur_frm.add_fetch('employee','employee_name','employee_name'); - cur_frm.cscript.onload = function(doc, dt, dn) { if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); @@ -127,4 +126,8 @@ cur_frm.cscript.calculate_total_days = function(doc, dt, dn) { } } -cur_frm.fields_dict.employee.get_query = erpnext.utils.employee_query; \ No newline at end of file +cur_frm.fields_dict.employee.get_query = function() { + return { + query: "hr.doctype.leave_application.leave_application.query_for_permitted_employees" + }; +} \ No newline at end of file diff --git a/hr/doctype/leave_application/leave_application.py b/hr/doctype/leave_application/leave_application.py index b9f9e5bfef..839c73031b 100755 --- a/hr/doctype/leave_application/leave_application.py +++ b/hr/doctype/leave_application/leave_application.py @@ -330,3 +330,20 @@ def add_holidays(events, start, end, employee, company): "title": _("Holiday") + ": " + cstr(holiday.description), "name": holiday.name }) + +@webnotes.whitelist() +def query_for_permitted_employees(doctype, txt, searchfield, start, page_len, filters): + txt = cstr(txt) + "%" + + return webnotes.conn.sql("""select name, employee_name from `tabEmployee` emp + where status = 'Active' and docstatus < 2 and + (`%s` like %s or employee_name like %s) and + (exists(select ela.name from `tabEmployee Leave Approver` ela + where ela.parent=emp.name and ela.leave_approver=%s) or + not exists(select ela.name from `tabEmployee Leave Approver` ela where ela.parent=emp.name) + or user_id = %s) + order by + case when name like %s then 0 else 1 end, + case when employee_name like %s then 0 else 1 end, + name limit %s, %s""" % tuple([searchfield] + ["%s"]*8), + (txt, txt, webnotes.session.user, webnotes.session.user, txt, txt, start, page_len), debug=1) From 81c6a3beefb665e203e30961418af0ca4b3324e8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Apr 2013 11:39:54 +0530 Subject: [PATCH 023/295] stock uom mandatory for item --- stock/doctype/item/item.txt | 252 ++++++++++++++++++++---------------- 1 file changed, 144 insertions(+), 108 deletions(-) diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index b7f7be45ad..1bd4f4cf13 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 15:53:11", + "creation": "2013-03-28 15:56:38", "docstatus": 0, - "modified": "2013-03-20 15:10:12", + "modified": "2013-04-23 11:39:22", "modified_by": "Administrator", "owner": "Administrator" }, @@ -33,6 +33,7 @@ "parent": "Item", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, "report": 1, "submit": 0 @@ -47,7 +48,8 @@ "fieldtype": "Section Break", "label": "Item", "no_copy": 0, - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "description": "Item will be saved by this name in the data base.", @@ -58,6 +60,7 @@ "label": "Item Code", "oldfieldname": "item_code", "oldfieldtype": "Data", + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -70,6 +73,7 @@ "label": "Item Name", "oldfieldname": "item_name", "oldfieldtype": "Data", + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -83,6 +87,7 @@ "oldfieldname": "item_group", "oldfieldtype": "Link", "options": "Item Group", + "read_only": 0, "reqd": 1 }, { @@ -94,7 +99,8 @@ "oldfieldname": "stock_uom", "oldfieldtype": "Link", "options": "UOM", - "reqd": 0 + "read_only": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -106,25 +112,29 @@ "oldfieldtype": "Link", "options": "Brand", "print_hide": 1, + "read_only": 0, "reqd": 0 }, { "doctype": "DocField", "fieldname": "barcode", "fieldtype": "Data", - "label": "Barcode" + "label": "Barcode", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break0", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "image", "fieldtype": "Select", "label": "Image", - "options": "attach_files:" + "options": "attach_files:", + "read_only": 0 }, { "doctype": "DocField", @@ -132,7 +142,8 @@ "fieldtype": "Image", "in_list_view": 1, "label": "Image View", - "options": "image" + "options": "image", + "read_only": 0 }, { "doctype": "DocField", @@ -143,6 +154,7 @@ "label": "Description", "oldfieldname": "description", "oldfieldtype": "Text", + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -150,21 +162,24 @@ "doctype": "DocField", "fieldname": "description_html", "fieldtype": "Small Text", - "label": "Description HTML" + "label": "Description HTML", + "read_only": 0 }, { "description": "Generates HTML to include selected image in the description", "doctype": "DocField", "fieldname": "add_image", "fieldtype": "Button", - "label": "Generate Description HTML" + "label": "Generate Description HTML", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "inventory", "fieldtype": "Section Break", "label": "Inventory", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "default": "Yes", @@ -176,6 +191,7 @@ "oldfieldname": "is_stock_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -187,7 +203,8 @@ "label": "Default Reserved Warehouse", "oldfieldname": "default_warehouse", "oldfieldtype": "Link", - "options": "Warehouse" + "options": "Warehouse", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -197,7 +214,8 @@ "fieldtype": "Float", "label": "Allowance Percent", "oldfieldname": "tolerance", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -205,7 +223,8 @@ "fieldname": "valuation_method", "fieldtype": "Select", "label": "Valuation Method", - "options": "\nFIFO\nMoving Average" + "options": "\nFIFO\nMoving Average", + "read_only": 0 }, { "default": "0.00", @@ -217,7 +236,8 @@ "hidden": 0, "label": "Minimum Order Qty", "oldfieldname": "min_order_qty", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -225,6 +245,7 @@ "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -238,6 +259,7 @@ "oldfieldname": "is_asset_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -250,6 +272,7 @@ "oldfieldname": "has_batch_no", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -264,6 +287,7 @@ "oldfieldname": "has_serial_no", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -273,7 +297,8 @@ "fieldtype": "Data", "label": "Warranty Period (in days)", "oldfieldname": "warranty_period", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -282,7 +307,8 @@ "fieldtype": "Date", "label": "End of Life", "oldfieldname": "end_of_life", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -290,7 +316,8 @@ "doctype": "DocField", "fieldname": "net_weight", "fieldtype": "Float", - "label": "Net Weight" + "label": "Net Weight", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -298,14 +325,16 @@ "fieldname": "weight_uom", "fieldtype": "Link", "label": "Weight UOM", - "options": "UOM" + "options": "UOM", + "read_only": 0 }, { "description": "Auto-raise Material Request if quantity goes below re-order level in a warehouse", "doctype": "DocField", "fieldname": "reorder_section", "fieldtype": "Section Break", - "label": "Re-order" + "label": "Re-order", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -314,19 +343,22 @@ "fieldtype": "Float", "label": "Re-Order Level", "oldfieldname": "re_order_level", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", "doctype": "DocField", "fieldname": "re_order_qty", "fieldtype": "Float", - "label": "Re-Order Qty" + "label": "Re-Order Qty", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break_31", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "read_only": 0 }, { "depends_on": "eval:doc.is_stock_item==\"Yes\"", @@ -334,27 +366,31 @@ "doctype": "DocField", "fieldname": "email_notify", "fieldtype": "Check", - "label": "Notify by Email on Re-order" + "label": "Notify by Email on Re-order", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "section_break_31", "fieldtype": "Section Break", - "options": "Simple" + "options": "Simple", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "item_reorder", "fieldtype": "Table", "label": "Warehouse-wise Item Reorder", - "options": "Item Reorder" + "options": "Item Reorder", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "purchase_details", "fieldtype": "Section Break", "label": "Purchase Details", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "default": "Yes", @@ -366,6 +402,7 @@ "oldfieldname": "is_purchase_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -377,7 +414,8 @@ "label": "Lead Time Days", "no_copy": 1, "oldfieldname": "lead_time_days", - "oldfieldtype": "Int" + "oldfieldtype": "Int", + "read_only": 0 }, { "depends_on": "eval:doc.is_purchase_item==\"Yes\"", @@ -388,7 +426,8 @@ "label": "Default Expense Account", "oldfieldname": "purchase_account", "oldfieldtype": "Link", - "options": "Account" + "options": "Account", + "read_only": 0 }, { "depends_on": "eval:doc.is_purchase_item==\"Yes\"", @@ -399,7 +438,8 @@ "label": "Default Cost Center", "oldfieldname": "cost_center", "oldfieldtype": "Link", - "options": "Cost Center" + "options": "Cost Center", + "read_only": 0 }, { "depends_on": "eval:doc.is_purchase_item==\"Yes\"", @@ -419,7 +459,8 @@ "fieldtype": "Float", "label": "Standard Rate", "oldfieldname": "standard_rate", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "read_only": 0 }, { "depends_on": "eval:doc.is_purchase_item==\"Yes\"", @@ -427,6 +468,7 @@ "fieldname": "column_break2", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -438,21 +480,24 @@ "no_copy": 1, "oldfieldname": "uom_conversion_details", "oldfieldtype": "Table", - "options": "UOM Conversion Detail" + "options": "UOM Conversion Detail", + "read_only": 0 }, { "depends_on": "eval:doc.is_purchase_item==\"Yes\"", "doctype": "DocField", "fieldname": "manufacturer", "fieldtype": "Data", - "label": "Manufacturer" + "label": "Manufacturer", + "read_only": 0 }, { "depends_on": "eval:doc.is_purchase_item==\"Yes\"", "doctype": "DocField", "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number" + "label": "Manufacturer Part Number", + "read_only": 0 }, { "depends_on": "eval:doc.is_purchase_item==\"Yes\"", @@ -460,14 +505,16 @@ "fieldname": "item_supplier_details", "fieldtype": "Table", "label": "Item Supplier Details", - "options": "Item Supplier" + "options": "Item Supplier", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "sales_details", "fieldtype": "Section Break", "label": "Sales Details", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "default": "Yes", @@ -480,6 +527,7 @@ "oldfieldname": "is_sales_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -494,6 +542,7 @@ "oldfieldname": "is_service_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -507,6 +556,7 @@ "oldfieldname": "is_sample_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -516,7 +566,8 @@ "fieldtype": "Float", "label": "Max Discount (%)", "oldfieldname": "max_discount", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "read_only": 0 }, { "depends_on": "eval:doc.is_sales_item==\"Yes\"", @@ -524,7 +575,8 @@ "fieldname": "default_income_account", "fieldtype": "Link", "label": "Default Income Account", - "options": "Account" + "options": "Account", + "read_only": 0 }, { "depends_on": "eval:doc.is_sales_item==\"Yes\"", @@ -532,7 +584,8 @@ "fieldname": "default_sales_cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "read_only": 0 }, { "depends_on": "eval:doc.is_sales_item==\"Yes\"", @@ -542,7 +595,8 @@ "hidden": 1, "label": "Sales Rate", "oldfieldname": "sales_rate", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "read_only": 0 }, { "depends_on": "eval:doc.is_sales_item==\"Yes\"", @@ -550,6 +604,7 @@ "fieldname": "column_break3", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -559,14 +614,16 @@ "fieldname": "item_customer_details", "fieldtype": "Table", "label": "Customer Codes", - "options": "Item Customer Detail" + "options": "Item Customer Detail", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "item_tax_section_break", "fieldtype": "Section Break", "label": "Item Tax", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", @@ -575,13 +632,15 @@ "label": "Item Tax1", "oldfieldname": "item_tax", "oldfieldtype": "Table", - "options": "Item Tax" + "options": "Item Tax", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "price_list_section", "fieldtype": "Section Break", - "label": "Price Lists and Rates" + "label": "Price Lists and Rates", + "read_only": 0 }, { "description": "Create a price list from Price List master and enter standard ref rates against each of them. On selection of a price list in Quotation, Sales Order or Delivery Note, corresponding ref rate will be fetched for this item.", @@ -591,14 +650,16 @@ "label": "Item Prices", "oldfieldname": "ref_rate_details", "oldfieldtype": "Table", - "options": "Item Price" + "options": "Item Price", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "inspection_criteria", "fieldtype": "Section Break", "label": "Inspection Criteria", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "default": "No", @@ -610,6 +671,7 @@ "oldfieldname": "inspection_required", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -621,14 +683,16 @@ "label": "Item Quality Inspection Parameter", "oldfieldname": "item_specification_details", "oldfieldtype": "Table", - "options": "Item Quality Inspection Parameter" + "options": "Item Quality Inspection Parameter", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "manufacturing", "fieldtype": "Section Break", "label": "Manufacturing", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "default": "No", @@ -640,6 +704,7 @@ "oldfieldname": "is_manufactured_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -665,6 +730,7 @@ "oldfieldname": "is_pro_applicable", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -677,6 +743,7 @@ "oldfieldname": "is_sub_contracted_item", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "reqd": 1 }, { @@ -686,7 +753,8 @@ "hidden": 1, "label": "File List", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -696,19 +764,22 @@ "in_filter": 1, "label": "Customer Code", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "website_section", "fieldtype": "Section Break", - "label": "Website" + "label": "Website", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "show_in_website", "fieldtype": "Check", - "label": "Show in Website" + "label": "Show in Website", + "read_only": 0 }, { "depends_on": "show_in_website", @@ -726,6 +797,7 @@ "fieldname": "weightage", "fieldtype": "Int", "label": "Weightage", + "read_only": 0, "search_index": 1 }, { @@ -735,7 +807,8 @@ "fieldname": "slideshow", "fieldtype": "Link", "label": "Slideshow", - "options": "Website Slideshow" + "options": "Website Slideshow", + "read_only": 0 }, { "depends_on": "show_in_website", @@ -744,12 +817,14 @@ "fieldname": "website_image", "fieldtype": "Select", "label": "Image", - "options": "attach_files:" + "options": "attach_files:", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "cb72", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "read_only": 0 }, { "depends_on": "show_in_website", @@ -758,7 +833,8 @@ "fieldname": "website_price_list", "fieldtype": "Link", "label": "Website Price List", - "options": "Price List" + "options": "Price List", + "read_only": 0 }, { "depends_on": "show_in_website", @@ -767,7 +843,8 @@ "fieldname": "website_warehouse", "fieldtype": "Link", "label": "Website Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "read_only": 0 }, { "depends_on": "show_in_website", @@ -776,19 +853,22 @@ "fieldname": "website_item_groups", "fieldtype": "Table", "label": "Website Item Groups", - "options": "Website Item Group" + "options": "Website Item Group", + "read_only": 0 }, { "depends_on": "show_in_website", "doctype": "DocField", "fieldname": "sb72", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "copy_from_item_group", "fieldtype": "Button", - "label": "Copy From Item Group" + "label": "Copy From Item Group", + "read_only": 0 }, { "depends_on": "show_in_website", @@ -796,37 +876,21 @@ "fieldname": "item_website_specifications", "fieldtype": "Table", "label": "Item Website Specifications", - "options": "Item Website Specification" + "options": "Item Website Specification", + "read_only": 0 }, { "depends_on": "show_in_website", "doctype": "DocField", "fieldname": "web_long_description", "fieldtype": "Text Editor", - "label": "Website Description" + "label": "Website Description", + "read_only": 0 }, { "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "role": "System Manager", - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1, - "role": "Material Master Manager", - "write": 0 - }, - { - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, "role": "Material Master Manager", "write": 1 }, @@ -835,15 +899,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "permlevel": 1, - "role": "System Manager" - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1, "role": "Material Manager", "write": 0 }, @@ -852,25 +907,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "permlevel": 0, - "role": "Material Manager", - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1, - "role": "Material User", - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 0, "role": "Material User", "write": 0 } From 6f83e7701081a32081aec6108b1ae34704a3a462 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 23 Apr 2013 12:24:31 +0530 Subject: [PATCH 024/295] [leave application] [employee query] fixes --- hr/doctype/leave_application/leave_application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hr/doctype/leave_application/leave_application.py b/hr/doctype/leave_application/leave_application.py index 839c73031b..6e39751b71 100755 --- a/hr/doctype/leave_application/leave_application.py +++ b/hr/doctype/leave_application/leave_application.py @@ -333,7 +333,7 @@ def add_holidays(events, start, end, employee, company): @webnotes.whitelist() def query_for_permitted_employees(doctype, txt, searchfield, start, page_len, filters): - txt = cstr(txt) + "%" + txt = "%" + cstr(txt) + "%" return webnotes.conn.sql("""select name, employee_name from `tabEmployee` emp where status = 'Active' and docstatus < 2 and @@ -346,4 +346,4 @@ def query_for_permitted_employees(doctype, txt, searchfield, start, page_len, fi case when name like %s then 0 else 1 end, case when employee_name like %s then 0 else 1 end, name limit %s, %s""" % tuple([searchfield] + ["%s"]*8), - (txt, txt, webnotes.session.user, webnotes.session.user, txt, txt, start, page_len), debug=1) + (txt, txt, webnotes.session.user, webnotes.session.user, txt, txt, start, page_len)) From 890265928b88c880ae534f8c7df04f91d2d0956b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 23 Apr 2013 12:30:38 +0530 Subject: [PATCH 025/295] [backup manager] [error handling] on error, send complete traceback as email --- setup/doctype/backup_manager/backup_manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup/doctype/backup_manager/backup_manager.py b/setup/doctype/backup_manager/backup_manager.py index 4fb6ab3069..ce0f1e1729 100644 --- a/setup/doctype/backup_manager/backup_manager.py +++ b/setup/doctype/backup_manager/backup_manager.py @@ -27,8 +27,8 @@ def take_backups_dropbox(): from setup.doctype.backup_manager.backup_dropbox import backup_to_dropbox backup_to_dropbox() send_email(True, "Dropbox") - except Exception, e: - send_email(False, "Dropbox", e) + except Exception: + send_email(False, "Dropbox", webnotes.getTraceback()) #backup to gdrive @webnotes.whitelist() @@ -37,8 +37,8 @@ def take_backups_gdrive(): from setup.doctype.backup_manager.backup_googledrive import backup_to_gdrive backup_to_gdrive() send_email(True, "Google Drive") - except Exception, e: - send_email(False, "Google Drive", e) + except Exception: + send_email(False, "Google Drive", webnotes.getTraceback()) def send_email(success, service_name, error_status=None): if success: From 0a480f4efd1fabacd3d0b7a37d0577a6ae7bbaa2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Apr 2013 12:54:07 +0530 Subject: [PATCH 026/295] fixes in item: organized code --- stock/doctype/item/item.py | 323 +++++++++++++++++++----------------- stock/doctype/item/item.txt | 2 +- 2 files changed, 168 insertions(+), 157 deletions(-) diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index a16296d18b..4767742e3e 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -24,50 +24,169 @@ from webnotes import msgprint, _ from webnotes.model.controller import DocListController class DocType(DocListController): - def get_tax_rate(self, tax_type): - rate = webnotes.conn.sql("select tax_rate from tabAccount where name = %s", tax_type) - ret = { - 'tax_rate' : rate and flt(rate[0][0]) or 0 - } - return ret + def validate(self): + if not self.doc.stock_uom: + msgprint(_("Please enter Default Unit of Measure"), raise_exception=1) + + self.check_stock_uom_with_bin() + self.validate_conversion_factor() + self.add_default_uom_in_conversion_factor_table() + self.valiadte_item_type() + self.check_for_active_boms() + self.check_ref_rate_detail() + self.fill_customer_code() + self.check_item_tax() + self.validate_barcode() + self.check_non_asset_warehouse() + self.cant_change() + if self.doc.name: + self.old_page_name = webnotes.conn.get_value('Item', self.doc.name, 'page_name') + def on_update(self): self.validate_name_with_item_group() - - # webpage updates self.update_website() - + + def add_default_uom_in_conversion_factor_table(self): + uom_conv_list = [d.uom for d in self.doclist.get({"parentfield": "uom_conversion_details"})] + if self.doc.stock_uom not in uom_conv_list: + ch = addchild(self.doc, 'uom_conversion_details', 'UOM Conversion Detail', self.doclist) + ch.uom = self.doc.stock_uom + ch.conversion_factor = 1 + + def check_stock_uom_with_bin(self): bin = webnotes.conn.sql("select stock_uom from `tabBin` where item_code = %s", self.doc.item_code) - if bin and cstr(bin[0][0]) and cstr(bin[0][0]) != cstr(self.doc.stock_uom): - msgprint("Please Update Stock UOM with the help of Stock UOM Replace Utility.") - raise Exception + if self.doc.stock_uom and bin and cstr(bin[0][0]) \ + and cstr(bin[0][0]) != cstr(self.doc.stock_uom): + msgprint(_("Please Update Stock UOM with the help of Stock UOM Replace Utility."), + raise_exception=1) + + def validate_conversion_factor(self): check_list = [] for d in getlist(self.doclist,'uom_conversion_details'): - if not self.doc.stock_uom: - msgprint("Please enter Stock UOM first.") - raise Exception - if cstr(d.uom) in check_list: - msgprint("UOM %s has been entered more than once in Conversion Factor Details." % cstr(d.uom)) - raise Exception + msgprint(_("UOM %s has been entered more than once in Conversion Factor Table." % + cstr(d.uom)), raise_exception=1) else: check_list.append(cstr(d.uom)) - if cstr(d.uom) == cstr(self.doc.stock_uom): - if flt(d.conversion_factor) != 1: - msgprint("Conversion Factor of UOM : %s should be equal to 1. As UOM : %s is Stock UOM of Item: %s." % ( cstr(d.uom), cstr(d.uom), cstr(self.doc.name))) - raise Exception - elif cstr(d.uom) != cstr(self.doc.stock_uom) and flt(d.conversion_factor) == 1: - msgprint("Conversion Factor of UOM : %s should not be equal to 1. As UOM : %s is not Stock UOM of Item: %s." % ( cstr(d.uom), cstr(d.uom), cstr(self.doc.name))) - raise Exception + if d.uom and cstr(d.uom) == cstr(self.doc.stock_uom) and flt(d.conversion_factor) != 1: + msgprint(_("""Conversion Factor of UOM: %s should be equal to 1. + As UOM: %s is Stock UOM of Item: %s.""" % + (d.uom, d.uom, self.doc.name)), raise_exception=1) + elif d.uom and cstr(d.uom)!= self.doc.stock_uom and flt(d.conversion_factor) == 1: + msgprint(_("""Conversion Factor of UOM: %s should not be equal to 1. + As UOM: %s is not Stock UOM of Item: %s""" % + (d.uom, d.uom, self.doc.name)), raise_exception=1) + + def valiadte_item_type(self): + if cstr(self.doc.is_manufactured_item) == "No": + self.doc.is_pro_applicable = "No" - if not cstr(self.doc.stock_uom) in check_list : - child = addchild( self.doc, 'uom_conversion_details', - 'UOM Conversion Detail', self.doclist) - child.uom = self.doc.stock_uom - child.conversion_factor = 1 - child.save() + if self.doc.is_pro_applicable == 'Yes' and self.doc.is_stock_item == 'No': + msgprint("As Production Order can be made for this Item, then Is Stock Item Should be 'Yes' as we maintain it's stock. Refer Manufacturing and Inventory section.", raise_exception=1) + + if self.doc.has_serial_no == 'Yes' and self.doc.is_stock_item == 'No': + msgprint("'Has Serial No' can not be 'Yes' for non-stock item", raise_exception=1) + + def check_for_active_boms(self): + def _check_for_active_boms(field_label): + if field_label in ['Is Active', 'Is Purchase Item']: + bom_mat = webnotes.conn.sql("""select distinct t1.parent + from `tabBOM Item` t1, `tabBOM` t2 where t2.name = t1.parent + and t1.item_code =%s and ifnull(t1.bom_no, '') = '' and t2.is_active = 1 + and t2.docstatus = 1 and t1.docstatus =1 """, self.doc.name) + if bom_mat and bom_mat[0][0]: + msgprint(_(field_label) + _(" should be 'Yes'. As Item: ") + self.doc.name + + _(" is present in one or many Active BOMs"), raise_exception=1) + + if ((field_label == 'Allow Production Order' + and self.doc.is_sub_contracted_item != 'Yes') + or (field_label == 'Is Sub Contracted Item' + and self.doc.is_manufactured_item != 'Yes')): + bom = webnotes.conn.sql("""select name from `tabBOM` where item = %s + and is_active = 1""", (self.doc.name,)) + if bom and bom[0][0]: + msgprint(_(field_label) + _(" should be 'Yes'. As Item: ") + self.doc.name + + _(" is present in one or many Active BOMs"), raise_exception=1) + + if not cint(self.doc.fields.get("__islocal")): + fl = {'is_manufactured_item' :'Allow Bill of Materials', + 'is_sub_contracted_item':'Is Sub Contracted Item', + 'is_purchase_item' :'Is Purchase Item', + 'is_pro_applicable' :'Allow Production Order'} + for d in fl: + if cstr(self.doc.fields.get(d)) != 'Yes': + _check_for_active_boms(fl[d]) + + def check_ref_rate_detail(self): + check_list=[] + for d in getlist(self.doclist,'ref_rate_details'): + if [cstr(d.price_list_name), cstr(d.ref_currency), + cint(d.selling), cint(d.buying)] in check_list: + msgprint("Ref Rate is entered twice for Price List : '%s' and Currency : '%s'." % + (d.price_list_name,d.ref_currency), raise_exception=1) + else: + check_list.append([cstr(d.price_list_name),cstr(d.ref_currency)]) + + def fill_customer_code(self): + """ Append all the customer codes and insert into "customer_code" field of item table """ + cust_code=[] + for d in getlist(self.doclist,'item_customer_details'): + cust_code.append(d.ref_code) + self.doc.customer_code=','.join(cust_code) + + def check_item_tax(self): + """Check whether Tax Rate is not entered twice for same Tax Type""" + check_list=[] + for d in getlist(self.doclist,'item_tax'): + if d.tax_type: + account_type = webnotes.conn.get_value("Account", d.tax_type, "account_type") + + if account_type not in ['Tax', 'Chargeable']: + msgprint("'%s' is not Tax / Chargeable Account" % d.tax_type, raise_exception=1) + else: + if d.tax_type in check_list: + msgprint("Rate is entered twice for: '%s'" % d.tax_type, raise_exception=1) + else: + check_list.append(d.tax_type) + + def validate_barcode(self): + if self.doc.barcode: + duplicate = webnotes.conn.sql("select name from tabItem where barcode = %s and name != %s", (self.doc.barcode, self.doc.name)) + if duplicate: + msgprint("Barcode: %s already used in item: %s" % + (self.doc.barcode, cstr(duplicate[0][0])), raise_exception = 1) + + def check_non_asset_warehouse(self): + if self.doc.is_asset_item == "Yes": + existing_qty = webnotes.conn.sql("select t1.warehouse, t1.actual_qty from tabBin t1, tabWarehouse t2 where t1.item_code=%s and (t2.warehouse_type!='Fixed Asset' or t2.warehouse_type is null) and t1.warehouse=t2.name and t1.actual_qty > 0", self.doc.name) + for e in existing_qty: + msgprint("%s Units exist in Warehouse %s, which is not an Asset Warehouse." % + (e[1],e[0])) + if existing_qty: + self.doc.is_asset_item = 'No' + msgprint(_("""Please transfer the above quantities to an asset warehouse \ + before changing this item to an asset item"""), raise_exception=1) + + def cant_change(self): + if not self.doc.fields.get("__islocal"): + vals = webnotes.conn.get_value("Item", self.doc.name, + ["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True) + + if vals and ((self.doc.is_stock_item == "No" and vals.is_stock_item == "Yes") or + vals.has_serial_no != self.doc.has_serial_no or + vals.valuation_method != self.doc.valuation_method): + if self.check_if_sle_exists() == "exists": + webnotes.msgprint(_("As there are existing stock transactions for this \ + item, you can not change the values of 'Has Serial No', \ + 'Is Stock Item' and 'Valuation Method'"), raise_exception=1) + + def check_if_sle_exists(self): + sle = webnotes.conn.sql("""select name from `tabStock Ledger Entry` + where item_code = %s and ifnull(is_cancelled, 'No') = 'No'""", self.doc.name) + return sle and 'exists' or 'not exists' def validate_name_with_item_group(self): if webnotes.conn.exists("Item Group", self.doc.name): @@ -104,107 +223,17 @@ class DocType(DocListController): webnotes.conn.set(self.doc, "page_name", None) - # On delete 1. Delete BIN (if none of the corrosponding transactions present, it gets deleted. if present, rolled back due to exception) - def on_trash(self): - webnotes.conn.sql("""delete from tabBin where item_code=%s""", self.doc.item_code) - webnotes.conn.sql("""delete from `tabStock Ledger Entry` - where item_code=%s and is_cancelled='Yes' """, self.doc.item_code) - - if self.doc.page_name: - from webnotes.webutils import clear_cache - clear_cache(self.doc.page_name) - - # Check whether Ref Rate is not entered twice for same Price List and Currency - def check_ref_rate_detail(self): - check_list=[] - for d in getlist(self.doclist,'ref_rate_details'): - if [cstr(d.price_list_name),cstr(d.ref_currency),cint(d.selling),cint(d.buying)] in check_list: - msgprint("Ref Rate is entered twice for Price List : '%s' and Currency : '%s'." % (d.price_list_name,d.ref_currency)) - raise Exception - else: - check_list.append([cstr(d.price_list_name),cstr(d.ref_currency)]) + def get_tax_rate(self, tax_type): + return { "tax_rate": webnotes.conn.get_value("Account", tax_type, "tax_rate") } - # Append all the customer codes and insert into "customer_code" field of item table - def fill_customer_code(self): - cust_code=[] - for d in getlist(self.doclist,'item_customer_details'): - cust_code.append(d.ref_code) - self.doc.customer_code=','.join(cust_code) + def prepare_template_args(self): + from website.helpers.product import get_parent_item_groups + self.parent_groups = get_parent_item_groups(self.doc.item_group) + [{"name":self.doc.name}] + self.doc.title = self.doc.item_name - def check_item_tax(self): - """Check whether Tax Rate is not entered twice for same Tax Type""" - check_list=[] - for d in getlist(self.doclist,'item_tax'): - if d.tax_type: - account_type = webnotes.conn.get_value("Account", d.tax_type, "account_type") - - if account_type not in ['Tax', 'Chargeable']: - msgprint("'%s' is not Tax / Chargeable Account" % d.tax_type, raise_exception=1) - else: - if d.tax_type in check_list: - msgprint("Rate is entered twice for: '%s'" % d.tax_type, raise_exception=1) - else: - check_list.append(d.tax_type) - - def check_for_active_boms(self, field_label): - if field_label in ['Is Active', 'Is Purchase Item']: - bom_mat = webnotes.conn.sql("select distinct t1.parent from `tabBOM Item` t1, `tabBOM` t2 where t1.item_code =%s and (t1.bom_no = '' or t1.bom_no is NULL) and t2.name = t1.parent and t2.is_active = 1 and t2.docstatus = 1 and t1.docstatus =1 ", self.doc.name) - if bom_mat and bom_mat[0][0]: - msgprint("%s should be 'Yes'. As Item %s is present in one or many Active BOMs." % (cstr(field_label), cstr(self.doc.name))) - raise Exception - if ((field_label == 'Allow Production Order' - and self.doc.is_sub_contracted_item != 'Yes') - or (field_label == 'Is Sub Contracted Item' - and self.doc.is_manufactured_item != 'Yes')): - bom = webnotes.conn.sql("select name from `tabBOM` where item = %s and is_active = 1", - (self.doc.name,)) - if bom and bom[0][0]: - msgprint("%s should be 'Yes'. As Item %s is present in one or many Active BOMs." % (cstr(field_label), cstr(self.doc.name))) - raise Exception - - def validate_barcode(self): - if self.doc.barcode: - duplicate = webnotes.conn.sql("select name from tabItem where barcode = %s and name != %s", (self.doc.barcode, self.doc.name)) - if duplicate: - msgprint("Barcode: %s already used in item: %s" % (self.doc.barcode, cstr(duplicate[0][0])), raise_exception = 1) - - def validate(self): - if not cint(self.doc.fields.get("__islocal")): - fl = {'is_manufactured_item' :'Allow Bill of Materials', - 'is_sub_contracted_item':'Is Sub Contracted Item', - 'is_purchase_item' :'Is Purchase Item', - 'is_pro_applicable' :'Allow Production Order'} - for d in fl: - if cstr(self.doc.fields.get(d)) != 'Yes': - self.check_for_active_boms(fl[d]) - self.check_ref_rate_detail() - self.fill_customer_code() - self.check_item_tax() - self.validate_barcode() - self.check_non_asset_warehouse() - self.cant_change() - - if cstr(self.doc.is_manufactured_item) == "No": - self.doc.is_pro_applicable = "No" - - if self.doc.is_pro_applicable == 'Yes' and self.doc.is_stock_item == 'No': - msgprint("As Production Order can be made for this Item, then Is Stock Item Should be 'Yes' as we maintain it's stock. Refer Manufacturing and Inventory section.", raise_exception=1) - - if self.doc.has_serial_no == 'Yes' and self.doc.is_stock_item == 'No': - msgprint("'Has Serial No' can not be 'Yes' for non-stock item", raise_exception=1) - - if self.doc.name: - self.old_page_name = webnotes.conn.get_value('Item', self.doc.name, 'page_name') - - def check_non_asset_warehouse(self): - if self.doc.is_asset_item == "Yes": - existing_qty = webnotes.conn.sql("select t1.warehouse, t1.actual_qty from tabBin t1, tabWarehouse t2 where t1.item_code=%s and (t2.warehouse_type!='Fixed Asset' or t2.warehouse_type is null) and t1.warehouse=t2.name and t1.actual_qty > 0", self.doc.name) - for e in existing_qty: - msgprint("%s Units exist in Warehouse %s, which is not an Asset Warehouse." % (e[1],e[0])) - if existing_qty: - msgprint("Please transfer the above quantities to an asset warehouse before changing this item to an asset item.") - self.doc.is_asset_item = 'No' - raise Exception + if self.doc.slideshow: + from website.helpers.slideshow import get_slideshow + get_slideshow(self) def get_file_details(self, arg = ''): file = webnotes.conn.sql("select file_group, description from tabFile where name = %s", eval(arg)['file_name'], as_dict = 1) @@ -215,35 +244,17 @@ class DocType(DocListController): } return ret + def on_trash(self): + webnotes.conn.sql("""delete from tabBin where item_code=%s""", self.doc.item_code) + webnotes.conn.sql("""delete from `tabStock Ledger Entry` + where item_code=%s and is_cancelled='Yes' """, self.doc.item_code) - def check_if_sle_exists(self): - sle = webnotes.conn.sql("select name from `tabStock Ledger Entry` where item_code = %s and ifnull(is_cancelled, 'No') = 'No'", self.doc.name) - return sle and 'exists' or 'not exists' + if self.doc.page_name: + from webnotes.webutils import clear_cache + clear_cache(self.doc.page_name) def on_rename(self,newdn,olddn): webnotes.conn.sql("update tabItem set item_code = %s where name = %s", (newdn, olddn)) if self.doc.page_name: from webnotes.webutils import clear_cache - clear_cache(self.doc.page_name) - - def prepare_template_args(self): - from website.helpers.product import get_parent_item_groups - self.parent_groups = get_parent_item_groups(self.doc.item_group) + [{"name":self.doc.name}] - self.doc.title = self.doc.item_name - - if self.doc.slideshow: - from website.helpers.slideshow import get_slideshow - get_slideshow(self) - - def cant_change(self): - if not self.doc.fields.get("__islocal"): - vals = webnotes.conn.get_value("Item", self.doc.name, - ["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True) - - if vals and ((self.doc.is_stock_item == "No" and vals.is_stock_item == "Yes") or - vals.has_serial_no != self.doc.has_serial_no or - vals.valuation_method != self.doc.valuation_method): - if self.check_if_sle_exists() == "exists": - webnotes.msgprint(_("As there are existing stock transactions for this \ - item, you can not change the values of 'Has Serial No', \ - 'Is Stock Item' and 'Valuation Method'"), raise_exception=1) + clear_cache(self.doc.page_name) \ No newline at end of file diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index 1bd4f4cf13..38b8bcc7ac 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-28 15:56:38", "docstatus": 0, - "modified": "2013-04-23 11:39:22", + "modified": "2013-04-23 11:44:39", "modified_by": "Administrator", "owner": "Administrator" }, From 76d7ec51fb4897de241a17faf8e1968a6ba732e6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 23 Apr 2013 13:05:40 +0530 Subject: [PATCH 027/295] [backup manager] [fixes] fix in dropbox folder path --- .../doctype/backup_manager/backup_dropbox.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/setup/doctype/backup_manager/backup_dropbox.py b/setup/doctype/backup_manager/backup_dropbox.py index 2901638c04..c6556d2e8c 100644 --- a/setup/doctype/backup_manager/backup_dropbox.py +++ b/setup/doctype/backup_manager/backup_dropbox.py @@ -81,9 +81,10 @@ def backup_to_dropbox(): backup = new_backup() filename = os.path.join(get_base_path(), "public", "backups", os.path.basename(backup.backup_path_db)) - upload_file_to_dropbox(filename, "database", dropbox_client) + upload_file_to_dropbox(filename, "/database", dropbox_client) response = dropbox_client.metadata("/files") + # upload files to files folder path = os.path.join(get_base_path(), "public", "files") for filename in os.listdir(path): @@ -94,7 +95,7 @@ def backup_to_dropbox(): found = True break if not found: - upload_file_to_dropbox(filepath, "files", dropbox_client) + upload_file_to_dropbox(filepath, "/files", dropbox_client) def get_dropbox_session(): try: @@ -113,21 +114,21 @@ def get_dropbox_session(): def upload_file_to_dropbox(filename, folder, dropbox_client): from dropbox import rest size = os.stat(filename).st_size - f = open(filename,'r') - # if max packet size reached, use chunked uploader - max_packet_size = 4194304 + with open(filename, 'r') as f: + # if max packet size reached, use chunked uploader + max_packet_size = 4194304 - if size > max_packet_size: - uploader = dropbox_client.get_chunked_uploader(f, size) - while uploader.offset < size: - try: - uploader.upload_chunked() - uploader.finish(folder + "/" + os.path.basename(filename), overwrite=True) - except rest.ErrorResponse: - pass - else: - dropbox_client.put_file(folder + "/" + os.path.basename(filename), f, overwrite=True) + if size > max_packet_size: + uploader = dropbox_client.get_chunked_uploader(f, size) + while uploader.offset < size: + try: + uploader.upload_chunked() + uploader.finish(folder + "/" + os.path.basename(filename), overwrite=True) + except rest.ErrorResponse: + pass + else: + dropbox_client.put_file(folder + "/" + os.path.basename(filename), f, overwrite=True) if __name__=="__main__": backup_to_dropbox() \ No newline at end of file From ed7cf1bced9064baeeced3ec1aa041a6dfb2d308 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 23 Apr 2013 14:23:03 +0530 Subject: [PATCH 028/295] [backup manager] [fixes] fixes in dropbox and google drive --- .../doctype/backup_manager/backup_dropbox.py | 21 +++++++++++++---- .../backup_manager/backup_googledrive.py | 23 +++++++++++++++---- .../doctype/backup_manager/backup_manager.js | 6 +++-- .../doctype/backup_manager/backup_manager.py | 22 ++++++++++++++---- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/setup/doctype/backup_manager/backup_dropbox.py b/setup/doctype/backup_manager/backup_dropbox.py index c6556d2e8c..ac07824a88 100644 --- a/setup/doctype/backup_manager/backup_dropbox.py +++ b/setup/doctype/backup_manager/backup_dropbox.py @@ -7,12 +7,14 @@ # dropbox_access_key # dropbox_access_secret - +from __future__ import unicode_literals import os import webnotes -from webnotes.utils import get_request_site_address, get_base_path +from webnotes.utils import get_request_site_address, get_base_path, cstr from webnotes import _ +from backup_manager import ignore_list + @webnotes.whitelist() def get_dropbox_authorize_url(): sess = get_dropbox_session() @@ -58,7 +60,7 @@ def dropbox_callback(oauth_token=None, not_approved=False): webnotes.message_title = "Dropbox Approval" webnotes.message = "

    %s

    Please close this window.

    " % message - + webnotes.conn.commit() webnotes.response['type'] = 'page' webnotes.response['page_name'] = 'message.html' @@ -86,8 +88,13 @@ def backup_to_dropbox(): response = dropbox_client.metadata("/files") # upload files to files folder + did_not_upload = [] + error_log = [] path = os.path.join(get_base_path(), "public", "files") for filename in os.listdir(path): + if filename in ignore_list: + continue + found = False filepath = os.path.join(path, filename) for file_metadata in response["contents"]: @@ -95,7 +102,13 @@ def backup_to_dropbox(): found = True break if not found: - upload_file_to_dropbox(filepath, "/files", dropbox_client) + try: + upload_file_to_dropbox(filepath, "/files", dropbox_client) + except Exception, e: + did_not_upload.append(filename) + error_log.append(cstr(e)) + + return did_not_upload, list(set(error_log)) def get_dropbox_session(): try: diff --git a/setup/doctype/backup_manager/backup_googledrive.py b/setup/doctype/backup_manager/backup_googledrive.py index 4b2a82cb89..24cd857667 100644 --- a/setup/doctype/backup_manager/backup_googledrive.py +++ b/setup/doctype/backup_manager/backup_googledrive.py @@ -10,12 +10,13 @@ # gdrive_client_id # gdrive_client_secret +from __future__ import unicode_literals import httplib2 import os import mimetypes import webnotes import oauth2client.client -from webnotes.utils import get_base_path +from webnotes.utils import get_base_path, cstr from webnotes import _, msgprint from apiclient.discovery import build from apiclient.http import MediaFileUpload @@ -30,6 +31,9 @@ def get_gdrive_authorize_url(): @webnotes.whitelist() def upload_files(name, mimetype, service, folder_id): + import logging + logging.basicConfig() + if not webnotes.conn: webnotes.connect() file_name = os.path.basename(name) @@ -67,8 +71,11 @@ def backup_to_gdrive(): # upload files to database folder upload_files(filename, 'application/x-gzip', drive_service, webnotes.conn.get_value("Backup Manager", None, "database_folder_id")) - + # upload files to files folder + did_not_upload = [] + error_log = [] + path = os.path.join(get_base_path(), "public", "files") for filename in os.listdir(path): found = False @@ -78,7 +85,8 @@ def backup_to_gdrive(): if ext == 'gz' or ext == 'gzip': mimetype = 'application/x-gzip' else: - mimetype = mimetypes.types_map["." + ext] + mimetype = mimetypes.types_map.get("." + ext) or "application/octet-stream" + #Compare Local File with Server File param = {} children = drive_service.children().list( @@ -90,7 +98,14 @@ def backup_to_gdrive(): found = True break if not found: - upload_files(filepath, mimetype, drive_service, webnotes.conn.get_value("Backup Manager", None, "files_folder_id")) + try: + upload_files(filepath, mimetype, drive_service, + webnotes.conn.get_value("Backup Manager", None, "files_folder_id")) + except Exception, e: + did_not_upload.append(filename) + error_log.append(cstr(e)) + + return did_not_upload, list(set(error_log)) def get_gdrive_flow(): from oauth2client.client import OAuth2WebServerFlow diff --git a/setup/doctype/backup_manager/backup_manager.js b/setup/doctype/backup_manager/backup_manager.js index 28315c5bc5..8d2d8de853 100644 --- a/setup/doctype/backup_manager/backup_manager.js +++ b/setup/doctype/backup_manager/backup_manager.js @@ -41,7 +41,9 @@ cur_frm.cscript.allow_gdrive_access = function(doc) { wn.call({ method: "setup.doctype.backup_manager.backup_googledrive.get_gdrive_authorize_url", callback: function(r) { - window.open(r.message.authorize_url); + if(!r.exc) { + window.open(r.message.authorize_url); + } } }) } @@ -49,7 +51,7 @@ cur_frm.cscript.allow_gdrive_access = function(doc) { cur_frm.cscript.validate_gdrive = function(doc) { wn.call({ - method: "setup.doctype.backup_manager.backup_manager.gdrive_callback", + method: "setup.doctype.backup_manager.backup_googledrive.gdrive_callback", args: { verification_code: doc.verification_code }, diff --git a/setup/doctype/backup_manager/backup_manager.py b/setup/doctype/backup_manager/backup_manager.py index ce0f1e1729..feeddf035e 100644 --- a/setup/doctype/backup_manager/backup_manager.py +++ b/setup/doctype/backup_manager/backup_manager.py @@ -4,6 +4,8 @@ from __future__ import unicode_literals import webnotes from webnotes import _ +ignore_list = [] + class DocType: def __init__(self, d, dl): self.doc, self.doclist = d, dl @@ -23,22 +25,32 @@ def take_backups_if(freq): @webnotes.whitelist() def take_backups_dropbox(): + did_not_upload, error_log = [], [] try: from setup.doctype.backup_manager.backup_dropbox import backup_to_dropbox - backup_to_dropbox() + did_not_upload, error_log = backup_to_dropbox() + if did_not_upload: raise Exception + send_email(True, "Dropbox") except Exception: - send_email(False, "Dropbox", webnotes.getTraceback()) + error_message = ("\n".join(error_log) + "\n" + webnotes.getTraceback()) + print error_message + send_email(False, "Dropbox", error_message) #backup to gdrive @webnotes.whitelist() def take_backups_gdrive(): + did_not_upload, error_log = [], [] try: from setup.doctype.backup_manager.backup_googledrive import backup_to_gdrive - backup_to_gdrive() + did_not_upload, error_log = backup_to_gdrive() + if did_not_upload: raise Exception + send_email(True, "Google Drive") except Exception: - send_email(False, "Google Drive", webnotes.getTraceback()) + error_message = ("\n".join(error_log) + "\n" + webnotes.getTraceback()) + print error_message + send_email(False, "Google Drive", error_message) def send_email(success, service_name, error_status=None): if success: @@ -58,4 +70,4 @@ def send_email(success, service_name, error_status=None): # email system managers from webnotes.utils.email_lib import sendmail sendmail(webnotes.conn.get_value("Backup Manager", None, "send_notifications_to").split(","), - subject=subject, msg=message) \ No newline at end of file + subject=subject, msg=message) From ebd514442acb61213ba5a46927d51cb097915f10 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Apr 2013 15:36:26 +0530 Subject: [PATCH 029/295] [script report] accounts payable migrated to new style --- accounts/page/accounts_home/accounts_home.js | 5 + accounts/report/accounts_payable/__init__.py | 0 .../accounts_payable/accounts_payable.js | 42 +++++ .../accounts_payable/accounts_payable.py | 148 ++++++++++++++++++ .../accounts_payable/accounts_payable.txt | 21 +++ .../accounts_receivable.py | 4 +- controllers/accounts_controller.py | 14 +- controllers/buying_controller.py | 16 +- 8 files changed, 236 insertions(+), 14 deletions(-) create mode 100644 accounts/report/accounts_payable/__init__.py create mode 100644 accounts/report/accounts_payable/accounts_payable.js create mode 100644 accounts/report/accounts_payable/accounts_payable.py create mode 100644 accounts/report/accounts_payable/accounts_payable.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 0dd00ed659..3e9ea9243c 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -144,6 +144,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Accounts Receivable", doctype: "Sales Invoice" }, + { + "label":wn._("Accounts Payable"), + route: "query-report/Accounts Payable", + doctype: "Purchase Invoice" + }, ] }, { diff --git a/accounts/report/accounts_payable/__init__.py b/accounts/report/accounts_payable/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/accounts_payable/accounts_payable.js b/accounts/report/accounts_payable/accounts_payable.js new file mode 100644 index 0000000000..7ee38f2f31 --- /dev/null +++ b/accounts/report/accounts_payable/accounts_payable.js @@ -0,0 +1,42 @@ +wn.query_reports["Accounts Payable"] = { + "filters": [ + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = wn.query_report.filters_by_name.company.get_value(); + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Credit", + "company": company, + "master_type": "Supplier" + } + } + } + }, + { + "fieldname":"report_date", + "label": "Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname":"ageing_based_on", + "label": "Ageing Based On", + "fieldtype": "Select", + "options": 'Posting Date' + NEWLINE + 'Due Date', + "default": "Posting Date" + } + ] +} \ No newline at end of file diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py new file mode 100644 index 0000000000..ebf33a1e05 --- /dev/null +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -0,0 +1,148 @@ +from __future__ import unicode_literals +import webnotes +from webnotes.utils import getdate, nowdate, flt, cstr + +def execute(filters=None): + if not filters: filters = {} + columns = get_columns() + + entries = get_gl_entries(filters) + + entries_after_report_date = [[gle.voucher_type, gle.voucher_no] + for gle in get_gl_entries(filters, before_report_date=False)] + + account_supplier_type_map = get_account_supplier_type_map() + pi_map = get_pi_map() + + # Age of the invoice on this date + age_on = getdate(filters.get("report_date")) > getdate(nowdate()) \ + and nowdate() or filters.get("report_date") + + data = [] + total_invoiced_amount = total_paid = total_outstanding = 0 + for gle in entries: + if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ + or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: + + due_date = (gle.voucher_type == "Purchase Invoice") \ + and pi_map.get(gle.voucher_no).get("due_date") or "" + + invoiced_amount = gle.credit > 0 and gle.credit or 0 + paid_amount = get_paid_amount(gle, filters.get("report_date") or nowdate(), + entries_after_report_date) + outstanding_amount = invoiced_amount - paid_amount + + if abs(flt(outstanding_amount)) > 0.01: + row = [gle.posting_date, gle.account, gle.voucher_type, gle.voucher_no, + gle.remarks, account_supplier_type_map.get(gle.account), due_date, + pi_map.get("bill_no"), pi_map.get("bill_date"), invoiced_amount, + paid_amount, outstanding_amount] + + # Ageing + if filters.get("ageing_based_on") == "Due Date": + ageing_based_on_date = due_date + else: + ageing_based_on_date = gle.posting_date + row += get_ageing_data(ageing_based_on_date, age_on, outstanding_amount) + + # Add to total + total_invoiced_amount += flt(invoiced_amount) + total_paid += flt(paid_amount) + total_outstanding += flt(outstanding_amount) + data.append(row) + if data: + data.append(["", "", "", "", "", "", "", "Total", "", total_invoiced_amount, total_paid, + total_outstanding, "", "", "", ""]) + + return columns, data + +def get_columns(): + return [ + "Posting Date:Date:80", "Account:Link/Account:150", "Voucher Type::110", + "Voucher No::120", "Remarks::150", "Supplier Type:Link/Supplier Type:120", + "Due Date:Date:80", "Bill No::80", "Bill Date:Date:80", + "Invoiced Amount:Currency:100", "Paid Amount:Currency:100", + "Outstanding Amount:Currency:100", "Age:Int:50", "0-30:Currency:100", + "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" + ] + +def get_gl_entries(filters, before_report_date=True): + conditions, supplier_accounts = get_conditions(filters, before_report_date) + gl_entries = [] + gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` + where ifnull(is_cancelled, 'No') = 'No' %s order by posting_date, account""" % + (conditions) % (", ".join(['%s']*len(supplier_accounts))), + tuple(supplier_accounts), as_dict=1) + return gl_entries + +def get_conditions(filters, before_report_date=True): + conditions = "" + if filters.get("company"): + conditions += " and company='%s'" % filters["company"] + + supplier_accounts = [] + if filters.get("account"): + supplier_accounts = [filters["account"]] + elif filters.get("company"): + supplier_accounts = webnotes.conn.sql_list("""select name from `tabAccount` + where ifnull(master_type, '') = 'Supplier' and docstatus < 2 %s""" % + conditions, filters) + + if supplier_accounts: + conditions += " and account in (%s)" + + if filters.get("report_date"): + if before_report_date: + conditions += " and posting_date<='%s'" % filters["report_date"] + else: + conditions += " and posting_date>'%s'" % filters["report_date"] + + return conditions, supplier_accounts + +def get_account_supplier_type_map(): + account_supplier_type_map = {} + for each in webnotes.conn.sql("""select t2.name, t1.supplier_type from `tabSupplier` t1, + `tabAccount` t2 where t1.name = t2.master_name group by t2.name"""): + account_supplier_type_map[each[0]] = each[1] + + return account_supplier_type_map + +def get_pi_map(): + """ get due_date from sales invoice """ + pi_map = {} + for t in webnotes.conn.sql("""select name, due_date, bill_no, bill_date + from `tabPurchase Invoice`""", as_dict=1): + pi_map[t.name] = t + + return pi_map + +def get_paid_amount(gle, report_date, entries_after_report_date): + + paid_amount = 0 + if flt(gle.debit) > 0 and (not gle.against_voucher or + [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date): + paid_amount = gle.debit + elif flt(gle.credit) > 0: + paid_amount = webnotes.conn.sql(""" + select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) from `tabGL Entry` + where account = %s and posting_date <= %s and against_voucher_type = %s + and against_voucher = %s and name != %s and ifnull(is_cancelled, 'No') = 'No'""", + (gle.account, report_date, gle.voucher_type, gle.voucher_no, gle.name))[0][0] + + return flt(paid_amount) + +def get_ageing_data(ageing_based_on_date, age_on, outstanding_amount): + val1 = val2 = val3 = val4 = diff = 0 + diff = age_on and ageing_based_on_date \ + and (getdate(age_on) - getdate(ageing_based_on_date)).days or 0 + + if diff <= 30: + val1 = outstanding_amount + elif 30 < diff <= 60: + val2 = outstanding_amount + elif 60 < diff <= 90: + val3 = outstanding_amount + elif diff > 90: + val4 = outstanding_amount + + return [diff, val1, val2, val3, val4] \ No newline at end of file diff --git a/accounts/report/accounts_payable/accounts_payable.txt b/accounts/report/accounts_payable/accounts_payable.txt new file mode 100644 index 0000000000..8920a0bf2e --- /dev/null +++ b/accounts/report/accounts_payable/accounts_payable.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-04-22 16:16:03", + "docstatus": 0, + "modified": "2013-04-23 14:54:27", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Invoice", + "report_name": "Accounts Payable", + "report_type": "Report Builder" + }, + { + "doctype": "Report", + "name": "Accounts Payable" + } +] \ No newline at end of file diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index 4c0d4e11d3..d385b36b90 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -58,7 +58,7 @@ def get_columns(): return [ "Posting Date:Date:80", "Account:Link/Account:150", "Voucher Type::110", "Voucher No::120", "Remarks::150", "Due Date:Date:80", "Territory:Link/Territory:80", - "Invoiced Amount:Currency:100", "Payment Amount:Currency:100", + "Invoiced Amount:Currency:100", "Payment Received:Currency:100", "Outstanding Amount:Currency:100", "Age:Int:50", "0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" ] @@ -97,7 +97,7 @@ def get_conditions(filters, upto_report_date=True): def get_account_territory_map(): account_territory_map = {} for each in webnotes.conn.sql("""select t2.name, t1.territory from `tabCustomer` t1, - `tabAccount` t2 where t1.name = t2.master_name group by t2.name"""): + `tabAccount` t2 where t1.name = t2.master_name"""): account_territory_map[each[0]] = each[1] return account_territory_map diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 0e7c108f3d..04e4bbdaee 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes import msgprint, _ from webnotes.utils import flt from utilities.transaction_base import TransactionBase @@ -83,10 +82,13 @@ class AccountsController(TransactionBase): @property def stock_items(self): if not hasattr(self, "_stock_items"): - item_codes = list(set(item.item_code for item in self.doclist.get({"parentfield": self.fname}))) - self._stock_items = [r[0] for r in webnotes.conn.sql("""select name - from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \ - (", ".join((["%s"]*len(item_codes))),), item_codes)] + self._stock_items = [] + item_codes = list(set(item.item_code for item in + self.doclist.get({"parentfield": self.fname}))) + if item_codes: + self._stock_items = [r[0] for r in webnotes.conn.sql("""select name + from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \ + (", ".join((["%s"]*len(item_codes))),), item_codes)] return self._stock_items @@ -95,4 +97,4 @@ class AccountsController(TransactionBase): if not hasattr(self, "_abbr"): self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") - return self._abbr + return self._abbr \ No newline at end of file diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 560dec2a15..429737e53e 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -419,21 +419,25 @@ class BuyingController(StockController): @property def sub_contracted_items(self): if not hasattr(self, "_sub_contracted_items"): + self._sub_contracted_items = [] item_codes = list(set(item.item_code for item in self.doclist.get({"parentfield": self.fname}))) - self._sub_contracted_items = [r[0] for r in webnotes.conn.sql("""select name - from `tabItem` where name in (%s) and is_sub_contracted_item='Yes'""" % \ - (", ".join((["%s"]*len(item_codes))),), item_codes)] + if item_codes: + self._sub_contracted_items = [r[0] for r in webnotes.conn.sql("""select name + from `tabItem` where name in (%s) and is_sub_contracted_item='Yes'""" % \ + (", ".join((["%s"]*len(item_codes))),), item_codes)] return self._sub_contracted_items @property def purchase_items(self): if not hasattr(self, "_purchase_items"): + self._purchase_items = [] item_codes = list(set(item.item_code for item in self.doclist.get({"parentfield": self.fname}))) - self._purchase_items = [r[0] for r in webnotes.conn.sql("""select name - from `tabItem` where name in (%s) and is_purchase_item='Yes'""" % \ - (", ".join((["%s"]*len(item_codes))),), item_codes)] + if item_codes: + self._purchase_items = [r[0] for r in webnotes.conn.sql("""select name + from `tabItem` where name in (%s) and is_purchase_item='Yes'""" % \ + (", ".join((["%s"]*len(item_codes))),), item_codes)] return self._purchase_items From b2972dca980bbb5a9723613cda5849202ece1334 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Apr 2013 15:53:33 +0530 Subject: [PATCH 030/295] [script report] accounts payable migrated to new style --- .../report/accounts_payable/accounts_payable.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index ebf33a1e05..34c8ccd2be 100644 --- a/accounts/report/accounts_payable/accounts_payable.py +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -13,7 +13,7 @@ def execute(filters=None): account_supplier_type_map = get_account_supplier_type_map() pi_map = get_pi_map() - + # Age of the invoice on this date age_on = getdate(filters.get("report_date")) > getdate(nowdate()) \ and nowdate() or filters.get("report_date") @@ -24,8 +24,13 @@ def execute(filters=None): if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: - due_date = (gle.voucher_type == "Purchase Invoice") \ - and pi_map.get(gle.voucher_no).get("due_date") or "" + if gle.voucher_type == "Purchase Invoice": + pi_info = pi_map.get(gle.voucher_no) + due_date = pi_info.get("due_date") + bill_no = pi_info.get("bill_no") + bill_date = pi_info.get("bill_date") + else: + due_date = bill_no = bill_date = "" invoiced_amount = gle.credit > 0 and gle.credit or 0 paid_amount = get_paid_amount(gle, filters.get("report_date") or nowdate(), @@ -34,9 +39,8 @@ def execute(filters=None): if abs(flt(outstanding_amount)) > 0.01: row = [gle.posting_date, gle.account, gle.voucher_type, gle.voucher_no, - gle.remarks, account_supplier_type_map.get(gle.account), due_date, - pi_map.get("bill_no"), pi_map.get("bill_date"), invoiced_amount, - paid_amount, outstanding_amount] + gle.remarks, account_supplier_type_map.get(gle.account), due_date, bill_no, + bill_date, invoiced_amount, paid_amount, outstanding_amount] # Ageing if filters.get("ageing_based_on") == "Due Date": From cb9c480967a8464eb737ae00fcf4bfdcf4b2e3b6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Apr 2013 16:08:13 +0530 Subject: [PATCH 031/295] [report] total added in daily time log summary report --- .../daily_time_log_summary/daily_time_log_summary.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/projects/report/daily_time_log_summary/daily_time_log_summary.py b/projects/report/daily_time_log_summary/daily_time_log_summary.py index 22e48077b5..808d1ba7d0 100644 --- a/projects/report/daily_time_log_summary/daily_time_log_summary.py +++ b/projects/report/daily_time_log_summary/daily_time_log_summary.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals import webnotes +from webnotes.utils import flt def execute(filters=None): if not filters: @@ -22,6 +23,7 @@ def execute(filters=None): if time_logs: profiles = [time_logs[0].owner] + total_hours = 0 for tl in time_logs: if tl.owner not in profiles: profiles.append(tl.owner) @@ -29,6 +31,11 @@ def execute(filters=None): data.append([tl.name, profile_map[tl.owner], tl.from_time, tl.to_time, tl.hours, tl.activity_type, tl.task, task_map.get(tl.task), tl.project, tl.status]) + + total_hours += flt(tl.hours) + + if total_hours: + data.append(["", "", "", "Total", total_hours, "", "", "", "", ""]) return columns, data From 10f09092aa64309556b46417c8a0c213a8cfda1c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 23 Apr 2013 17:07:52 +0530 Subject: [PATCH 032/295] [backup manager] [fixes] show erroneous files in error email message --- setup/doctype/backup_manager/backup_googledrive.py | 8 +++++--- setup/doctype/backup_manager/backup_manager.py | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/setup/doctype/backup_manager/backup_googledrive.py b/setup/doctype/backup_manager/backup_googledrive.py index 24cd857667..7d980debe7 100644 --- a/setup/doctype/backup_manager/backup_googledrive.py +++ b/setup/doctype/backup_manager/backup_googledrive.py @@ -21,6 +21,11 @@ from webnotes import _, msgprint from apiclient.discovery import build from apiclient.http import MediaFileUpload +# define log config for google drive api's log messages +# basicConfig redirects log to stderr +import logging +logging.basicConfig() + @webnotes.whitelist() def get_gdrive_authorize_url(): flow = get_gdrive_flow() @@ -31,9 +36,6 @@ def get_gdrive_authorize_url(): @webnotes.whitelist() def upload_files(name, mimetype, service, folder_id): - import logging - logging.basicConfig() - if not webnotes.conn: webnotes.connect() file_name = os.path.basename(name) diff --git a/setup/doctype/backup_manager/backup_manager.py b/setup/doctype/backup_manager/backup_manager.py index feeddf035e..a8ecd636db 100644 --- a/setup/doctype/backup_manager/backup_manager.py +++ b/setup/doctype/backup_manager/backup_manager.py @@ -33,7 +33,8 @@ def take_backups_dropbox(): send_email(True, "Dropbox") except Exception: - error_message = ("\n".join(error_log) + "\n" + webnotes.getTraceback()) + file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] + error_message = ("\n".join(file_and_error) + "\n" + webnotes.getTraceback()) print error_message send_email(False, "Dropbox", error_message) @@ -48,7 +49,8 @@ def take_backups_gdrive(): send_email(True, "Google Drive") except Exception: - error_message = ("\n".join(error_log) + "\n" + webnotes.getTraceback()) + file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] + error_message = ("\n".join(file_and_error) + "\n" + webnotes.getTraceback()) print error_message send_email(False, "Google Drive", error_message) From 627a08b7dbbd12f80e956c06a5f6e3fedaddd25f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Apr 2013 17:49:24 +0530 Subject: [PATCH 033/295] [report] total added in daily time log summary report --- .../daily_time_log_summary.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/projects/report/daily_time_log_summary/daily_time_log_summary.py b/projects/report/daily_time_log_summary/daily_time_log_summary.py index 808d1ba7d0..8c1e2668cd 100644 --- a/projects/report/daily_time_log_summary/daily_time_log_summary.py +++ b/projects/report/daily_time_log_summary/daily_time_log_summary.py @@ -19,23 +19,29 @@ def execute(filters=None): time_logs = webnotes.conn.sql("""select * from `tabTime Log` where docstatus < 2 %s order by owner asc""" % (conditions, ), filters, as_dict=1) - data = [] if time_logs: profiles = [time_logs[0].owner] - total_hours = 0 + data = [] + total_hours = total_employee_hours = count = 0 for tl in time_logs: if tl.owner not in profiles: profiles.append(tl.owner) - data.append([]) - + data.append(["", "", "", "Total", total_employee_hours, "", "", "", "", ""]) + total_employee_hours = 0 + data.append([tl.name, profile_map[tl.owner], tl.from_time, tl.to_time, tl.hours, tl.activity_type, tl.task, task_map.get(tl.task), tl.project, tl.status]) - + + count += 1 total_hours += flt(tl.hours) - + total_employee_hours += flt(tl.hours) + + if count == len(time_logs): + data.append(["", "", "", "Total Hours", total_employee_hours, "", "", "", "", ""]) + if total_hours: - data.append(["", "", "", "Total", total_hours, "", "", "", "", ""]) + data.append(["", "", "", "Grand Total", total_hours, "", "", "", "", ""]) return columns, data From 77bb4cef39186c18b6890aade3b5cb348753052c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 24 Apr 2013 15:32:39 +0530 Subject: [PATCH 034/295] [naming series] [fixes] consider custom fields with fieldname naming_series --- setup/doctype/naming_series/naming_series.py | 30 ++++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/setup/doctype/naming_series/naming_series.py b/setup/doctype/naming_series/naming_series.py index 599118152d..7b804f8a96 100644 --- a/setup/doctype/naming_series/naming_series.py +++ b/setup/doctype/naming_series/naming_series.py @@ -29,12 +29,12 @@ class DocType: def get_transactions(self, arg=None): return { - "transactions": "\n".join([''] + [i[0] for i in - sql("""select `tabDocField`.`parent` - FROM `tabDocField`, `tabDocType` - WHERE `tabDocField`.`fieldname` = 'naming_series' - and `tabDocType`.name=`tabDocField`.parent - order by `tabDocField`.parent""")]), + "transactions": "\n".join([''] + sorted(list(set( + webnotes.conn.sql_list("""select parent + from `tabDocField` where fieldname='naming_series'""") + + webnotes.conn.sql_list("""select dt from `tabCustom Field` + where fieldname='naming_series'""") + )))), "prefixes": "\n".join([''] + [i[0] for i in sql("""select name from tabSeries""")]) } @@ -89,7 +89,6 @@ class DocType: 'property': prop, 'value': prop_dict[prop], 'property_type': 'Select', - 'select_doctype': doctype }) ps.save(1) @@ -101,11 +100,18 @@ class DocType: from core.doctype.doctype.doctype import DocType dt = DocType() - parent = sql("""select dt.name from `tabDocField` df, `tabDocType` dt - where dt.name = df.parent and df.fieldname='naming_series' and dt.name != %s""", - self.doc.select_doc_for_series) - sr = ([webnotes.model.doctype.get_property(p[0], 'options', 'naming_series'), p[0]] - for p in parent) + parent = list(set( + webnotes.conn.sql_list("""select dt.name + from `tabDocField` df, `tabDocType` dt + where dt.name = df.parent and df.fieldname='naming_series' and dt.name != %s""", + self.doc.select_doc_for_series) + + webnotes.conn.sql_list("""select dt.name + from `tabCustom Field` df, `tabDocType` dt + where dt.name = df.dt and df.fieldname='naming_series' and dt.name != %s""", + self.doc.select_doc_for_series) + )) + sr = [[webnotes.model.doctype.get_property(p, 'options', 'naming_series'), p] + for p in parent] options = self.scrub_options_list(self.doc.set_options.split("\n")) for series in options: dt.validate_series(series, self.doc.select_doc_for_series) From 5c2cb74e7a7dbecc229a5bc207b0b27de17b33f1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 24 Apr 2013 15:46:09 +0530 Subject: [PATCH 035/295] [script report] sales register migrated into new script report --- accounts/page/accounts_home/accounts_home.js | 5 + accounts/report/sales_register/__init__.py | 0 .../report/sales_register/sales_register.js | 42 +++++ .../report/sales_register/sales_register.py | 161 ++++++++++++++++++ .../report/sales_register/sales_register.txt | 21 +++ .../sales_register/sales_register.js | 2 +- 6 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 accounts/report/sales_register/__init__.py create mode 100644 accounts/report/sales_register/sales_register.js create mode 100644 accounts/report/sales_register/sales_register.py create mode 100644 accounts/report/sales_register/sales_register.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 3e9ea9243c..2703d27f69 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -149,6 +149,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Accounts Payable", doctype: "Purchase Invoice" }, + { + "label":wn._("Sales Register"), + route: "query-report/Sales Register", + doctype: "Sales Invoice" + }, ] }, { diff --git a/accounts/report/sales_register/__init__.py b/accounts/report/sales_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/sales_register/sales_register.js b/accounts/report/sales_register/sales_register.js new file mode 100644 index 0000000000..82246198c9 --- /dev/null +++ b/accounts/report/sales_register/sales_register.js @@ -0,0 +1,42 @@ +wn.query_reports["Sales Register"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = wn.query_report.filters_by_name.company.get_value(); + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Debit", + "company": company, + "master_type": "Customer" + } + } + } + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + } + ] +} \ No newline at end of file diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py new file mode 100644 index 0000000000..07772cb4e2 --- /dev/null +++ b/accounts/report/sales_register/sales_register.py @@ -0,0 +1,161 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + columns, income_accounts, tax_accounts = get_columns() + + invoice_list = get_invoices(filters) + invoice_income_map = get_invoice_income_map(invoice_list) + invoice_tax_map = get_invoice_tax_map(invoice_list) + + invoice_so_dn_map = get_invoice_so_dn_map(invoice_list) + customer_map = get_customer_deatils(invoice_list) + account_map = get_account_details(invoice_list) + + data = [] + for inv in invoice_list: + # invoice details + sales_order = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("sales_order", [])) + delivery_note = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("delivery_note", [])) + # webnotes.errprint(customer_map.get(inv.customer, [])) + row = [inv.name, inv.posting_date, inv.customer, inv.debit_to, + account_map.get(inv.debit_to), customer_map.get(inv.customer), inv.project_name, + inv.remarks, sales_order, delivery_note] + + # map income values + for income_acc in income_accounts: + row.append(invoice_income_map.get(inv.name, {}).get(income_acc)) + + # net total + row.append(inv.net_total) + + # tax account + for tax_acc in tax_accounts: + row.append(invoice_tax_map.get(inv.name, {}).get(tax_acc)) + + # total tax, grand total + row += [inv.other_charges_total, inv.grand_total] + + data.append(row) + + return columns, data + + +def get_columns(): + """return columns based on filters""" + columns = [ + "Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer:Link/Customer:120", + "Customer Account:Link/Account:120", "Account Group:LInk/Account:120", + "Territory:Link/Territory:80", "Project:Link/Project:80", + "Remarks::150", "Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100" + ] + + income_accounts = webnotes.conn.sql_list("""select distinct income_account + from `tabSales Invoice Item` where docstatus = 1 order by income_account""") + + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' + and docstatus = 1 order by account_head""") + + columns = columns + [(account + ":Currency:120") for account in income_accounts] + \ + ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ + ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + + return columns, income_accounts, tax_accounts + +def get_conditions(filters): + conditions = "" + + if filters.get("company"): conditions += " and company=%(company)s" + if filters.get("account"): conditions += " and account = %(account)s" + + if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s" + + return conditions + +def get_invoices(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select name, posting_date, territory, debit_to, territory, + project_name, customer, remarks, net_total, other_charges_total, grand_total + from `tabSales Invoice` where docstatus = 1 %s + order by posting_date desc, name desc""" % conditions, filters, as_dict=1) + +def get_invoice_income_map(invoice_list): + income_details = webnotes.conn.sql("""select parent, income_account, sum(amount) as amount + from `tabSales Invoice Item` where parent in (%s) group by parent, income_account""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_income_map = {} + for d in income_details: + invoice_income_map.setdefault(d.parent, webnotes._dict()).setdefault(d.income_account, []) + invoice_income_map[d.parent][d.income_account] = flt(d.amount) + + return invoice_income_map + +def get_invoice_tax_map(invoice_list): + tax_details = webnotes.conn.sql("""select parent, account_head, sum(tax_amount) as tax_amount + from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_tax_map = {} + for d in tax_details: + invoice_tax_map.setdefault(d.parent, webnotes._dict()).setdefault(d.account_head, []) + invoice_tax_map[d.parent][d.account_head] = flt(d.tax_amount) + + return invoice_tax_map + +def get_invoice_so_dn_map(invoice_list): + si_items = webnotes.conn.sql("""select parent, sales_order, delivery_note + from `tabSales Invoice Item` where parent in (%s) + and (ifnull(sales_order, '') != '' or ifnull(delivery_note, '') != '')""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_so_dn_map = {} + for d in si_items: + if d.sales_order: + invoice_so_dn_map.setdefault(d.parent, webnotes._dict()).setdefault( + "sales_order", []).append(d.sales_order) + if d.delivery_note: + invoice_so_dn_map.setdefault(d.parent, webnotes._dict()).setdefault( + "delivery_note", []).append(d.delivery_note) + + return invoice_so_dn_map + +def get_customer_deatils(invoice_list): + customer_map = {} + customers = list(set([inv.customer for inv in invoice_list])) + for cust in webnotes.conn.sql("""select name, territory from `tabCustomer` + where name in (%s)""" % ", ".join(["%s"]*len(customers)), tuple(customers), as_dict=1): + customer_map.setdefault(cust.name, "") + customer_map[cust.name] = cust.territory + + return customer_map + +def get_account_details(invoice_list): + account_map = {} + accounts = list(set([inv.debit_to for inv in invoice_list])) + for acc in webnotes.conn.sql("""select name, parent_account from tabAccount + where name in (%s)""" % ", ".join(["%s"]*len(accounts)), tuple(accounts), as_dict=1): + account_map.setdefault(acc.name, "") + account_map[acc.name] = acc.parent_account + + return account_map \ No newline at end of file diff --git a/accounts/report/sales_register/sales_register.txt b/accounts/report/sales_register/sales_register.txt new file mode 100644 index 0000000000..5aef814596 --- /dev/null +++ b/accounts/report/sales_register/sales_register.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-04-23 18:15:29", + "docstatus": 0, + "modified": "2013-04-23 18:15:29", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Invoice", + "report_name": "Sales Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Sales Register" + } +] \ No newline at end of file diff --git a/accounts/search_criteria/sales_register/sales_register.js b/accounts/search_criteria/sales_register/sales_register.js index 5a09713cd7..872e198a35 100644 --- a/accounts/search_criteria/sales_register/sales_register.js +++ b/accounts/search_criteria/sales_register/sales_register.js @@ -28,5 +28,5 @@ report.customize_filters = function() { this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Grand Total <='].df.filter_hide = 1; this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 1; this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Sales Partner'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Is Opening'].df.filter_hide = 1; + this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Is Opening Entry'].df.filter_hide = 1; } From 181bcd015d2ce82894118140dcfd6f51d8354301 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 24 Apr 2013 18:52:41 +0530 Subject: [PATCH 036/295] [bom] [fixes] fixes in bom --- manufacturing/doctype/bom/bom.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/manufacturing/doctype/bom/bom.py b/manufacturing/doctype/bom/bom.py index 75bf305411..b4eeaff462 100644 --- a/manufacturing/doctype/bom/bom.py +++ b/manufacturing/doctype/bom/bom.py @@ -32,7 +32,7 @@ class DocType: def autoname(self): last_name = sql("""select max(name) from `tabBOM` - where name like 'BOM/%s/%%'""" % self.doc.item) + where name like "BOM/%s/%%" """ % cstr(self.doc.item).replace('"', '\\"')) if last_name: idx = cint(cstr(last_name[0][0]).split('/')[-1]) + 1 else: @@ -67,16 +67,16 @@ class DocType: self.manage_default_bom() def get_item_det(self, item_code): - item = sql("""select name, is_asset_item, is_purchase_item, docstatus, description, - is_sub_contracted_item, stock_uom, default_bom, + item = webnotes.conn.sql("""select name, is_asset_item, is_purchase_item, + docstatus, description, is_sub_contracted_item, stock_uom, default_bom, last_purchase_rate, standard_rate, is_manufactured_item - from `tabItem` where item_code = %s""", item_code, as_dict = 1) + from `tabItem` where name=%s""", item_code, as_dict = 1) return item def get_item_details(self, item_code): - res = sql("""select description, stock_uom as uom - from `tabItem` where item_code = %s""", item_code, as_dict = 1) + res = webnotes.conn.sql("""select description, stock_uom as uom + from `tabItem` where name=%s""", item_code, as_dict = 1) return res and res[0] or {} def get_workstation_details(self,workstation): @@ -209,7 +209,7 @@ class DocType: msgprint("""Operation no: %s against item: %s at row no: %s \ is not present at Operations table""" % (m.operation_no, m.item_code, m.idx), raise_exception = 1) - + item = self.get_item_det(m.item_code) if item[0]['is_manufactured_item'] == 'Yes': if not m.bom_no: From d9ad2e02710d9a97070507326506fa37638d660a Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 25 Apr 2013 10:31:36 +0530 Subject: [PATCH 037/295] [master database] [patched] updated patched master.sql.gz --- master.sql.gz | Bin 222996 -> 232097 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/master.sql.gz b/master.sql.gz index f331784cf176a40251975498bd9b9d3ecbed9903..030ee05b2f5f3b55c6cff409b2ff00a4cf758709 100644 GIT binary patch literal 232097 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

    X&5yJsA`KB-MN`(onE>SmJSG>vqSbxs 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 *u+O&O_weLuUo& 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 literal 222996 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%0AXRl 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

    b~pY9?|yUR{~F%AQfG~}gNa;f&V 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

    Use the Data Import Tool to upload, update Item Prices in bulk:\n
      \n
    1. Go to Data Import Tool.\n
    2. Select \"Item\"\n
    3. Check on \"With Data\"\n
    4. Download \"Item Price\" from Child Tables.\n
    5. Update the prices required and add new rows if required.\n
    6. Check on \"Overwrite\"\n
    7. Upload the modified sheet.\n
    \n" + "options": "
    Use the Data Import Tool to upload, update Item Prices in bulk:\n
      \n
    1. Go to Data Import Tool.\n
    2. Select \"Item\"\n
    3. Check on \"With Data\"\n
    4. Download \"Item Price\" from Child Tables.\n
    5. Update the prices required and add new rows if required.\n
    6. Check on \"Overwrite\"\n
    7. Upload the modified sheet.\n
    \n" }, { "cancel": 0, @@ -78,7 +86,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "role": "Sales Master Manager", "write": 1 } diff --git a/setup/doctype/price_list/test_price_list.py b/setup/doctype/price_list/test_price_list.py index 53b86a39fc..30262dc8aa 100644 --- a/setup/doctype/price_list/test_price_list.py +++ b/setup/doctype/price_list/test_price_list.py @@ -1,6 +1,7 @@ test_records = [ [{ "doctype": "Price List", - "price_list_name": "_Test Price List" + "price_list_name": "_Test Price List", + "currency": "INR" }] ] \ No newline at end of file diff --git a/stock/doctype/item/item.js b/stock/doctype/item/item.js index 8b3e04484a..2635f3e904 100644 --- a/stock/doctype/item/item.js +++ b/stock/doctype/item/item.js @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +cur_frm.add_fetch("price_list_name", "currency", "ref_currency") + cur_frm.cscript.refresh = function(doc) { // make sensitive fields(has_serial_no, is_stock_item, valuation_method) // read only if any stock ledger entry exists diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index c6bf017647..8a804d8c13 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -24,6 +24,8 @@ from webnotes import msgprint, _ from webnotes.model.controller import DocListController +class PriceListCurrencyMismatch(Exception): pass + class DocType(DocListController): def validate(self): if not self.doc.stock_uom: @@ -34,7 +36,7 @@ class DocType(DocListController): self.add_default_uom_in_conversion_factor_table() self.valiadte_item_type() self.check_for_active_boms() - self.check_ref_rate_detail() + self.validate_price_lists() self.fill_customer_code() self.check_item_tax() self.validate_barcode() @@ -122,14 +124,20 @@ class DocType(DocListController): if cstr(self.doc.fields.get(d)) != 'Yes': _check_for_active_boms(fl[d]) - def check_ref_rate_detail(self): - check_list=[] + def validate_price_lists(self): + price_lists=[] for d in getlist(self.doclist,'ref_rate_details'): - if d.price_list_name in check_list: + if d.price_list_name in price_lists: msgprint(_("Cannot have two prices for same Price List") + ": " + d.price_list_name, raise_exception= webnotes.DuplicateEntryError) else: - check_list.append(d.price_list_name) + price_list_currency = webnotes.conn.get_value("Price List", d.price_list_name, "currency") + if price_list_currency and d.ref_currency != price_list_currency: + msgprint(_("Currency does not match Price List Currency for Price List") \ + + ": " + d.price_list_name, raise_exception=PriceListCurrencyMismatch) + + price_lists.append(d.price_list_name) + def fill_customer_code(self): """ Append all the customer codes and insert into "customer_code" field of item table """ diff --git a/stock/doctype/item/test_item.py b/stock/doctype/item/test_item.py index a59747c3e3..f5a688ca70 100644 --- a/stock/doctype/item/test_item.py +++ b/stock/doctype/item/test_item.py @@ -28,6 +28,14 @@ class TestItem(unittest.TestCase): item.doclist.append(webnotes.doc(item_price.fields.copy())) self.assertRaises(webnotes.DuplicateEntryError, item.insert) + def test_price_list_mismatch(self): + from stock.doctype.item.item import PriceListCurrencyMismatch + item = webnotes.bean(copy=test_records[0]) + item.doc.item_code = "_Test Item 11" + item_price = item.doclist.get({"doctype": "Item Price"})[0].ref_currency="USD" + self.assertRaises(PriceListCurrencyMismatch, item.insert) + + test_records = [ [{ "doctype": "Item", From 4d7a1c1f358778eb072ac0c23f6d20766fec5bc4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 2 May 2013 15:19:00 +0530 Subject: [PATCH 077/295] [script report] payments made with ageing --- accounts/page/accounts_home/accounts_home.js | 5 ++ .../accounts_payable/accounts_payable.py | 6 +- .../accounts_receivable.py | 6 +- .../payment_collection_with_ageing.py | 10 ++- .../payment_made_with_ageing/__init__.py | 0 .../payment_made_with_ageing.js | 40 +++++++++ .../payment_made_with_ageing.py | 89 +++++++++++++++++++ .../payment_made_with_ageing.txt | 22 +++++ 8 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 accounts/report/payment_made_with_ageing/__init__.py create mode 100644 accounts/report/payment_made_with_ageing/payment_made_with_ageing.js create mode 100644 accounts/report/payment_made_with_ageing/payment_made_with_ageing.py create mode 100644 accounts/report/payment_made_with_ageing/payment_made_with_ageing.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 7038fe9720..d2b0a0ea0d 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -212,6 +212,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Payment Collection With Ageing", doctype: "Journal Voucher" }, + { + "label":wn._("Payment Made With Ageing"), + route: "query-report/Payment Made With Ageing", + doctype: "Journal Voucher" + }, ] } ] diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index 4e9b2c88ba..71aeb35ce0 100644 --- a/accounts/report/accounts_payable/accounts_payable.py +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import getdate, nowdate, flt, cstr +from webnotes import msgprint, _ from accounts.report.accounts_receivable.accounts_receivable import get_ageing_data def execute(filters=None): @@ -79,13 +80,16 @@ def get_conditions(filters, before_report_date=True): supplier_accounts = [] if filters.get("account"): supplier_accounts = [filters["account"]] - elif filters.get("company"): + else: supplier_accounts = webnotes.conn.sql_list("""select name from `tabAccount` where ifnull(master_type, '') = 'Supplier' and docstatus < 2 %s""" % conditions, filters) if supplier_accounts: conditions += " and account in (%s)" % (", ".join(['%s']*len(supplier_accounts))) + else: + msgprint(_("No Supplier Accounts found. Supplier Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) if filters.get("report_date"): if before_report_date: diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index 47908c3f13..d791fad23f 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from webnotes.utils import getdate, nowdate, flt, cstr def execute(filters=None): @@ -68,13 +69,16 @@ def get_conditions(filters, upto_report_date=True): customer_accounts = [] if filters.get("account"): customer_accounts = [filters["account"]] - elif filters.get("company"): + else: customer_accounts = webnotes.conn.sql_list("""select name from `tabAccount` where ifnull(master_type, '') = 'Customer' and docstatus < 2 %s""" % conditions, filters) if customer_accounts: conditions += " and account in (%s)" % (", ".join(['%s']*len(customer_accounts))) + else: + msgprint(_("No Customer Accounts found. Customer Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) if filters.get("report_date"): if upto_report_date: diff --git a/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py index 50f74e0e82..12688525c6 100644 --- a/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py +++ b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from accounts.report.accounts_receivable.accounts_receivable import get_ageing_data def execute(filters=None): @@ -55,13 +56,16 @@ def get_conditions(filters): customer_accounts = [] if filters.get("account"): customer_accounts = [filters["account"]] - elif filters.get("company"): + else: + cond = filters.get("company") and (" and company = '%s'" % filters["company"]) or "" customer_accounts = webnotes.conn.sql_list("""select name from `tabAccount` - where ifnull(master_type, '') = 'Customer' and docstatus < 2 - and company = %s""", filters["company"]) + where ifnull(master_type, '') = 'Customer' and docstatus < 2 %s""" % cond) if customer_accounts: conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(customer_accounts))) + else: + msgprint(_("No Customer Accounts found. Customer Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) if filters.get("from_date"): conditions += " and jv.posting_date >= '%s'" % filters["from_date"] if filters.get("to_date"): conditions += " and jv.posting_date <= '%s'" % filters["to_date"] diff --git a/accounts/report/payment_made_with_ageing/__init__.py b/accounts/report/payment_made_with_ageing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/payment_made_with_ageing/payment_made_with_ageing.js b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.js new file mode 100644 index 0000000000..533fe614d7 --- /dev/null +++ b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.js @@ -0,0 +1,40 @@ +wn.query_reports["Payment Made With Ageing"] = { + "filters": [ + { + fieldname: "from_date", + label: "From Date", + fieldtype: "Date", + default: wn.defaults.get_user_default("year_start_date"), + }, + { + fieldname:"to_date", + label: "To Date", + fieldtype: "Date", + default: get_today() + }, + { + fieldname:"account", + label: "Supplier Account", + fieldtype: "Link", + options: "Account", + get_query: function() { + return { + query: "accounts.utils.get_account_list", + filters: { + is_pl_account: "No", + debit_or_credit: "Credit", + company: wn.query_report.filters_by_name.company.get_value(), + master_type: "Supplier" + } + } + } + }, + { + fieldname:"company", + label: "Company", + fieldtype: "Link", + options: "Company", + default: sys_defaults.company + }, + ] +} \ No newline at end of file diff --git a/accounts/report/payment_made_with_ageing/payment_made_with_ageing.py b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.py new file mode 100644 index 0000000000..e7f13ef121 --- /dev/null +++ b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.py @@ -0,0 +1,89 @@ +# 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 import msgprint, _ +from accounts.report.accounts_receivable.accounts_receivable import get_ageing_data + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + entries = get_entries(filters) + pi_posting_date_map = get_pi_posting_date_map() + + data = [] + for d in entries: + against_voucher_date = d.against_voucher and pi_posting_date_map[d.against_voucher] or "" + + row = [d.name, d.account, d.posting_date, d.against_voucher, against_voucher_date, + d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark] + + if d.against_voucher: + row += get_ageing_data(against_voucher_date, d.posting_date, d.debit or -1*d.credit) + else: + row += ["", "", "", "", ""] + + data.append(row) + + return columns, data + +def get_columns(): + return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", + "Posting Date:Date:100", "Against Invoice:Link/Purchase Invoice:130", + "Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120", + "Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40", + "0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" + ] + +def get_conditions(filters): + conditions = "" + supplier_accounts = [] + if filters.get("account"): + supplier_accounts = [filters["account"]] + else: + cond = filters.get("company") and (" and company = '%s'" % filters["company"]) or "" + supplier_accounts = webnotes.conn.sql_list("""select name from `tabAccount` + where ifnull(master_type, '') = 'Supplier' and docstatus < 2 %s""" % cond) + + if supplier_accounts: + conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(supplier_accounts))) + else: + msgprint(_("No Supplier Accounts found. Supplier Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) + + if filters.get("from_date"): conditions += " and jv.posting_date >= '%s'" % filters["from_date"] + if filters.get("to_date"): conditions += " and jv.posting_date <= '%s'" % filters["to_date"] + + return conditions, supplier_accounts + +def get_entries(filters): + conditions, supplier_accounts = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jvd.account, jv.posting_date, + jvd.against_voucher, jvd.debit, jvd.credit, jv.cheque_no, jv.cheque_date, jv.remark + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" % + (conditions), tuple(supplier_accounts), as_dict=1) + + return entries + +def get_pi_posting_date_map(): + pi_posting_date_map = {} + for t in webnotes.conn.sql("""select name, posting_date from `tabPurchase Invoice`"""): + pi_posting_date_map[t[0]] = t[1] + + return pi_posting_date_map \ No newline at end of file diff --git a/accounts/report/payment_made_with_ageing/payment_made_with_ageing.txt b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.txt new file mode 100644 index 0000000000..c5c85da89a --- /dev/null +++ b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-02 12:10:21", + "docstatus": 0, + "modified": "2013-05-02 12:10:21", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Payment Made With Ageing", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Payment Made With Ageing" + } +] \ No newline at end of file From f5f478ee78deee8d77f5d65ad31cecbb33aaee08 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 2 May 2013 15:36:33 +0530 Subject: [PATCH 078/295] [item] [naming] by series or code / [naming series] optionally set number of digits --- home/page/latest_updates/latest_updates.js | 4 +- .../global_defaults/global_defaults.py | 1 + .../global_defaults/global_defaults.txt | 18 ++++- setup/doctype/naming_series/naming_series.py | 9 ++- setup/doctype/naming_series/naming_series.txt | 8 +- stock/doctype/item/item.js | 6 +- stock/doctype/item/item.py | 7 ++ stock/doctype/item/item.txt | 77 ++++++++++++++++--- 8 files changed, 108 insertions(+), 22 deletions(-) diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index e11b9c9243..f254bf2349 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,6 +1,8 @@ erpnext.updates = [ ["2nd May", ["Buying: Warehouse must belong to same company as transaction", - "Price List": "Added Currency Field. One price list can have only one currency"]], + "Price List: Added Currency Field. One price list can have only one currency", + "Item: Naming can now be by series or item code", + "Naming Series: Set number of digits in series (optionally)"]], ["18th April", ["Cost Center: Set a default Cost Center for a Company"]], ["12th April", ["Employee: List of Leave Approvers who can approve the Employee's Leave Applications"]], ["10th April", ["Redesigned File Uploads and added File Manager in Setup"]], diff --git a/setup/doctype/global_defaults/global_defaults.py b/setup/doctype/global_defaults/global_defaults.py index 8275513dc9..191a47edf2 100644 --- a/setup/doctype/global_defaults/global_defaults.py +++ b/setup/doctype/global_defaults/global_defaults.py @@ -31,6 +31,7 @@ keydict = { 'item_group': 'default_item_group', 'customer_group': 'default_customer_group', 'cust_master_name': 'cust_master_name', + "item_naming_by": "item_naming_by", 'supplier_type': 'default_supplier_type', 'supp_master_name': 'supp_master_name', 'territory': 'default_territory', diff --git a/setup/doctype/global_defaults/global_defaults.txt b/setup/doctype/global_defaults/global_defaults.txt index 7f81618469..853bb57705 100644 --- a/setup/doctype/global_defaults/global_defaults.txt +++ b/setup/doctype/global_defaults/global_defaults.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 11:08:14", + "creation": "2013-04-01 15:05:24", "docstatus": 0, - "modified": "2013-03-28 15:41:03", + "modified": "2013-05-02 15:05:21", "modified_by": "Administrator", "owner": "Administrator" }, @@ -27,8 +27,6 @@ "permlevel": 0 }, { - "amend": 0, - "cancel": 0, "create": 1, "doctype": "DocPerm", "name": "__common__", @@ -167,6 +165,13 @@ "read_only": 0, "width": "50%" }, + { + "doctype": "DocField", + "fieldname": "item_naming_by", + "fieldtype": "Select", + "label": "Item Naming By", + "options": "Item Code\nNaming Series" + }, { "doctype": "DocField", "fieldname": "default_item_group", @@ -502,6 +507,11 @@ "label": "SMS Sender Name", "read_only": 0 }, + { + "amend": 0, + "cancel": 0, + "doctype": "DocPerm" + }, { "doctype": "DocPerm" } diff --git a/setup/doctype/naming_series/naming_series.py b/setup/doctype/naming_series/naming_series.py index 7b804f8a96..3a6b36d1ff 100644 --- a/setup/doctype/naming_series/naming_series.py +++ b/setup/doctype/naming_series/naming_series.py @@ -123,7 +123,14 @@ class DocType: def validate_series_name(self, n): import re - if not re.match('[a-zA-Z0-9]+(([-/][a-zA-Z0-9])?[-/][a-zA-Z0-9]*)*',n): + if "." in n: + parts = n.split(".") + if len(parts) > 2: + msgprint("Only one dot (.) allowed in " + n, raise_exception=1) + if not re.match("#+$", parts[-1]): + msgprint("Numbering series must be in hashes (e.g. ####)", raise_exception=1) + n = n[0] + if not re.match("^[a-zA-Z0-9-/]*$", n): msgprint('Special Characters except "-" and "/" not allowed in naming series') raise Exception diff --git a/setup/doctype/naming_series/naming_series.txt b/setup/doctype/naming_series/naming_series.txt index 3de9e5cfab..0dab9e970d 100644 --- a/setup/doctype/naming_series/naming_series.txt +++ b/setup/doctype/naming_series/naming_series.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-10 16:34:23", + "creation": "2013-01-25 11:35:08", "docstatus": 0, - "modified": "2013-01-22 14:56:34", + "modified": "2013-05-02 15:34:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -25,8 +25,6 @@ "permlevel": 0 }, { - "amend": 0, - "cancel": 0, "create": 1, "doctype": "DocPerm", "name": "__common__", @@ -62,7 +60,7 @@ "fieldname": "help_html", "fieldtype": "HTML", "label": "Help HTML", - "options": "
    \nEdit list of Series in the box below. Each Series Prefix on a new line.

    \nAllowed special characters are \"/\" and \"-\"
    \nExamples:
    \nINV-
    \nINV-10-
    \nINVK-
    \n
    " + "options": "
    \nEdit list of Series in the box below. Rules:\n
      \n
    • Each Series Prefix on a new line.
    • \n
    • Allowed special characters are \"/\" and \"-\"
    • \n
    • Optionally, set the number of digits in the series using dot (.) followed by hashes (#). For example, \".####\" means that the series will have four digits. Default is five digits.
    • \n
    \nExamples:
    \nINV-
    \nINV-10-
    \nINVK-
    \nINV-.####
    \n
    " }, { "doctype": "DocField", diff --git a/stock/doctype/item/item.js b/stock/doctype/item/item.js index 2635f3e904..a344ad3ce9 100644 --- a/stock/doctype/item/item.js +++ b/stock/doctype/item/item.js @@ -20,7 +20,11 @@ cur_frm.cscript.refresh = function(doc) { // make sensitive fields(has_serial_no, is_stock_item, valuation_method) // read only if any stock ledger entry exists - cur_frm.toggle_enable("item_code", doc.__islocal); + cur_frm.toggle_display("naming_series", sys_defaults.item_naming_by=="Naming Series" + && doc.__islocal) + cur_frm.toggle_display("item_code", sys_defaults.item_naming_by!="Naming Series" + && doc.__islocal) + if ((!doc.__islocal) && (doc.is_stock_item == 'Yes')) { var callback = function(r, rt) { diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index 8a804d8c13..63275047ad 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -27,6 +27,13 @@ from webnotes.model.controller import DocListController class PriceListCurrencyMismatch(Exception): pass class DocType(DocListController): + def autoname(self): + if webnotes.conn.get_default("item_naming_by")=="Naming Series": + from webnotes.model.doc import make_autoname + self.doc.item_code = make_autoname(self.doc.naming_series+'.#####') + + self.doc.name = self.doc.item_code + def validate(self): if not self.doc.stock_uom: msgprint(_("Please enter Default Unit of Measure"), raise_exception=1) diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index 3497402076..274719eec5 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-28 15:56:38", + "creation": "2013-04-25 10:56:55", "docstatus": 0, - "modified": "2013-04-23 11:44:39", + "modified": "2013-05-02 15:10:53", "modified_by": "Administrator", "owner": "Administrator" }, @@ -34,7 +34,6 @@ "parent": "Item", "parentfield": "permissions", "parenttype": "DocType", - "permlevel": 0, "read": 1, "submit": 0 }, @@ -51,6 +50,13 @@ "oldfieldtype": "Section Break", "read_only": 0 }, + { + "doctype": "DocField", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "\nITEM" + }, { "description": "Item will be saved by this name in the data base.", "doctype": "DocField", @@ -877,17 +883,12 @@ "label": "Website Description", "read_only": 0 }, - { - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "role": "Material Master Manager", - "write": 1 - }, { "cancel": 0, "create": 0, "doctype": "DocPerm", + "permlevel": 1, + "report": 0, "role": "Material Manager", "write": 0 }, @@ -895,7 +896,63 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", + "permlevel": 0, + "report": 1, + "role": "Material Manager", + "write": 0 + }, + { + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "permlevel": 1, + "report": 0, "role": "Material User", "write": 0 + }, + { + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "permlevel": 0, + "report": 1, + "role": "Material User", + "write": 0 + }, + { + "cancel": 1, + "create": 1, + "doctype": "DocPerm", + "permlevel": 0, + "report": 1, + "role": "Material Master Manager", + "write": 1 + }, + { + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "permlevel": 1, + "report": 0, + "role": "Material Master Manager", + "write": 0 + }, + { + "cancel": 1, + "create": 1, + "doctype": "DocPerm", + "permlevel": 0, + "report": 1, + "role": "System Manager", + "write": 1 + }, + { + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "permlevel": 1, + "report": 0, + "role": "System Manager", + "write": 0 } ] \ No newline at end of file From 07f6958c1ffa145ba9fb83b7983370f2523ae8f6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 15:51:45 +0530 Subject: [PATCH 079/295] [login] [disable signup] option to disable signup link in login page --- startup/website.py | 13 ++++++++----- .../website_settings/website_settings.txt | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/startup/website.py b/startup/website.py index 295e5bb0ca..4c2f5653b4 100644 --- a/startup/website.py +++ b/startup/website.py @@ -1,4 +1,5 @@ import webnotes, conf, os +from webnotes.utils import cint, cstr def get_templates_path(): return os.path.join(os.path.dirname(conf.__file__), "app", "website", "templates") @@ -61,15 +62,17 @@ def update_template_args(page_name, args): settings = webnotes.doc("Website Settings", "Website Settings") for k in ["banner_html", "brand_html", "copyright", "address", "twitter_share_via", - "favicon", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share"]: + "favicon", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share", + "disable_signup"]: if k in settings.fields: args[k] = settings.fields.get(k) - for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share"]: - args[k] = int(args.get(k) or 0) + for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share", + "disable_signup"]: + args[k] = cint(args.get(k) or 0) - args.url = quote(str(get_request_site_address(full_address=True)), str("")) - args.encoded_title = quote(str(args.title or ""), str("")) + args.url = quote(cstr(get_request_site_address(full_address=True)), cstr("")) + args.encoded_title = quote(cstr(args.title or ""), cstr("")) return args \ No newline at end of file diff --git a/website/doctype/website_settings/website_settings.txt b/website/doctype/website_settings/website_settings.txt index 615c0a5b1b..9c15480f07 100644 --- a/website/doctype/website_settings/website_settings.txt +++ b/website/doctype/website_settings/website_settings.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 06:51:18", + "creation": "2013-04-30 12:58:46", "docstatus": 0, - "modified": "2013-04-17 11:51:30", + "modified": "2013-05-02 15:51:14", "modified_by": "Administrator", "owner": "Administrator" }, @@ -219,13 +219,16 @@ "reqd": 0 }, { - "description": "Enter domain names associated to this website, each on a new line", "doctype": "DocField", - "fieldname": "domain_list", - "fieldtype": "Text", - "hidden": 1, - "label": "Domain List", - "reqd": 0 + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "description": "Disable Customer Signup link in Login page", + "doctype": "DocField", + "fieldname": "disable_signup", + "fieldtype": "Check", + "label": "Disable Signup" }, { "create": 1, From 97e2a7eeb9cd8bf2d5e702a2a54ac96871d5f80c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 15:54:28 +0530 Subject: [PATCH 080/295] [latest updates] --- home/page/latest_updates/latest_updates.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index f254bf2349..5600c9e2b1 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -2,7 +2,8 @@ erpnext.updates = [ ["2nd May", ["Buying: Warehouse must belong to same company as transaction", "Price List: Added Currency Field. One price list can have only one currency", "Item: Naming can now be by series or item code", - "Naming Series: Set number of digits in series (optionally)"]], + "Naming Series: Set number of digits in series (optionally)", + "Login: Disable Signup link in the login page"]], ["18th April", ["Cost Center: Set a default Cost Center for a Company"]], ["12th April", ["Employee: List of Leave Approvers who can approve the Employee's Leave Applications"]], ["10th April", ["Redesigned File Uploads and added File Manager in Setup"]], From ff1e155a77c9a0b8ce47fabbaec0227363734078 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 16:25:59 +0530 Subject: [PATCH 081/295] [price list] country-wise price list --- patches/april_2013/p08_price_list_country.py | 1 + setup/doctype/price_list/price_list.js | 11 ++++++++--- setup/doctype/price_list/price_list.py | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/patches/april_2013/p08_price_list_country.py b/patches/april_2013/p08_price_list_country.py index 1179e1da5b..65643cc239 100644 --- a/patches/april_2013/p08_price_list_country.py +++ b/patches/april_2013/p08_price_list_country.py @@ -1,4 +1,5 @@ import webnotes def execute(): + webnotes.reload_doc("Setup", "DocType", "Price List") webnotes.conn.sql("""update `tabPrice List` set valid_for_all_countries=1""") \ No newline at end of file diff --git a/setup/doctype/price_list/price_list.js b/setup/doctype/price_list/price_list.js index 0020edaf20..b68627f73f 100644 --- a/setup/doctype/price_list/price_list.js +++ b/setup/doctype/price_list/price_list.js @@ -29,9 +29,14 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { } cur_frm.cscript.show_item_prices = function() { - cur_frm.toggle_display("item_prices_section", cur_frm.doc.__item_price && cur_frm.doc.__item_price.length); + var item_price = wn.model.get("Item Price", {price_list_name: cur_frm.doc.name}); + + var show = item_price && item_price.length; + + cur_frm.toggle_display("item_prices_section", show); $(cur_frm.fields_dict.item_prices.wrapper).empty(); - if (!cur_frm.doc.__item_price) return; + if (!show) return; + var out = '\ \ \ @@ -40,7 +45,7 @@ cur_frm.cscript.show_item_prices = function() { \ \ ' - + $.map(cur_frm.doc.__item_price.sort(function(a, b) { return a.parent.localeCompare(b.parent); }), function(d) { + + $.map(item_price.sort(function(a, b) { return a.parent.localeCompare(b.parent); }), function(d) { return '' + '' + '' diff --git a/setup/doctype/price_list/price_list.py b/setup/doctype/price_list/price_list.py index 9fef9154d7..ae49bf868d 100644 --- a/setup/doctype/price_list/price_list.py +++ b/setup/doctype/price_list/price_list.py @@ -25,8 +25,8 @@ class DocType: self.doc, self.doclist = d, dl def onload(self): - self.doc.fields["__item_price"] = webnotes.conn.sql("""select parent, ref_rate, ref_currency, selling, buying - from `tabItem Price` where price_list_name=%s""", self.doc.name, as_dict=True) + self.doclist.extend(webnotes.conn.sql("""select * from `tabItem Price` + where price_list_name=%s""", self.doc.name, as_dict=True, update={"doctype": "Item Price"})) def validate(self): if not (cint(self.doc.valid_for_all_countries) or len(self.doclist.get({"parentfield": "valid_for_countries"}))): From 8323b6da45db7df231d9ab7769012cf982aacec2 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 16:41:20 +0530 Subject: [PATCH 082/295] [patch] [fix] reload print format doctype --- patches/patch_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/patch_list.py b/patches/patch_list.py index 83fb43fd14..3a04b996b6 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -154,12 +154,12 @@ patch_list = [ "patches.january_2013.remove_tds_entry_from_gl_mapper", "patches.january_2013.update_number_format", "patches.january_2013.purchase_price_list", + "execute:webnotes.reload_doc('core', 'doctype', 'print_format')", "execute:webnotes.reload_doc('accounts','Print Format','Payment Receipt Voucher')", "patches.january_2013.update_fraction_for_usd", "patches.january_2013.enable_currencies", "patches.january_2013.remove_unwanted_permission", "patches.january_2013.remove_landed_cost_master", - "execute:webnotes.reload_doc('core', 'doctype', 'print_format')", "patches.january_2013.reload_print_format", "patches.january_2013.rebuild_tree", "execute:webnotes.reload_doc('core','doctype','docfield') #2013-01-28", From 0382d5a01da60c81fd006b7e46ce2fffcb99108a Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 16:42:26 +0530 Subject: [PATCH 083/295] [patch] [fix] reload print format doctype --- patches/patch_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/patch_list.py b/patches/patch_list.py index 3a04b996b6..ed1c8462cd 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -154,7 +154,7 @@ patch_list = [ "patches.january_2013.remove_tds_entry_from_gl_mapper", "patches.january_2013.update_number_format", "patches.january_2013.purchase_price_list", - "execute:webnotes.reload_doc('core', 'doctype', 'print_format')", + "execute:webnotes.reload_doc('core', 'doctype', 'print_format') #2013-01", "execute:webnotes.reload_doc('accounts','Print Format','Payment Receipt Voucher')", "patches.january_2013.update_fraction_for_usd", "patches.january_2013.enable_currencies", From cd772daf1d0935b7634824102c301e91be7d6532 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 17:47:17 +0530 Subject: [PATCH 084/295] [website] [fix] --- startup/website.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/startup/website.py b/startup/website.py index 4c2f5653b4..be8eba6def 100644 --- a/startup/website.py +++ b/startup/website.py @@ -71,8 +71,8 @@ def update_template_args(page_name, args): "disable_signup"]: args[k] = cint(args.get(k) or 0) - args.url = quote(cstr(get_request_site_address(full_address=True)), cstr("")) - args.encoded_title = quote(cstr(args.title or ""), cstr("")) + args.url = quote(str(get_request_site_address(full_address=True)), str("")) + args.encoded_title = quote(str(args.title or ""), str("")) return args \ No newline at end of file From eb41ffd24ea85693d72b9f5e802f192caccc808a Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 18:10:19 +0530 Subject: [PATCH 085/295] [buying controller] [fix] fixed message for company warehouse mismatch validation --- controllers/buying_controller.py | 3 +-- setup/doctype/price_list/test_price_list.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 0c25b9855e..9e181bc8fe 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -50,8 +50,7 @@ class BuyingController(StockController): for warehouse, company in webnotes.conn.get_values("Warehouse", self.doclist.get_distinct_values("warehouse"), "company").items(): if company and company != self.doc.company: - webnotes.msgprint(_("Warehouse must belong to company") + \ - (": %s (%s, %s)" % (warehouse, company, self.doc.company)), + webnotes.msgprint(_("Company mismatch for Warehouse") + (": %s" % (warehouse,)), raise_exception=WrongWarehouseCompany) def validate_stock_or_nonstock_items(self): diff --git a/setup/doctype/price_list/test_price_list.py b/setup/doctype/price_list/test_price_list.py index 30262dc8aa..fe87821904 100644 --- a/setup/doctype/price_list/test_price_list.py +++ b/setup/doctype/price_list/test_price_list.py @@ -2,6 +2,7 @@ test_records = [ [{ "doctype": "Price List", "price_list_name": "_Test Price List", - "currency": "INR" + "currency": "INR", + "valid_for_all_countries": 1 }] ] \ No newline at end of file From c4b0e19b47ada5787bcc15c575a10a27013c1a0e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 May 2013 11:02:13 +0530 Subject: [PATCH 086/295] [fixes] about us settings --- website/doctype/about_us_settings/about_us_settings.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/doctype/about_us_settings/about_us_settings.txt b/website/doctype/about_us_settings/about_us_settings.txt index ef6122ec61..baf9cc0af8 100644 --- a/website/doctype/about_us_settings/about_us_settings.txt +++ b/website/doctype/about_us_settings/about_us_settings.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 12:48:00", + "creation": "2013-03-19 12:02:15", "docstatus": 0, - "modified": "2013-03-12 14:48:34", + "modified": "2013-05-03 11:01:30", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,7 +10,7 @@ "allow_attach": 1, "description": "Settings for the About Us Page", "doctype": "DocType", - "document_type": "Master", + "document_type": "Other", "issingle": 1, "module": "Website", "name": "__common__" @@ -21,7 +21,8 @@ "parent": "About Us Settings", "parentfield": "fields", "parenttype": "DocType", - "permlevel": 0 + "permlevel": 0, + "read_only": 0 }, { "create": 1, From d2b8b83e209f42e8ec1dca3ba7b567dca2dc6ae3 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 3 May 2013 13:22:41 +0530 Subject: [PATCH 087/295] [sales invoice] [fetching] fetch customer info, pos settings and item details if not fetched --- .../doctype/sales_invoice/sales_invoice.py | 31 +++++++++++++------ .../sales_invoice/test_sales_invoice.py | 14 ++++++--- .../global_defaults/global_defaults.py | 1 + 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index f0222a0ac7..18611793fe 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -45,15 +45,7 @@ class DocType(SellingController): def validate(self): super(DocType, self).validate() - - if not (self.doc.contact_person and self.doc.customer_address): - for fieldname, val in self.get_default_address_and_contact("customer").items(): - if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): - self.doc.fields[fieldname] = val - - if cint(self.doc.is_pos): - self.set_pos_fields(for_validate=True) - + self.fetch_missing_values() self.validate_posting_time() self.so_dn_required() self.validate_proj_cust() @@ -145,6 +137,26 @@ class DocType(SellingController): def on_update_after_submit(self): self.validate_recurring_invoice() self.convert_to_recurring() + + def fetch_missing_values(self): + # fetch contact and address details for customer, if they are not mentioned + if not (self.doc.contact_person and self.doc.customer_address): + for fieldname, val in self.get_default_address_and_contact("customer").items(): + if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): + self.doc.fields[fieldname] = val + + # fetch missing item values + for item in self.doclist.get({"parentfield": "entries"}): + if item.fields.get("item_code"): + ret = get_obj('Sales Common').get_item_details(item.fields, self) + for fieldname, value in ret.items(): + if self.meta.get_field(fieldname, parentfield="entries") and \ + not item.fields.get(fieldname): + item.fields[fieldname] = value + + # fetch pos details, if they are not fetched + if cint(self.doc.is_pos): + self.set_pos_fields(for_validate=True) def update_time_log_batch(self, sales_invoice): for d in self.doclist.get({"doctype":"Sales Invoice Item"}): @@ -294,6 +306,7 @@ class DocType(SellingController): ret = self.apply_pos_settings(args, ret) return ret + elif cint(self.doc.is_pos) == 1 and self.pos_settings: for doc in self.doclist.get({"parentfield": "entries"}): if doc.fields.get('item_code'): diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index 31bf024e7f..b46cdd1777 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -90,6 +90,9 @@ class TestSalesInvoice(unittest.TestCase): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + old_default_company = webnotes.conn.get_default("company") + webnotes.conn.set_default("company", "_Test Company") + self._insert_purchase_receipt() self._insert_pos_settings() @@ -106,7 +109,8 @@ class TestSalesInvoice(unittest.TestCase): # check stock ledger entries sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry` - where voucher_type = 'Sales Invoice' and voucher_no = %s""", si.doc.name, as_dict=1)[0] + where voucher_type = 'Sales Invoice' and voucher_no = %s""", + si.doc.name, as_dict=1)[0] self.assertTrue(sle) self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty], ["_Test Item", "_Test Warehouse", -5.0]) @@ -145,6 +149,7 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(gl_count[0][0], 16) webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + webnotes.conn.set_default("company", old_default_company) def test_sales_invoice_gl_entry_with_aii_no_item_code(self): webnotes.defaults.set_global_default("auto_inventory_accounting", 1) @@ -337,7 +342,7 @@ class TestSalesInvoice(unittest.TestCase): # change posting date but keep recuring day to be today si7 = webnotes.bean(copy=base_si.doclist) si7.doc.fields.update({ - "posting_date": add_to_date(today, days=-3) + "posting_date": add_to_date(today, days=-1) }) si7.insert() si7.submit() @@ -345,7 +350,7 @@ class TestSalesInvoice(unittest.TestCase): # setting so that _test function works si7.doc.posting_date = today self._test_recurring_invoice(si7, True) - + def _test_recurring_invoice(self, base_si, first_and_last_day): from webnotes.utils import add_months, get_last_day, getdate from accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices @@ -361,7 +366,8 @@ class TestSalesInvoice(unittest.TestCase): manage_recurring_invoices(next_date=next_date, commit=False) recurred_invoices = webnotes.conn.sql("""select name from `tabSales Invoice` - where recurring_id=%s and docstatus=1 order by name desc""", base_si.doc.recurring_id) + where recurring_id=%s and docstatus=1 order by name desc""", + base_si.doc.recurring_id) self.assertEquals(i+2, len(recurred_invoices)) diff --git a/setup/doctype/global_defaults/global_defaults.py b/setup/doctype/global_defaults/global_defaults.py index 191a47edf2..dc7f6b4b73 100644 --- a/setup/doctype/global_defaults/global_defaults.py +++ b/setup/doctype/global_defaults/global_defaults.py @@ -21,6 +21,7 @@ import webnotes.defaults from webnotes.utils import cint keydict = { + # "key in defaults": "key in Global Defaults" "print_style": "print_style", "fiscal_year": "current_fiscal_year", 'company': 'default_company', From fdd919a1d3024fa264e7fbf60832da1ddb944bf3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 May 2013 14:24:24 +0530 Subject: [PATCH 088/295] [report] sales personwise transaction summary --- selling/page/selling_home/selling_home.js | 4 + .../__init__.py | 0 .../sales_person_wise_transaction_summary.js | 54 +++++++++++++ .../sales_person_wise_transaction_summary.py | 81 +++++++++++++++++++ .../sales_person_wise_transaction_summary.txt | 22 +++++ 5 files changed, 161 insertions(+) create mode 100644 selling/report/sales_person_wise_transaction_summary/__init__.py create mode 100644 selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js create mode 100644 selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py create mode 100644 selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.txt diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 994bb4a555..1ffa600fd5 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -157,6 +157,10 @@ wn.module_page["Selling"] = [ "label":wn._("Sales Orders Pending to be Delivered"), route: "query-report/Sales Orders Pending To Be Delivered" }, + { + "label":wn._("Sales Person-wise Transaction Summary"), + route: "query-report/Sales Person-wise Transaction Summary", + }, ] } ] diff --git a/selling/report/sales_person_wise_transaction_summary/__init__.py b/selling/report/sales_person_wise_transaction_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js new file mode 100644 index 0000000000..4a8e6787ff --- /dev/null +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js @@ -0,0 +1,54 @@ +wn.query_reports["Sales Person-wise Transaction Summary"] = { + "filters": [ + { + fieldname: "sales_person", + label: "Sales Person", + fieldtype: "Link", + options: "Sales Person" + }, + { + fieldname: "doc_type", + label: "Document Type", + fieldtype: "Select", + options: "Sales Order\nDelivery Note\nSales Invoice", + default: "Sales Order" + }, + { + fieldname: "from_date", + label: "From Date", + fieldtype: "Date", + default: wn.defaults.get_user_default("year_start_date"), + }, + { + fieldname:"to_date", + label: "To Date", + fieldtype: "Date", + default: get_today() + }, + { + fieldname:"company", + label: "Company", + fieldtype: "Link", + options: "Company", + default: sys_defaults.company + }, + { + fieldname:"item_group", + label: "Item Group", + fieldtype: "Link", + options: "Item Group", + }, + { + fieldname:"brand", + label: "Brand", + fieldtype: "Link", + options: "Brand", + }, + { + fieldname:"customer", + label: "Customer", + fieldtype: "Link", + options: "Customer", + }, + ] +} \ No newline at end of file diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py new file mode 100644 index 0000000000..612cb7425d --- /dev/null +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -0,0 +1,81 @@ +# 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 import msgprint, _ + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + data = get_entries(filters) + + return columns, data + +def get_columns(filters): + if not filters.get("doc_type"): + msgprint(_("Please select the document type first"), raise_exception=1) + + return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140", + "Customer:Link/Customer:140", "Territory:Link/Territory:100", "Posting Date:Date:100", + "Item Code:Link/Item:120", "Qty:Currency:100", "Amount:Currency:120", + "Sales Person:Link/Sales Person:140", "Contribution %:Currency:110", + "Contribution Amount:Currency:140"] + +def get_entries(filters): + date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date" + conditions, items = get_conditions(filters, date_field) + entries = webnotes.conn.sql("""select dt.name, dt.customer, dt.territory, dt.%s, + dt_item.item_code, dt_item.qty, dt_item.amount, st.sales_person, + st.allocated_percentage, dt_item.amount*st.allocated_percentage/100 + from `tab%s` dt, `tab%s Item` dt_item, `tabSales Team` st + where st.parent = dt.name and dt.name = dt_item.parent and st.parenttype = '%s' + and dt.docstatus = 1 %s order by st.sales_person, dt.name desc""" % + (date_field, filters["doc_type"], filters["doc_type"], filters["doc_type"], conditions), + tuple(items), as_list=1) + + return entries + +def get_conditions(filters, date_field): + conditions = "" + if filters.get("company"): conditions += " and dt.company = '%s'" % filters["company"] + if filters.get("customer"): conditions += " and dt.customer = '%s'" % filters["customer"] + + if filters.get("from_date"): conditions += " and dt.%s >= '%s'" % \ + (date_field, filters["from_date"]) + if filters.get("to_date"): conditions += " and dt.%s <= '%s'" % (date_field, filters["to_date"]) + + if filters.get("sales_person"): conditions += " and st.sales_person = '%s'" % \ + filters["sales_person"] + + items = get_items(filters) + if items: + conditions += " and dt_item.item_code in (%s)" % ', '.join(['%s']*len(items)) + + return conditions, items + +def get_items(filters): + if filters.get("item_group"): key = "item_group" + elif filters.get("brand"): key = "brand" + else: key = "" + + items = [] + if key: + items = webnotes.conn.sql_list("""select name from tabItem where %s = %s""" % + (key, '%s'), (filters[key])) + + return items \ No newline at end of file diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.txt b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.txt new file mode 100644 index 0000000000..abd69f3f2f --- /dev/null +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-03 11:31:05", + "docstatus": 0, + "modified": "2013-05-03 11:31:05", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Order", + "report_name": "Sales Person-wise Transaction Summary", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Sales Person-wise Transaction Summary" + } +] \ No newline at end of file From 0f5f5846515ebd9f606295d355fa9fea2279e442 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 3 May 2013 14:34:12 +0530 Subject: [PATCH 089/295] [stock entry] [test] fixes in test cases --- accounts/doctype/sales_invoice/sales_invoice.py | 2 +- stock/doctype/stock_entry/test_stock_entry.py | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 18611793fe..87f73c19ab 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -157,7 +157,7 @@ class DocType(SellingController): # fetch pos details, if they are not fetched if cint(self.doc.is_pos): self.set_pos_fields(for_validate=True) - + def update_time_log_batch(self, sales_invoice): for d in self.doclist.get({"doctype":"Sales Invoice Item"}): if d.time_log_batch: diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index 7c406f8e8c..c3ce2d7f40 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -25,6 +25,8 @@ class TestStockEntry(unittest.TestCase): where item_code='_Test Item'""") self.assertTrue(mr_name) + + webnotes.conn.set_default("company", self.old_default_company) def test_warehouse_company_validation(self): from stock.doctype.stock_ledger_entry.stock_ledger_entry import InvalidWarehouseCompany @@ -71,7 +73,7 @@ class TestStockEntry(unittest.TestCase): webnotes.defaults.set_global_default("auto_inventory_accounting", 0) def test_material_issue_gl_entry(self): - webnotes.conn.sql("delete from `tabStock Ledger Entry`") + self._clear_stock() webnotes.defaults.set_global_default("auto_inventory_accounting", 1) mr = webnotes.bean(copy=test_records[0]) @@ -111,9 +113,10 @@ class TestStockEntry(unittest.TestCase): ) webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + webnotes.conn.set_default("company", self.old_default_company) def test_material_transfer_gl_entry(self): - webnotes.conn.sql("delete from `tabStock Ledger Entry`") + self._clear_stock() webnotes.defaults.set_global_default("auto_inventory_accounting", 1) mr = webnotes.bean(copy=test_records[0]) @@ -145,6 +148,7 @@ class TestStockEntry(unittest.TestCase): self.assertFalse(gl_entries) webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + webnotes.conn.set_default("company", self.old_default_company) def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle): # check stock ledger entries @@ -173,6 +177,9 @@ class TestStockEntry(unittest.TestCase): def _clear_stock(self): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.conn.sql("""delete from `tabBin`""") + + self.old_default_company = webnotes.conn.get_default("company") + webnotes.conn.set_default("company", "_Test Company") def _insert_material_receipt(self): self._clear_stock() @@ -185,6 +192,8 @@ class TestStockEntry(unittest.TestCase): se2.insert() se2.submit() + webnotes.conn.set_default("company", self.old_default_company) + def _get_actual_qty(self): return flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item", "warehouse": "_Test Warehouse"}, "actual_qty")) @@ -463,6 +472,8 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(actual_qty_1 - 5, actual_qty_2) + webnotes.conn.set_default("company", self.old_default_company) + return se, pr.doc.name def test_over_stock_return(self): @@ -563,6 +574,8 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(actual_qty_1 - 5, actual_qty_2) + webnotes.conn.set_default("company", self.old_default_company) + return se, pr.doc.name test_records = [ From cba730c7bdc7f0f4b601ee17c9c669666f7137aa Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 3 May 2013 14:47:59 +0530 Subject: [PATCH 090/295] [sales common] [fix] minor fix in validation message --- selling/doctype/sales_common/sales_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py index 7b1528bafe..78f842291d 100644 --- a/selling/doctype/sales_common/sales_common.py +++ b/selling/doctype/sales_common/sales_common.py @@ -374,7 +374,7 @@ class DocType(TransactionBase): msgprint("Please Enter Appropriate Conversion Rate for Customer's Currency to Base Currency (%s --> %s)" % (obj.doc.currency, default_currency), raise_exception = 1) if (obj.doc.price_list_currency == default_currency and flt(obj.doc.plc_conversion_rate) != 1.00) or not obj.doc.plc_conversion_rate or (obj.doc.price_list_currency != default_currency and flt(obj.doc.plc_conversion_rate) == 1.00): - msgprint("Please Enter Appropriate Conversion Rate for Price List Currency to Base Currency ( (%s --> %s)" % (obj.doc.price_list_currency, default_currency), raise_exception = 1) + msgprint("Please Enter Appropriate Conversion Rate for Price List Currency to Base Currency (%s --> %s)" % (obj.doc.price_list_currency, default_currency), raise_exception = 1) From 2a9735a2f10075f029b965f13c347b357f6472f1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 3 May 2013 14:48:13 +0530 Subject: [PATCH 091/295] [pos setting] [permission] removed system manager permission --- accounts/doctype/pos_setting/pos_setting.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/accounts/doctype/pos_setting/pos_setting.txt b/accounts/doctype/pos_setting/pos_setting.txt index 4e30b57593..80cb1ecdc9 100755 --- a/accounts/doctype/pos_setting/pos_setting.txt +++ b/accounts/doctype/pos_setting/pos_setting.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 11:03:07", + "creation": "2013-04-30 12:58:25", "docstatus": 0, - "modified": "2013-03-26 12:48:18", + "modified": "2013-05-03 14:36:24", "modified_by": "Administrator", "owner": "Administrator" }, @@ -40,6 +40,7 @@ "doctype": "DocField", "fieldname": "user", "fieldtype": "Link", + "in_list_view": 1, "label": "User", "oldfieldname": "user", "oldfieldtype": "Link", @@ -99,6 +100,7 @@ "doctype": "DocField", "fieldname": "company", "fieldtype": "Link", + "in_list_view": 1, "label": "Company", "oldfieldname": "company", "oldfieldtype": "Link", @@ -210,12 +212,6 @@ "oldfieldtype": "Select", "options": "link:Print Heading" }, - { - "create": 1, - "doctype": "DocPerm", - "role": "System Manager", - "write": 1 - }, { "create": 1, "doctype": "DocPerm", From 9c7eb486edcb49845dfc893420f360d78c5dc8f8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 May 2013 14:50:12 +0530 Subject: [PATCH 092/295] [Query Report] Item-wise Sales History --- selling/page/selling_home/selling_home.js | 4 ++++ .../item_wise_sales_history/__init__.py | 0 .../item_wise_sales_history.txt | 23 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 selling/report/item_wise_sales_history/__init__.py create mode 100644 selling/report/item_wise_sales_history/item_wise_sales_history.txt diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 1ffa600fd5..682978bd17 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -161,6 +161,10 @@ wn.module_page["Selling"] = [ "label":wn._("Sales Person-wise Transaction Summary"), route: "query-report/Sales Person-wise Transaction Summary", }, + { + "label":wn._("Item-wise Sales History"), + route: "query-report/Item-wise Sales History", + }, ] } ] diff --git a/selling/report/item_wise_sales_history/__init__.py b/selling/report/item_wise_sales_history/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/item_wise_sales_history/item_wise_sales_history.txt b/selling/report/item_wise_sales_history/item_wise_sales_history.txt new file mode 100644 index 0000000000..2558b35228 --- /dev/null +++ b/selling/report/item_wise_sales_history/item_wise_sales_history.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-03 14:38:34", + "docstatus": 0, + "modified": "2013-05-03 14:49:05", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Currency:100\",\n\tso_item.stock_uom as \"UOM::80\",\n\tso_item.basic_rate as \"Rate:Currency:120\",\n\tso_item.amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n\tso.territory as \"Territory:Link/Territory:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Currency:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item\nwhere\n\tso.name = so_item.parent\n\tand so.docstatus = 1\norder by so.name desc", + "ref_doctype": "Sales Order", + "report_name": "Item-wise Sales History", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Item-wise Sales History" + } +] \ No newline at end of file From 317f0273e74d1e3c28fd720b0328bded48de6af0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 May 2013 15:08:12 +0530 Subject: [PATCH 093/295] [query report] item-wise purchase history --- buying/page/buying_home/buying_home.js | 11 +++++++++ buying/report/__init__.py | 0 .../item_wise_purchase_history/__init__.py | 0 .../item_wise_purchase_history.txt | 23 +++++++++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 buying/report/__init__.py create mode 100644 buying/report/item_wise_purchase_history/__init__.py create mode 100644 buying/report/item_wise_purchase_history/item_wise_purchase_history.txt diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js index 2df5f6fb07..56328cc574 100644 --- a/buying/page/buying_home/buying_home.js +++ b/buying/page/buying_home/buying_home.js @@ -98,6 +98,17 @@ wn.module_page["Buying"] = [ }, ] }, + { + title: wn._("Reports"), + right: true, + icon: "icon-list", + items: [ + { + "label":wn._("Item-wise Purchase History"), + route: "query-report/Item-wise Purchase History", + }, + ] + } ] pscript['onload_buying-home'] = function(wrapper) { diff --git a/buying/report/__init__.py b/buying/report/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/buying/report/item_wise_purchase_history/__init__.py b/buying/report/item_wise_purchase_history/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt new file mode 100644 index 0000000000..b499707062 --- /dev/null +++ b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-03 14:55:53", + "docstatus": 0, + "modified": "2013-05-03 15:07:13", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Currency:100\",\n\tpo_item.stock_uom as \"UOM:Link/UOM:80\",\n\tpo_item.purchase_rate as \"Rate:Currency:120\",\n\tpo_item.amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Currency:120\",\n\tifnull(po_item.billed_qty, 0) as \"Billed Qty:Currency:120\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item\nwhere\n\tpo.name = po_item.parent and po.docstatus = 1\norder by po.name desc", + "ref_doctype": "Purchase Order", + "report_name": "Item-wise Purchase History", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Item-wise Purchase History" + } +] \ No newline at end of file From c51c0ce3e99c996ed22d585eef20bc8f5cdef284 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 May 2013 15:40:26 +0530 Subject: [PATCH 094/295] [fixes] salary slip --- .../item_wise_purchase_history/item_wise_purchase_history.txt | 4 ++-- hr/doctype/salary_slip/salary_slip.py | 4 ++-- .../item_wise_sales_history/item_wise_sales_history.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt index b499707062..3f9e702f5f 100644 --- a/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt +++ b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-03 14:55:53", "docstatus": 0, - "modified": "2013-05-03 15:07:13", + "modified": "2013-05-03 15:13:34", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Currency:100\",\n\tpo_item.stock_uom as \"UOM:Link/UOM:80\",\n\tpo_item.purchase_rate as \"Rate:Currency:120\",\n\tpo_item.amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Currency:120\",\n\tifnull(po_item.billed_qty, 0) as \"Billed Qty:Currency:120\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item\nwhere\n\tpo.name = po_item.parent and po.docstatus = 1\norder by po.name desc", + "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Currency:100\",\n\tpo_item.stock_uom as \"UOM:Link/UOM:80\",\n\tpo_item.purchase_rate as \"Rate:Currency:120\",\n\tpo_item.amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n\tpo_item.project_name as \"Project:Link/Project:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Currency:120\",\n\tifnull(po_item.billed_qty, 0) as \"Billed Qty:Currency:120\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item\nwhere\n\tpo.name = po_item.parent and po.docstatus = 1\norder by po.name desc", "ref_doctype": "Purchase Order", "report_name": "Item-wise Purchase History", "report_type": "Query Report" diff --git a/hr/doctype/salary_slip/salary_slip.py b/hr/doctype/salary_slip/salary_slip.py index 3edf410954..d1ce3ccf1d 100644 --- a/hr/doctype/salary_slip/salary_slip.py +++ b/hr/doctype/salary_slip/salary_slip.py @@ -152,7 +152,7 @@ class DocType(TransactionBase): d.e_modified_amount = round(flt(d.e_amount)*flt(self.doc.payment_days)/cint(self.doc.total_days_in_month), 2) elif not self.doc.payment_days: d.e_modified_amount = 0 - self.doc.gross_pay += d.e_modified_amount + self.doc.gross_pay += flt(d.e_modified_amount) def calculate_ded_total(self): """ @@ -165,7 +165,7 @@ class DocType(TransactionBase): elif not self.doc.payment_days: d.d_modified_amount = 0 - self.doc.total_deduction += d.d_modified_amount + self.doc.total_deduction += flt(d.d_modified_amount) def calculate_net_pay(self): """ diff --git a/selling/report/item_wise_sales_history/item_wise_sales_history.txt b/selling/report/item_wise_sales_history/item_wise_sales_history.txt index 2558b35228..cf13bdc3ea 100644 --- a/selling/report/item_wise_sales_history/item_wise_sales_history.txt +++ b/selling/report/item_wise_sales_history/item_wise_sales_history.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-03 14:38:34", "docstatus": 0, - "modified": "2013-05-03 14:49:05", + "modified": "2013-05-03 15:15:11", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Currency:100\",\n\tso_item.stock_uom as \"UOM::80\",\n\tso_item.basic_rate as \"Rate:Currency:120\",\n\tso_item.amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n\tso.territory as \"Territory:Link/Territory:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Currency:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item\nwhere\n\tso.name = so_item.parent\n\tand so.docstatus = 1\norder by so.name desc", + "query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Currency:100\",\n\tso_item.stock_uom as \"UOM:Link/UOM:80\",\n\tso_item.basic_rate as \"Rate:Currency:120\",\n\tso_item.amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project_name as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Currency:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item\nwhere\n\tso.name = so_item.parent\n\tand so.docstatus = 1\norder by so.name desc", "ref_doctype": "Sales Order", "report_name": "Item-wise Sales History", "report_type": "Query Report" From 1ae4b51cb25a55bd2eab7b9b8aad821ec314d87d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 May 2013 18:27:23 +0530 Subject: [PATCH 095/295] [report] issued items against production orderr --- .../manufacturing_home/manufacturing_home.js | 11 +++++++++ manufacturing/report/__init__.py | 0 .../__init__.py | 0 .../issued_items_against_production_order.txt | 23 +++++++++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 manufacturing/report/__init__.py create mode 100644 manufacturing/report/issued_items_against_production_order/__init__.py create mode 100644 manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt diff --git a/manufacturing/page/manufacturing_home/manufacturing_home.js b/manufacturing/page/manufacturing_home/manufacturing_home.js index b7f28edc41..d4841df2fb 100644 --- a/manufacturing/page/manufacturing_home/manufacturing_home.js +++ b/manufacturing/page/manufacturing_home/manufacturing_home.js @@ -58,6 +58,17 @@ wn.module_page["Manufacturing"] = [ }, ] }, + { + title: wn._("Reports"), + right: true, + icon: "icon-list", + items: [ + { + "label":wn._("Issued Items Against Production Order"), + route: "query-report/Issued Items Against Production Order", + }, + ] + } ] pscript['onload_manufacturing-home'] = function(wrapper) { diff --git a/manufacturing/report/__init__.py b/manufacturing/report/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/manufacturing/report/issued_items_against_production_order/__init__.py b/manufacturing/report/issued_items_against_production_order/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt b/manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt new file mode 100644 index 0000000000..e00123d592 --- /dev/null +++ b/manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-03 17:48:46", + "docstatus": 0, + "modified": "2013-05-03 18:24:05", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 0, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Currency:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture/Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code", + "ref_doctype": "Production Order", + "report_name": "Issued Items Against Production Order", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Issued Items Against Production Order" + } +] \ No newline at end of file From e23d2a35c4e49cde58ff6ee461dfd1f6a2c69ab9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 May 2013 11:08:21 +0530 Subject: [PATCH 096/295] [fixes] sales/purchase register --- .../report/purchase_register/purchase_register.py | 15 ++++++++++----- accounts/report/sales_register/sales_register.py | 12 ++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index c131c17b4b..d4f23927d5 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -20,9 +20,10 @@ from webnotes.utils import flt def execute(filters=None): if not filters: filters = {} - columns, expense_accounts, tax_accounts = get_columns() - + invoice_list = get_invoices(filters) + columns, expense_accounts, tax_accounts = get_columns(invoice_list) + invoice_expense_map = get_invoice_expense_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) @@ -55,7 +56,7 @@ def execute(filters=None): return columns, data -def get_columns(): +def get_columns(invoice_list): """return columns based on filters""" columns = [ "Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier:Link/Supplier:120", @@ -66,10 +67,14 @@ def get_columns(): expense_accounts = webnotes.conn.sql_list("""select distinct expense_head from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' - order by expense_head""") + and parent in (%s) order by expense_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + tax_accounts = webnotes.conn.sql_list("""select distinct account_head from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' - and docstatus = 1 and ifnull(account_head, '') != '' order by account_head""") + and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) + order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 23d2227fc2..b15097457d 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -20,9 +20,10 @@ from webnotes.utils import flt def execute(filters=None): if not filters: filters = {} - columns, income_accounts, tax_accounts = get_columns() invoice_list = get_invoices(filters) + columns, income_accounts, tax_accounts = get_columns(invoice_list) + invoice_income_map = get_invoice_income_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) @@ -59,7 +60,7 @@ def execute(filters=None): return columns, data -def get_columns(): +def get_columns(invoice_list): """return columns based on filters""" columns = [ "Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer:Link/Customer:120", @@ -69,11 +70,14 @@ def get_columns(): ] income_accounts = webnotes.conn.sql_list("""select distinct income_account - from `tabSales Invoice Item` where docstatus = 1 order by income_account""") + from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) + order by income_account""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) tax_accounts = webnotes.conn.sql_list("""select distinct account_head from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' - and docstatus = 1 order by account_head""") + and docstatus = 1 and parent in (%s) order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in income_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ From 52f7b6daf86ba5d2f2c5c45cdad54ca830fbad1c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 May 2013 12:24:27 +0530 Subject: [PATCH 097/295] [report] purchase in transit in new style --- buying/page/buying_home/buying_home.js | 4 ++++ stock/page/stock_home/stock_home.js | 4 ++++ stock/report/purchase_in_transit/__init__.py | 0 .../purchase_in_transit.txt | 22 +++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 stock/report/purchase_in_transit/__init__.py create mode 100644 stock/report/purchase_in_transit/purchase_in_transit.txt diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js index 56328cc574..e7532dda2e 100644 --- a/buying/page/buying_home/buying_home.js +++ b/buying/page/buying_home/buying_home.js @@ -107,6 +107,10 @@ wn.module_page["Buying"] = [ "label":wn._("Item-wise Purchase History"), route: "query-report/Item-wise Purchase History", }, + { + "label":wn._("Purchase In Transit"), + route: "query-report/Purchase In Transit", + }, ] } ] diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index db77fced0e..bfcaf8acc1 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -197,6 +197,10 @@ wn.module_page["Stock"] = [ route: "query-report/Item-Wise Price List", doctype: "Item" }, + { + "label":wn._("Purchase In Transit"), + route: "query-report/Purchase In Transit", + }, ] } ] diff --git a/stock/report/purchase_in_transit/__init__.py b/stock/report/purchase_in_transit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/purchase_in_transit/purchase_in_transit.txt b/stock/report/purchase_in_transit/purchase_in_transit.txt new file mode 100644 index 0000000000..60ce0da460 --- /dev/null +++ b/stock/report/purchase_in_transit/purchase_in_transit.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-06 12:09:05", + "docstatus": 0, + "modified": "2013-05-06 12:22:52", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "SELECT\n pi.name as \"Purchase Invoice:Link/Purchase Invoice:120\",\n\tpi.posting_date as \"Posting Date:Date:100\",\n\tpi.credit_to as \"Supplier Account:Link/Account:120\",\n\tpi_item.item_code as \"Item Code:Link/Item:120\",\n\tpi_item.description as \"Description:Data:140\",\n\tpi_item.qty as \"Qty:Float:120\",\n\tpi_item.amount as \"Amount:Currency:120\",\n\tpi_item.purchase_order as \"Purchase Order:Link/Purchase Order:120\",\n\tpi_item.purchase_receipt as \"Purchase Receipt:Link/Purchase Receipt:120\",\n\tpr.posting_date as \"PR Posting Date:Date:130\",\n\tpi.company as \"Company:Link/Company:120\"\nFROM\n\t`tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item, `tabPurchase Receipt` pr\nWHERE\n\tpi.name = pi_item.parent and pi_item.purchase_receipt = pr.name\n\tand pi.docstatus = 1 and pr.posting_date > pi.posting_date\nORDER BY\n\tpi.name desc", + "ref_doctype": "Purchase Receipt", + "report_name": "Purchase In Transit", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Purchase In Transit" + } +] \ No newline at end of file From a55723bea28ea040101a8ebe3ff3420d916627b2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 May 2013 12:46:07 +0530 Subject: [PATCH 098/295] [report] sales partners commission --- accounts/page/accounts_home/accounts_home.js | 5 +++++ .../sales_partners_commission/__init__.py | 0 .../sales_partners_commission.txt | 22 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 accounts/report/sales_partners_commission/__init__.py create mode 100644 accounts/report/sales_partners_commission/sales_partners_commission.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index d2b0a0ea0d..c3d4cf18ce 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -217,6 +217,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Payment Made With Ageing", doctype: "Journal Voucher" }, + { + "label":wn._("Sales Partners Commission"), + route: "query-report/Sales Partners Commission", + doctype: "Sales Invoice" + }, ] } ] diff --git a/accounts/report/sales_partners_commission/__init__.py b/accounts/report/sales_partners_commission/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/sales_partners_commission/sales_partners_commission.txt b/accounts/report/sales_partners_commission/sales_partners_commission.txt new file mode 100644 index 0000000000..52bbf3c751 --- /dev/null +++ b/accounts/report/sales_partners_commission/sales_partners_commission.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-06 12:28:23", + "docstatus": 0, + "modified": "2013-05-06 12:41:15", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "SELECT\n sales_partner as \"Sales Partner:Link/Sales Partner:150\",\n\tsum(net_total) as \"Invoiced Amount (Exculsive Tax):Currency:210\",\n\tsum(total_commission) as \"Total Commission:Currency:150\",\n\tsum(total_commission)*100/sum(net_total) as \"Average Commission Rate:Currency:170\"\nFROM\n\t`tabSales Invoice`\nWHERE\n\tdocstatus = 1 and ifnull(net_total, 0) > 0 and ifnull(total_commission, 0) > 0\nGROUP BY\n\tsales_partner\nORDER BY\n\t\"Total Commission:Currency:120\"", + "ref_doctype": "Sales Invoice", + "report_name": "Sales Partners Commission", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Sales Partners Commission" + } +] \ No newline at end of file From 6d309f727f45eef02156bf63f5ec9e64289f6f8b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 May 2013 14:34:33 +0530 Subject: [PATCH 099/295] [report] maintenance schedules --- support/page/support_home/support_home.js | 12 ++++++++++ support/report/__init__.py | 0 .../report/maintenance_schedules/__init__.py | 0 .../maintenance_schedules.txt | 22 +++++++++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 support/report/__init__.py create mode 100644 support/report/maintenance_schedules/__init__.py create mode 100644 support/report/maintenance_schedules/maintenance_schedules.txt diff --git a/support/page/support_home/support_home.js b/support/page/support_home/support_home.js index d397daa6ce..bde5e5c00a 100644 --- a/support/page/support_home/support_home.js +++ b/support/page/support_home/support_home.js @@ -72,6 +72,18 @@ wn.module_page["Support"] = [ }, ] }, + { + title: wn._("Reports"), + right: true, + icon: "icon-list", + items: [ + { + "label":wn._("Maintenance Schedules"), + route: "query-report/Maintenance Schedules", + doctype: "Maintenance Schedule" + } + ] + } ] pscript['onload_support-home'] = function(wrapper) { diff --git a/support/report/__init__.py b/support/report/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/support/report/maintenance_schedules/__init__.py b/support/report/maintenance_schedules/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/support/report/maintenance_schedules/maintenance_schedules.txt b/support/report/maintenance_schedules/maintenance_schedules.txt new file mode 100644 index 0000000000..525f4834ba --- /dev/null +++ b/support/report/maintenance_schedules/maintenance_schedules.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-06 14:25:21", + "docstatus": 0, + "modified": "2013-05-06 14:32:47", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "SELECT\n ms_item.scheduled_date as \"Schedule Date:Date:120\",\n\tms_item.item_code as \"Item Code:Link/Item:120\",\n\tms_item.item_name as \"Item Name::120\",\n\tms_item.serial_no as \"Serial No::120\",\n\tms_item.incharge_name as \"Incharge::120\",\n\tms.customer_name as \"Customer:Link/Customer:120\",\n\tms.address_display as \"Customer Address::120\",\n\tms.sales_order_no as \"Sales Order:Link/Sales Order:120\",\n\tms.company as \"Company:Link/Company:120\"\n\t\nFROM\n\t`tabMaintenance Schedule` ms, `tabMaintenance Schedule Detail` ms_item\nWHERE\n\tms.name = ms_item.parent and ms.docstatus = 1\nORDER BY\n\tms_item.scheduled_date asc, ms_item.item_code asc", + "ref_doctype": "Maintenance Schedule", + "report_name": "Maintenance Schedules", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Maintenance Schedules" + } +] \ No newline at end of file From 15697ed93716c97b10699601ca8d7f571d7982be Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 May 2013 17:01:19 +0530 Subject: [PATCH 100/295] [fixes] hour_rate fetching in bom --- manufacturing/doctype/bom/bom.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manufacturing/doctype/bom/bom.js b/manufacturing/doctype/bom/bom.js index f0c15fa2a9..b1f43f7620 100644 --- a/manufacturing/doctype/bom/bom.js +++ b/manufacturing/doctype/bom/bom.js @@ -64,9 +64,9 @@ cur_frm.add_fetch("item", "stock_uom", "uom"); cur_frm.cscript.workstation = function(doc,dt,dn) { var d = locals[dt][dn]; - wn.model.with_doc("Workstation", d.workstation, function(i, v) { - d.hour_rate = v.hour_rate; - refresh_field("hour_rate"); + wn.model.with_doc("Workstation", d.workstation, function(i, r) { + d.hour_rate = r.docs[0].hour_rate; + refresh_field("hour_rate", dn, "bom_operations"); calculate_op_cost(doc); calculate_total(doc); }); From 35e7e8f58f9ff5e389f2058d92bd52d93b4f011b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 May 2013 18:37:57 +0530 Subject: [PATCH 101/295] [report] employee birthday --- hr/page/hr_home/hr_home.js | 4 ++ hr/report/employee_birthday/__init__.py | 0 .../employee_birthday/employee_birthday.js | 19 +++++++ .../employee_birthday/employee_birthday.py | 50 +++++++++++++++++++ .../employee_birthday/employee_birthday.txt | 21 ++++++++ 5 files changed, 94 insertions(+) create mode 100644 hr/report/employee_birthday/__init__.py create mode 100644 hr/report/employee_birthday/employee_birthday.js create mode 100644 hr/report/employee_birthday/employee_birthday.py create mode 100644 hr/report/employee_birthday/employee_birthday.txt diff --git a/hr/page/hr_home/hr_home.js b/hr/page/hr_home/hr_home.js index df3264501a..1e0e5bc28f 100644 --- a/hr/page/hr_home/hr_home.js +++ b/hr/page/hr_home/hr_home.js @@ -169,6 +169,10 @@ wn.module_page["HR"] = [ "label":wn._("Employee Leave Balance"), route: "query-report/Employee Leave Balance" }, + { + "label":wn._("Employee Birthday"), + route: "query-report/Employee Birthday" + }, ] } ]; diff --git a/hr/report/employee_birthday/__init__.py b/hr/report/employee_birthday/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hr/report/employee_birthday/employee_birthday.js b/hr/report/employee_birthday/employee_birthday.js new file mode 100644 index 0000000000..f2bc9cbe56 --- /dev/null +++ b/hr/report/employee_birthday/employee_birthday.js @@ -0,0 +1,19 @@ +wn.query_reports["Employee Birthday"] = { + "filters": [ + { + "fieldname":"month", + "label": "Month", + "fieldtype": "Select", + "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", + "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"][wn.datetime.str_to_obj(wn.datetime.get_today()).getMonth()], + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": wn.defaults.get_user_default("company") + } + ] +} \ No newline at end of file diff --git a/hr/report/employee_birthday/employee_birthday.py b/hr/report/employee_birthday/employee_birthday.py new file mode 100644 index 0000000000..7268055b72 --- /dev/null +++ b/hr/report/employee_birthday/employee_birthday.py @@ -0,0 +1,50 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + data = get_employees(filters) + + return columns, data + +def get_columns(): + return [ + "Employee:Link/Employee:120", "Date of Birth:Date:100", "Branch:Link/Branch:120", + "Department:Link/Department:120", "Designation:Link/Designation:120", "Gender::60", + "Company:Link/Company:120" + ] + +def get_employees(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select name, date_of_birth, branch, department, designation, + gender, company from tabEmployee where status = 'Active' %s""" % conditions, as_list=1) + +def get_conditions(filters): + conditions = "" + if filters.get("month"): + month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"].index(filters["month"]) + 1 + conditions += " and month(date_of_birth) = '%s'" % month + + if filters.get("company"): conditions += " and company = '%s'" % filters["company"] + + return conditions \ No newline at end of file diff --git a/hr/report/employee_birthday/employee_birthday.txt b/hr/report/employee_birthday/employee_birthday.txt new file mode 100644 index 0000000000..575ae73352 --- /dev/null +++ b/hr/report/employee_birthday/employee_birthday.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-05-06 17:56:03", + "docstatus": 0, + "modified": "2013-05-06 17:56:03", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Employee", + "report_name": "Employee Birthday", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Employee Birthday" + } +] \ No newline at end of file From 6754dda4a89a469c5f693e0ca5340cc1cd8c21ed Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 6 May 2013 18:50:29 +0530 Subject: [PATCH 102/295] [report] employee information --- hr/page/hr_home/hr_home.js | 4 ++++ hr/report/employee_information/__init__.py | 0 .../employee_information.txt | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 hr/report/employee_information/__init__.py create mode 100644 hr/report/employee_information/employee_information.txt diff --git a/hr/page/hr_home/hr_home.js b/hr/page/hr_home/hr_home.js index 1e0e5bc28f..b2cce73f15 100644 --- a/hr/page/hr_home/hr_home.js +++ b/hr/page/hr_home/hr_home.js @@ -173,6 +173,10 @@ wn.module_page["HR"] = [ "label":wn._("Employee Birthday"), route: "query-report/Employee Birthday" }, + { + "label":wn._("Employee Information"), + route: "Report2/Employee/Employee Information" + }, ] } ]; diff --git a/hr/report/employee_information/__init__.py b/hr/report/employee_information/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hr/report/employee_information/employee_information.txt b/hr/report/employee_information/employee_information.txt new file mode 100644 index 0000000000..b9d190b029 --- /dev/null +++ b/hr/report/employee_information/employee_information.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-06 18:43:53", + "docstatus": 0, + "modified": "2013-05-06 18:47:43", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "json": "{\"filters\":[],\"columns\":[[\"name\",\"Employee\"],[\"employee_number\",\"Employee\"],[\"date_of_joining\",\"Employee\"],[\"branch\",\"Employee\"],[\"department\",\"Employee\"],[\"designation\",\"Employee\"],[\"gender\",\"Employee\"],[\"status\",\"Employee\"],[\"company\",\"Employee\"],[\"employment_type\",\"Employee\"],[\"grade\",\"Employee\"],[\"reports_to\",\"Employee\"],[\"company_email\",\"Employee\"]],\"sort_by\":\"Employee.bank_ac_no\",\"sort_order\":\"desc\",\"sort_by_next\":\"\",\"sort_order_next\":\"desc\"}", + "name": "__common__", + "ref_doctype": "Employee", + "report_name": "Employee Information", + "report_type": "Report Builder" + }, + { + "doctype": "Report", + "name": "Employee Information" + } +] \ No newline at end of file From c1b961932186a327d58e2f9fcab23e0d62fbd315 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 7 May 2013 11:16:20 +0530 Subject: [PATCH 103/295] [fixes] company for auto-material request --- stock/doctype/bin/bin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stock/doctype/bin/bin.py b/stock/doctype/bin/bin.py index be343129bf..2d98c2634f 100644 --- a/stock/doctype/bin/bin.py +++ b/stock/doctype/bin/bin.py @@ -79,7 +79,7 @@ class DocType: if (flt(args.get("actual_qty")) < 0 or flt(args.get("reserved_qty")) > 0) \ and args.get("is_cancelled") == 'No' and args.get("is_amended")=='No': - self.reorder_item(args.get("voucher_type"), args.get("voucher_no")) + self.reorder_item(args.get("voucher_type"), args.get("voucher_no"), args.get("company")) def get_first_sle(self): sle = sql(""" @@ -92,7 +92,7 @@ class DocType: """, (self.doc.item_code, self.doc.warehouse), as_dict=1) return sle and sle[0] or None - def reorder_item(self,doc_type,doc_name): + def reorder_item(self,doc_type,doc_name, company): """ Reorder item if stock reaches reorder level""" if not hasattr(webnotes, "auto_indent"): webnotes.auto_indent = webnotes.conn.get_value('Global Defaults', None, 'auto_indent') @@ -111,10 +111,10 @@ class DocType: material_request_type = "Purchase" if flt(reorder_qty) and flt(self.doc.projected_qty) < flt(reorder_level): - self.create_material_request(doc_type, doc_name, reorder_level, reorder_qty, - material_request_type) + self.create_material_request(doc_type, doc_name, reorder_level, reorder_qty, + company, material_request_type) - def create_material_request(self, doc_type, doc_name, reorder_level, reorder_qty, + def create_material_request(self, doc_type, doc_name, reorder_level, reorder_qty, company, material_request_type="Purchase"): """ Create indent on reaching reorder level """ defaults = webnotes.defaults.get_defaults() @@ -122,7 +122,7 @@ class DocType: mr = webnotes.bean([{ "doctype": "Material Request", - "company": defaults.company, + "company": company or defaults.company, "fiscal_year": defaults.fiscal_year, "transaction_date": nowdate(), "material_request_type": material_request_type, From 620576080a41f16522ed00306589c9ab20d1cfe1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 7 May 2013 12:03:33 +0530 Subject: [PATCH 104/295] [fixes][currency symbol] fieldtype in report --- .../purchase_taxes_and_charges.txt | 23 ++++++++++++++----- .../item_wise_purchase_history.txt | 4 ++-- .../issued_items_against_production_order.txt | 4 ++-- .../item_wise_sales_history.txt | 4 ++-- .../sales_person_wise_transaction_summary.py | 4 ++-- .../item_wise_price_list.txt | 6 ++--- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt b/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt index 576730779a..619aed1954 100644 --- a/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt +++ b/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 06:51:12", + "creation": "2013-04-19 11:00:06", "docstatus": 0, - "modified": "2013-04-17 14:05:19", + "modified": "2013-05-07 11:23:56", "modified_by": "Administrator", "owner": "Administrator" }, @@ -35,6 +35,7 @@ "oldfieldname": "category", "oldfieldtype": "Select", "options": "Valuation and Total\nValuation\nTotal", + "read_only": 0, "reqd": 1 }, { @@ -45,6 +46,7 @@ "oldfieldname": "charge_type", "oldfieldtype": "Select", "options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total", + "read_only": 0, "reqd": 1 }, { @@ -55,6 +57,7 @@ "oldfieldname": "account_head", "oldfieldtype": "Link", "options": "Account", + "read_only": 0, "reqd": 1 }, { @@ -65,7 +68,8 @@ "label": "Cost Center", "oldfieldname": "cost_center", "oldfieldtype": "Link", - "options": "Cost Center" + "options": "Cost Center", + "read_only": 0 }, { "doctype": "DocField", @@ -75,17 +79,18 @@ "oldfieldname": "description", "oldfieldtype": "Small Text", "print_width": "300px", + "read_only": 0, "reqd": 1, "width": "300px" }, { "doctype": "DocField", "fieldname": "rate", - "fieldtype": "Currency", + "fieldtype": "Float", "label": "Rate", "oldfieldname": "rate", "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "read_only": 0, "reqd": 0 }, { @@ -96,6 +101,7 @@ "oldfieldname": "tax_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "read_only": 0, "reqd": 0 }, { @@ -115,7 +121,8 @@ "hidden": 0, "label": "Enter Row", "oldfieldname": "row_id", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "read_only": 0 }, { "default": "Add", @@ -126,6 +133,7 @@ "oldfieldname": "add_deduct_tax", "oldfieldtype": "Select", "options": "Add\nDeduct", + "read_only": 0, "reqd": 1 }, { @@ -149,6 +157,7 @@ "oldfieldname": "parenttype", "oldfieldtype": "Data", "print_hide": 1, + "read_only": 0, "search_index": 0 }, { @@ -163,6 +172,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, + "read_only": 0, "report_hide": 1 }, { @@ -177,6 +187,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, + "read_only": 0, "report_hide": 1 } ] \ No newline at end of file diff --git a/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt index 3f9e702f5f..5d36d9445b 100644 --- a/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt +++ b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-03 14:55:53", "docstatus": 0, - "modified": "2013-05-03 15:13:34", + "modified": "2013-05-07 11:20:09", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Currency:100\",\n\tpo_item.stock_uom as \"UOM:Link/UOM:80\",\n\tpo_item.purchase_rate as \"Rate:Currency:120\",\n\tpo_item.amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n\tpo_item.project_name as \"Project:Link/Project:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Currency:120\",\n\tifnull(po_item.billed_qty, 0) as \"Billed Qty:Currency:120\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item\nwhere\n\tpo.name = po_item.parent and po.docstatus = 1\norder by po.name desc", + "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Float:100\",\n\tpo_item.stock_uom as \"UOM:Link/UOM:80\",\n\tpo_item.purchase_rate as \"Rate:Currency:120\",\n\tpo_item.amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n\tpo_item.project_name as \"Project:Link/Project:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Float:120\",\n\tifnull(po_item.billed_qty, 0) as \"Billed Qty:Float:120\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item\nwhere\n\tpo.name = po_item.parent and po.docstatus = 1\norder by po.name desc", "ref_doctype": "Purchase Order", "report_name": "Item-wise Purchase History", "report_type": "Query Report" diff --git a/manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt b/manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt index e00123d592..a5a03bfa00 100644 --- a/manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt +++ b/manufacturing/report/issued_items_against_production_order/issued_items_against_production_order.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-03 17:48:46", "docstatus": 0, - "modified": "2013-05-03 18:24:05", + "modified": "2013-05-07 11:49:56", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Currency:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture/Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code", + "query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture/Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code", "ref_doctype": "Production Order", "report_name": "Issued Items Against Production Order", "report_type": "Query Report" diff --git a/selling/report/item_wise_sales_history/item_wise_sales_history.txt b/selling/report/item_wise_sales_history/item_wise_sales_history.txt index cf13bdc3ea..6fee050e0d 100644 --- a/selling/report/item_wise_sales_history/item_wise_sales_history.txt +++ b/selling/report/item_wise_sales_history/item_wise_sales_history.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-03 14:38:34", "docstatus": 0, - "modified": "2013-05-03 15:15:11", + "modified": "2013-05-07 11:19:40", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Currency:100\",\n\tso_item.stock_uom as \"UOM:Link/UOM:80\",\n\tso_item.basic_rate as \"Rate:Currency:120\",\n\tso_item.amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project_name as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Currency:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item\nwhere\n\tso.name = so_item.parent\n\tand so.docstatus = 1\norder by so.name desc", + "query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Float:100\",\n\tso_item.stock_uom as \"UOM:Link/UOM:80\",\n\tso_item.basic_rate as \"Rate:Currency:120\",\n\tso_item.amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project_name as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Float:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item\nwhere\n\tso.name = so_item.parent\n\tand so.docstatus = 1\norder by so.name desc", "ref_doctype": "Sales Order", "report_name": "Item-wise Sales History", "report_type": "Query Report" diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py index 612cb7425d..23e8819242 100644 --- a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -32,8 +32,8 @@ def get_columns(filters): return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140", "Customer:Link/Customer:140", "Territory:Link/Territory:100", "Posting Date:Date:100", - "Item Code:Link/Item:120", "Qty:Currency:100", "Amount:Currency:120", - "Sales Person:Link/Sales Person:140", "Contribution %:Currency:110", + "Item Code:Link/Item:120", "Qty:Float:100", "Amount:Currency:120", + "Sales Person:Link/Sales Person:140", "Contribution %:Float:110", "Contribution Amount:Currency:140"] def get_entries(filters): diff --git a/stock/report/item_wise_price_list/item_wise_price_list.txt b/stock/report/item_wise_price_list/item_wise_price_list.txt index 6c2afad897..824c603597 100644 --- a/stock/report/item_wise_price_list/item_wise_price_list.txt +++ b/stock/report/item_wise_price_list/item_wise_price_list.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-02 14:22:51", + "creation": "2013-02-22 18:01:55", "docstatus": 0, - "modified": "2013-02-22 15:53:01", + "modified": "2013-05-07 11:50:46", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,7 +10,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select\n item.name as \"ID:Link/Item:120\", \n item.item_name as \"Item Name::120\", \n item_price.price_list_name as \"Price List::80\",\n item_price.ref_currency as \"Currency::40\", \n item_price.ref_rate as \"Rate:Currency:80\",\n item.description as \"Description::160\",\n item.item_group as \"Item Group:Link/Item Group:100\",\n item.brand as \"Brand::100\"\nfrom `tabItem` item, `tabItem Price` item_price\nwhere\n item_price.parent = item.name", + "query": "select\n item.name as \"ID:Link/Item:120\", \n item.item_name as \"Item Name::120\", \n item_price.price_list_name as \"Price List::80\",\n item_price.ref_currency as \"Currency::40\", \n item_price.ref_rate as \"Rate:Float:80\",\n item.description as \"Description::160\",\n item.item_group as \"Item Group:Link/Item Group:100\",\n item.brand as \"Brand::100\"\nfrom `tabItem` item, `tabItem Price` item_price\nwhere\n item_price.parent = item.name", "ref_doctype": "Item", "report_name": "Item-Wise Price List", "report_type": "Query Report" From f2d4df975ee97fcbf480df6d1a4cfd6ca23a5553 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 7 May 2013 13:12:02 +0530 Subject: [PATCH 105/295] [fixes] floating point issue in buying rate --- controllers/selling_controller.py | 3 ++- stock/doctype/stock_entry/stock_entry.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index b22042d0fe..80af337031 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -59,7 +59,8 @@ class SellingController(StockController): buying_amount = get_buying_amount(item.item_code, item.warehouse, -1*item.qty, self.doc.doctype, self.doc.name, item.name, stock_ledger_entries, item_sales_bom) - item.buying_amount = buying_amount > 0 and buying_amount or 0 + + item.buying_amount = buying_amount >= 0.01 and buying_amount or 0 webnotes.conn.set_value(item.doctype, item.name, "buying_amount", item.buying_amount) diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index fa60072518..d08deefa2b 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -194,10 +194,10 @@ class DocType(StockController): total_valuation_amount = 0 for item in self.doclist.get({"parentfield": "mtn_details"}): if item.t_warehouse and not item.s_warehouse: - total_valuation_amount += flt(item.incoming_rate) * flt(item.transfer_qty) + total_valuation_amount += flt(item.incoming_rate, 2) * flt(item.transfer_qty) if item.s_warehouse and not item.t_warehouse: - total_valuation_amount -= flt(item.incoming_rate) * flt(item.transfer_qty) + total_valuation_amount -= flt(item.incoming_rate, 2) * flt(item.transfer_qty) return total_valuation_amount @@ -607,7 +607,7 @@ class DocType(StockController): 'voucher_no': self.doc.name, 'voucher_detail_no': d.name, 'actual_qty': qty, - 'incoming_rate': flt(d.incoming_rate) or 0, + 'incoming_rate': flt(d.incoming_rate, 2) or 0, 'stock_uom': d.stock_uom, 'company': self.doc.company, 'is_cancelled': (is_cancelled ==1) and 'Yes' or 'No', From 04f888fa52f13822d23442fd6203363168945ac7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 7 May 2013 16:03:10 +0530 Subject: [PATCH 106/295] [fixes] item_code non-mandatory in item if naming_series --- .../bank_reconciliation.py | 2 +- .../bom_replace_tool/bom_replace_tool.py | 6 +- stock/doctype/item/item.py | 4 +- stock/doctype/item/item.txt | 70 ++++--------------- 4 files changed, 17 insertions(+), 65 deletions(-) diff --git a/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 980af5844f..f19df48306 100644 --- a/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -66,6 +66,6 @@ class DocType: vouchers.append(d.voucher_id) if vouchers: - msgprint("Clearance Date updated in %s" % vouchers) + msgprint("Clearance Date updated in %s" % ", ".join(vouchers)) else: msgprint("Clearance Date not mentioned") \ No newline at end of file diff --git a/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py b/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py index 177adcda83..4c9c42da2e 100644 --- a/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py +++ b/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py @@ -46,11 +46,7 @@ class DocType: webnotes.conn.sql("""update `tabBOM Item` set bom_no=%s, rate=%s, amount=qty*%s where bom_no = %s and docstatus < 2""", (self.doc.new_bom, current_bom_unitcost, current_bom_unitcost, self.doc.current_bom)) - - def get_parent_boms(bom_no): - return [d[0] for d in webnotes.conn.sql("""select distinct parent from - `tabBOM Item` where ifnull(bom_no, '')=%s and docstatus < 2""", bom_no)] - + def get_parent_boms(self): return [d[0] for d in webnotes.conn.sql("""select distinct parent from `tabBOM Item` where ifnull(bom_no, '') = %s and docstatus < 2""", diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index 63275047ad..fde532c96c 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -31,7 +31,9 @@ class DocType(DocListController): if webnotes.conn.get_default("item_naming_by")=="Naming Series": from webnotes.model.doc import make_autoname self.doc.item_code = make_autoname(self.doc.naming_series+'.#####') - + elif not self.doc.item_code: + msgprint(_("Item Code is mandatory"), raise_exception=1) + self.doc.name = self.doc.item_code def validate(self): diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index 274719eec5..c799029d95 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-25 10:56:55", + "creation": "2013-05-03 10:45:46", "docstatus": 0, - "modified": "2013-05-02 15:10:53", + "modified": "2013-05-07 15:58:58", "modified_by": "Administrator", "owner": "Administrator" }, @@ -28,13 +28,14 @@ "permlevel": 0 }, { - "amend": 0, "doctype": "DocPerm", "name": "__common__", "parent": "Item", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, + "report": 1, "submit": 0 }, { @@ -55,7 +56,8 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Naming Series", - "options": "\nITEM" + "options": "\nITEM", + "read_only": 0 }, { "description": "Item will be saved by this name in the data base.", @@ -64,10 +66,11 @@ "fieldtype": "Data", "in_filter": 0, "label": "Item Code", + "no_copy": 1, "oldfieldname": "item_code", "oldfieldtype": "Data", "read_only": 0, - "reqd": 1, + "reqd": 0, "search_index": 0 }, { @@ -883,76 +886,27 @@ "label": "Website Description", "read_only": 0 }, - { - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1, - "report": 0, - "role": "Material Manager", - "write": 0 - }, - { - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Material Manager", - "write": 0 - }, - { - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 1, - "report": 0, - "role": "Material User", - "write": 0 - }, - { - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Material User", - "write": 0 - }, { "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, "role": "Material Master Manager", "write": 1 }, { + "amend": 0, "cancel": 0, "create": 0, "doctype": "DocPerm", - "permlevel": 1, - "report": 0, - "role": "Material Master Manager", + "role": "Material Manager", "write": 0 }, { - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "System Manager", - "write": 1 - }, - { + "amend": 0, "cancel": 0, "create": 0, "doctype": "DocPerm", - "permlevel": 1, - "report": 0, - "role": "System Manager", + "role": "Material User", "write": 0 } ] \ No newline at end of file From f7bdf8e05aa80ca00ba96e5af7cd3053d38ca16d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 7 May 2013 16:48:55 +0530 Subject: [PATCH 107/295] [fixes][jv] set clearance date as null on saving document --- accounts/doctype/journal_voucher/journal_voucher.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accounts/doctype/journal_voucher/journal_voucher.py b/accounts/doctype/journal_voucher/journal_voucher.py index 9b1ca7a105..f7d4035a58 100644 --- a/accounts/doctype/journal_voucher/journal_voucher.py +++ b/accounts/doctype/journal_voucher/journal_voucher.py @@ -43,6 +43,8 @@ class DocType(AccountsController): if not self.doc.is_opening: self.doc.is_opening='No' + self.doc.clearance_date = None + self.validate_debit_credit() self.validate_cheque_info() self.validate_entries_for_advance() From 75eeb010526c035115a5d897bb0ee9e570dad8b4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 8 May 2013 12:35:33 +0530 Subject: [PATCH 108/295] [attachments] [fix] on adding and removing attachments, update the select fields with options attach_files: --- stock/doctype/item/item.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/stock/doctype/item/item.js b/stock/doctype/item/item.js index a344ad3ce9..fa42129ce8 100644 --- a/stock/doctype/item/item.js +++ b/stock/doctype/item/item.js @@ -134,11 +134,7 @@ cur_frm.fields_dict.item_supplier_details.grid.get_field("supplier").get_query = erpnext.utils.supplier_query; cur_frm.cscript.on_remove_attachment = function(doc) { - // refresh image list before unsetting image - refresh_field("image"); if(!inList(cur_frm.fields_dict.image.df.options.split("\n"), doc.image)) { - // if the selected image is removed from attachment, unset it - cur_frm.set_value("image", ""); msgprint(wn._("Attachment removed. You may need to update: ") + wn.meta.get_docfield(doc.doctype, "description_html").label); } From 4f8a81ca97790e6ae780a75c8ff2a629f10dad48 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 8 May 2013 17:40:35 +0530 Subject: [PATCH 109/295] [item] [usability] change image view on change of image --- stock/doctype/item/item.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stock/doctype/item/item.js b/stock/doctype/item/item.js index fa42129ce8..acc78e77dc 100644 --- a/stock/doctype/item/item.js +++ b/stock/doctype/item/item.js @@ -153,3 +153,7 @@ cur_frm.cscript.copy_from_item_group = function(doc) { cur_frm.refresh(); }); } + +cur_frm.cscript.image = function() { + refresh_field("image_view"); +} \ No newline at end of file From bddd5d9b0c3a761121d41c49457e4b1d4402094f Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 9 May 2013 12:45:18 +0530 Subject: [PATCH 110/295] [rename] [fix] merge should be passed to on_rename method of controller for further processing --- accounts/doctype/account/account.py | 2 +- accounts/doctype/cost_center/cost_center.py | 2 +- buying/doctype/supplier/supplier.py | 4 +- selling/doctype/customer/customer.py | 4 +- selling/doctype/customer/test_customer.py | 64 +++++++++++++++++++++ setup/doctype/company/company.py | 2 +- stock/doctype/item/item.py | 2 +- stock/doctype/serial_no/serial_no.py | 2 +- 8 files changed, 73 insertions(+), 9 deletions(-) diff --git a/accounts/doctype/account/account.py b/accounts/doctype/account/account.py index eb65604e02..bdc26e46ce 100644 --- a/accounts/doctype/account/account.py +++ b/accounts/doctype/account/account.py @@ -187,7 +187,7 @@ class DocType: sql("""delete from `tabGL Entry` where account = %s and ifnull(is_cancelled, 'No') = 'Yes'""", self.doc.name) - def on_rename(self, new, old): + def on_rename(self, new, old, merge=False): company_abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") parts = new.split(" - ") diff --git a/accounts/doctype/cost_center/cost_center.py b/accounts/doctype/cost_center/cost_center.py index a7672452aa..bf0918879f 100644 --- a/accounts/doctype/cost_center/cost_center.py +++ b/accounts/doctype/cost_center/cost_center.py @@ -87,7 +87,7 @@ class DocType(DocTypeNestedSet): self.validate_mandatory() self.validate_budget_details() - def on_rename(self, new, old): + def on_rename(self, new, old, merge=False): company_abbr = webnotes.conn.get_value("Company", self.doc.company_name, "abbr") parts = new.split(" - ") diff --git a/buying/doctype/supplier/supplier.py b/buying/doctype/supplier/supplier.py index 0137504b30..d41b86c32c 100644 --- a/buying/doctype/supplier/supplier.py +++ b/buying/doctype/supplier/supplier.py @@ -168,7 +168,7 @@ class DocType(TransactionBase): self.delete_supplier_communication() self.delete_supplier_account() - def on_rename(self, new, old): + def on_rename(self, new, old, merge=False): #update supplier_name if not naming series if webnotes.defaults.get_global_default('supp_master_name') == 'Supplier Name': update_fields = [ @@ -186,7 +186,7 @@ class DocType(TransactionBase): for account in webnotes.conn.sql("""select name, account_name from tabAccount where master_name=%s and master_type='Supplier'""", old, as_dict=1): if account.account_name != new: - webnotes.rename_doc("Account", account.name, new) + webnotes.rename_doc("Account", account.name, new, merge=merge) #update master_name in doctype account webnotes.conn.sql("""update `tabAccount` set master_name = %s, diff --git a/selling/doctype/customer/customer.py b/selling/doctype/customer/customer.py index 7e16341a4a..6f54ef9641 100644 --- a/selling/doctype/customer/customer.py +++ b/selling/doctype/customer/customer.py @@ -216,7 +216,7 @@ class DocType(TransactionBase): if self.doc.lead_name: sql("update `tabLead` set status='Interested' where name=%s",self.doc.lead_name) - def on_rename(self, new, old): + def on_rename(self, new, old, merge=False): #update customer_name if not naming series if webnotes.defaults.get_global_default('cust_master_name') == 'Customer Name': update_fields = [ @@ -244,7 +244,7 @@ class DocType(TransactionBase): for account in webnotes.conn.sql("""select name, account_name from tabAccount where master_name=%s and master_type='Customer'""", old, as_dict=1): if account.account_name != new: - webnotes.rename_doc("Account", account.name, new) + webnotes.rename_doc("Account", account.name, new, merge=merge) #update master_name in doctype account webnotes.conn.sql("""update `tabAccount` set master_name = %s, diff --git a/selling/doctype/customer/test_customer.py b/selling/doctype/customer/test_customer.py index 551b03f0be..806585f1e1 100644 --- a/selling/doctype/customer/test_customer.py +++ b/selling/doctype/customer/test_customer.py @@ -1,4 +1,60 @@ +from __future__ import unicode_literals +import webnotes +import unittest + +class TestCustomer(unittest.TestCase): + def test_rename(self): + self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer 1"), + (("_Test Customer 1",),)) + + webnotes.rename_doc("Customer", "_Test Customer 1", "_Test Customer 1 Renamed") + + self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer 1 Renamed"), + (("_Test Customer 1 Renamed",),)) + self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer 1"), ()) + + def test_merge(self): + from webnotes.test_runner import make_test_records + make_test_records("Sales Invoice") + + # clear transactions for new name + webnotes.conn.sql("""delete from `tabSales Invoice` where customer='_Test Customer 1'""") + + # check if they exist + self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer"), + (("_Test Customer",),)) + self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer 1"), + (("_Test Customer 1",),)) + self.assertEqual(webnotes.conn.exists("Account", "_Test Customer - _TC"), + (("_Test Customer - _TC",),)) + self.assertEqual(webnotes.conn.exists("Account", "_Test Customer 1 - _TC"), + (("_Test Customer 1 - _TC",),)) + + # check if transactions exists + self.assertNotEquals(webnotes.conn.sql("""select count(*) from `tabSales Invoice` + where customer='_Test Customer'""", )[0][0], 0) + self.assertNotEquals(webnotes.conn.sql("""select count(*) from `tabSales Invoice` + where debit_to='_Test Customer - _TC'""", )[0][0], 0) + + webnotes.rename_doc("Customer", "_Test Customer", "_Test Customer 1", merge=True) + + # check that no transaction exists for old name + self.assertNotEquals(webnotes.conn.sql("""select count(*) from `tabSales Invoice` + where customer='_Test Customer 1'""", )[0][0], 0) + self.assertNotEquals(webnotes.conn.sql("""select count(*) from `tabSales Invoice` + where debit_to='_Test Customer 1 - _TC'""", )[0][0], 0) + + # check that transactions exist for new name + self.assertEquals(webnotes.conn.sql("""select count(*) from `tabSales Invoice` + where customer='_Test Customer'""", )[0][0], 0) + self.assertEquals(webnotes.conn.sql("""select count(*) from `tabSales Invoice` + where debit_to='_Test Customer - _TC'""", )[0][0], 0) + + # check that old name doesn't exist + self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer"), ()) + self.assertEqual(webnotes.conn.exists("Account", "_Test Customer - _TC"), ()) + test_records = [ [{ "doctype": "Customer", @@ -7,5 +63,13 @@ test_records = [ "customer_group": "_Test Customer Group", "territory": "_Test Territory", "company": "_Test Company" + }], + [{ + "doctype": "Customer", + "customer_name": "_Test Customer 1", + "customer_type": "Individual", + "customer_group": "_Test Customer Group", + "territory": "_Test Territory", + "company": "_Test Company" }] ] \ No newline at end of file diff --git a/setup/doctype/company/company.py b/setup/doctype/company/company.py index 78be5380b2..e383fb1bc8 100644 --- a/setup/doctype/company/company.py +++ b/setup/doctype/company/company.py @@ -287,7 +287,7 @@ class DocType: where doctype='Global Defaults' and field='default_company' and value=%s""", self.doc.name) - def on_rename(self,newdn,olddn): + def on_rename(self,newdn,olddn, merge=False): webnotes.conn.sql("""update `tabCompany` set company_name=%s where name=%s""", (newdn, olddn)) diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index fde532c96c..bc438a877a 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -272,7 +272,7 @@ class DocType(DocListController): from webnotes.webutils import clear_cache clear_cache(self.doc.page_name) - def on_rename(self,newdn,olddn): + def on_rename(self,newdn,olddn, merge=False): webnotes.conn.sql("update tabItem set item_code = %s where name = %s", (newdn, olddn)) if self.doc.page_name: from webnotes.webutils import clear_cache diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py index bbf55b35e3..e85a947899 100644 --- a/stock/doctype/serial_no/serial_no.py +++ b/stock/doctype/serial_no/serial_no.py @@ -117,7 +117,7 @@ class DocType(StockController): self.make_stock_ledger_entry(1) self.make_gl_entries() - def on_rename(self, new, old): + def on_rename(self, new, old, merge=False): """rename serial_no text fields""" for dt in webnotes.conn.sql("""select parent from tabDocField where fieldname='serial_no' and fieldtype='Text'"""): From 21b854b7cc46723ad44464b61250ea20e864fbff Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 9 May 2013 13:21:13 +0530 Subject: [PATCH 111/295] [rename] [fix] merge related fixes --- setup/doctype/company/company.py | 4 ++++ stock/doctype/serial_no/serial_no.py | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/setup/doctype/company/company.py b/setup/doctype/company/company.py index e383fb1bc8..9863d7d28b 100644 --- a/setup/doctype/company/company.py +++ b/setup/doctype/company/company.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import webnotes +from webnotes import _, msgprint from webnotes.utils import cstr from webnotes.model.doc import Document @@ -288,6 +289,9 @@ class DocType: and value=%s""", self.doc.name) def on_rename(self,newdn,olddn, merge=False): + if merge: + msgprint(_("Sorry. Companies cannot be merged"), raise_exception=True) + webnotes.conn.sql("""update `tabCompany` set company_name=%s where name=%s""", (newdn, olddn)) diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py index e85a947899..501b535c6e 100644 --- a/stock/doctype/serial_no/serial_no.py +++ b/stock/doctype/serial_no/serial_no.py @@ -19,7 +19,7 @@ import webnotes from webnotes.utils import cint, getdate, nowdate import datetime -from webnotes import msgprint +from webnotes import msgprint, _ from controllers.stock_controller import StockController @@ -119,6 +119,9 @@ class DocType(StockController): def on_rename(self, new, old, merge=False): """rename serial_no text fields""" + if merge: + msgprint(_("Sorry. Serial Nos. cannot be merged"), raise_exception=True) + for dt in webnotes.conn.sql("""select parent from tabDocField where fieldname='serial_no' and fieldtype='Text'"""): From 5dc5bf9972f9f32ab15e4d5ac79cb865d0180feb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 9 May 2013 14:15:24 +0530 Subject: [PATCH 112/295] [stock entry] total amount field added --- stock/doctype/stock_entry/stock_entry.py | 6 ++++- stock/doctype/stock_entry/stock_entry.txt | 28 +++++++++++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index d08deefa2b..bce0f620d4 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -57,6 +57,7 @@ class DocType(StockController): self.validate_return_reference_doc() self.validate_with_material_request() self.validate_fiscal_year() + self.set_total_amount() def on_submit(self): self.update_serial_no(1) @@ -174,6 +175,9 @@ class DocType(StockController): elif self.doc.purpose != "Material Transfer": self.doc.production_order = None + def set_total_amount(self): + self.doc.total_amount = sum([flt(item.amount) for item in self.doclist.get({"parentfield": "mtn_details"})]) + def make_gl_entries(self): if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")): return @@ -220,7 +224,7 @@ class DocType(StockController): if not flt(d.incoming_rate): d.incoming_rate = self.get_incoming_rate(args) - d.amount = flt(d.qty) * flt(d.incoming_rate) + d.amount = flt(d.transfer_qty) * flt(d.incoming_rate) def get_incoming_rate(self, args): incoming_rate = 0 diff --git a/stock/doctype/stock_entry/stock_entry.txt b/stock/doctype/stock_entry/stock_entry.txt index fef710b513..d88b0b76fd 100644 --- a/stock/doctype/stock_entry/stock_entry.txt +++ b/stock/doctype/stock_entry/stock_entry.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-28 15:56:40", + "creation": "2013-04-09 11:43:55", "docstatus": 0, - "modified": "2013-03-29 15:31:42", + "modified": "2013-05-09 13:31:00", "modified_by": "Administrator", "owner": "Administrator" }, @@ -518,6 +518,14 @@ "read_only": 0, "width": "50%" }, + { + "doctype": "DocField", + "fieldname": "total_amount", + "fieldtype": "Currency", + "label": "Total Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "project_name", @@ -558,6 +566,14 @@ "read_only": 0, "reqd": 1 }, + { + "doctype": "DocField", + "fieldname": "col5", + "fieldtype": "Column Break", + "print_width": "50%", + "read_only": 0, + "width": "50%" + }, { "allow_on_submit": 0, "doctype": "DocField", @@ -576,14 +592,6 @@ "reqd": 1, "search_index": 0 }, - { - "doctype": "DocField", - "fieldname": "col5", - "fieldtype": "Column Break", - "print_width": "50%", - "read_only": 0, - "width": "50%" - }, { "allow_on_submit": 0, "doctype": "DocField", From ef06a4b349613607359c730a5931a6baef03ee7b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 9 May 2013 15:13:47 +0530 Subject: [PATCH 113/295] [autoname] [cleanup] strip blank spaces and remove autoname where naming_series: can be set in doctype --- accounts/doctype/c_form/c_form.py | 4 ---- accounts/doctype/cost_center/cost_center.py | 2 +- .../journal_voucher/journal_voucher.py | 5 ----- .../doctype/sales_invoice/sales_invoice.py | 4 ---- .../quality_inspection/quality_inspection.py | 20 ++++-------------- .../quality_inspection/quality_inspection.txt | 6 +++--- hr/doctype/attendance/attendance.py | 4 ---- hr/doctype/employee/employee.py | 2 +- .../production_order/production_order.py | 3 --- .../installation_note/installation_note.py | 7 +------ .../installation_note/installation_note.txt | 18 +++++----------- utilities/doctype/address/address.py | 3 ++- utilities/doctype/contact/contact.py | 21 ++++++++++--------- 13 files changed, 28 insertions(+), 71 deletions(-) diff --git a/accounts/doctype/c_form/c_form.py b/accounts/doctype/c_form/c_form.py index 9f89ad5d07..25a8c3bfc1 100644 --- a/accounts/doctype/c_form/c_form.py +++ b/accounts/doctype/c_form/c_form.py @@ -17,16 +17,12 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt, getdate -from webnotes.model.doc import make_autoname from webnotes.model.bean import getlist class DocType: def __init__(self,d,dl): self.doc, self.doclist = d,dl - def autoname(self): - self.doc.name = make_autoname(self.doc.naming_series + '.#####') - def validate(self): """Validate invoice that c-form is applicable and no other c-form is received for that""" diff --git a/accounts/doctype/cost_center/cost_center.py b/accounts/doctype/cost_center/cost_center.py index bf0918879f..4e9b7fd9e7 100644 --- a/accounts/doctype/cost_center/cost_center.py +++ b/accounts/doctype/cost_center/cost_center.py @@ -29,7 +29,7 @@ class DocType(DocTypeNestedSet): def autoname(self): company_abbr = webnotes.conn.sql("select abbr from tabCompany where name=%s", self.doc.company_name)[0][0] - self.doc.name = self.doc.cost_center_name + ' - ' + company_abbr + self.doc.name = self.doc.cost_center_name.strip() + ' - ' + company_abbr def validate_mandatory(self): if not self.doc.group_or_ledger: diff --git a/accounts/doctype/journal_voucher/journal_voucher.py b/accounts/doctype/journal_voucher/journal_voucher.py index f7d4035a58..a5a4f10f34 100644 --- a/accounts/doctype/journal_voucher/journal_voucher.py +++ b/accounts/doctype/journal_voucher/journal_voucher.py @@ -34,11 +34,6 @@ class DocType(AccountsController): self.credit_days_global = -1 self.is_approving_authority = -1 - def autoname(self): - if not self.doc.naming_series: - webnotes.msgprint("""Naming Series is mandatory""", raise_exception=1) - self.doc.name = make_autoname(self.doc.naming_series+'.#####') - def validate(self): if not self.doc.is_opening: self.doc.is_opening='No' diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 87f73c19ab..b643007add 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -30,7 +30,6 @@ from webnotes import _, msgprint month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12} - from controllers.selling_controller import SellingController class DocType(SellingController): @@ -40,9 +39,6 @@ class DocType(SellingController): self.tname = 'Sales Invoice Item' self.fname = 'entries' - def autoname(self): - self.doc.name = make_autoname(self.doc.naming_series+ '.#####') - def validate(self): super(DocType, self).validate() self.fetch_missing_values() diff --git a/buying/doctype/quality_inspection/quality_inspection.py b/buying/doctype/quality_inspection/quality_inspection.py index 336aabee8d..48a9a7a6f5 100644 --- a/buying/doctype/quality_inspection/quality_inspection.py +++ b/buying/doctype/quality_inspection/quality_inspection.py @@ -17,28 +17,16 @@ from __future__ import unicode_literals import webnotes -from webnotes.model import db_exists -from webnotes.model.doc import addchild, make_autoname -from webnotes.model.bean import copy_doclist - -sql = webnotes.conn.sql - - +from webnotes.model.doc import addchild class DocType: def __init__(self, doc, doclist=[]): self.doc = doc self.doclist = doclist - # Autoname - # --------- - def autoname(self): - self.doc.name = make_autoname(self.doc.naming_series+'.#####') - - def get_item_specification_details(self): self.doclist = self.doc.clear_table(self.doclist, 'qa_specification_details') - specification = sql("select specification, value from `tabItem Quality Inspection Parameter` \ + specification = webnotes.conn.sql("select specification, value from `tabItem Quality Inspection Parameter` \ where parent = '%s' order by idx" % (self.doc.item_code)) for d in specification: child = addchild(self.doc, 'qa_specification_details', 'Quality Inspection Reading', self.doclist) @@ -48,13 +36,13 @@ class DocType: def on_submit(self): if self.doc.purchase_receipt_no: - sql("update `tabPurchase Receipt Item` t1, `tabPurchase Receipt` t2 set t1.qa_no = '%s', t2.modified = '%s' \ + webnotes.conn.sql("update `tabPurchase Receipt Item` t1, `tabPurchase Receipt` t2 set t1.qa_no = '%s', t2.modified = '%s' \ where t1.parent = '%s' and t1.item_code = '%s' and t1.parent = t2.name" \ % (self.doc.name, self.doc.modified, self.doc.purchase_receipt_no, self.doc.item_code)) def on_cancel(self): if self.doc.purchase_receipt_no: - sql("update `tabPurchase Receipt Item` t1, `tabPurchase Receipt` t2 set t1.qa_no = '', t2.modified = '%s' \ + webnotes.conn.sql("update `tabPurchase Receipt Item` t1, `tabPurchase Receipt` t2 set t1.qa_no = '', t2.modified = '%s' \ where t1.parent = '%s' and t1.item_code = '%s' and t1.parent = t2.name" \ % (self.doc.modified, self.doc.purchase_receipt_no, self.doc.item_code)) diff --git a/buying/doctype/quality_inspection/quality_inspection.txt b/buying/doctype/quality_inspection/quality_inspection.txt index e8650e0554..60ede7093e 100644 --- a/buying/doctype/quality_inspection/quality_inspection.txt +++ b/buying/doctype/quality_inspection/quality_inspection.txt @@ -1,13 +1,13 @@ [ { - "creation": "2013-01-10 16:34:11", + "creation": "2013-04-30 13:13:03", "docstatus": 0, - "modified": "2013-01-22 14:57:21", + "modified": "2013-05-09 14:34:10", "modified_by": "Administrator", "owner": "Administrator" }, { - "autoname": "QAI/.######", + "autoname": "naming_series:", "doctype": "DocType", "is_submittable": 1, "module": "Buying", diff --git a/hr/doctype/attendance/attendance.py b/hr/doctype/attendance/attendance.py index 67af429dd0..ac41acf815 100644 --- a/hr/doctype/attendance/attendance.py +++ b/hr/doctype/attendance/attendance.py @@ -18,7 +18,6 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import getdate, nowdate -from webnotes.model.doc import make_autoname from webnotes import msgprint, _ sql = webnotes.conn.sql @@ -28,9 +27,6 @@ class DocType: self.doc = doc self.doclist = doclist - def autoname(self): - self.doc.name = make_autoname(self.doc.naming_series+'.#####') - def get_emp_name(self): return { "employee_name": webnotes.conn.get_value("Employee", diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py index 87fe9a45e9..9a9ed136af 100644 --- a/hr/doctype/employee/employee.py +++ b/hr/doctype/employee/employee.py @@ -36,7 +36,7 @@ class DocType: if ret[0][0]=='Naming Series': self.doc.name = make_autoname(self.doc.naming_series + '.####') elif ret[0][0]=='Employee Number': - self.doc.name = make_autoname(self.doc.employee_number) + self.doc.name = self.doc.employee_number self.doc.employee = self.doc.name diff --git a/manufacturing/doctype/production_order/production_order.py b/manufacturing/doctype/production_order/production_order.py index a0498e063f..c76a87f6b8 100644 --- a/manufacturing/doctype/production_order/production_order.py +++ b/manufacturing/doctype/production_order/production_order.py @@ -18,9 +18,6 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cstr, flt, now, nowdate -from webnotes.model import db_exists -from webnotes.model.doc import make_autoname -from webnotes.model.bean import copy_doclist from webnotes.model.code import get_obj from webnotes import msgprint diff --git a/selling/doctype/installation_note/installation_note.py b/selling/doctype/installation_note/installation_note.py index b0e1d966d9..ea20d51d64 100644 --- a/selling/doctype/installation_note/installation_note.py +++ b/selling/doctype/installation_note/installation_note.py @@ -18,9 +18,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cstr, getdate -from webnotes.model import db_exists -from webnotes.model.doc import make_autoname -from webnotes.model.bean import getlist, copy_doclist +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 @@ -37,9 +35,6 @@ class DocType(TransactionBase): self.tname = 'Installation Note Item' self.fname = 'installed_item_details' - def autoname(self): - self.doc.name = make_autoname(self.doc.naming_series+'.#####') - def validate(self): self.validate_fiscal_year() self.validate_installation_date() diff --git a/selling/doctype/installation_note/installation_note.txt b/selling/doctype/installation_note/installation_note.txt index 52917e0b01..9dd851d583 100644 --- a/selling/doctype/installation_note/installation_note.txt +++ b/selling/doctype/installation_note/installation_note.txt @@ -1,13 +1,13 @@ [ { - "creation": "2013-01-10 16:34:18", + "creation": "2013-04-30 13:13:06", "docstatus": 0, - "modified": "2013-01-22 14:56:02", + "modified": "2013-05-09 14:43:28", "modified_by": "Administrator", "owner": "Administrator" }, { - "autoname": "IN/.####", + "autoname": "naming_series:", "doctype": "DocType", "is_submittable": 1, "module": "Selling", @@ -33,6 +33,7 @@ "permlevel": 0, "read": 1, "report": 1, + "role": "Sales User", "submit": 1, "write": 1 }, @@ -302,15 +303,6 @@ "options": "Installation Note Item" }, { - "doctype": "DocPerm", - "role": "System Manager" - }, - { - "doctype": "DocPerm", - "role": "Sales User" - }, - { - "doctype": "DocPerm", - "role": "Sales Manager" + "doctype": "DocPerm" } ] \ No newline at end of file diff --git a/utilities/doctype/address/address.py b/utilities/doctype/address/address.py index 243bbdd7e3..cfcbea582f 100644 --- a/utilities/doctype/address/address.py +++ b/utilities/doctype/address/address.py @@ -18,6 +18,7 @@ from __future__ import unicode_literals import webnotes from webnotes import msgprint +from webnotes.utils import cstr class DocType: def __init__(self, doc, doclist=[]): @@ -29,7 +30,7 @@ class DocType: self.doc.address_title = self.doc.customer or self.doc.supplier or self.doc.sales_partner if self.doc.address_title: - self.doc.name = self.doc.address_title + "-" + self.doc.address_type + self.doc.name = cstr(self.doc.address_title).strip() + "-" + cstr(self.doc.address_type).strip() else: webnotes.msgprint("""Address Title is mandatory.""", raise_exception=True) diff --git a/utilities/doctype/contact/contact.py b/utilities/doctype/contact/contact.py index bceee7d6a1..a19501f100 100644 --- a/utilities/doctype/contact/contact.py +++ b/utilities/doctype/contact/contact.py @@ -16,7 +16,7 @@ from __future__ import unicode_literals import webnotes - +from webnotes.utils import cstr from utilities.transaction_base import TransactionBase @@ -32,15 +32,16 @@ class DocType(TransactionBase): webnotes.conn.set(self.doc, 'status', 'Replied') def autoname(self): - if self.doc.customer: - self.doc.name = self.doc.first_name + (self.doc.last_name and ' ' + self.doc.last_name or '') + '-' + self.doc.customer - elif self.doc.supplier: - self.doc.name = self.doc.first_name + (self.doc.last_name and ' ' + self.doc.last_name or '') + '-' + self.doc.supplier - elif self.doc.sales_partner: - self.doc.name = self.doc.first_name + (self.doc.last_name and ' ' + self.doc.last_name or '') + '-' + self.doc.sales_partner - else: - self.doc.name = self.doc.first_name + (self.doc.last_name and ' ' + self.doc.last_name or '') - + # concat first and last name + self.doc.name = " ".join(filter(None, + [cstr(self.doc.fields.get(f)).strip() for f in ["first_name", "last_name"]])) + + # concat party name if reqd + for fieldname in ("customer", "supplier", "sales_partner"): + if self.doc.fields.get(fieldname): + self.doc.name = self.doc.name + "-" + cstr(self.doc.fields.get(fieldname)).strip() + break + def validate(self): self.validate_primary_contact() From e1b2ae573909b9abfae9ad0fc716822d6f88c66f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 9 May 2013 16:56:19 +0530 Subject: [PATCH 114/295] [fixes] float precision for pur invoice --- accounts/doctype/purchase_invoice/purchase_invoice.py | 8 +++++--- controllers/buying_controller.py | 10 +++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index 50d5d43d80..c53b6d94fc 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -464,15 +464,17 @@ class DocType(BuyingController): # if auto inventory accounting enabled and stock item, # then do stock related gl entries # expense will be booked in sales invoice - stock_item_and_auto_inventory_accounting = True + valuation_amt = (flt(item.amount, self.precision.item.amount) + + flt(item.item_tax_amount, self.precision.item.item_tax_amount) + + flt(item.rm_supp_cost, self.precision.item.rm_supp_cost)) + gl_entries.append( self.get_gl_dict({ "account": stock_account, "against": self.doc.credit_to, - "debit": flt(item.valuation_rate) * flt(item.conversion_factor) \ - * flt(item.qty), + "debit": valuation_amt, "remarks": self.doc.remarks or "Accounting Entry for Stock" }) ) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 9e181bc8fe..28d2db646b 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -367,9 +367,13 @@ class BuyingController(StockController): if d.item_code and d.qty: # if no item code, which is sometimes the case in purchase invoice, # then it is not possible to track valuation against it - d.valuation_rate = (flt(d.purchase_rate or d.rate) - + (flt(d.item_tax_amount) + flt(d.rm_supp_cost)) / flt(d.qty) - ) / flt(d.conversion_factor) + d.valuation_rate = flt((flt(d.purchase_rate, self.precision.item.purchase_rate) or + flt(d.rate, self.precision.item.rate) + + (flt(d.item_tax_amount, self.precision.item.item_tax_amount) + + flt(d.rm_supp_cost, self.precision.item.rm_supp_cost)) / + flt(d.qty, self.precision.item.qty)) / + flt(d.conversion_factor, self.precision.item.conversion_factor), + self.precision.item.valuation_rate) else: d.valuation_rate = 0.0 From 9e1d120186846d720c7e3359ed6567f2f5efdb72 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 9 May 2013 19:34:34 +0530 Subject: [PATCH 115/295] [time log] [query] search task based on subject --- projects/doctype/time_log/time_log.js | 12 ++++++++---- projects/utils.py | 17 ++++++++++++++++- public/js/queries.js | 6 +++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/projects/doctype/time_log/time_log.js b/projects/doctype/time_log/time_log.js index a6023320f1..22f9610b8f 100644 --- a/projects/doctype/time_log/time_log.js +++ b/projects/doctype/time_log/time_log.js @@ -1,5 +1,9 @@ -$.extend(cur_frm.cscript, { - refresh: function(doc) { - +wn.provide("erpnext.projects"); + +erpnext.projects.TimeLog = wn.ui.form.Controller.extend({ + setup: function() { + this.frm.set_query("task", erpnext.queries.task); } -}); \ No newline at end of file +}); + +cur_frm.cscript = new erpnext.projects.TimeLog({frm: cur_frm}); \ No newline at end of file diff --git a/projects/utils.py b/projects/utils.py index 7a45b08d9d..70f6995bf5 100644 --- a/projects/utils.py +++ b/projects/utils.py @@ -5,4 +5,19 @@ import webnotes @webnotes.whitelist() def get_time_log_list(doctype, txt, searchfield, start, page_len, filters): - return webnotes.conn.get_values("Time Log", filters, ["name", "activity_type", "owner"]) \ No newline at end of file + return webnotes.conn.get_values("Time Log", filters, ["name", "activity_type", "owner"]) + +@webnotes.whitelist() +def query_task(doctype, txt, searchfield, start, page_len, filters): + search_string = "%%%s%%" % txt + order_by_string = "%s%%" % txt + return webnotes.conn.sql("""select name, subject from `tabTask` + where `%s` like %s or `subject` like %s + order by + case when `subject` like %s then 0 else 1 end, + case when `%s` like %s then 0 else 1 end, + `%s`, + subject + limit %s, %s""" % + (searchfield, "%s", "%s", "%s", searchfield, "%s", searchfield, "%s", "%s"), + (search_string, search_string, order_by_string, order_by_string, start, page_len)) \ No newline at end of file diff --git a/public/js/queries.js b/public/js/queries.js index 9809cd980d..24ddc13590 100644 --- a/public/js/queries.js +++ b/public/js/queries.js @@ -160,4 +160,8 @@ erpnext.queries.bom = function(opts) { : "") + " LIMIT 50" -} \ No newline at end of file +} + +erpnext.queries.task = function() { + return { query: "projects.utils.query_task" }; +}; \ No newline at end of file From 2186e836703e9fbcbbdb922fa55f30e6198f81ea Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 10 May 2013 00:39:15 +0530 Subject: [PATCH 116/295] [lead to customer] [fix] fix for creating customer from lead when there are restrictive permissions based on company for creating customer --- selling/doctype/lead/lead.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/selling/doctype/lead/lead.js b/selling/doctype/lead/lead.js index d8d322d324..118b4c723e 100644 --- a/selling/doctype/lead/lead.js +++ b/selling/doctype/lead/lead.js @@ -104,7 +104,17 @@ cur_frm.cscript['Create Customer'] = function(){ 'from_to_list':"[['Lead', 'Customer']]" }, function(r,rt) { - loaddoc("Customer", n); + wn.model.with_doctype("Customer", function() { + var customer = wn.model.get_doc("Customer", n); + var customer_copy = $.extend({}, customer); + + var updated = wn.model.set_default_values(customer_copy); + $.each(updated, function(i, f) { + if(!customer[f]) customer[f] = customer_copy[f]; + }); + + loaddoc("Customer", n); + }); } ); } From e762b264b130431d582a0c8b13639400204ac3ef Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 May 2013 11:11:46 +0530 Subject: [PATCH 117/295] [fixes] bom autoname --- manufacturing/doctype/bom/bom.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manufacturing/doctype/bom/bom.py b/manufacturing/doctype/bom/bom.py index 5f42f4da5d..5a1d47fd4e 100644 --- a/manufacturing/doctype/bom/bom.py +++ b/manufacturing/doctype/bom/bom.py @@ -34,7 +34,8 @@ class DocType: last_name = sql("""select max(name) from `tabBOM` where name like "BOM/%s/%%" """ % cstr(self.doc.item).replace('"', '\\"')) if last_name: - idx = cint(cstr(last_name[0][0]).split('/')[-1]) + 1 + idx = cint(cstr(last_name[0][0]).split('/')[-1].split('-')[0]) + 1 + else: idx = 1 self.doc.name = 'BOM/' + self.doc.item + ('/%.3i' % idx) From 1252729e1cfcc41c5df3d4fc13741f7d804da9f0 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 10 May 2013 13:38:23 +0530 Subject: [PATCH 118/295] [sales invoice] [fix] bug fix in setting pos values on save --- accounts/doctype/sales_invoice/sales_invoice.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index b643007add..d18b967dfa 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -191,11 +191,11 @@ class DocType(SellingController): self.doc.fields[fieldname] = pos.get(fieldname) # set pos values in items - for doc in self.doclist.get({"parentfield": "entries"}): - if doc.fields.get('item_code'): - for fieldname, val in self.apply_pos_settings(doc.fields).items(): - if (not for_validate) or (for_validate and not self.doc.fields.get(fieldname)): - doc.fields[fieldname] = val + for item in self.doclist.get({"parentfield": "entries"}): + if item.fields.get('item_code'): + for fieldname, val in self.apply_pos_settings(item.fields).items(): + if (not for_validate) or (for_validate and not item.fields.get(fieldname)): + item.fields[fieldname] = val # fetch terms if self.doc.tc_name and not self.doc.terms: From 4e1dcfc561c800416488ecb86b0f5cb867e7e1ec Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 May 2013 15:17:20 +0530 Subject: [PATCH 119/295] [patch] repost stock for no posting date --- patches/may_2013/__init__.py | 0 .../repost_stock_for_no_posting_time.py | 34 +++++++++++++++++++ patches/patch_list.py | 1 + 3 files changed, 35 insertions(+) create mode 100644 patches/may_2013/__init__.py create mode 100644 patches/may_2013/repost_stock_for_no_posting_time.py diff --git a/patches/may_2013/__init__.py b/patches/may_2013/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/patches/may_2013/repost_stock_for_no_posting_time.py b/patches/may_2013/repost_stock_for_no_posting_time.py new file mode 100644 index 0000000000..04ceae5eca --- /dev/null +++ b/patches/may_2013/repost_stock_for_no_posting_time.py @@ -0,0 +1,34 @@ +# 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 + from stock.stock_ledger import update_entries_after + + res = webnotes.conn.sql("""select distinct item_code, warehouse from `tabStock Ledger Entry` + where posting_time = '00:00'""") + + i=0 + for d in res: + try: + update_entries_after({ "item_code": d[0], "warehouse": d[1] }) + except: + pass + i += 1 + if i%100 == 0: + webnotes.conn.sql("commit") + webnotes.conn.sql("start transaction") \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index ed1c8462cd..5475d0f8bc 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -250,4 +250,5 @@ patch_list = [ "patches.april_2013.p07_update_file_data_2", "patches.april_2013.rebuild_sales_browser", "patches.april_2013.p08_price_list_country", + "patches.may_2013.repost_stock_for_no_posting_time", ] \ No newline at end of file From 765383b3cf3e392de8eea817527d2c626bb53da8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 May 2013 18:41:58 +0530 Subject: [PATCH 120/295] [report] monthly salary register --- .../report/sales_register/sales_register.py | 2 +- hr/page/hr_home/hr_home.js | 4 + hr/report/monthly_salary_register/__init__.py | 0 .../monthly_salary_register.js | 32 +++++ .../monthly_salary_register.py | 119 ++++++++++++++++++ .../monthly_salary_register.txt | 22 ++++ 6 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 hr/report/monthly_salary_register/__init__.py create mode 100644 hr/report/monthly_salary_register/monthly_salary_register.js create mode 100644 hr/report/monthly_salary_register/monthly_salary_register.py create mode 100644 hr/report/monthly_salary_register/monthly_salary_register.txt diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index b15097457d..3946f0033d 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -36,7 +36,7 @@ def execute(filters=None): # invoice details sales_order = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("sales_order", [])) delivery_note = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("delivery_note", [])) - # webnotes.errprint(customer_map.get(inv.customer, [])) + row = [inv.name, inv.posting_date, inv.customer, inv.debit_to, account_map.get(inv.debit_to), customer_map.get(inv.customer), inv.project_name, inv.remarks, sales_order, delivery_note] diff --git a/hr/page/hr_home/hr_home.js b/hr/page/hr_home/hr_home.js index b2cce73f15..8a501846b1 100644 --- a/hr/page/hr_home/hr_home.js +++ b/hr/page/hr_home/hr_home.js @@ -177,6 +177,10 @@ wn.module_page["HR"] = [ "label":wn._("Employee Information"), route: "Report2/Employee/Employee Information" }, + { + "label":wn._("Monthly Salary Register"), + route: "query-report/Monthly Salary Register" + }, ] } ]; diff --git a/hr/report/monthly_salary_register/__init__.py b/hr/report/monthly_salary_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hr/report/monthly_salary_register/monthly_salary_register.js b/hr/report/monthly_salary_register/monthly_salary_register.js new file mode 100644 index 0000000000..da881378ff --- /dev/null +++ b/hr/report/monthly_salary_register/monthly_salary_register.js @@ -0,0 +1,32 @@ +wn.query_reports["Monthly Salary Register"] = { + "filters": [ + { + "fieldname":"month", + "label": "Month", + "fieldtype": "Select", + "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", + "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"][wn.datetime.str_to_obj(wn.datetime.get_today()).getMonth()], + }, + { + "fieldname":"fiscal_year", + "label": "Fiscal Year", + "fieldtype": "Link", + "options": "Fiscal Year", + "default": sys_defaults.fiscal_year, + }, + { + "fieldname":"employee", + "label": "Employee", + "fieldtype": "Link", + "options": "Employee" + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + } + ] +} \ No newline at end of file diff --git a/hr/report/monthly_salary_register/monthly_salary_register.py b/hr/report/monthly_salary_register/monthly_salary_register.py new file mode 100644 index 0000000000..cc25dc3e32 --- /dev/null +++ b/hr/report/monthly_salary_register/monthly_salary_register.py @@ -0,0 +1,119 @@ +# 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, _ + +def execute(filters=None): + if not filters: filters = {} + + salary_slips = get_salary_slips(filters) + columns, earning_types, ded_types = get_columns(salary_slips) + ss_earning_map = get_ss_earning_map(salary_slips) + ss_ded_map = get_ss_ded_map(salary_slips) + + data = [] + for ss in salary_slips: + row = [ss.employee, ss.employee_name, ss.branch, ss.department, ss.designation, + ss.company, ss.month, ss.leave_withut_pay, ss.payment_days] + + for e in earning_types: + row.append(ss_earning_map.get(ss.name, {}).get(e)) + + row += [ss.arrear_amount, ss.leave_encashment_amount, ss.gross_pay] + + for d in ded_types: + row.append(ss_ded_map.get(ss.name, {}).get(d)) + + row += [ss.total_deduction, ss.net_pay] + + data.append(row) + + return columns, data + +def get_columns(salary_slips): + columns = [ + "Employee:Link/Employee:120", "Employee Name::140", "Branch:Link/Branch:120", + "Department:Link/Department:120", "Designation:Link/Designation:120", + "Company:Link/Company:120", "Month::80", "Leave Without pay:Float:130", + "Payment Days:Float:120" + ] + + earning_types = webnotes.conn.sql_list("""select distinct e_type from `tabSalary Slip Earning` + where ifnull(e_modified_amount, 0) != 0 and parent in (%s)""" % + (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips])) + + ded_types = webnotes.conn.sql_list("""select distinct d_type from `tabSalary Slip Deduction` + where ifnull(d_modified_amount, 0) != 0 and parent in (%s)""" % + (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips])) + + columns = columns + [(e + ":Link/Earning Type:120") for e in earning_types] + \ + ["Arrear Amount:Currency:120", "Leave Encashment Amount:Currency:150", + "Gross Pay:Currency:120"] + [(d + ":Link/Deduction Type:120") for d in ded_types] + \ + ["Total Deduction:Currency:120", "Net Pay:Currency:120"] + + return columns, earning_types, ded_types + +def get_salary_slips(filters): + conditions, filters = get_conditions(filters) + salary_slips = webnotes.conn.sql("""select * from `tabSalary Slip` where docstatus = 1 %s""" % + conditions, filters, as_dict=1) + + if not salary_slips: + msgprint(_("No salary slip found for month: ") + cstr(filters.get("month")) + + _(" and year: ") + cstr(filters.get("fiscal_year")), raise_exception=1) + + return salary_slips + +def get_conditions(filters): + conditions = "" + if filters.get("month"): + month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"].index(filters["month"]) + 1 + filters["month"] = month + conditions += " and month = %(month)s" + + if filters.get("fiscal_year"): conditions += " and fiscal_year = %(fiscal_year)s" + if filters.get("company"): conditions += " and company = %(company)s" + if filters.get("employee"): conditions += " and employee = %(employee)s" + + return conditions, filters + +def get_ss_earning_map(salary_slips): + ss_earnings = webnotes.conn.sql("""select parent, e_type, e_modified_amount + from `tabSalary Slip Earning` where parent in (%s)""" % + (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1) + + ss_earning_map = {} + for d in ss_earnings: + ss_earning_map.setdefault(d.parent, webnotes._dict()).setdefault(d.e_type, []) + ss_earning_map[d.parent][d.e_type] = flt(d.e_modified_amount) + + return ss_earning_map + +def get_ss_ded_map(salary_slips): + ss_deductions = webnotes.conn.sql("""select parent, d_type, d_modified_amount + from `tabSalary Slip Deduction` where parent in (%s)""" % + (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1) + + ss_ded_map = {} + for d in ss_deductions: + ss_ded_map.setdefault(d.parent, webnotes._dict()).setdefault(d.d_type, []) + ss_ded_map[d.parent][d.e_type] = flt(d.d_modified_amount) + + return ss_ded_map \ No newline at end of file diff --git a/hr/report/monthly_salary_register/monthly_salary_register.txt b/hr/report/monthly_salary_register/monthly_salary_register.txt new file mode 100644 index 0000000000..b0d5880eaf --- /dev/null +++ b/hr/report/monthly_salary_register/monthly_salary_register.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-07 18:09:42", + "docstatus": 0, + "modified": "2013-05-07 18:09:42", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Salary Slip", + "report_name": "Monthly Salary Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Monthly Salary Register" + } +] \ No newline at end of file From ebf3199dbe58879895caa471782ce4826cef1d24 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 May 2013 11:15:53 +0530 Subject: [PATCH 121/295] [fixes] Write off cost center linked to cost center in pur invoice --- .../purchase_invoice/purchase_invoice.txt | 227 ++++++++++-------- 1 file changed, 126 insertions(+), 101 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.txt b/accounts/doctype/purchase_invoice/purchase_invoice.txt index 43d2c790fc..283c612371 100755 --- a/accounts/doctype/purchase_invoice/purchase_invoice.txt +++ b/accounts/doctype/purchase_invoice/purchase_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-09 10:18:10", + "creation": "2013-05-07 13:50:30", "docstatus": 0, - "modified": "2013-03-22 18:17:14", + "modified": "2013-05-13 11:12:56", "modified_by": "Administrator", "owner": "Administrator" }, @@ -30,7 +30,9 @@ "parent": "Purchase Invoice", "parentfield": "permissions", "parenttype": "DocType", - "read": 1 + "permlevel": 0, + "read": 1, + "report": 1 }, { "doctype": "DocType", @@ -41,6 +43,7 @@ "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -54,6 +57,7 @@ "oldfieldtype": "Select", "options": "BILL\nBILLJ", "print_hide": 1, + "read_only": 0, "report_hide": 0, "reqd": 1 }, @@ -68,6 +72,7 @@ "oldfieldtype": "Link", "options": "Account", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -80,7 +85,8 @@ "oldfieldname": "supplier", "oldfieldtype": "Link", "options": "Supplier", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -131,6 +137,7 @@ "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "read_only": 0, "reqd": 0, "width": "50%" }, @@ -146,6 +153,7 @@ "oldfieldname": "posting_date", "oldfieldtype": "Date", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -159,6 +167,7 @@ "oldfieldname": "bill_no", "oldfieldtype": "Data", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -171,6 +180,7 @@ "oldfieldname": "bill_date", "oldfieldtype": "Date", "print_hide": 1, + "read_only": 0, "reqd": 0, "search_index": 1 }, @@ -179,7 +189,8 @@ "fieldname": "items", "fieldtype": "Section Break", "label": "Items", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "allow_on_submit": 1, @@ -189,25 +200,29 @@ "label": "Entries", "oldfieldname": "entries", "oldfieldtype": "Table", - "options": "Purchase Invoice Item" + "options": "Purchase Invoice Item", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "section_break0", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "recalculate", "fieldtype": "Button", "label": "Recalculate", - "oldfieldtype": "Button" + "oldfieldtype": "Button", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "section_break_17", "fieldtype": "Section Break", - "options": "Simple" + "options": "Simple", + "read_only": 0 }, { "description": "Select Items from Purchase Order", @@ -218,7 +233,8 @@ "oldfieldname": "purchase_order_main", "oldfieldtype": "Link", "options": "Purchase Order", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "description": "Select Items from Purchase Receipt", @@ -229,7 +245,8 @@ "oldfieldname": "purchase_receipt_main", "oldfieldtype": "Link", "options": "Purchase Receipt", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -237,13 +254,15 @@ "fieldtype": "Button", "label": "Get Items", "oldfieldtype": "Button", - "options": "pull_details" + "options": "pull_details", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "currency_price_list", "fieldtype": "Section Break", - "label": "Currency & Price List" + "label": "Currency & Price List", + "read_only": 0 }, { "doctype": "DocField", @@ -253,7 +272,8 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "default": "1", @@ -264,12 +284,14 @@ "label": "Exchange Rate", "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "read_only": 0 }, { "description": "Consider this Price List for fetching rate. (only which have \"For Buying\" as checked)", @@ -278,7 +300,8 @@ "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "price_list_name", @@ -287,7 +310,8 @@ "fieldtype": "Link", "label": "Price List Currency", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "price_list_name", @@ -295,7 +319,8 @@ "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "description": "Add / Edit Taxes and Charges", @@ -303,7 +328,8 @@ "fieldname": "taxes", "fieldtype": "Section Break", "label": "Taxes", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", @@ -313,7 +339,8 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Master", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -322,7 +349,8 @@ "label": "Get Tax Detail", "oldfieldtype": "Button", "options": "get_purchase_tax_details", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -331,7 +359,8 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "read_only": 0 }, { "doctype": "DocField", @@ -339,7 +368,8 @@ "fieldtype": "Button", "label": "Calculate Tax", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -347,7 +377,8 @@ "fieldtype": "HTML", "label": "Tax Calculation", "oldfieldtype": "HTML", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -364,7 +395,8 @@ "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", - "label": "Contact Info" + "label": "Contact Info", + "read_only": 0 }, { "depends_on": "eval:doc.supplier", @@ -372,12 +404,14 @@ "fieldname": "supplier_address", "fieldtype": "Link", "label": "Supplier Address", - "options": "Address" + "options": "Address", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "col_break23", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -387,14 +421,16 @@ "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "totals", "fieldtype": "Section Break", "label": "Totals", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", @@ -497,6 +533,7 @@ "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "read_only": 0, "width": "50%" }, { @@ -562,7 +599,8 @@ "label": "Write Off Amount", "no_copy": 1, "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -572,7 +610,8 @@ "label": "Write Off Account", "no_copy": 1, "options": "Account", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -581,8 +620,9 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "no_copy": 1, - "options": "Account", - "print_hide": 1 + "options": "Cost Center", + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -594,6 +634,7 @@ "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", "print_hide": 1, + "read_only": 0, "report_hide": 0 }, { @@ -602,7 +643,8 @@ "fieldtype": "Section Break", "label": "Advances", "oldfieldtype": "Section Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -611,7 +653,8 @@ "label": "Get Advances Paid", "oldfieldtype": "Button", "options": "get_advances", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -622,7 +665,8 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -630,7 +674,8 @@ "fieldtype": "Section Break", "label": "More Info", "oldfieldtype": "Section Break", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "default": "No", @@ -644,6 +689,7 @@ "oldfieldtype": "Select", "options": "No\nYes", "print_hide": 1, + "read_only": 0, "search_index": 1 }, { @@ -655,6 +701,7 @@ "oldfieldname": "aging_date", "oldfieldtype": "Date", "print_hide": 1, + "read_only": 0, "search_index": 0 }, { @@ -680,6 +727,7 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, + "read_only": 0, "report_hide": 1 }, { @@ -692,6 +740,7 @@ "oldfieldname": "due_date", "oldfieldtype": "Date", "print_hide": 0, + "read_only": 0, "search_index": 1 }, { @@ -701,12 +750,14 @@ "label": "Mode of Payment", "oldfieldname": "mode_of_payment", "oldfieldtype": "Select", - "options": "link:Mode of Payment" + "options": "link:Mode of Payment", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break_63", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "read_only": 0 }, { "doctype": "DocField", @@ -718,6 +769,7 @@ "oldfieldtype": "Link", "options": "Company", "print_hide": 1, + "read_only": 0, "search_index": 1 }, { @@ -730,6 +782,7 @@ "oldfieldtype": "Select", "options": "link:Fiscal Year", "print_hide": 1, + "read_only": 0, "search_index": 1 }, { @@ -753,81 +806,53 @@ "oldfieldname": "remarks", "oldfieldtype": "Text", "print_hide": 1, + "read_only": 0, "reqd": 0 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Accounts Manager", - "submit": 0, - "write": 0 - }, { "amend": 1, "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Accounts Manager", - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Accounts User", - "submit": 0, - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "role": "Purchase User", - "submit": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "doctype": "DocPerm", - "match": "", - "permlevel": 0, - "report": 1, - "role": "Purchase User", - "submit": 0, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, "role": "Accounts User", "submit": 1, "write": 1 }, { + "amend": 0, + "cancel": 0, + "create": 1, + "doctype": "DocPerm", + "role": "Purchase User", + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, "doctype": "DocPerm", "match": "supplier", - "permlevel": 0, - "report": 1, - "role": "Supplier" + "role": "Supplier", + "submit": 0, + "write": 0 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "doctype": "DocPerm", + "role": "Accounts Manager", + "submit": 1, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "role": "Auditor", + "submit": 0, + "write": 0 } ] \ No newline at end of file From 0d504491dd9561dd68fe69b9b12d9bc3f001ef8b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 13 May 2013 15:15:20 +0530 Subject: [PATCH 122/295] [task] [query] added match conditions to task query --- projects/utils.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/projects/utils.py b/projects/utils.py index 70f6995bf5..e37a21c2c4 100644 --- a/projects/utils.py +++ b/projects/utils.py @@ -9,15 +9,21 @@ def get_time_log_list(doctype, txt, searchfield, start, page_len, filters): @webnotes.whitelist() def query_task(doctype, txt, searchfield, start, page_len, filters): + from webnotes.widgets.reportview import build_match_conditions + search_string = "%%%s%%" % txt order_by_string = "%s%%" % txt + match_conditions = build_match_conditions("Task") + match_conditions = ("and" + match_conditions) if match_conditions else "" + return webnotes.conn.sql("""select name, subject from `tabTask` - where `%s` like %s or `subject` like %s + where (`%s` like %s or `subject` like %s) %s order by case when `subject` like %s then 0 else 1 end, case when `%s` like %s then 0 else 1 end, `%s`, subject limit %s, %s""" % - (searchfield, "%s", "%s", "%s", searchfield, "%s", searchfield, "%s", "%s"), + (searchfield, "%s", "%s", match_conditions, "%s", + searchfield, "%s", searchfield, "%s", "%s"), (search_string, search_string, order_by_string, order_by_string, start, page_len)) \ No newline at end of file From c518e8be12227088fef6cb9c4dd95d419371ce89 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 May 2013 15:59:50 +0530 Subject: [PATCH 123/295] [report] monthly attendance sheet --- hr/page/hr_home/hr_home.js | 4 + .../monthly_attendance_sheet/__init__.py | 0 .../monthly_attendance_sheet.js | 32 ++++++ .../monthly_attendance_sheet.py | 107 ++++++++++++++++++ .../monthly_attendance_sheet.txt | 22 ++++ 5 files changed, 165 insertions(+) create mode 100644 hr/report/monthly_attendance_sheet/__init__.py create mode 100644 hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js create mode 100644 hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py create mode 100644 hr/report/monthly_attendance_sheet/monthly_attendance_sheet.txt diff --git a/hr/page/hr_home/hr_home.js b/hr/page/hr_home/hr_home.js index 8a501846b1..e35a80849b 100644 --- a/hr/page/hr_home/hr_home.js +++ b/hr/page/hr_home/hr_home.js @@ -181,6 +181,10 @@ wn.module_page["HR"] = [ "label":wn._("Monthly Salary Register"), route: "query-report/Monthly Salary Register" }, + { + "label":wn._("Monthly Attendance Sheet"), + route: "query-report/Monthly Attendance Sheet" + }, ] } ]; diff --git a/hr/report/monthly_attendance_sheet/__init__.py b/hr/report/monthly_attendance_sheet/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js new file mode 100644 index 0000000000..6dc8d78ea5 --- /dev/null +++ b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js @@ -0,0 +1,32 @@ +wn.query_reports["Monthly Attendance Sheet"] = { + "filters": [ + { + "fieldname":"month", + "label": "Month", + "fieldtype": "Select", + "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", + "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"][wn.datetime.str_to_obj(wn.datetime.get_today()).getMonth()], + }, + { + "fieldname":"fiscal_year", + "label": "Fiscal Year", + "fieldtype": "Link", + "options": "Fiscal Year", + "default": sys_defaults.fiscal_year, + }, + { + "fieldname":"employee", + "label": "Employee", + "fieldtype": "Link", + "options": "Employee" + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + } + ] +} \ No newline at end of file diff --git a/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py new file mode 100644 index 0000000000..520f6e93e6 --- /dev/null +++ b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -0,0 +1,107 @@ +# 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 cstr, cint +from webnotes import msgprint, _ + +def execute(filters=None): + if not filters: filters = {} + + conditions, filters = get_conditions(filters) + columns = get_columns(filters) + att_map = get_attendance_list(conditions, filters) + emp_map = get_employee_details() + + data = [] + for emp in sorted(att_map): + emp_det = emp_map.get(emp) + row = [emp, emp_det.employee_name, emp_det.branch, emp_det.department, emp_det.designation, + emp_det.company] + + total_p = total_a = 0.0 + for day in range(filters["total_days_in_month"]): + status = att_map.get(emp).get(day + 1, "Absent") + status_map = {"Present": "P", "Absent": "A", "Half Day": "HD"} + row.append(status_map[status]) + + if status == "Present": + total_p += 1 + elif status == "Absent": + total_a += 1 + elif status == "Half Day": + total_p += 0.5 + total_a += 0.5 + + row += [total_p, total_a] + + data.append(row) + + return columns, data + +def get_columns(filters): + columns = [ + "Employee:Link/Employee:120", "Employee Name::140", "Branch:Link/Branch:120", + "Department:Link/Department:120", "Designation:Link/Designation:120", + "Company:Link/Company:120" + ] + + for day in range(filters["total_days_in_month"]): + columns.append(cstr(day+1) +"::20") + + columns += ["Total Present:Float:80", "Total Absent:Float:80"] + return columns + +def get_attendance_list(conditions, filters): + attendance_list = webnotes.conn.sql("""select employee, day(att_date) as day_of_month, + status from tabAttendance where docstatus < 2 %s order by employee, att_date""" % + conditions, filters, as_dict=1) + + att_map = {} + for d in attendance_list: + att_map.setdefault(d.employee, webnotes._dict()).setdefault(d.day_of_month, "") + att_map[d.employee][d.day_of_month] = d.status + + return att_map + +def get_conditions(filters): + if not (filters.get("month") and filters.get("fiscal_year")): + msgprint(_("Please select month and year"), raise_exception=1) + + filters["month"] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"].index(filters["month"]) + 1 + + from calendar import monthrange + filters["total_days_in_month"] = monthrange(cint(filters["fiscal_year"].split("-")[-1]), + filters["month"])[1] + + conditions = " and month(att_date) = %(month)s and fiscal_year = %(fiscal_year)s" + + if filters.get("company"): conditions += " and company = %(company)s" + if filters.get("employee"): conditions += " and employee = %(employee)s" + + return conditions, filters + +def get_employee_details(): + employee = webnotes.conn.sql("""select name, employee_name, designation, department, + branch, company from tabEmployee where docstatus < 2 and status = 'Active'""", as_dict=1) + + emp_map = {} + for emp in employee: + emp_map[emp.name] = emp + + return emp_map \ No newline at end of file diff --git a/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.txt b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.txt new file mode 100644 index 0000000000..3c53aae8e4 --- /dev/null +++ b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-13 14:04:03", + "docstatus": 0, + "modified": "2013-05-13 14:32:42", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 0, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Attendance", + "report_name": "Monthly Attendance Sheet", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Monthly Attendance Sheet" + } +] \ No newline at end of file From da369d8eee25a7388c53b77fa12f9fe2fa7e33c3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 May 2013 16:29:28 +0530 Subject: [PATCH 124/295] [report] requested qty to be ordered --- buying/page/buying_home/buying_home.js | 4 ++++ .../requested_items_to_be_ordered/__init__.py | 0 .../requested_items_to_be_ordered.txt | 23 +++++++++++++++++++ .../monthly_attendance_sheet.py | 2 +- stock/page/stock_home/stock_home.js | 4 ++++ .../purchase_order_items_to_be_received.txt | 7 +++--- 6 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 buying/report/requested_items_to_be_ordered/__init__.py create mode 100644 buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.txt diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js index e7532dda2e..2070fd4e33 100644 --- a/buying/page/buying_home/buying_home.js +++ b/buying/page/buying_home/buying_home.js @@ -111,6 +111,10 @@ wn.module_page["Buying"] = [ "label":wn._("Purchase In Transit"), route: "query-report/Purchase In Transit", }, + { + "label":wn._("Requested Items To Be Ordered"), + route: "query-report/Requested Items To Be Ordered", + }, ] } ] diff --git a/buying/report/requested_items_to_be_ordered/__init__.py b/buying/report/requested_items_to_be_ordered/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.txt b/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.txt new file mode 100644 index 0000000000..49c747854a --- /dev/null +++ b/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-13 16:10:02", + "docstatus": 0, + "modified": "2013-05-13 16:21:07", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tmr_item.qty as \"Qty:Float:100\",\n\tmr_item.ordered_qty as \"Ordered Qty:Float:100\", \n\t(mr_item.qty - ifnull(mr_item.ordered_qty, 0)) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\n\tand ifnull(mr_item.ordered_qty, 0) < ifnull(mr_item.qty, 0)\norder by mr.transaction_date asc", + "ref_doctype": "Purchase Order", + "report_name": "Requested Items To Be Ordered", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Requested Items To Be Ordered" + } +] \ No newline at end of file diff --git a/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 520f6e93e6..42a977025a 100644 --- a/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -68,7 +68,7 @@ def get_columns(filters): def get_attendance_list(conditions, filters): attendance_list = webnotes.conn.sql("""select employee, day(att_date) as day_of_month, - status from tabAttendance where docstatus < 2 %s order by employee, att_date""" % + status from tabAttendance where docstatus = 1 %s order by employee, att_date""" % conditions, filters, as_dict=1) att_map = {} diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index bfcaf8acc1..75e4ee15b0 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -201,6 +201,10 @@ wn.module_page["Stock"] = [ "label":wn._("Purchase In Transit"), route: "query-report/Purchase In Transit", }, + { + "label":wn._("Requested Items To Be Transferred"), + route: "query-report/Requested Items To Be Transferred", + }, ] } ] diff --git a/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt b/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt index 45e3a42dee..7a2f6365bf 100644 --- a/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt +++ b/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt @@ -1,16 +1,17 @@ [ { - "creation": "2013-02-21 14:26:49", + "creation": "2013-02-22 18:01:55", "docstatus": 0, - "modified": "2013-02-22 15:53:01", + "modified": "2013-05-13 16:11:27", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "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 `tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n `tabPurchase Order`.`project_name` as \"Project\",\n `tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n `tabPurchase Order Item`.qty as \"Qty:Float:100\",\n `tabPurchase Order Item`.received_qty as \"Received Qty:Float:100\", \n (`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive:Float:100\",\n `tabPurchase Order Item`.item_name as \"Item Name::150\",\n `tabPurchase Order Item`.description as \"Description::200\"\nfrom\n `tabPurchase Order`, `tabPurchase Order Item`\nwhere\n `tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n and `tabPurchase Order`.docstatus = 1\n and `tabPurchase Order`.status != \"Stopped\"\n and ifnull(`tabPurchase Order Item`.received_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\t`tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order`.`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`.received_qty as \"Received Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive: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`.received_qty, 0) < ifnull(`tabPurchase Order Item`.qty, 0)\norder by `tabPurchase Order`.transaction_date asc", "ref_doctype": "Purchase Receipt", "report_name": "Purchase Order Items To Be Received", "report_type": "Query Report" From ece32981597d436481a1bf91d82ccd0fc47da0c1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 13 May 2013 16:29:50 +0530 Subject: [PATCH 125/295] [report] requested qty to be transferred --- .../__init__.py | 0 .../requested_items_to_be_transferred.txt | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 stock/report/requested_items_to_be_transferred/__init__.py create mode 100644 stock/report/requested_items_to_be_transferred/requested_items_to_be_transferred.txt diff --git a/stock/report/requested_items_to_be_transferred/__init__.py b/stock/report/requested_items_to_be_transferred/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/requested_items_to_be_transferred/requested_items_to_be_transferred.txt b/stock/report/requested_items_to_be_transferred/requested_items_to_be_transferred.txt new file mode 100644 index 0000000000..030ed242d8 --- /dev/null +++ b/stock/report/requested_items_to_be_transferred/requested_items_to_be_transferred.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-13 16:23:05", + "docstatus": 0, + "modified": "2013-05-13 16:25:08", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tmr_item.qty as \"Qty:Float:100\",\n\tmr_item.ordered_qty as \"Transferred Qty:Float:100\", \n\t(mr_item.qty - ifnull(mr_item.ordered_qty, 0)) as \"Qty to Transfer:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Transfer\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\n\tand ifnull(mr_item.ordered_qty, 0) < ifnull(mr_item.qty, 0)\norder by mr.transaction_date asc", + "ref_doctype": "Stock Entry", + "report_name": "Requested Items To Be Transferred", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Requested Items To Be Transferred" + } +] \ No newline at end of file From 220ff30878c7581237db9fa89141944825134449 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 14 May 2013 15:33:34 +0530 Subject: [PATCH 126/295] [website] [product] show currency symbol and show specifications below the image --- website/helpers/product.py | 19 ++++++++++++++----- website/templates/css/product_page.css | 3 +++ website/templates/html/product_page.html | 18 ++++++++++-------- website/templates/js/product_page.js | 4 ++-- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/website/helpers/product.py b/website/helpers/product.py index d6f16fb753..a107d9b02b 100644 --- a/website/helpers/product.py +++ b/website/helpers/product.py @@ -4,10 +4,9 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cstr +from webnotes.utils import cstr, cint from webnotes.webutils import build_html, delete_page_cache - @webnotes.whitelist(allow_guest=True) def get_product_info(item_code): """get product price / stock info""" @@ -20,10 +19,20 @@ def get_product_info(item_code): in_stock = in_stock[0][0] > 0 and 1 or 0 else: in_stock = -1 + + price = price_list and webnotes.conn.sql("""select ref_rate, ref_currency from + `tabItem Price` where parent=%s and price_list_name=%s""", + (item_code, price_list), as_dict=1) or [] + + price = price and price[0] or None + + if price: + price["ref_currency"] = not cint(webnotes.conn.get_default("hide_currency_symbol")) \ + and (webnotes.conn.get_value("Currency", price.ref_currency, "symbol") or price.ref_currency) \ + or "" + return { - "price": price_list and webnotes.conn.sql("""select ref_rate, ref_currency from - `tabItem Price` where parent=%s and price_list_name=%s""", - (item_code, price_list), as_dict=1) or [], + "price": price, "stock": in_stock } diff --git a/website/templates/css/product_page.css b/website/templates/css/product_page.css index 566b6b57aa..71be9ee56b 100644 --- a/website/templates/css/product_page.css +++ b/website/templates/css/product_page.css @@ -7,4 +7,7 @@ font-size: 18px; line-height: 200%; } + .item-price { + margin-top: 20px; + } \ No newline at end of file diff --git a/website/templates/html/product_page.html b/website/templates/html/product_page.html index 23091ad435..3fda8cd271 100644 --- a/website/templates/html/product_page.html +++ b/website/templates/html/product_page.html @@ -35,23 +35,25 @@ {{ web_long_description or web_short_description or "[No description given]" }} - {% if obj.doclist.get({"doctype":"Item Website Specification"}) %} +
    +
    + + + {% if obj.doclist.get({"doctype":"Item Website Specification"}) -%} +
    +

    Specifications

    ' + wn._("Item Code") + '' + wn._("Valid For Buying") + '
    ' + d.parent + '' + format_currency(d.ref_rate, d.ref_currency) + '
    {% for d in obj.doclist.get( - {"doctype":"Item Website Specification"}) %} + {"doctype":"Item Website Specification"}) -%} - {% endfor %} + {%- endfor %}
    {{ d.label }} {{ d.description }}
    - {% endif %} -
    -

    Price:

    -
    -
    + {%- endif %} {% endblock %} \ No newline at end of file diff --git a/website/templates/js/product_page.js b/website/templates/js/product_page.js index 653cfa3239..69e9cd52fe 100644 --- a/website/templates/js/product_page.js +++ b/website/templates/js/product_page.js @@ -26,8 +26,8 @@ $(document).ready(function() { success: function(data) { if(data.message) { if(data.message.price) { - $("

    ").html(data.message.price[0].ref_currency + " " - + data.message.price[0].ref_rate).appendTo(".item-price"); + $("

    ").html(data.message.price.ref_currency + " " + + data.message.price.ref_rate).appendTo(".item-price"); $(".item-price").toggle(true); } if(data.message.stock==0) { From 6e3706d733d9b98fb750065608d83f15e51e80cd Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 14 May 2013 17:11:27 +0530 Subject: [PATCH 127/295] [employee] [fix] on user id, set Employee role to profile --- hr/doctype/employee/employee.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py index 9a9ed136af..036980326c 100644 --- a/hr/doctype/employee/employee.py +++ b/hr/doctype/employee/employee.py @@ -80,7 +80,7 @@ class DocType: if not "Employee" in webnotes.conn.sql_list("""select role from tabUserRole where parent=%s""", self.doc.user_id): from webnotes.profile import add_role - add_role(self.doc.user_id, "HR User") + add_role(self.doc.user_id, "Employee") profile_wrapper = webnotes.bean("Profile", self.doc.user_id) From 4430c0d718f8ef5cf085d78ae71c16acf6510506 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 14 May 2013 19:23:37 +0530 Subject: [PATCH 128/295] [upload attendance] [fix] fixed import issue for role restricted whitelist methods --- hr/doctype/upload_attendance/upload_attendance.js | 1 - hr/doctype/upload_attendance/upload_attendance.py | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hr/doctype/upload_attendance/upload_attendance.js b/hr/doctype/upload_attendance/upload_attendance.js index 35a00ed69d..4e5b47fe00 100644 --- a/hr/doctype/upload_attendance/upload_attendance.js +++ b/hr/doctype/upload_attendance/upload_attendance.js @@ -75,7 +75,6 @@ erpnext.hr.AttendanceControlPanel = wn.ui.form.Controller.extend({ r.messages = ["

    Import Successful!

    "]. concat(r.messages) } - console.log(r.messages); $.each(r.messages, function(i, v) { var $p = $('

    ').html(v).appendTo($log_wrapper); diff --git a/hr/doctype/upload_attendance/upload_attendance.py b/hr/doctype/upload_attendance/upload_attendance.py index e48cbefb69..ee4234a279 100644 --- a/hr/doctype/upload_attendance/upload_attendance.py +++ b/hr/doctype/upload_attendance/upload_attendance.py @@ -100,7 +100,6 @@ def get_naming_series(): def upload(): from webnotes.utils.datautils import read_csv_content_from_uploaded_file from webnotes.modules import scrub - from core.page.data_import_tool.data_import_tool import check_record, import_doc rows = read_csv_content_from_uploaded_file() if not rows: @@ -112,6 +111,9 @@ def upload(): ret = [] error = False + from webnotes.utils.datautils import check_record, import_doc + doctype_dl = webnotes.get_doctype("Attendance") + for i, row in enumerate(rows[5:]): if not row: continue row_idx = i + 5 @@ -121,7 +123,7 @@ def upload(): d["docstatus"] = webnotes.conn.get_value("Attendance", d.name, "docstatus") try: - check_record(d) + check_record(d, doctype_dl=doctype_dl) ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True)) except Exception, e: error = True From 2e311acaa5077163974fedcffbae6e9fd85539de Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 15 May 2013 11:42:09 +0530 Subject: [PATCH 129/295] [website] [fix] fixes in support ticket and profile pages, when using guest user login --- config.json | 6 +++++- support/doctype/support_ticket/support_ticket.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index ff6f80ffff..cb1a83164b 100644 --- a/config.json +++ b/config.json @@ -114,7 +114,7 @@ "ticket": { "no_cache": true, "template": "app/website/templates/pages/ticket", - "get_website_args": "support.doctype.support_ticket.support_ticket.get_website_args" + "args_method": "support.doctype.support_ticket.support_ticket.get_website_args" }, "tickets": { "template": "app/website/templates/pages/tickets" @@ -122,6 +122,10 @@ "writers": { "template": "app/website/templates/pages/writers", "args_method": "website.helpers.blog.get_writers_args" + }, + "profile": { + "no_cache": true, + "template": "app/website/templates/pages/profile" } }, "generators": { diff --git a/support/doctype/support_ticket/support_ticket.py b/support/doctype/support_ticket/support_ticket.py index 8a705f41c7..ca46a8e073 100644 --- a/support/doctype/support_ticket/support_ticket.py +++ b/support/doctype/support_ticket/support_ticket.py @@ -86,7 +86,7 @@ def get_tickets(): webnotes.session.user, as_dict=1) return tickets -def get_website_args(): +def get_website_args(): bean = webnotes.bean("Support Ticket", webnotes.form_dict.name) if bean.doc.raised_by != webnotes.session.user: return { From bfc18ea6c44778808e6f5013ec3a8bb7299ec366 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 15 May 2013 12:24:50 +0530 Subject: [PATCH 130/295] [setup home] combined Update Application, Backup Manager and Scheduler Error Log under Setup > Administration --- setup/page/setup/setup.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/setup/page/setup/setup.js b/setup/page/setup/setup.js index b47f6f28d1..1ebd7307a6 100644 --- a/setup/page/setup/setup.js +++ b/setup/page/setup/setup.js @@ -193,21 +193,8 @@ wn.module_page["Setup"] = [ ] }, { - title: wn._("Backups"), - icon: "icon-cloud-upload", - right: true, - items: [ - { - "route":"Form/Backup Manager", - doctype:"Backup Manager", - label: wn._("Backup Manager"), - "description":wn._("Sync backups with remote tools like Dropbox etc.") - }, - ] - }, - { - title: wn._("Update Manager"), - icon: "icon-magnet", + title: wn._("Administration"), + icon: "icon-rocket", right: true, items: [ { @@ -215,6 +202,18 @@ wn.module_page["Setup"] = [ label: wn._("Update This Application"), "description":wn._("Apply latest updates and patches to this app") }, + { + "route":"Form/Backup Manager", + doctype:"Backup Manager", + label: wn._("Backup Manager"), + "description":wn._("Sync backups with remote tools like Dropbox etc.") + }, + { + "route":"List/Scheduler Log", + doctype:"Scheduler Log", + label: wn._("Scheduler Error Log"), + "description":wn._("Get a list of errors encountered by the Scheduler") + }, ] }, ] From 1fd546f424673ab6caa5c01825b002f2d622b3c1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 16 May 2013 11:02:07 +0530 Subject: [PATCH 131/295] [fixes] prosting date for gl entry --- stock/doctype/serial_no/serial_no.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py index 501b535c6e..09181db0d2 100644 --- a/stock/doctype/serial_no/serial_no.py +++ b/stock/doctype/serial_no/serial_no.py @@ -142,7 +142,8 @@ class DocType(StockController): gl_entries = self.get_gl_entries_for_stock(against_stock_account, self.doc.purchase_rate) for entry in gl_entries: - entry["posting_date"] = self.doc.purchase_date + entry["posting_date"] = self.doc.purchase_date or (self.doc.creation and + self.doc.creation.split(' ')[0]) or nowdate() if gl_entries: make_gl_entries(gl_entries, cancel) \ No newline at end of file From 4221814131c4d6d5fe0fb2b298481582294f16b0 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:28:19 +0530 Subject: [PATCH 132/295] [patches] [fix] replace sync with reload_doc in old patches --- patches/august_2012/change_profile_permission.py | 3 +-- patches/july_2012/address_contact_perms.py | 5 ++--- patches/july_2012/blog_guest_permission.py | 3 +-- patches/july_2012/project_patch_repeat.py | 7 +++---- patches/july_2012/supplier_quotation.py | 13 ++++++------- patches/june_2012/cms2.py | 11 +++++------ patches/june_2012/copy_uom_for_pur_inv_item.py | 8 +++----- patches/june_2012/reports_list_permission.py | 5 ++--- patches/june_2012/set_recurring_type.py | 3 +-- patches/june_2012/support_ticket_autoreply.py | 3 +-- patches/mar_2012/clean_property_setter.py | 3 +-- patches/mar_2012/create_custom_fields.py | 3 +-- patches/may_2012/cleanup_notification_control.py | 3 +-- patches/may_2012/cs_server_readonly.py | 3 +-- patches/may_2012/profile_perm_patch.py | 3 +-- patches/may_2012/remove_communication_log.py | 3 +-- patches/may_2012/std_pf_readonly.py | 3 +-- 17 files changed, 32 insertions(+), 50 deletions(-) diff --git a/patches/august_2012/change_profile_permission.py b/patches/august_2012/change_profile_permission.py index 27169d8792..7e945d5d9a 100644 --- a/patches/august_2012/change_profile_permission.py +++ b/patches/august_2012/change_profile_permission.py @@ -31,5 +31,4 @@ def execute(): webnotes.conn.commit() webnotes.conn.begin() - import webnotes.model.sync - webnotes.model.sync.sync('core', 'profile') \ No newline at end of file + webnotes.reload_doc('core', 'doctype', 'profile') \ No newline at end of file diff --git a/patches/july_2012/address_contact_perms.py b/patches/july_2012/address_contact_perms.py index 5b79f22937..882cf728a5 100644 --- a/patches/july_2012/address_contact_perms.py +++ b/patches/july_2012/address_contact_perms.py @@ -6,7 +6,6 @@ def execute(): where parent in ('Address', 'Contact')""") webnotes.conn.commit() - import webnotes.model.sync - webnotes.model.sync.sync('utilities', 'address') - webnotes.model.sync.sync('utilities', 'contact') + webnotes.reload_doc('utilities', 'doctype', 'address') + webnotes.reload_doc('utilities', 'doctype', 'contact') webnotes.conn.begin() \ No newline at end of file diff --git a/patches/july_2012/blog_guest_permission.py b/patches/july_2012/blog_guest_permission.py index bc42a9d05c..3fcaa0d291 100644 --- a/patches/july_2012/blog_guest_permission.py +++ b/patches/july_2012/blog_guest_permission.py @@ -6,7 +6,6 @@ def execute(): webnotes.conn.commit() - import webnotes.model.sync - webnotes.model.sync.sync('website', 'blog', 1) + webnotes.reload_doc('website', 'doctype', 'blog') webnotes.conn.begin() diff --git a/patches/july_2012/project_patch_repeat.py b/patches/july_2012/project_patch_repeat.py index bd52522938..e031e35980 100644 --- a/patches/july_2012/project_patch_repeat.py +++ b/patches/july_2012/project_patch_repeat.py @@ -12,8 +12,7 @@ def execute(): and ifnull(t1.project_name, '') = ''""") webnotes.conn.commit() - from webnotes.model.sync import sync - sync("buying", "purchase_order") - sync("buying", "purchase_request") - sync("accounts", "purchase_invoice") + webnotes.reload_doc("buying", "doctype", "purchase_order") + webnotes.reload_doc("buying", "doctype", "purchase_request") + webnotes.reload_doc("accounts", "doctype", "purchase_invoice") webnotes.conn.begin() \ No newline at end of file diff --git a/patches/july_2012/supplier_quotation.py b/patches/july_2012/supplier_quotation.py index 49fa14dc9b..c51399c75b 100644 --- a/patches/july_2012/supplier_quotation.py +++ b/patches/july_2012/supplier_quotation.py @@ -1,13 +1,12 @@ from __future__ import unicode_literals def execute(): """sync supplier quotatoin and create supplier quotation mappers""" - from webnotes.model.sync import sync - sync('buying', 'supplier_quotation') - sync('buying', 'supplier_quotation_item') - sync('buying', 'purchase_request') - sync('buying', 'purchase_request_item') - sync('buying', 'purchase_order') - sync('buying', 'purchase_order_item') + webnotes.reload_doc('buying', 'doctype', 'supplier_quotation') + webnotes.reload_doc('buying', 'doctype', 'supplier_quotation_item') + webnotes.reload_doc('buying', 'doctype', 'purchase_request') + webnotes.reload_doc('buying', 'doctype', 'purchase_request_item') + webnotes.reload_doc('buying', 'doctype', 'purchase_order') + webnotes.reload_doc('buying', 'doctype', 'purchase_order_item') from webnotes.modules import reload_doc reload_doc('buying', 'DocType Mapper', 'Material Request-Supplier Quotation') diff --git a/patches/june_2012/cms2.py b/patches/june_2012/cms2.py index 414af73c00..f912f86278 100644 --- a/patches/june_2012/cms2.py +++ b/patches/june_2012/cms2.py @@ -1,14 +1,13 @@ from __future__ import unicode_literals def execute(): import webnotes - import webnotes.model.sync # sync doctypes required for the patch - webnotes.model.sync.sync('website', 'web_cache') - webnotes.model.sync.sync('website', 'web_page') - webnotes.model.sync.sync('website', 'blog') - webnotes.model.sync.sync('website', 'website_settings') - webnotes.model.sync.sync('stock', 'item') + webnotes.reload_doc('website', 'doctype', 'web_cache') + webnotes.reload_doc('website', 'doctype', 'web_page') + webnotes.reload_doc('website', 'doctype', 'blog') + webnotes.reload_doc('website', 'doctype', 'website_settings') + webnotes.reload_doc('stock', 'doctype', 'item') cleanup() diff --git a/patches/june_2012/copy_uom_for_pur_inv_item.py b/patches/june_2012/copy_uom_for_pur_inv_item.py index b374249942..a22146c6a3 100644 --- a/patches/june_2012/copy_uom_for_pur_inv_item.py +++ b/patches/june_2012/copy_uom_for_pur_inv_item.py @@ -2,11 +2,9 @@ from __future__ import unicode_literals def execute(): import webnotes - # perform sync - import webnotes.model.sync - webnotes.model.sync.sync('buying', 'purchase_order_item') - webnotes.model.sync.sync('accounts', 'purchase_invoice_item') - webnotes.model.sync.sync('stock', 'purchase_receipt_item') + webnotes.reload_doc('buying', 'doctype', 'purchase_order_item') + webnotes.reload_doc('accounts', 'doctype', 'purchase_invoice_item') + webnotes.reload_doc('stock', 'doctype', 'purchase_receipt_item') webnotes.conn.sql("update `tabPurchase Invoice Item` t1, `tabPurchase Order Item` t2 set t1.uom = t2.uom where ifnull(t1.po_detail, '') != '' and t1.po_detail = t2.name") webnotes.conn.sql("update `tabPurchase Invoice Item` t1, `tabPurchase Receipt Item` t2 set t1.uom = t2.uom where ifnull(t1.pr_detail, '') != '' and t1.pr_detail = t2.name") \ No newline at end of file diff --git a/patches/june_2012/reports_list_permission.py b/patches/june_2012/reports_list_permission.py index a02f4fa37f..e34eb5ae46 100644 --- a/patches/june_2012/reports_list_permission.py +++ b/patches/june_2012/reports_list_permission.py @@ -8,8 +8,7 @@ def execute(): webnotes.conn.commit() - import webnotes.model.sync - webnotes.model.sync.sync('core', 'search_criteria') - webnotes.model.sync.sync('core', 'report') + webnotes.reload_doc('core', 'doctype', 'search_criteria') + webnotes.reload_doc('core', 'doctype', 'report') webnotes.conn.begin() \ No newline at end of file diff --git a/patches/june_2012/set_recurring_type.py b/patches/june_2012/set_recurring_type.py index 79dd286fbc..7fb416ed7e 100644 --- a/patches/june_2012/set_recurring_type.py +++ b/patches/june_2012/set_recurring_type.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals def execute(): import webnotes - from webnotes.model.sync import sync - sync('accounts', 'sales_invoice') + webnotes.reload_doc('accounts', 'doctype', 'sales_invoice') webnotes.conn.sql("update `tabSales Invoice` set recurring_type = 'Monthly' where ifnull(convert_into_recurring_invoice, 0) = 1") \ No newline at end of file diff --git a/patches/june_2012/support_ticket_autoreply.py b/patches/june_2012/support_ticket_autoreply.py index 9fb0534bdd..32e095665c 100644 --- a/patches/june_2012/support_ticket_autoreply.py +++ b/patches/june_2012/support_ticket_autoreply.py @@ -4,9 +4,8 @@ def execute(): import webnotes import webnotes.utils - import webnotes.model.sync webnotes.conn.commit() - webnotes.model.sync.sync('setup', 'email_settings') + webnotes.reload_doc('setup', 'doctype', 'email_settings') webnotes.conn.begin() sync_support_mails = webnotes.utils.cint(webnotes.conn.get_value('Email Settings', diff --git a/patches/mar_2012/clean_property_setter.py b/patches/mar_2012/clean_property_setter.py index 08a0a94e1f..a9c7b81f36 100644 --- a/patches/mar_2012/clean_property_setter.py +++ b/patches/mar_2012/clean_property_setter.py @@ -12,8 +12,7 @@ def execute(): clean_docfield_properties() def change_property_setter_fieldnames(): - import webnotes.model.sync - webnotes.model.sync.sync('core', 'property_setter') + webnotes.reload_doc('core', 'doctype', 'property_setter') docfield_list = webnotes.conn.sql("""\ SELECT name, fieldname FROM `tabDocField`""", as_list=1) custom_field_list = webnotes.conn.sql("""\ diff --git a/patches/mar_2012/create_custom_fields.py b/patches/mar_2012/create_custom_fields.py index d4c1a13453..a91c765e5f 100644 --- a/patches/mar_2012/create_custom_fields.py +++ b/patches/mar_2012/create_custom_fields.py @@ -94,8 +94,7 @@ from webnotes.model.code import get_obj from webnotes.model.doc import Document def execute(): - import webnotes.model.sync - webnotes.model.sync.sync('core', 'custom_field') + webnotes.reload_doc('core', 'doctype', 'custom_field') for f in field_list: res = webnotes.conn.sql("""SELECT name FROM `tabCustom Field` WHERE dt=%s AND fieldname=%s""", (f[0], f[1])) diff --git a/patches/may_2012/cleanup_notification_control.py b/patches/may_2012/cleanup_notification_control.py index 25a704e198..1a7730ba5b 100644 --- a/patches/may_2012/cleanup_notification_control.py +++ b/patches/may_2012/cleanup_notification_control.py @@ -25,5 +25,4 @@ def execute(): webnotes.conn.commit() webnotes.conn.begin() - import webnotes.model.sync - webnotes.model.sync.sync('setup', 'notification_control') \ No newline at end of file + webnotes.reload_doc('setup', 'doctype', 'notification_control') \ No newline at end of file diff --git a/patches/may_2012/cs_server_readonly.py b/patches/may_2012/cs_server_readonly.py index b68060682c..51a9b760cf 100644 --- a/patches/may_2012/cs_server_readonly.py +++ b/patches/may_2012/cs_server_readonly.py @@ -27,5 +27,4 @@ def execute(): doc.save() webnotes.conn.commit() webnotes.conn.begin() - import webnotes.model.sync - webnotes.model.sync.sync('core', 'custom_script') \ No newline at end of file + webnotes.reload_doc('core', 'doctype', 'custom_script') \ No newline at end of file diff --git a/patches/may_2012/profile_perm_patch.py b/patches/may_2012/profile_perm_patch.py index 4423fdb15d..29fa9c05be 100644 --- a/patches/may_2012/profile_perm_patch.py +++ b/patches/may_2012/profile_perm_patch.py @@ -19,5 +19,4 @@ def execute(): doc.save() webnotes.conn.commit() webnotes.conn.begin() - import webnotes.model.sync - webnotes.model.sync.sync('core', 'profile') \ No newline at end of file + webnotes.reload_doc('core', 'doctype', 'profile') \ No newline at end of file diff --git a/patches/may_2012/remove_communication_log.py b/patches/may_2012/remove_communication_log.py index e44e673701..b6e7e7d276 100644 --- a/patches/may_2012/remove_communication_log.py +++ b/patches/may_2012/remove_communication_log.py @@ -1,8 +1,7 @@ from __future__ import unicode_literals def execute(): import webnotes - import webnotes.model.sync - webnotes.model.sync.sync('support', 'communication') + webnotes.reload_doc('support', 'doctype', 'communication') webnotes.conn.commit() webnotes.conn.begin() diff --git a/patches/may_2012/std_pf_readonly.py b/patches/may_2012/std_pf_readonly.py index 83b581325c..9fbbfe9a3c 100644 --- a/patches/may_2012/std_pf_readonly.py +++ b/patches/may_2012/std_pf_readonly.py @@ -27,5 +27,4 @@ def execute(): doc.save() webnotes.conn.commit() webnotes.conn.begin() - import webnotes.model.sync - webnotes.model.sync.sync('core', 'print_format') \ No newline at end of file + webnotes.reload_doc('core', 'doctype', 'print_format') \ No newline at end of file From 1d4b5b4ca6e772aecf9cf6ae197f2c9174ab2948 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:35:08 +0530 Subject: [PATCH 133/295] [patches] [fix] removed blog reload --- patches/july_2012/blog_guest_permission.py | 11 ----------- patches/june_2012/cms2.py | 2 -- patches/patch_list.py | 1 - 3 files changed, 14 deletions(-) delete mode 100644 patches/july_2012/blog_guest_permission.py diff --git a/patches/july_2012/blog_guest_permission.py b/patches/july_2012/blog_guest_permission.py deleted file mode 100644 index 3fcaa0d291..0000000000 --- a/patches/july_2012/blog_guest_permission.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -def execute(): - """allocate read write permission to guest for doctype 'Blog'""" - import webnotes - webnotes.conn.sql("""delete from `tabDocPerm` where parent = 'Blog'""") - - webnotes.conn.commit() - - webnotes.reload_doc('website', 'doctype', 'blog') - - webnotes.conn.begin() diff --git a/patches/june_2012/cms2.py b/patches/june_2012/cms2.py index f912f86278..17b7d23cc1 100644 --- a/patches/june_2012/cms2.py +++ b/patches/june_2012/cms2.py @@ -3,9 +3,7 @@ def execute(): import webnotes # sync doctypes required for the patch - webnotes.reload_doc('website', 'doctype', 'web_cache') webnotes.reload_doc('website', 'doctype', 'web_page') - webnotes.reload_doc('website', 'doctype', 'blog') webnotes.reload_doc('website', 'doctype', 'website_settings') webnotes.reload_doc('stock', 'doctype', 'item') diff --git a/patches/patch_list.py b/patches/patch_list.py index 5475d0f8bc..b7ee2d1b4f 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -74,7 +74,6 @@ patch_list = [ "patches.july_2012.auth_table", "patches.july_2012.remove_event_role_owner_match", "patches.july_2012.deprecate_bulk_rename", - "patches.july_2012.blog_guest_permission", "patches.july_2012.bin_permission", "patches.july_2012.project_patch_repeat", "patches.july_2012.repost_stock_due_to_wrong_packing_list", From c852596f15e82e79d2c7190cea9631c6a4ca13c4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:37:36 +0530 Subject: [PATCH 134/295] [patches] [fix] removed purchase request reload --- patches/july_2012/project_patch_repeat.py | 1 - patches/july_2012/supplier_quotation.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/patches/july_2012/project_patch_repeat.py b/patches/july_2012/project_patch_repeat.py index e031e35980..b1386614c4 100644 --- a/patches/july_2012/project_patch_repeat.py +++ b/patches/july_2012/project_patch_repeat.py @@ -13,6 +13,5 @@ def execute(): webnotes.conn.commit() webnotes.reload_doc("buying", "doctype", "purchase_order") - webnotes.reload_doc("buying", "doctype", "purchase_request") webnotes.reload_doc("accounts", "doctype", "purchase_invoice") webnotes.conn.begin() \ No newline at end of file diff --git a/patches/july_2012/supplier_quotation.py b/patches/july_2012/supplier_quotation.py index c51399c75b..4b8e66c53c 100644 --- a/patches/july_2012/supplier_quotation.py +++ b/patches/july_2012/supplier_quotation.py @@ -3,8 +3,6 @@ def execute(): """sync supplier quotatoin and create supplier quotation mappers""" webnotes.reload_doc('buying', 'doctype', 'supplier_quotation') webnotes.reload_doc('buying', 'doctype', 'supplier_quotation_item') - webnotes.reload_doc('buying', 'doctype', 'purchase_request') - webnotes.reload_doc('buying', 'doctype', 'purchase_request_item') webnotes.reload_doc('buying', 'doctype', 'purchase_order') webnotes.reload_doc('buying', 'doctype', 'purchase_order_item') From 6febab9f9233e1906f3bf086f01cb67aac1a945e Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:40:07 +0530 Subject: [PATCH 135/295] [patches] [fix] fix in an old patch --- patches/july_2012/supplier_quotation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patches/july_2012/supplier_quotation.py b/patches/july_2012/supplier_quotation.py index 4b8e66c53c..0a1ab35ef9 100644 --- a/patches/july_2012/supplier_quotation.py +++ b/patches/july_2012/supplier_quotation.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals +import webnotes + def execute(): """sync supplier quotatoin and create supplier quotation mappers""" webnotes.reload_doc('buying', 'doctype', 'supplier_quotation') From 71c265dee8b3e1a08b1fe708ed56c43f4259b671 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:42:56 +0530 Subject: [PATCH 136/295] [patches] [fix] fix in old patch --- patches/august_2012/report_supplier_quotations.py | 4 ---- patches/patch_list.py | 1 - 2 files changed, 5 deletions(-) delete mode 100644 patches/august_2012/report_supplier_quotations.py diff --git a/patches/august_2012/report_supplier_quotations.py b/patches/august_2012/report_supplier_quotations.py deleted file mode 100644 index 8eaf707c4c..0000000000 --- a/patches/august_2012/report_supplier_quotations.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import unicode_literals -def execute(): - from webnotes.modules import reload_doc - reload_doc("buying", "report", "supplier_quotations") diff --git a/patches/patch_list.py b/patches/patch_list.py index b7ee2d1b4f..a3a8ac6a97 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -78,7 +78,6 @@ patch_list = [ "patches.july_2012.project_patch_repeat", "patches.july_2012.repost_stock_due_to_wrong_packing_list", "patches.july_2012.supplier_quotation", - "patches.august_2012.report_supplier_quotations", "patches.august_2012.task_allocated_to_assigned", "patches.august_2012.change_profile_permission", "patches.august_2012.changed_blog_date_format", From 101877276eb24657e4aabfd58cfb6876db93ea11 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:45:06 +0530 Subject: [PATCH 137/295] [patches] [fix] fixed an old patch --- patches/august_2012/changed_blog_date_format.py | 5 ----- patches/patch_list.py | 1 - 2 files changed, 6 deletions(-) delete mode 100644 patches/august_2012/changed_blog_date_format.py diff --git a/patches/august_2012/changed_blog_date_format.py b/patches/august_2012/changed_blog_date_format.py deleted file mode 100644 index df51977be5..0000000000 --- a/patches/august_2012/changed_blog_date_format.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals -def execute(): - import webnotes - from webnotes.model.bean import Bean - Bean("Website Settings", "Website Settings").save() \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index a3a8ac6a97..57da64c397 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -80,7 +80,6 @@ patch_list = [ "patches.july_2012.supplier_quotation", "patches.august_2012.task_allocated_to_assigned", "patches.august_2012.change_profile_permission", - "patches.august_2012.changed_blog_date_format", "patches.august_2012.repost_billed_amt", "patches.august_2012.remove_cash_flow_statement", "patches.september_2012.stock_report_permissions_for_accounts", From 6e0e1c8fcc64265549439723072324268c0ef723 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:46:32 +0530 Subject: [PATCH 138/295] [patches] [fix] fixed an old patch --- patches/patch_list.py | 1 - patches/september_2012/reload_gross_profit.py | 21 ------------------- 2 files changed, 22 deletions(-) delete mode 100644 patches/september_2012/reload_gross_profit.py diff --git a/patches/patch_list.py b/patches/patch_list.py index 57da64c397..c176a9122d 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -90,7 +90,6 @@ patch_list = [ "patches.september_2012.plot_patch", "patches.september_2012.event_permission", "patches.september_2012.repost_stock", - "patches.september_2012.reload_gross_profit", "patches.september_2012.rebuild_trees", "patches.september_2012.deprecate_account_balance", "patches.september_2012.profile_delete_permission", diff --git a/patches/september_2012/reload_gross_profit.py b/patches/september_2012/reload_gross_profit.py deleted file mode 100644 index 0a3f9efed7..0000000000 --- a/patches/september_2012/reload_gross_profit.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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(): - # reload gross profit report - from webnotes.modules import reload_doc - reload_doc('selling', 'search_criteria', 'gross_profit') \ No newline at end of file From cae99dbf717d78234e4f25af07c9e6d32d02e203 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 15:57:45 +0530 Subject: [PATCH 139/295] [patches] [fix] fixed an old patch --- patches/february_2013/p03_material_request.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/patches/february_2013/p03_material_request.py b/patches/february_2013/p03_material_request.py index 66b2bf6cbb..f0373bdffb 100644 --- a/patches/february_2013/p03_material_request.py +++ b/patches/february_2013/p03_material_request.py @@ -23,3 +23,7 @@ def execute(): os.system("rm -rf app/hr/doctype/holiday_block_list_allow") os.system("rm -rf app/hr/doctype/holiday_block_list_date") + for dt in ("Purchase Request", "Purchase Request Item"): + if webnotes.conn.exists("DocType", dt): + webnotes.delete_doc("DocType", dt) + \ No newline at end of file From 9a22e3f1db74457de3a8a98475ced773cf42ec92 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 16:13:16 +0530 Subject: [PATCH 140/295] [patches] [fix] fixed an old patch --- patches/april_2013/p05_update_file_data.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index 787991251e..abf9985b6e 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -9,7 +9,9 @@ def execute(): for doctype in webnotes.conn.sql_list("""select parent from tabDocField where fieldname='file_list'"""): - update_file_list(doctype, singles) + # the other scenario is handled in p07_update_file_data_2 + if doctype in singles: + update_file_list(doctype, singles) webnotes.conn.sql("""delete from tabDocField where fieldname='file_list' and parent=%s""", doctype) From 27ff675a07364f278b4f0a33ce28b230bb9ac9da Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 16:15:19 +0530 Subject: [PATCH 141/295] [patches] [fix] fixed an old patch --- patches/april_2013/p05_update_file_data.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index abf9985b6e..39449a6252 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -13,9 +13,6 @@ def execute(): if doctype in singles: update_file_list(doctype, singles) - webnotes.conn.sql("""delete from tabDocField where fieldname='file_list' - and parent=%s""", doctype) - # export_to_files([["DocType", doctype]]) def get_single_doctypes(): From 44a01faff9cb02c3f80cd3f8e19326e999d89442 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 16 May 2013 16:17:15 +0530 Subject: [PATCH 142/295] [patches] [fix] fixed an old patch --- patches/april_2013/p07_update_file_data_2.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/patches/april_2013/p07_update_file_data_2.py b/patches/april_2013/p07_update_file_data_2.py index 2405e80d65..0cb44d0c20 100644 --- a/patches/april_2013/p07_update_file_data_2.py +++ b/patches/april_2013/p07_update_file_data_2.py @@ -13,6 +13,4 @@ def execute(): webnotes.conn.sql("""delete from `tabCustom Field` where fieldname='file_list' and parent=%s""", doctype) - webnotes.conn.sql("""delete from `tabDocField` where fieldname='file_list' - and parent=%s""", doctype) \ No newline at end of file From f826b93a75dc96edd463acbb826d1f1acd949407 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 17 May 2013 12:19:50 +0530 Subject: [PATCH 143/295] [patch] set conversion factor in pur invoice and repost stock_received_but_not_billed patch --- .../may_2013/p01_conversion_factor_and_aii.py | 26 +++++++++++++++++++ patches/patch_list.py | 1 + 2 files changed, 27 insertions(+) create mode 100644 patches/may_2013/p01_conversion_factor_and_aii.py diff --git a/patches/may_2013/p01_conversion_factor_and_aii.py b/patches/may_2013/p01_conversion_factor_and_aii.py new file mode 100644 index 0000000000..89d82e010d --- /dev/null +++ b/patches/may_2013/p01_conversion_factor_and_aii.py @@ -0,0 +1,26 @@ +import webnotes +from webnotes.utils import cint +from accounts.utils import create_stock_in_hand_jv + +def execute(): + webnotes.conn.auto_commit_on_many_writes = True + + aii_enabled = cint(webnotes.conn.get_value("Global Defaults", None, + "auto_inventory_accounting")) + + if aii_enabled: + create_stock_in_hand_jv(reverse = True) + + webnotes.conn.sql("""update `tabPurchase Invoice Item` pi_item + set conversion_factor = (select ifnull(if(conversion_factor=0, 1, conversion_factor), 1) + from `tabUOM Conversion Detail` + where parent = pi_item.item_code and uom = pi_item.uom + ) + where ifnull(conversion_factor, 0)=0""") + + if aii_enabled: + create_stock_in_hand_jv() + + webnotes.conn.auto_commit_on_many_writes = False + + \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 5475d0f8bc..74a8af7854 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -251,4 +251,5 @@ patch_list = [ "patches.april_2013.rebuild_sales_browser", "patches.april_2013.p08_price_list_country", "patches.may_2013.repost_stock_for_no_posting_time", + "patches.may_2013.p01_conversion_factor_and_aii", ] \ No newline at end of file From b9669de3ff5c0cb5a6eb8b25383295de59a8ec45 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 17 May 2013 12:21:13 +0530 Subject: [PATCH 144/295] [patch] repost_stock: commit after every 50 --- patches/may_2013/repost_stock_for_no_posting_time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/may_2013/repost_stock_for_no_posting_time.py b/patches/may_2013/repost_stock_for_no_posting_time.py index 04ceae5eca..b4d52ec4fc 100644 --- a/patches/may_2013/repost_stock_for_no_posting_time.py +++ b/patches/may_2013/repost_stock_for_no_posting_time.py @@ -29,6 +29,6 @@ def execute(): except: pass i += 1 - if i%100 == 0: + if i%50 == 0: webnotes.conn.sql("commit") webnotes.conn.sql("start transaction") \ No newline at end of file From 8a7d4f51b40e31af3659e957cbbc4f78f89de8b8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 17 May 2013 12:49:15 +0530 Subject: [PATCH 145/295] [latest updates] --- home/page/latest_updates/latest_updates.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index 38c0026965..910c1b1602 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,4 +1,6 @@ erpnext.updates = [ + ["17th May", ["Patch: Set Conversion Factor for purchase invoice item in all old records. And repost JV to book Stock Received But Not Billed account, if Auto Inventory Integration enabed." + ]], ["2nd May", ["Buying: Warehouse must belong to same company as transaction", "Price List: Added Currency Field. One price list can have only one currency", "Item: Naming can now be by series or item code", From d37ac68f1b9a874762e26bdc8b1056e13ed25b8f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 17 May 2013 13:17:01 +0530 Subject: [PATCH 146/295] [fixes] fixes in patch --- patches/may_2013/p01_conversion_factor_and_aii.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/may_2013/p01_conversion_factor_and_aii.py b/patches/may_2013/p01_conversion_factor_and_aii.py index 89d82e010d..2fd0d369a5 100644 --- a/patches/may_2013/p01_conversion_factor_and_aii.py +++ b/patches/may_2013/p01_conversion_factor_and_aii.py @@ -14,7 +14,7 @@ def execute(): webnotes.conn.sql("""update `tabPurchase Invoice Item` pi_item set conversion_factor = (select ifnull(if(conversion_factor=0, 1, conversion_factor), 1) from `tabUOM Conversion Detail` - where parent = pi_item.item_code and uom = pi_item.uom + where parent = pi_item.item_code and uom = pi_item.uom limit 1 ) where ifnull(conversion_factor, 0)=0""") From 031b7b3069fc647bf32059cf502d6c64285b3f18 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 20 May 2013 12:19:57 +0530 Subject: [PATCH 147/295] [fixes] repost stock: commit after every 20 --- patches/may_2013/repost_stock_for_no_posting_time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/may_2013/repost_stock_for_no_posting_time.py b/patches/may_2013/repost_stock_for_no_posting_time.py index b4d52ec4fc..489511c53e 100644 --- a/patches/may_2013/repost_stock_for_no_posting_time.py +++ b/patches/may_2013/repost_stock_for_no_posting_time.py @@ -29,6 +29,6 @@ def execute(): except: pass i += 1 - if i%50 == 0: + if i%20 == 0: webnotes.conn.sql("commit") webnotes.conn.sql("start transaction") \ No newline at end of file From 6b2aa7ed0dcd185aa72bece28af0bf1e56b1f742 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 20 May 2013 13:43:50 +0530 Subject: [PATCH 148/295] [country info] [fix] added additional timezones + fixed fresh install --- public/js/complete_setup.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/public/js/complete_setup.js b/public/js/complete_setup.js index f5d06722c6..f0b21c8516 100644 --- a/public/js/complete_setup.js +++ b/public/js/complete_setup.js @@ -40,7 +40,7 @@ $.extend(erpnext.complete_setup, { {fieldname:'country', label: 'Country', reqd:1, options: "", fieldtype: 'Select'}, {fieldname:'currency', label: 'Default Currency', reqd:1, - options: "Currency", fieldtype: 'Link'}, + options: "", fieldtype: 'Select'}, {fieldname:'timezone', label: 'Time Zone', reqd:1, options: "", fieldtype: 'Select'}, {fieldname:'industry', label: 'Industry', reqd:1, @@ -55,11 +55,17 @@ $.extend(erpnext.complete_setup, { } wn.call({ - method:"webnotes.country_info.get_all", + method:"webnotes.country_info.get_country_timezone_info", callback: function(data) { - erpnext.country_info = data.message; + erpnext.country_info = data.message.country_info; + erpnext.all_timezones = data.message.all_timezones; d.get_input("country").empty() - .add_options([""].concat(keys(data.message).sort())); + .add_options([""].concat(keys(erpnext.country_info).sort())); + d.get_input("currency").empty() + .add_options(wn.utils.unique([""].concat($.map(erpnext.country_info, + function(opts, country) { return opts.currency; }))).sort()); + d.get_input("timezone").empty() + .add_options([""].concat(erpnext.all_timezones)); } }) @@ -82,19 +88,15 @@ $.extend(erpnext.complete_setup, { var country = d.fields_dict.country.input.value; var $timezone = $(d.fields_dict.timezone.input); $timezone.empty(); + // add country specific timezones first if(country){ - var timezone_list = erpnext.country_info[country].timezones; - if(timezone_list.length==0) { - timezone_list = $.map(erpnext.country_info, function(m) { - return m.timezones - }); - } - $timezone.empty().add_options(timezone_list); - - console.log(d.get_input("currency")) + var timezone_list = erpnext.country_info[country].timezones || []; + $timezone.add_options(timezone_list.sort()); d.get_input("currency").val(erpnext.country_info[country].currency); } + // add all timezones at the end, so that user has the option to change it to any timezone + $timezone.add_options([""].concat(erpnext.all_timezones)); }; @@ -127,5 +129,5 @@ $.extend(erpnext.complete_setup, { 'Finance', 'Food and Beverage', 'Government', 'Healthcare', 'Hospitality', 'Information Technology', 'Insurance', 'Machinery', 'Manufacturing', 'Media', 'Not For Profit', 'Recreation', 'Retail', 'Shipping', 'Technology', - 'Telecommunications', 'Transportation', 'Trading', 'Utilities', 'Other'], + 'Telecommunications', 'Transportation', 'Trading', 'Utilities', 'Other'], }); \ No newline at end of file From fdefe0750aae179a6afe93e8c01582dd25ef9eb4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 20 May 2013 13:59:24 +0530 Subject: [PATCH 149/295] [fixes] item valuation rate in pur cycle --- controllers/buying_controller.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 28d2db646b..25efe4dbd3 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -367,13 +367,14 @@ class BuyingController(StockController): if d.item_code and d.qty: # if no item code, which is sometimes the case in purchase invoice, # then it is not possible to track valuation against it - d.valuation_rate = flt((flt(d.purchase_rate, self.precision.item.purchase_rate) or - flt(d.rate, self.precision.item.rate) + + d.valuation_rate = flt(((flt(d.purchase_rate, self.precision.item.purchase_rate) or + flt(d.rate, self.precision.item.rate)) + (flt(d.item_tax_amount, self.precision.item.item_tax_amount) + flt(d.rm_supp_cost, self.precision.item.rm_supp_cost)) / flt(d.qty, self.precision.item.qty)) / flt(d.conversion_factor, self.precision.item.conversion_factor), - self.precision.item.valuation_rate) + self.precision.item.valuation_rate) + else: d.valuation_rate = 0.0 From 9053cbc9f83b2ec70372d24d93ecba45bb0b7043 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 20 May 2013 15:28:44 +0530 Subject: [PATCH 150/295] [fixes][patch] update valuation rate in purchase cycle and repost patch --- controllers/buying_controller.py | 1 - patches/may_2013/p02_update_valuation_rate.py | 48 +++++++++++++++++++ patches/patch_list.py | 1 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 patches/may_2013/p02_update_valuation_rate.py diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 25efe4dbd3..3deda0284b 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -374,7 +374,6 @@ class BuyingController(StockController): flt(d.qty, self.precision.item.qty)) / flt(d.conversion_factor, self.precision.item.conversion_factor), self.precision.item.valuation_rate) - else: d.valuation_rate = 0.0 diff --git a/patches/may_2013/p02_update_valuation_rate.py b/patches/may_2013/p02_update_valuation_rate.py new file mode 100644 index 0000000000..d538d609dd --- /dev/null +++ b/patches/may_2013/p02_update_valuation_rate.py @@ -0,0 +1,48 @@ +# 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(): + from stock.stock_ledger import update_entries_after + item_warehouse = [] + # update valuation_rate in transaction + doctypes = {"Purchase Receipt": "purchase_receipt_details", + "Purchase Invoice": "entries"} + + for dt in doctypes: + for d in webnotes.conn.sql("""select name from `tab%s` + where modified >= '2013-05-09' and docstatus=1""" % dt): + rec = webnotes.get_obj(dt, d[0]) + rec.update_valuation_rate(doctypes[dt]) + + for item in rec.doclist.get({"parentfield": doctypes[dt]}): + webnotes.conn.sql("""update `tab%s Item` set valuation_rate = %s + where name = %s"""% (dt, '%s', '%s'), tuple([item.valuation_rate, item.name])) + + if dt == "Purchase Receipt": + webnotes.conn.sql("""update `tabStock Ledger Entry` set incoming_rate = %s + where voucher_detail_no = %s""", (item.valuation_rate, item.name)) + if [item.item_code, item.warehouse] not in item_warehouse: + item_warehouse.append([item.item_code, item.warehouse]) + + for d in item_warehouse: + try: + update_entries_after({"item_code": d[0], "warehouse": d[1], + "posting_date": "2013-01-01", "posting_time": "00:05:00"}) + webnotes.conn.commit() + except: + pass \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 16100097ce..bd7acb2fc6 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -248,4 +248,5 @@ patch_list = [ "patches.april_2013.p08_price_list_country", "patches.may_2013.repost_stock_for_no_posting_time", "patches.may_2013.p01_conversion_factor_and_aii", + "patches.may_2013.p02_update_valuation_rate", ] \ No newline at end of file From f8525fea063665a7078dc1a2ef43eaece34ec169 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 20 May 2013 15:30:02 +0530 Subject: [PATCH 151/295] [fixes][patch] update valuation rate in purchase cycle and repost patch --- patches/may_2013/p02_update_valuation_rate.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patches/may_2013/p02_update_valuation_rate.py b/patches/may_2013/p02_update_valuation_rate.py index d538d609dd..280473ccef 100644 --- a/patches/may_2013/p02_update_valuation_rate.py +++ b/patches/may_2013/p02_update_valuation_rate.py @@ -20,8 +20,7 @@ def execute(): from stock.stock_ledger import update_entries_after item_warehouse = [] # update valuation_rate in transaction - doctypes = {"Purchase Receipt": "purchase_receipt_details", - "Purchase Invoice": "entries"} + doctypes = {"Purchase Receipt": "purchase_receipt_details", "Purchase Invoice": "entries"} for dt in doctypes: for d in webnotes.conn.sql("""select name from `tab%s` From a35466b00862d0c5feb05443b37b8c665ce25e61 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 20 May 2013 18:32:06 +0530 Subject: [PATCH 152/295] [price list] on deletion of price list, delete all item price record for the same price list --- setup/doctype/price_list/price_list.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup/doctype/price_list/price_list.py b/setup/doctype/price_list/price_list.py index ae49bf868d..5c03a3a853 100644 --- a/setup/doctype/price_list/price_list.py +++ b/setup/doctype/price_list/price_list.py @@ -33,3 +33,6 @@ class DocType: msgprint(_("""Please check "Valid For All Countries" or \ enter atlease one row in the "Countries" table."""), raise_exception=True) + def on_trash(self): + webnotes.conn.sql("""delete from `tabItem Price` where price_list_name = %s""", + self.doc.name) \ No newline at end of file From 5e50ffa308fe4790f48f15d8cbd23af4d028718a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 21 May 2013 15:31:32 +0530 Subject: [PATCH 153/295] [fixes] employee query in leave application --- .../leave_application/leave_application.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/hr/doctype/leave_application/leave_application.py b/hr/doctype/leave_application/leave_application.py index 6e39751b71..7c47d6a09e 100755 --- a/hr/doctype/leave_application/leave_application.py +++ b/hr/doctype/leave_application/leave_application.py @@ -335,15 +335,22 @@ def add_holidays(events, start, end, employee, company): def query_for_permitted_employees(doctype, txt, searchfield, start, page_len, filters): txt = "%" + cstr(txt) + "%" - return webnotes.conn.sql("""select name, employee_name from `tabEmployee` emp + if "Leave Approver" in webnotes.user.get_roles(): + condition = """and (exists(select ela.name from `tabEmployee Leave Approver` ela + where ela.parent=`tabEmployee`.name and ela.leave_approver= "%s") or + not exists(select ela.name from `tabEmployee Leave Approver` ela + where ela.parent=`tabEmployee`.name) + or user_id = "%s")""" % (webnotes.session.user, webnotes.session.user) + else: + from webnotes.widgets.reportview import build_match_conditions + condition = build_match_conditions("Employee") + condition = ("and " + condition) if condition else "" + + return webnotes.conn.sql("""select name, employee_name from `tabEmployee` where status = 'Active' and docstatus < 2 and - (`%s` like %s or employee_name like %s) and - (exists(select ela.name from `tabEmployee Leave Approver` ela - where ela.parent=emp.name and ela.leave_approver=%s) or - not exists(select ela.name from `tabEmployee Leave Approver` ela where ela.parent=emp.name) - or user_id = %s) + (`%s` like %s or employee_name like %s) %s order by case when name like %s then 0 else 1 end, case when employee_name like %s then 0 else 1 end, - name limit %s, %s""" % tuple([searchfield] + ["%s"]*8), - (txt, txt, webnotes.session.user, webnotes.session.user, txt, txt, start, page_len)) + name limit %s, %s""" % tuple([searchfield] + ["%s"]*2 + [condition] + ["%s"]*4), + (txt, txt, txt, txt, start, page_len)) From e2089fb7122e05f6722adf134ba9ef0907361150 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 21 May 2013 16:53:31 +0530 Subject: [PATCH 154/295] [fixes] set lead, contact and company on support ticket --- .../support_ticket/get_support_mails.py | 4 +- .../doctype/support_ticket/support_ticket.py | 24 ++++++-- .../doctype/support_ticket/support_ticket.txt | 59 +++++++------------ 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/support/doctype/support_ticket/get_support_mails.py b/support/doctype/support_ticket/get_support_mails.py index d1fe0dc8da..82ae9a34b6 100644 --- a/support/doctype/support_ticket/get_support_mails.py +++ b/support/doctype/support_ticket/get_support_mails.py @@ -50,7 +50,7 @@ class SupportMailbox(POP3Mailbox): "subject": mail.mail["Subject"], "raised_by": mail.from_email, "content_type": mail.content_type, - "status": "Open" + "status": "Open", }]) ticket.insert() new_ticket = True @@ -58,7 +58,7 @@ class SupportMailbox(POP3Mailbox): mail.save_attachments_in_doc(ticket.doc) make(content=mail.content, sender=mail.from_email, subject = ticket.doc.subject, - doctype="Support Ticket", name=ticket.doc.name, + doctype="Support Ticket", name=ticket.doc.name, lead = ticket.doc.lead, contact=ticket.doc.contact, date=mail.date) if new_ticket and cint(self.email_settings.send_autoreply) and \ diff --git a/support/doctype/support_ticket/support_ticket.py b/support/doctype/support_ticket/support_ticket.py index ca46a8e073..5f01516ba0 100644 --- a/support/doctype/support_ticket/support_ticket.py +++ b/support/doctype/support_ticket/support_ticket.py @@ -18,7 +18,6 @@ from __future__ import unicode_literals import webnotes from utilities.transaction_base import TransactionBase -from home import update_feed from webnotes.utils import now class DocType(TransactionBase): @@ -44,6 +43,7 @@ class DocType(TransactionBase): def validate(self): self.update_status() + self.set_lead_contact(self.doc.raised_by) if self.doc.status == "Closed": from webnotes.widgets.form.assign_to import clear @@ -51,10 +51,24 @@ class DocType(TransactionBase): def on_communication_sent(self, comm): webnotes.conn.set(self.doc, 'status', 'Waiting for Customer') - if comm.lead and not self.doc.lead: - webnotes.conn.set(self.doc, 'lead', comm.lead) - if comm.contact and not self.doc.contact: - webnotes.conn.set(self.doc, 'contact', comm.contact) + + + def set_lead_contact(self, email_id): + import email.utils + email_id = email.utils.parseaddr(email_id) + if email_id: + if not self.doc.lead: + self.doc.lead = webnotes.conn.get_value("Lead", {"email_id": email_id}) + if not self.doc.contact: + self.doc.contact = webnotes.conn.get_value("Contact", {"email_id": email_id}) + + if not self.doc.company: + if self.doc.lead: + company = webnotes.conn.get_value("Lead", self.doc.lead, "company") + elif self.doc.contact: + company = webnotes.conn.get_value("Contact", self.doc.contact, "company") + + self.doc.company = company or webnotes.conn.get_default("company") def on_trash(self): webnotes.conn.sql("""update `tabCommunication` set support_ticket=NULL diff --git a/support/doctype/support_ticket/support_ticket.txt b/support/doctype/support_ticket/support_ticket.txt index 769bb9d701..50e547ece7 100644 --- a/support/doctype/support_ticket/support_ticket.txt +++ b/support/doctype/support_ticket/support_ticket.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-31 22:22:27", + "creation": "2013-02-01 10:36:25", "docstatus": 0, - "modified": "2013-01-31 22:17:24", + "modified": "2013-05-21 16:27:46", "modified_by": "Administrator", "owner": "Administrator" }, @@ -23,13 +23,18 @@ "permlevel": 0 }, { + "amend": 0, + "create": 1, "doctype": "DocPerm", "name": "__common__", "parent": "Support Ticket", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, - "submit": 0 + "report": 1, + "submit": 0, + "write": 1 }, { "doctype": "DocType", @@ -191,6 +196,15 @@ "oldfieldtype": "Time", "read_only": 1 }, + { + "doctype": "DocField", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "print_hide": 1, + "reqd": 0 + }, { "depends_on": "eval:!doc.__islocal", "doctype": "DocField", @@ -237,51 +251,18 @@ "label": "Content Type" }, { - "amend": 0, "cancel": 0, - "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Guest", - "write": 1 + "role": "Guest" }, { - "create": 1, + "cancel": 0, "doctype": "DocPerm", - "match": "customer", - "permlevel": 0, - "report": 1, - "role": "Customer", - "write": 1 + "role": "Customer" }, { "cancel": 1, - "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Support Team", - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 1, - "role": "Support Team", - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 2, "role": "Support Team" } ] \ No newline at end of file From cb9904576427a1b0fe0487b9cb47044c9a91ea36 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 21 May 2013 17:17:40 +0530 Subject: [PATCH 155/295] [patch] update lead, contact in support ticket --- patches/may_2013/p03_update_support_ticket.py | 25 +++++++++++++++++++ patches/patch_list.py | 1 + .../doctype/support_ticket/support_ticket.py | 10 +++----- 3 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 patches/may_2013/p03_update_support_ticket.py diff --git a/patches/may_2013/p03_update_support_ticket.py b/patches/may_2013/p03_update_support_ticket.py new file mode 100644 index 0000000000..bbdb6e24ec --- /dev/null +++ b/patches/may_2013/p03_update_support_ticket.py @@ -0,0 +1,25 @@ +# 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(): + for d in webnotes.conn.sql("""select name, raised_by from `tabSupport Ticket` + where docstatus < 2""", as_dict=True): + tic = webnotes.get_obj("Support Ticket", d.name) + tic.set_lead_contact(d.raised_by) + webnotes.conn.sql("""update `tabSupport Ticket` set lead = %s, contact = %s, company = %s + where name = %s""", (tic.doc.lead, tic.doc.contact, tic.doc.company, d.name)) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index bd7acb2fc6..f28751c818 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -249,4 +249,5 @@ patch_list = [ "patches.may_2013.repost_stock_for_no_posting_time", "patches.may_2013.p01_conversion_factor_and_aii", "patches.may_2013.p02_update_valuation_rate", + "patches.may_2013.p03_update_support_ticket", ] \ No newline at end of file diff --git a/support/doctype/support_ticket/support_ticket.py b/support/doctype/support_ticket/support_ticket.py index 5f01516ba0..63548d3436 100644 --- a/support/doctype/support_ticket/support_ticket.py +++ b/support/doctype/support_ticket/support_ticket.py @@ -62,13 +62,9 @@ class DocType(TransactionBase): if not self.doc.contact: self.doc.contact = webnotes.conn.get_value("Contact", {"email_id": email_id}) - if not self.doc.company: - if self.doc.lead: - company = webnotes.conn.get_value("Lead", self.doc.lead, "company") - elif self.doc.contact: - company = webnotes.conn.get_value("Contact", self.doc.contact, "company") - - self.doc.company = company or webnotes.conn.get_default("company") + if not self.doc.company: + self.doc.company = webnotes.conn.get_value("Lead", self.doc.lead, "company") or \ + webnotes.conn.get_default("company") def on_trash(self): webnotes.conn.sql("""update `tabCommunication` set support_ticket=NULL From b7e07ac6a5ce39a77d5337a515254993e1258175 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 22 May 2013 12:10:16 +0530 Subject: [PATCH 156/295] [fixes] updated no_copy field --- .../purchase_invoice_item.txt | 8 ++- .../sales_invoice_item/sales_invoice_item.txt | 8 ++- .../purchase_order_item.txt | 38 ++++++++++---- .../supplier_quotation_item.txt | 34 ++++++++---- .../doctype/quotation_item/quotation_item.txt | 13 ++++- .../sales_order_item/sales_order_item.txt | 15 +++++- .../delivery_note_item/delivery_note_item.txt | 7 ++- .../purchase_receipt_item.txt | 52 ++++++++++++++----- 8 files changed, 132 insertions(+), 43 deletions(-) diff --git a/accounts/doctype/purchase_invoice_item/purchase_invoice_item.txt b/accounts/doctype/purchase_invoice_item/purchase_invoice_item.txt index e33ab7c569..8df4306f57 100755 --- a/accounts/doctype/purchase_invoice_item/purchase_invoice_item.txt +++ b/accounts/doctype/purchase_invoice_item/purchase_invoice_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-10 08:35:38", + "creation": "2013-04-19 11:00:07", "docstatus": 0, - "modified": "2013-04-17 14:05:20", + "modified": "2013-05-22 12:01:56", "modified_by": "Administrator", "owner": "Administrator" }, @@ -224,6 +224,7 @@ "fieldtype": "Link", "in_filter": 1, "label": "Pur Order", + "no_copy": 1, "oldfieldname": "purchase_order", "oldfieldtype": "Link", "options": "Purchase Order", @@ -238,6 +239,7 @@ "hidden": 1, "in_filter": 1, "label": "Purchase Order Item", + "no_copy": 1, "oldfieldname": "po_detail", "oldfieldtype": "Data", "print_hide": 1, @@ -250,6 +252,7 @@ "fieldtype": "Link", "in_filter": 1, "label": "Pur Receipt", + "no_copy": 1, "oldfieldname": "purchase_receipt", "oldfieldtype": "Link", "options": "Purchase Receipt", @@ -264,6 +267,7 @@ "hidden": 1, "in_filter": 1, "label": "PR Detail", + "no_copy": 1, "oldfieldname": "pr_detail", "oldfieldtype": "Data", "print_hide": 1, diff --git a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt index 2a6384d762..89c86f8219 100644 --- a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt +++ b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-10 08:35:44", + "creation": "2013-04-19 11:00:07", "docstatus": 0, - "modified": "2013-04-17 14:05:20", + "modified": "2013-05-22 12:06:15", "modified_by": "Administrator", "owner": "Administrator" }, @@ -305,6 +305,7 @@ "fieldtype": "Link", "in_filter": 1, "label": "Sales Order", + "no_copy": 1, "oldfieldname": "sales_order", "oldfieldtype": "Link", "options": "Sales Order", @@ -319,6 +320,7 @@ "hidden": 1, "in_filter": 1, "label": "SO Detail ", + "no_copy": 1, "oldfieldname": "so_detail", "oldfieldtype": "Data", "print_hide": 1, @@ -331,6 +333,7 @@ "fieldtype": "Link", "in_filter": 1, "label": "Delivery Note", + "no_copy": 1, "oldfieldname": "delivery_note", "oldfieldtype": "Link", "options": "Delivery Note", @@ -345,6 +348,7 @@ "hidden": 1, "in_filter": 1, "label": "DN Detail", + "no_copy": 1, "oldfieldname": "dn_detail", "oldfieldtype": "Data", "print_hide": 1, diff --git a/buying/doctype/purchase_order_item/purchase_order_item.txt b/buying/doctype/purchase_order_item/purchase_order_item.txt index cd00f87253..01a144a143 100755 --- a/buying/doctype/purchase_order_item/purchase_order_item.txt +++ b/buying/doctype/purchase_order_item/purchase_order_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:42", + "creation": "2013-03-07 11:42:55", "docstatus": 0, - "modified": "2013-03-07 07:03:27", + "modified": "2013-05-22 11:59:52", "modified_by": "Administrator", "owner": "Administrator" }, @@ -36,6 +36,7 @@ "oldfieldname": "schedule_date", "oldfieldtype": "Date", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -49,6 +50,7 @@ "oldfieldtype": "Link", "options": "Item", "print_hide": 0, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -72,6 +74,7 @@ "oldfieldname": "item_name", "oldfieldtype": "Data", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -83,6 +86,7 @@ "oldfieldname": "description", "oldfieldtype": "Small Text", "print_width": "300px", + "read_only": 0, "reqd": 1, "width": "300px" }, @@ -95,6 +99,7 @@ "oldfieldname": "qty", "oldfieldtype": "Currency", "print_width": "60px", + "read_only": 0, "reqd": 1, "width": "60px" }, @@ -108,6 +113,7 @@ "options": "UOM", "print_hide": 0, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -117,14 +123,16 @@ "fieldtype": "Currency", "label": "Ref Rate ", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "discount_rate", "fieldtype": "Float", "label": "Discount %", - "print_hide": 0 + "print_hide": 0, + "read_only": 0 }, { "doctype": "DocField", @@ -135,7 +143,8 @@ "oldfieldname": "import_rate", "oldfieldtype": "Currency", "options": "currency", - "print_hide": 0 + "print_hide": 0, + "read_only": 0 }, { "doctype": "DocField", @@ -153,7 +162,8 @@ "fieldtype": "Currency", "label": "Ref Rate*", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "default": "0.00", @@ -166,6 +176,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -192,6 +203,7 @@ "oldfieldtype": "Link", "options": "Warehouse", "print_hide": 1, + "read_only": 0, "reqd": 0 }, { @@ -202,6 +214,7 @@ "label": "Project Name", "options": "Project", "print_hide": 1, + "read_only": 0, "report_hide": 0 }, { @@ -214,6 +227,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -237,7 +251,7 @@ "fieldtype": "Data", "hidden": 1, "label": "Prevdoc DocType", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_doctype", "oldfieldtype": "Data", "print_hide": 1, @@ -250,7 +264,7 @@ "hidden": 0, "in_filter": 1, "label": "Material Request No", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_docname", "oldfieldtype": "Link", "options": "Material Request", @@ -267,6 +281,7 @@ "hidden": 1, "in_filter": 1, "label": "Material Request Date", + "no_copy": 1, "oldfieldname": "prevdoc_date", "oldfieldtype": "Date", "print_hide": 1, @@ -280,7 +295,7 @@ "hidden": 1, "in_filter": 1, "label": "Material Request Detail No", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_detail_docname", "oldfieldtype": "Data", "print_hide": 1, @@ -294,6 +309,7 @@ "hidden": 1, "in_filter": 0, "label": "Supplier Quotation", + "no_copy": 1, "options": "Supplier Quotation", "read_only": 1, "search_index": 0 @@ -304,6 +320,7 @@ "fieldtype": "Link", "hidden": 1, "label": "Supplier Quotation Item", + "no_copy": 1, "options": "Supplier Quotation Item", "read_only": 1 }, @@ -395,6 +412,7 @@ "no_copy": 1, "oldfieldname": "page_break", "oldfieldtype": "Check", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 } ] \ No newline at end of file diff --git a/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt b/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt index 53fa9f8d95..6b24d2f86f 100644 --- a/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt +++ b/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:43", + "creation": "2013-03-07 11:42:56", "docstatus": 0, - "modified": "2013-03-07 07:03:32", + "modified": "2013-05-22 12:02:28", "modified_by": "Administrator", "owner": "Administrator" }, @@ -35,6 +35,7 @@ "oldfieldtype": "Link", "options": "Item", "print_hide": 0, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -58,6 +59,7 @@ "oldfieldname": "item_name", "oldfieldtype": "Data", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -69,6 +71,7 @@ "oldfieldname": "description", "oldfieldtype": "Small Text", "print_width": "300px", + "read_only": 0, "reqd": 1, "width": "300px" }, @@ -81,6 +84,7 @@ "oldfieldname": "qty", "oldfieldtype": "Currency", "print_width": "60px", + "read_only": 0, "reqd": 1, "width": "60px" }, @@ -94,6 +98,7 @@ "options": "UOM", "print_hide": 0, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -103,14 +108,16 @@ "fieldtype": "Currency", "label": "Ref Rate ", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "discount_rate", "fieldtype": "Float", "label": "Discount %", - "print_hide": 0 + "print_hide": 0, + "read_only": 0 }, { "doctype": "DocField", @@ -121,7 +128,8 @@ "oldfieldname": "import_rate", "oldfieldtype": "Currency", "options": "currency", - "print_hide": 0 + "print_hide": 0, + "read_only": 0 }, { "doctype": "DocField", @@ -139,7 +147,8 @@ "fieldtype": "Currency", "label": "Ref Rate*", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "default": "0.00", @@ -152,6 +161,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -178,6 +188,7 @@ "oldfieldtype": "Link", "options": "Warehouse", "print_hide": 1, + "read_only": 0, "reqd": 0 }, { @@ -188,6 +199,7 @@ "label": "Project Name", "options": "Project", "print_hide": 1, + "read_only": 0, "report_hide": 0 }, { @@ -196,7 +208,7 @@ "fieldtype": "Data", "hidden": 1, "label": "Prevdoc DocType", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_doctype", "oldfieldtype": "Data", "print_hide": 1, @@ -209,7 +221,7 @@ "hidden": 0, "in_filter": 1, "label": "Material Request No", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_docname", "oldfieldtype": "Link", "options": "Material Request", @@ -226,6 +238,7 @@ "hidden": 1, "in_filter": 1, "label": "Material Request Date", + "no_copy": 1, "oldfieldname": "prevdoc_date", "oldfieldtype": "Date", "print_hide": 1, @@ -239,7 +252,7 @@ "hidden": 1, "in_filter": 1, "label": "Material Request Detail No", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_detail_docname", "oldfieldtype": "Data", "print_hide": 1, @@ -295,6 +308,7 @@ "no_copy": 1, "oldfieldname": "page_break", "oldfieldtype": "Check", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 } ] \ No newline at end of file diff --git a/selling/doctype/quotation_item/quotation_item.txt b/selling/doctype/quotation_item/quotation_item.txt index dccc503764..bcb9281cb9 100644 --- a/selling/doctype/quotation_item/quotation_item.txt +++ b/selling/doctype/quotation_item/quotation_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:52", + "creation": "2013-03-07 11:42:57", "docstatus": 0, - "modified": "2013-03-07 07:03:29", + "modified": "2013-05-22 12:08:32", "modified_by": "Administrator", "owner": "Administrator" }, @@ -37,6 +37,7 @@ "options": "Item", "print_hide": 0, "print_width": "150px", + "read_only": 0, "reqd": 1, "search_index": 1, "width": "150px" @@ -60,6 +61,7 @@ "oldfieldtype": "Data", "print_hide": 1, "print_width": "150px", + "read_only": 0, "reqd": 1, "search_index": 1, "width": "150px" @@ -73,6 +75,7 @@ "oldfieldtype": "Small Text", "print_hide": 0, "print_width": "300px", + "read_only": 0, "reqd": 1, "width": "300px" }, @@ -87,6 +90,7 @@ "oldfieldtype": "Currency", "print_hide": 0, "print_width": "100px", + "read_only": 0, "reqd": 1, "search_index": 0, "width": "100px" @@ -115,6 +119,7 @@ "options": "currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 0, "width": "100px" }, @@ -128,6 +133,7 @@ "oldfieldtype": "Float", "print_hide": 1, "print_width": "100px", + "read_only": 0, "width": "100px" }, { @@ -142,6 +148,7 @@ "options": "currency", "print_hide": 0, "print_width": "100px", + "read_only": 0, "reqd": 0, "search_index": 0, "width": "100px" @@ -188,6 +195,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 0, "search_index": 0, "width": "100px" @@ -291,6 +299,7 @@ "oldfieldname": "page_break", "oldfieldtype": "Check", "print_hide": 1, + "read_only": 0, "report_hide": 1 } ] \ No newline at end of file diff --git a/selling/doctype/sales_order_item/sales_order_item.txt b/selling/doctype/sales_order_item/sales_order_item.txt index fff2d080f3..c65ac0d938 100644 --- a/selling/doctype/sales_order_item/sales_order_item.txt +++ b/selling/doctype/sales_order_item/sales_order_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:52", + "creation": "2013-03-07 11:42:58", "docstatus": 0, - "modified": "2013-03-07 07:03:30", + "modified": "2013-05-22 12:09:03", "modified_by": "Administrator", "owner": "Administrator" }, @@ -35,6 +35,7 @@ "oldfieldtype": "Link", "options": "Item", "print_width": "150px", + "read_only": 0, "reqd": 1, "search_index": 1, "width": "150px" @@ -57,6 +58,7 @@ "oldfieldtype": "Data", "print_hide": 1, "print_width": "150", + "read_only": 0, "reqd": 1, "width": "150" }, @@ -69,6 +71,7 @@ "oldfieldname": "description", "oldfieldtype": "Small Text", "print_width": "300px", + "read_only": 0, "reqd": 1, "search_index": 1, "width": "300px" @@ -82,6 +85,7 @@ "oldfieldname": "qty", "oldfieldtype": "Currency", "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -109,6 +113,7 @@ "options": "currency", "print_hide": 1, "print_width": "70px", + "read_only": 0, "reqd": 0, "width": "70px" }, @@ -122,6 +127,7 @@ "oldfieldtype": "Float", "print_hide": 1, "print_width": "70px", + "read_only": 0, "width": "70px" }, { @@ -134,6 +140,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_width": "100px", + "read_only": 0, "reqd": 0, "width": "100px" }, @@ -176,6 +183,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 0, "width": "100px" }, @@ -206,6 +214,7 @@ "options": "Warehouse", "print_hide": 1, "print_width": "150px", + "read_only": 0, "reqd": 0, "width": "150px" }, @@ -329,6 +338,7 @@ "hidden": 0, "in_filter": 1, "label": "Quotation No.", + "no_copy": 1, "oldfieldname": "prevdoc_docname", "oldfieldtype": "Link", "options": "Quotation", @@ -345,6 +355,7 @@ "oldfieldname": "page_break", "oldfieldtype": "Check", "print_hide": 1, + "read_only": 0, "report_hide": 1 }, { diff --git a/stock/doctype/delivery_note_item/delivery_note_item.txt b/stock/doctype/delivery_note_item/delivery_note_item.txt index 1073f0cdc2..f90ba69f53 100644 --- a/stock/doctype/delivery_note_item/delivery_note_item.txt +++ b/stock/doctype/delivery_note_item/delivery_note_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-01 10:49:21", + "creation": "2013-04-22 13:15:44", "docstatus": 0, - "modified": "2013-04-17 17:20:58", + "modified": "2013-05-22 12:05:32", "modified_by": "Administrator", "owner": "Administrator" }, @@ -340,6 +340,7 @@ "hidden": 1, "in_filter": 1, "label": "Document Type", + "no_copy": 1, "oldfieldname": "prevdoc_doctype", "oldfieldtype": "Data", "print_hide": 1, @@ -371,6 +372,7 @@ "hidden": 1, "in_filter": 1, "label": "Against Document Date", + "no_copy": 1, "oldfieldname": "prevdoc_date", "oldfieldtype": "Date", "print_hide": 1, @@ -383,6 +385,7 @@ "hidden": 1, "in_filter": 1, "label": "Against Document Detail No", + "no_copy": 1, "oldfieldname": "prevdoc_detail_docname", "oldfieldtype": "Data", "print_hide": 1, diff --git a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt index 7f4e827aa3..8cef6a3534 100755 --- a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt +++ b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:28:03", + "creation": "2013-03-07 11:42:59", "docstatus": 0, - "modified": "2013-03-07 07:03:28", + "modified": "2013-05-22 12:01:08", "modified_by": "Administrator", "owner": "Administrator" }, @@ -35,6 +35,7 @@ "oldfieldtype": "Link", "options": "Item", "print_width": "100px", + "read_only": 0, "reqd": 1, "search_index": 1, "width": "100px" @@ -48,6 +49,7 @@ "oldfieldname": "item_name", "oldfieldtype": "Data", "print_hide": 1, + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -59,6 +61,7 @@ "oldfieldname": "description", "oldfieldtype": "Text", "print_width": "300px", + "read_only": 0, "reqd": 1, "width": "300px" }, @@ -72,6 +75,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -84,6 +88,7 @@ "oldfieldname": "qty", "oldfieldtype": "Currency", "print_width": "100px", + "read_only": 0, "width": "100px" }, { @@ -97,6 +102,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "search_index": 0, "width": "100px" }, @@ -110,6 +116,7 @@ "options": "UOM", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -119,14 +126,16 @@ "fieldtype": "Currency", "label": "Ref Rate ", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", "fieldname": "discount_rate", "fieldtype": "Float", "label": "Discount %", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "default": "0.00", @@ -139,6 +148,7 @@ "options": "currency", "print_hide": 0, "print_width": "100px", + "read_only": 0, "width": "100px" }, { @@ -157,7 +167,8 @@ "fieldtype": "Currency", "label": "Ref Rate*", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "default": "0.00", @@ -170,6 +181,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -184,6 +196,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 0, "width": "100px" }, @@ -198,6 +211,7 @@ "options": "Warehouse", "print_hide": 1, "print_width": "100px", + "read_only": 0, "width": "100px" }, { @@ -209,6 +223,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "reqd": 1, "width": "100px" }, @@ -235,6 +250,7 @@ "oldfieldname": "serial_no", "oldfieldtype": "Text", "print_hide": 0, + "read_only": 0, "report_hide": 0 }, { @@ -242,7 +258,8 @@ "fieldname": "rejected_serial_no", "fieldtype": "Text", "label": "Rejected Serial No", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -252,7 +269,8 @@ "oldfieldname": "batch_no", "oldfieldtype": "Link", "options": "Batch", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -278,6 +296,7 @@ "oldfieldname": "schedule_date", "oldfieldtype": "Date", "print_hide": 1, + "read_only": 0, "report_hide": 0, "reqd": 0 }, @@ -288,7 +307,8 @@ "in_filter": 1, "label": "Project Name", "options": "Project", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -299,7 +319,8 @@ "oldfieldname": "qa_no", "oldfieldtype": "Link", "options": "Quality Inspection", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -336,6 +357,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", + "read_only": 0, "width": "100px" }, { @@ -344,9 +366,11 @@ "fieldtype": "Data", "hidden": 1, "label": "Prevdoc Doctype", + "no_copy": 1, "oldfieldname": "prevdoc_doctype", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -355,7 +379,7 @@ "hidden": 0, "in_filter": 1, "label": "PO No", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_docname", "oldfieldtype": "Link", "options": "Purchase Order", @@ -373,6 +397,7 @@ "hidden": 1, "in_filter": 1, "label": "PO Date", + "no_copy": 1, "oldfieldname": "prevdoc_date", "oldfieldtype": "Date", "print_hide": 1, @@ -418,7 +443,7 @@ "hidden": 1, "in_filter": 1, "label": "Purchase Order Item No", - "no_copy": 0, + "no_copy": 1, "oldfieldname": "prevdoc_detail_docname", "oldfieldtype": "Data", "print_hide": 1, @@ -479,6 +504,7 @@ "label": "Page Break", "oldfieldname": "page_break", "oldfieldtype": "Check", - "print_hide": 1 + "print_hide": 1, + "read_only": 0 } ] \ No newline at end of file From dcfb4b7ab93d39361557e507474676c8871e25b9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 22 May 2013 12:14:31 +0530 Subject: [PATCH 157/295] [website] [fix] explicitly pass meta description --- setup/doctype/item_group/item_group.py | 1 + website/doctype/blog_post/blog_post.py | 2 ++ website/doctype/web_page/web_page.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/setup/doctype/item_group/item_group.py b/setup/doctype/item_group/item_group.py index 1445f3999f..1ff3d4a939 100644 --- a/setup/doctype/item_group/item_group.py +++ b/setup/doctype/item_group/item_group.py @@ -81,3 +81,4 @@ class DocType(DocTypeNestedSet): if self.doc.slideshow: from website.helpers.slideshow import get_slideshow get_slideshow(self) + \ No newline at end of file diff --git a/website/doctype/blog_post/blog_post.py b/website/doctype/blog_post/blog_post.py index 05236a1ca2..62cc910860 100644 --- a/website/doctype/blog_post/blog_post.py +++ b/website/doctype/blog_post/blog_post.py @@ -76,10 +76,12 @@ class DocType: self.doc.full_name = get_fullname(self.doc.owner) self.doc.updated = global_date_format(self.doc.published_on) self.doc.content_html = self.doc.content + if self.doc.blogger: self.doc.blogger_info = webnotes.doc("Blogger", self.doc.blogger).fields self.doc.description = self.doc.blog_intro or self.doc.content[:140] + self.doc.meta_description = self.doc.description self.doc.categories = webnotes.conn.sql_list("select name from `tabBlog Category` order by name") diff --git a/website/doctype/web_page/web_page.py b/website/doctype/web_page/web_page.py index d43bcb4e3d..6d0cafaf6a 100644 --- a/website/doctype/web_page/web_page.py +++ b/website/doctype/web_page/web_page.py @@ -44,3 +44,5 @@ class DocType(): if self.doc.slideshow: from website.helpers.slideshow import get_slideshow get_slideshow(self) + + self.doc.meta_description = self.doc.description From ace9cbad9ecbc7947b2a2831580a001cc9f8ede7 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 22 May 2013 12:22:29 +0530 Subject: [PATCH 158/295] [support] [fix] [patch] reload support ticket and communication before the patch --- patches/may_2013/p03_update_support_ticket.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patches/may_2013/p03_update_support_ticket.py b/patches/may_2013/p03_update_support_ticket.py index bbdb6e24ec..7dc5854b44 100644 --- a/patches/may_2013/p03_update_support_ticket.py +++ b/patches/may_2013/p03_update_support_ticket.py @@ -17,6 +17,8 @@ from __future__ import unicode_literals import webnotes def execute(): + webnotes.reload_doc("support", "doctype", "support_ticket") + webnotes.reload_doc("core", "doctype", "communication") for d in webnotes.conn.sql("""select name, raised_by from `tabSupport Ticket` where docstatus < 2""", as_dict=True): tic = webnotes.get_obj("Support Ticket", d.name) From 6241012f08157d2d3c58b020566699bdfdae4de1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 22 May 2013 12:37:50 +0530 Subject: [PATCH 159/295] [website] [slideshow] clear website cache on update of a slide show --- website/doctype/website_slideshow/website_slideshow.py | 7 ++++++- website/templates/html/slideshow.html | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/website/doctype/website_slideshow/website_slideshow.py b/website/doctype/website_slideshow/website_slideshow.py index 928aa9ff9f..86bd2a0433 100644 --- a/website/doctype/website_slideshow/website_slideshow.py +++ b/website/doctype/website_slideshow/website_slideshow.py @@ -5,4 +5,9 @@ import webnotes class DocType: def __init__(self, d, dl): - self.doc, self.doclist = d, dl \ No newline at end of file + self.doc, self.doclist = d, dl + + def on_update(self): + # a slide show can be in use and any change in it should get reflected + from webnotes.webutils import clear_cache + clear_cache() \ No newline at end of file diff --git a/website/templates/html/slideshow.html b/website/templates/html/slideshow.html index e0e9038d9f..b26338c5e5 100644 --- a/website/templates/html/slideshow.html +++ b/website/templates/html/slideshow.html @@ -8,7 +8,7 @@ {% if slide.heading or slide.description %}

    {% endif %} From 488a821534099ffde11da31c72fbde1fabaa2e66 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 22 May 2013 15:51:47 +0530 Subject: [PATCH 160/295] [website] [fix] fixes in sitemap and rss generators --- startup/website.py | 4 ++-- website/helpers/blog_feed.py | 8 +++++--- website/helpers/sitemap.py | 10 ++++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/startup/website.py b/startup/website.py index be8eba6def..5e6c3118c2 100644 --- a/startup/website.py +++ b/startup/website.py @@ -1,5 +1,5 @@ import webnotes, conf, os -from webnotes.utils import cint, cstr +from webnotes.utils import cint, cstr, encode def get_templates_path(): return os.path.join(os.path.dirname(conf.__file__), "app", "website", "templates") @@ -72,7 +72,7 @@ def update_template_args(page_name, args): args[k] = cint(args.get(k) or 0) args.url = quote(str(get_request_site_address(full_address=True)), str("")) - args.encoded_title = quote(str(args.title or ""), str("")) + args.encoded_title = quote(encode(args.title or ""), str("")) return args \ No newline at end of file diff --git a/website/helpers/blog_feed.py b/website/helpers/blog_feed.py index 41c203e0ad..c79c5cc3bb 100644 --- a/website/helpers/blog_feed.py +++ b/website/helpers/blog_feed.py @@ -49,9 +49,10 @@ rss_item = u""" def generate(): """generate rss feed""" - import webnotes, os + import os, urllib + import webnotes from webnotes.model.doc import Document - from website.helpers.blog import get_blog_content + from webnotes.utils import escape_html host = (os.environ.get('HTTPS') and 'https://' or 'http://') + os.environ.get('HTTP_HOST') @@ -62,7 +63,8 @@ def generate(): order by published_on desc limit 20""", as_dict=1) for blog in blog_list: - blog.link = host + '/' + blog.name + '.html' + blog.link = urllib.quote(host + '/' + blog.name + '.html') + blog.content = escape_html(blog.content or "") items += rss_item % blog diff --git a/website/helpers/sitemap.py b/website/helpers/sitemap.py index c8b6fd0a53..3956da1a70 100644 --- a/website/helpers/sitemap.py +++ b/website/helpers/sitemap.py @@ -2,6 +2,7 @@ # License: GNU General Public License (v3). For more information see license.txt from __future__ import unicode_literals + frame_xml = """ %s """ @@ -32,10 +33,11 @@ def generate(domain): for p in pages: if count >= max_items: break - page_url = os.path.join(domain, urllib.quote(p[0])) - modified = p[1].strftime('%Y-%m-%d') - site_map += link_xml % (page_url, modified) - count += 1 + if p[0]: + page_url = os.path.join(domain, urllib.quote(p[0])) + modified = p[1].strftime('%Y-%m-%d') + site_map += link_xml % (page_url, modified) + count += 1 if count >= max_items: break From 62d0629d6119b1c5139c1bc7f750ae611db4c5a8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 22 May 2013 16:19:10 +0530 Subject: [PATCH 161/295] [feature] reorder level checking through scheduler --- .../journal_voucher/test_journal_voucher.py | 117 +----------------- patches/may_2013/p04_reorder_level.py | 23 ++++ patches/patch_list.py | 1 + .../global_defaults/global_defaults.txt | 48 ++++--- startup/schedule_handlers.py | 4 + stock/doctype/bin/bin.py | 84 +------------ stock/doctype/item/item.py | 8 ++ stock/doctype/item/item.txt | 17 +-- stock/utils.py | 117 +++++++++++++++++- 9 files changed, 185 insertions(+), 234 deletions(-) create mode 100644 patches/may_2013/p04_reorder_level.py diff --git a/accounts/doctype/journal_voucher/test_journal_voucher.py b/accounts/doctype/journal_voucher/test_journal_voucher.py index 7cfeb595d8..feb1e2ca5a 100644 --- a/accounts/doctype/journal_voucher/test_journal_voucher.py +++ b/accounts/doctype/journal_voucher/test_journal_voucher.py @@ -122,119 +122,4 @@ test_records = [ "parentfield": "entries", "cost_center": "_Test Cost Center - _TC" }], -] - - - - - - -# -# -# import webnotes.model -# from webnotes.utils import nowdate, flt, add_days -# from accounts.utils import get_fiscal_year, get_balance_on -# -# company = webnotes.conn.get_default("company") -# abbr = webnotes.conn.get_value("Company", company, "abbr") -# -# data = { -# "expense_account": { -# "doctype": "Account", -# "account_name": "Test Expense", -# "parent_account": "Direct Expenses - %s" % abbr, -# "company": company, -# "debit_or_credit": "Debit", -# "is_pl_account": "Yes", -# "group_or_ledger": "Ledger" -# }, -# "supplier_account": { -# "doctype": "Account", -# "account_name": "Test Supplier", -# "parent_account": "Accounts Payable - %s" % abbr, -# "company": company, -# "debit_or_credit": "Credit", -# "is_pl_account": "No", -# "group_or_ledger": "Ledger" -# }, -# "test_cost_center": { -# "doctype": "Cost Center", -# "cost_center_name": "Test Cost Center", -# "parent_cost_center": "Root - %s" % abbr, -# "company_name": company, -# "group_or_ledger": "Ledger", -# "company_abbr": abbr -# }, -# "journal_voucher": [ -# { -# "doctype": "Journal Voucher", -# "voucher_type": "Journal Entry", -# "naming_series": "JV", -# "posting_date": nowdate(), -# "remark": "Test Journal Voucher", -# "fiscal_year": get_fiscal_year(nowdate())[0], -# "company": company -# }, -# { -# "doctype": "Journal Voucher Detail", -# "parentfield": "entries", -# "account": "Test Expense - %s" % abbr, -# "debit": 5000, -# "cost_center": "Test Cost Center - %s" % abbr, -# }, -# { -# "doctype": "Journal Voucher Detail", -# "parentfield": "entries", -# "account": "Test Supplier - %s" % abbr, -# "credit": 5000, -# }, -# ] -# } -# -# def get_name(s): -# return s + " - " + abbr -# -# class TestJournalVoucher(unittest.TestCase): -# def setUp(self): -# webnotes.conn.begin() -# -# # create a dummy account -# webnotes.model.insert([data["expense_account"]]) -# webnotes.model.insert([data["supplier_account"]]) -# webnotes.model.insert([data["test_cost_center"]]) -# -# def tearDown(self): -# webnotes.conn.rollback() -# -# def test_save_journal_voucher(self): -# expense_ac_balance = get_balance_on(get_name("Test Expense"), nowdate()) -# supplier_ac_balance = get_balance_on(get_name("Test Supplier"), nowdate()) -# -# dl = webnotes.model.insert(data["journal_voucher"]) -# dl.submit() -# dl.load_from_db() -# -# # test submitted jv -# self.assertTrue(webnotes.conn.exists("Journal Voucher", dl.doclist[0].name)) -# for d in dl.doclist[1:]: -# self.assertEquals(webnotes.conn.get_value("Journal Voucher Detail", -# d.name, "parent"), dl.doclist[0].name) -# -# # test gl entry -# gle = webnotes.conn.sql("""select account, debit, credit -# from `tabGL Entry` where voucher_no = %s order by account""", -# dl.doclist[0].name) -# -# self.assertEquals((gle[0][0], flt(gle[0][1]), flt(gle[0][2])), -# ('Test Expense - %s' % abbr, 5000.0, 0.0)) -# self.assertEquals((gle[1][0], flt(gle[1][1]), flt(gle[1][2])), -# ('Test Supplier - %s' % abbr, 0.0, 5000.0)) -# -# # check balance as on today -# self.assertEqual(get_balance_on(get_name("Test Expense"), nowdate()), -# expense_ac_balance + 5000) -# self.assertEqual(get_balance_on(get_name("Test Supplier"), nowdate()), -# supplier_ac_balance + 5000) -# -# # check previous balance -# self.assertEqual(get_balance_on(get_name("Test Expense"), add_days(nowdate(), -1)), 0) \ No newline at end of file +] \ No newline at end of file diff --git a/patches/may_2013/p04_reorder_level.py b/patches/may_2013/p04_reorder_level.py new file mode 100644 index 0000000000..8f4d669bc5 --- /dev/null +++ b/patches/may_2013/p04_reorder_level.py @@ -0,0 +1,23 @@ +# 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(): + webnotes.reload_doc("Setup", "DocType", "Global Defaults") + + if webnotes.conn.exists({"doctype": "Item", "email_notify": 1}): + webnotes.conn.set_value("Global Defaults", None, "reorder_email_notify", 1) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index f28751c818..89f48e5ce7 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -250,4 +250,5 @@ patch_list = [ "patches.may_2013.p01_conversion_factor_and_aii", "patches.may_2013.p02_update_valuation_rate", "patches.may_2013.p03_update_support_ticket", + "patches.may_2013.p04_reorder_level", ] \ No newline at end of file diff --git a/setup/doctype/global_defaults/global_defaults.txt b/setup/doctype/global_defaults/global_defaults.txt index 853bb57705..175ca9414b 100644 --- a/setup/doctype/global_defaults/global_defaults.txt +++ b/setup/doctype/global_defaults/global_defaults.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-01 15:05:24", + "creation": "2013-05-02 17:53:24", "docstatus": 0, - "modified": "2013-05-02 15:05:21", + "modified": "2013-05-22 15:57:26", "modified_by": "Administrator", "owner": "Administrator" }, @@ -27,6 +27,8 @@ "permlevel": 0 }, { + "amend": 0, + "cancel": 0, "create": 1, "doctype": "DocPerm", "name": "__common__", @@ -170,7 +172,8 @@ "fieldname": "item_naming_by", "fieldtype": "Select", "label": "Item Naming By", - "options": "Item Code\nNaming Series" + "options": "Item Code\nNaming Series", + "read_only": 0 }, { "doctype": "DocField", @@ -212,14 +215,6 @@ "label": "Allow Negative Stock", "read_only": 0 }, - { - "doctype": "DocField", - "fieldname": "default_warehouse_type", - "fieldtype": "Link", - "label": "Default Warehouse Type", - "options": "Warehouse Type", - "read_only": 0 - }, { "doctype": "DocField", "fieldname": "auto_indent", @@ -227,6 +222,21 @@ "label": "Raise Material Request when stock reaches re-order level", "read_only": 0 }, + { + "doctype": "DocField", + "fieldname": "reorder_email_notify", + "fieldtype": "Check", + "label": "Notify by Email on creation of automatic Material Request" + }, + { + "default": "Hourly", + "doctype": "DocField", + "fieldname": "reorder_level_checking_frequency", + "fieldtype": "Select", + "hidden": 1, + "label": "Reorder Level Checking Frequency", + "options": "Hourly\nDaily" + }, { "default": "1", "doctype": "DocField", @@ -235,6 +245,14 @@ "read_only": 0, "width": "50%" }, + { + "doctype": "DocField", + "fieldname": "default_warehouse_type", + "fieldtype": "Link", + "label": "Default Warehouse Type", + "options": "Warehouse Type", + "read_only": 0 + }, { "description": "Percentage you are allowed to receive or deliver more against the quantity ordered.

    For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units

    ", "doctype": "DocField", @@ -274,7 +292,8 @@ "fieldtype": "Check", "label": "Auto Inventory Accounting", "no_copy": 0, - "print_hide": 1 + "print_hide": 1, + "read_only": 0 }, { "description": "Accounting entry frozen up to this date, nobody can do / modify entry except authorized person", @@ -507,11 +526,6 @@ "label": "SMS Sender Name", "read_only": 0 }, - { - "amend": 0, - "cancel": 0, - "doctype": "DocPerm" - }, { "doctype": "DocPerm" } diff --git a/startup/schedule_handlers.py b/startup/schedule_handlers.py index 0799817206..cc0d1f4fea 100644 --- a/startup/schedule_handlers.py +++ b/startup/schedule_handlers.py @@ -55,6 +55,10 @@ def execute_daily(): from setup.doctype.backup_manager.backup_manager import take_backups_daily take_backups_daily() + # check reorder level + from stock.utils import reorder_item + run_fn(reorder_item) + def execute_weekly(): from setup.doctype.backup_manager.backup_manager import take_backups_weekly take_backups_weekly() diff --git a/stock/doctype/bin/bin.py b/stock/doctype/bin/bin.py index 2d98c2634f..61baafafa2 100644 --- a/stock/doctype/bin/bin.py +++ b/stock/doctype/bin/bin.py @@ -77,10 +77,6 @@ class DocType: self.doc.save() - if (flt(args.get("actual_qty")) < 0 or flt(args.get("reserved_qty")) > 0) \ - and args.get("is_cancelled") == 'No' and args.get("is_amended")=='No': - self.reorder_item(args.get("voucher_type"), args.get("voucher_no"), args.get("company")) - def get_first_sle(self): sle = sql(""" select * from `tabStock Ledger Entry` @@ -90,82 +86,4 @@ class DocType: order by timestamp(posting_date, posting_time) asc, name asc limit 1 """, (self.doc.item_code, self.doc.warehouse), as_dict=1) - return sle and sle[0] or None - - def reorder_item(self,doc_type,doc_name, company): - """ Reorder item if stock reaches reorder level""" - if not hasattr(webnotes, "auto_indent"): - webnotes.auto_indent = webnotes.conn.get_value('Global Defaults', None, 'auto_indent') - - if webnotes.auto_indent: - #check if re-order is required - item_reorder = webnotes.conn.get("Item Reorder", - {"parent": self.doc.item_code, "warehouse": self.doc.warehouse}) - if item_reorder: - reorder_level = item_reorder.warehouse_reorder_level - reorder_qty = item_reorder.warehouse_reorder_qty - material_request_type = item_reorder.material_request_type or "Purchase" - else: - reorder_level, reorder_qty = webnotes.conn.get_value("Item", self.doc.item_code, - ["re_order_level", "re_order_qty"]) - material_request_type = "Purchase" - - if flt(reorder_qty) and flt(self.doc.projected_qty) < flt(reorder_level): - self.create_material_request(doc_type, doc_name, reorder_level, reorder_qty, - company, material_request_type) - - def create_material_request(self, doc_type, doc_name, reorder_level, reorder_qty, company, - material_request_type="Purchase"): - """ Create indent on reaching reorder level """ - defaults = webnotes.defaults.get_defaults() - item = webnotes.doc("Item", self.doc.item_code) - - mr = webnotes.bean([{ - "doctype": "Material Request", - "company": company or defaults.company, - "fiscal_year": defaults.fiscal_year, - "transaction_date": nowdate(), - "material_request_type": material_request_type, - "remark": _("This is an auto generated Material Request.") + \ - _("It was raised because the (actual + ordered + indented - reserved) quantity reaches re-order level when the following record was created") + \ - ": " + _(doc_type) + " " + doc_name - }, { - "doctype": "Material Request Item", - "parenttype": "Material Request", - "parentfield": "indent_details", - "item_code": self.doc.item_code, - "schedule_date": add_days(nowdate(),cint(item.lead_time_days)), - "uom": self.doc.stock_uom, - "warehouse": self.doc.warehouse, - "item_name": item.item_name, - "description": item.description, - "item_group": item.item_group, - "qty": reorder_qty, - "brand": item.brand, - }]) - mr.insert() - mr.submit() - - msgprint("""Item: %s is to be re-ordered. Material Request %s raised. - It was generated from %s: %s""" % - (self.doc.item_code, mr.doc.name, doc_type, doc_name)) - - if(item.email_notify): - self.send_email_notification(doc_type, doc_name, mr) - - def send_email_notification(self, doc_type, doc_name, bean): - """ Notify user about auto creation of indent""" - - from webnotes.utils.email_lib import sendmail - email_list=[d[0] for d in sql("""select distinct r.parent from tabUserRole r, tabProfile p - where p.name = r.parent and p.enabled = 1 and p.docstatus < 2 - and r.role in ('Purchase Manager','Material Manager') - and p.name not in ('Administrator', 'All', 'Guest')""")] - - msg="""A new Material Request has been raised for Item: %s and Warehouse: %s \ - on %s due to %s: %s. See %s: %s """ % (self.doc.item_code, self.doc.warehouse, - formatdate(), doc_type, doc_name, bean.doc.doctype, - get_url_to_form(bean.doc.doctype, bean.doc.name)) - - sendmail(email_list, subject='Auto Material Request Generation Notification', msg = msg) - + return sle and sle[0] or None \ No newline at end of file diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index bc438a877a..d743a98005 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -51,6 +51,7 @@ class DocType(DocListController): self.validate_barcode() self.check_non_asset_warehouse() self.cant_change() + self.validate_item_type_for_reorder() if self.doc.name: self.old_page_name = webnotes.conn.get_value('Item', self.doc.name, 'page_name') @@ -201,6 +202,13 @@ class DocType(DocListController): webnotes.msgprint(_("As there are existing stock transactions for this \ item, you can not change the values of 'Has Serial No', \ 'Is Stock Item' and 'Valuation Method'"), raise_exception=1) + + def validate_item_type_for_reorder(self): + if self.doc.re_order_level or len(self.doclist.get({"parentfield": "item_reorder", + "material_request_type": "Purchase"})): + if not self.doc.is_purchase_item: + webnotes.msgprint(_("""To set reorder level, item must be Purchase Item"""), + raise_exception=1) def check_if_sle_exists(self): sle = webnotes.conn.sql("""select name from `tabStock Ledger Entry` diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index c799029d95..9e0a2fb24e 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-03 10:45:46", "docstatus": 0, - "modified": "2013-05-07 15:58:58", + "modified": "2013-05-22 15:48:27", "modified_by": "Administrator", "owner": "Administrator" }, @@ -363,21 +363,6 @@ "label": "Re-Order Qty", "read_only": 0 }, - { - "doctype": "DocField", - "fieldname": "column_break_31", - "fieldtype": "Column Break", - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "description": "Send an email to users of role \"Material Manager\" and \"Purchase Manager\" when re-order level is crossed.", - "doctype": "DocField", - "fieldname": "email_notify", - "fieldtype": "Check", - "label": "Notify by Email on Re-order", - "read_only": 0 - }, { "doctype": "DocField", "fieldname": "section_break_31", diff --git a/stock/utils.py b/stock/utils.py index a2541dc69e..5e7e53bb01 100644 --- a/stock/utils.py +++ b/stock/utils.py @@ -17,7 +17,7 @@ import webnotes from webnotes import msgprint, _ import json -from webnotes.utils import flt, cstr +from webnotes.utils import flt, cstr, nowdate, add_days, cint from webnotes.defaults import get_global_default def validate_end_of_life(item_code, end_of_life=None, verbose=1): @@ -194,4 +194,117 @@ def _get_buying_amount(voucher_type, voucher_no, item_row, item_code, warehouse, buying_amount = previous_stock_value - flt(sle.stock_value) return buying_amount - return 0.0 \ No newline at end of file + return 0.0 + + +def reorder_item(): + """ Reorder item if stock reaches reorder level""" + if not hasattr(webnotes, "auto_indent"): + webnotes.auto_indent = webnotes.conn.get_value('Global Defaults', None, 'auto_indent') + + if webnotes.auto_indent: + material_requests = {} + bin_list = webnotes.conn.sql("""select item_code, warehouse, projected_qty + from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != ''""", + as_dict=True) + for bin in bin_list: + #check if re-order is required + item_reorder = webnotes.conn.get("Item Reorder", + {"parent": bin.item_code, "warehouse": bin.warehouse}) + if item_reorder: + reorder_level = item_reorder.warehouse_reorder_level + reorder_qty = item_reorder.warehouse_reorder_qty + material_request_type = item_reorder.material_request_type or "Purchase" + else: + reorder_level, reorder_qty = webnotes.conn.get_value("Item", bin.item_code, + ["re_order_level", "re_order_qty"]) + material_request_type = "Purchase" + + if reorder_level and flt(bin.projected_qty) < flt(reorder_level): + if flt(reorder_level) - flt(bin.projected_qty) > flt(reorder_qty): + reorder_qty = flt(reorder_level) - flt(bin.projected_qty) + + company = webnotes.conn.get_value("Warehouse", bin.warehouse, "company") or \ + webnotes.defaults.get_defaults()["company"] or \ + webnotes.conn.sql("""select name from tabCompany limit 1""")[0][0] + + material_requests.setdefault(material_request_type, webnotes._dict()).setdefault( + company, []).append(webnotes._dict({ + "item_code": bin.item_code, + "warehouse": bin.warehouse, + "reorder_qty": reorder_qty + }) + ) + + create_material_request(material_requests) + +def create_material_request(material_requests): + """ Create indent on reaching reorder level """ + mr_list = [] + defaults = webnotes.defaults.get_defaults() + for request_type in material_requests: + for company in material_requests[request_type]: + items = material_requests[request_type][company] + if items: + mr = [{ + "doctype": "Material Request", + "company": company, + "fiscal_year": defaults.fiscal_year, + "transaction_date": nowdate(), + "material_request_type": request_type, + "remark": _("This is an auto generated Material Request.") + \ + _("""It was raised because the (actual + ordered + indented - reserved) + quantity reaches re-order level when the following record was created""") + }] + + for d in items: + item = webnotes.doc("Item", d.item_code) + mr.append({ + "doctype": "Material Request Item", + "parenttype": "Material Request", + "parentfield": "indent_details", + "item_code": d.item_code, + "schedule_date": add_days(nowdate(),cint(item.lead_time_days)), + "uom": item.stock_uom, + "warehouse": d.warehouse, + "item_name": item.item_name, + "description": item.description, + "item_group": item.item_group, + "qty": d.reorder_qty, + "brand": item.brand, + }) + + mr_bean = webnotes.bean(mr) + mr_bean.insert() + mr_bean.submit() + mr_list.append(mr_bean) + + if mr_list: + if not hasattr(webnotes, "reorder_email_notify"): + webnotes.reorder_email_notify = webnotes.conn.get_value('Global Defaults', None, + 'reorder_email_notify') + + if(webnotes.reorder_email_notify): + send_email_notification(mr_list) + +def send_email_notification(mr_list): + """ Notify user about auto creation of indent""" + + from webnotes.utils.email_lib import sendmail + email_list = webnotes.conn.sql_list("""select distinct r.parent + from tabUserRole r, tabProfile p + where p.name = r.parent and p.enabled = 1 and p.docstatus < 2 + and r.role in ('Purchase Manager','Material Manager') + and p.name not in ('Administrator', 'All', 'Guest')""") + + msg="""

    Following Material Requests has been raised automatically \ + based on item reorder level:

    """ + for mr in mr_list: + msg += "

    " + mr.doc.name + """

    + """ + for item in mr.doclist.get({"parentfield": "indent_details"}): + msg += "" + msg += "
    Item CodeWarehouseQtyUOM
    " + item.item_code + "" + item.warehouse + "" + \ + cstr(item.qty) + "" + cstr(item.uom) + "
    " + + sendmail(email_list, subject='Auto Material Request Generation Notification', msg = msg) \ No newline at end of file From 3f15a04a35aab81439b23c1711bfc493959b9849 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 22 May 2013 16:31:15 +0530 Subject: [PATCH 162/295] [website] [product group] show search and breadcrumbs below the item group's description --- website/templates/html/product_group.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/website/templates/html/product_group.html b/website/templates/html/product_group.html index b3c851197e..0ad4048cda 100644 --- a/website/templates/html/product_group.html +++ b/website/templates/html/product_group.html @@ -1,8 +1,6 @@ {% extends "app/website/templates/html/page.html" %} {% block content %} -{% include 'app/website/templates/html/product_search_box.html' %} -{% include 'app/website/templates/html/product_breadcrumbs.html' %}
    {% if slideshow %} {% include "app/website/templates/html/slideshow.html" %} @@ -12,6 +10,10 @@ {% else %}

    {{ name }}

    {% endif %} +
    +{% include 'app/website/templates/html/product_search_box.html' %} +{% include 'app/website/templates/html/product_breadcrumbs.html' %} +
    {% if sub_groups %}
    From 66928243c11301a87fc94f7c3115693c911c712a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 22 May 2013 16:55:10 +0530 Subject: [PATCH 163/295] [fixes] removed communication section break from quotation --- selling/doctype/quotation/quotation.txt | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt index feda14c591..4115be833b 100644 --- a/selling/doctype/quotation/quotation.txt +++ b/selling/doctype/quotation/quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-03 09:10:44", + "creation": "2013-05-22 12:10:46", "docstatus": 0, - "modified": "2013-04-03 09:58:02", + "modified": "2013-05-22 16:54:07", "modified_by": "Administrator", "owner": "Administrator" }, @@ -833,12 +833,11 @@ "width": "100px" }, { - "description": "Keep a track on communications regarding this Quotation. This will help you remember earlier communications in case the Customer comes back again", "doctype": "DocField", "fieldname": "communication_history", "fieldtype": "Section Break", - "label": "Communication History", "oldfieldtype": "Section Break", + "options": "Simple", "print_hide": 1, "read_only": 0 }, @@ -869,28 +868,23 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "role": "Sales Manager", + "role": "Sales User", "submit": 1, "write": 1 }, + { + "doctype": "DocPerm", + "role": "Customer" + }, { "amend": 1, "cancel": 1, "create": 1, "doctype": "DocPerm", - "role": "Sales User", + "role": "Sales Manager", "submit": 1, "write": 1 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "role": "Customer", - "submit": 0, - "write": 0 - }, { "amend": 1, "cancel": 1, From d39b30ab99cfade9335bd2455e0098f397dbe807 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 May 2013 11:44:18 +0530 Subject: [PATCH 164/295] [validation] invoice rate will be same as order/delivery --- .../doctype/sales_invoice/sales_invoice.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index d18b967dfa..4c71cb3342 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -74,9 +74,11 @@ class DocType(SellingController): self.set_aging_date() self.set_against_income_account() self.validate_c_form() + self.validate_rate_with_refdoc() self.validate_time_logs_are_submitted() self.validate_recurring_invoice() + def on_submit(self): if cint(self.doc.is_pos) == 1: if cint(self.doc.update_stock) == 1: @@ -213,8 +215,9 @@ class DocType(SellingController): if self.doc.customer: acc_head = webnotes.conn.sql("""select name from `tabAccount` where (name = %s or (master_name = %s and master_type = 'customer')) - and docstatus != 2""", - (cstr(self.doc.customer) + " - " + self.get_company_abbr(), self.doc.customer)) + and docstatus != 2 and company = %s""", + (cstr(self.doc.customer) + " - " + self.get_company_abbr(), + self.doc.customer, self.doc.company)) if acc_head and acc_head[0][0]: return acc_head[0][0] @@ -551,6 +554,19 @@ class DocType(SellingController): webnotes.conn.set(self.doc, 'c_form_no', '') + def validate_rate_with_refdoc(self): + """Validate values with reference document with previous document""" + for d in self.doclist.get({"parentfield": "entries"}): + if d.so_detail: + self.check_value("Sales Order", d.so_detail, d.export_rate, d.item_code) + if d.dn_detail: + self.check_value("Delivery Note", d.dn_detail, d.export_rate, d.item_code) + + def check_value(self, ref_dt, ref_dn, val, item_code): + ref_val = webnotes.conn.get_value(ref_dt + "Item", ref_dn, "export_rate") + if flt(ref_val) != val: + msgprint(_("Rate is not matching with ") + ref_dt + ": " + ref_dn + + _(" for item: ") + item_code, raise_exception=True) def update_current_stock(self): for d in getlist(self.doclist, 'entries'): From 5dc36f416a4df7df3cca20b249cedcc99ce4a777 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 May 2013 12:53:47 +0530 Subject: [PATCH 165/295] [permission] delete permission added for pos setting --- accounts/doctype/pos_setting/pos_setting.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/accounts/doctype/pos_setting/pos_setting.txt b/accounts/doctype/pos_setting/pos_setting.txt index 80cb1ecdc9..788af9ee06 100755 --- a/accounts/doctype/pos_setting/pos_setting.txt +++ b/accounts/doctype/pos_setting/pos_setting.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-30 12:58:25", + "creation": "2013-05-09 13:16:11", "docstatus": 0, - "modified": "2013-05-03 14:36:24", + "modified": "2013-05-23 12:52:09", "modified_by": "Administrator", "owner": "Administrator" }, @@ -213,6 +213,14 @@ "options": "link:Print Heading" }, { + "cancel": 1, + "create": 1, + "doctype": "DocPerm", + "role": "System Manager", + "write": 1 + }, + { + "cancel": 1, "create": 1, "doctype": "DocPerm", "role": "Accounts Manager", From aabe11b689df77a09a37cb785725efde54c5e90c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 23 May 2013 12:59:12 +0530 Subject: [PATCH 166/295] [sales order] [validate] validate item details specifically when submitting --- selling/doctype/sales_order/sales_order.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py index 6a52e5a0fa..c8db1f919f 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -217,7 +217,10 @@ class DocType(SellingController): self.validate_proj_cust() self.validate_po() #self.validate_reference_value() - self.validate_for_items() + + if self.doc.docstatus == 1: + self.validate_for_items() + sales_com_obj = get_obj(dt = 'Sales Common') sales_com_obj.check_active_sales_items(self) sales_com_obj.check_conversion_rate(self) From b56412c646be0f03d4e51f9ef6cff49dc22693c3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 May 2013 17:41:52 +0530 Subject: [PATCH 167/295] [fixes][aii] cancelled gl entry for delivery note and patch --- .../p05_update_cancelled_gl_entries.py | 29 +++++++++++++++++++ patches/patch_list.py | 1 + stock/doctype/delivery_note/delivery_note.py | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 patches/may_2013/p05_update_cancelled_gl_entries.py diff --git a/patches/may_2013/p05_update_cancelled_gl_entries.py b/patches/may_2013/p05_update_cancelled_gl_entries.py new file mode 100644 index 0000000000..59eed7e66f --- /dev/null +++ b/patches/may_2013/p05_update_cancelled_gl_entries.py @@ -0,0 +1,29 @@ +# 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 cint + +def execute(): + aii_enabled = cint(webnotes.conn.get_value("Global Defaults", None, + "auto_inventory_accounting")) + + if aii_enabled: + webnotes.conn.sql("""update `tabGL Entry` gle set is_cancelled = 'Yes' + where voucher_type = 'Delivery Note' + and exists(select name from `tabDelivery Note` + where name = gle.voucher_no and docstatus = 2)""") \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 89f48e5ce7..ea61a04dc3 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -251,4 +251,5 @@ patch_list = [ "patches.may_2013.p02_update_valuation_rate", "patches.may_2013.p03_update_support_ticket", "patches.may_2013.p04_reorder_level", + "patches.may_2013.p05_update_cancelled_gl_entries", ] \ No newline at end of file diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index 15e24ef293..6ffd960000 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -419,4 +419,4 @@ class DocType(SellingController): if gl_entries: from accounts.general_ledger import make_gl_entries - make_gl_entries(gl_entries) \ No newline at end of file + make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2)) \ No newline at end of file From f4591ecc248d8e1dd6e8f0513827c232015531a8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 May 2013 19:07:10 +0530 Subject: [PATCH 168/295] [stock ledger][fix] message for negative qty error --- stock/stock_ledger.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stock/stock_ledger.py b/stock/stock_ledger.py index 2480263c1f..10e490c8e0 100644 --- a/stock/stock_ledger.py +++ b/stock/stock_ledger.py @@ -35,6 +35,9 @@ def update_entries_after(args, verbose=1): "posting_time": "12:00" } """ + global _exceptions + _exceptions = [] + previous_sle = get_sle_before_datetime(args) qty_after_transaction = flt(previous_sle.get("qty_after_transaction")) From 3d75ad1896081db295026aa30c0b2ddeb246d121 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 May 2013 19:14:28 +0530 Subject: [PATCH 169/295] [sales invoice][fix] check item rate with sales order and delivery note --- accounts/doctype/sales_invoice/sales_invoice.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 4c71cb3342..a7f024c2df 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -558,13 +558,15 @@ class DocType(SellingController): """Validate values with reference document with previous document""" for d in self.doclist.get({"parentfield": "entries"}): if d.so_detail: - self.check_value("Sales Order", d.so_detail, d.export_rate, d.item_code) + self.check_value("Sales Order", d.sales_order, d.so_detail, + d.export_rate, d.item_code) if d.dn_detail: - self.check_value("Delivery Note", d.dn_detail, d.export_rate, d.item_code) + self.check_value("Delivery Note", d.delivery_note, d.dn_detail, + d.export_rate, d.item_code) - def check_value(self, ref_dt, ref_dn, val, item_code): - ref_val = webnotes.conn.get_value(ref_dt + "Item", ref_dn, "export_rate") - if flt(ref_val) != val: + def check_value(self, ref_dt, ref_dn, ref_item_dn, val, item_code): + ref_val = webnotes.conn.get_value(ref_dt + "Item", ref_item_dn, "export_rate") + if flt(ref_val) != flt(val): msgprint(_("Rate is not matching with ") + ref_dt + ": " + ref_dn + _(" for item: ") + item_code, raise_exception=True) From 93ed5b9dae50f247b79983e8227ac7414133011b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 May 2013 19:21:58 +0530 Subject: [PATCH 170/295] [sales invoice][fix] check item rate with sales order and delivery note --- accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index a7f024c2df..050a49055d 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -565,7 +565,7 @@ class DocType(SellingController): d.export_rate, d.item_code) def check_value(self, ref_dt, ref_dn, ref_item_dn, val, item_code): - ref_val = webnotes.conn.get_value(ref_dt + "Item", ref_item_dn, "export_rate") + ref_val = webnotes.conn.get_value(ref_dt + " Item", ref_item_dn, "export_rate") if flt(ref_val) != flt(val): msgprint(_("Rate is not matching with ") + ref_dt + ": " + ref_dn + _(" for item: ") + item_code, raise_exception=True) From 7cd49529f49aadb565eaf04446ea1979d401f5cd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 24 May 2013 13:51:25 +0530 Subject: [PATCH 171/295] [sales invoice][fix] float precision issue --- accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 050a49055d..6871b1e90f 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -566,7 +566,7 @@ class DocType(SellingController): def check_value(self, ref_dt, ref_dn, ref_item_dn, val, item_code): ref_val = webnotes.conn.get_value(ref_dt + " Item", ref_item_dn, "export_rate") - if flt(ref_val) != flt(val): + if flt(ref_val, 2) != flt(val, 2): msgprint(_("Rate is not matching with ") + ref_dt + ": " + ref_dn + _(" for item: ") + item_code, raise_exception=True) From da6e4670a3d380d13716d99d94745000bd1179df Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 May 2013 16:34:15 +0530 Subject: [PATCH 172/295] [report][fix] filter fixes in sales/pur register --- .../purchase_register/purchase_register.py | 29 +++++++++------ .../report/sales_register/sales_register.py | 35 +++++++++++-------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index d4f23927d5..7c4b38671d 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -17,6 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt +from webnotes import msgprint, _ def execute(filters=None): if not filters: filters = {} @@ -24,6 +25,11 @@ def execute(filters=None): invoice_list = get_invoices(filters) columns, expense_accounts, tax_accounts = get_columns(invoice_list) + + if not invoice_list: + msgprint(_("No record found")) + return columns, invoice_list + invoice_expense_map = get_invoice_expense_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) @@ -64,17 +70,18 @@ def get_columns(invoice_list): "Project:Link/Project:80", "Bill No::120", "Bill Date:Date:80", "Remarks::150", "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100" ] - - expense_accounts = webnotes.conn.sql_list("""select distinct expense_head - from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' - and parent in (%s) order by expense_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + expense_accounts = tax_accounts = [] + if invoice_list: + expense_accounts = webnotes.conn.sql_list("""select distinct expense_head + from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' + and parent in (%s) order by expense_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) - tax_accounts = webnotes.conn.sql_list("""select distinct account_head - from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' - and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) - order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' + and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) + order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ @@ -86,7 +93,7 @@ def get_conditions(filters): conditions = "" if filters.get("company"): conditions += " and company=%(company)s" - if filters.get("account"): conditions += " and account = %(account)s" + if filters.get("account"): conditions += " and credit_to = %(account)s" if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s" if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s" diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 3946f0033d..99057f9a2e 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -17,6 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt +from webnotes import msgprint, _ def execute(filters=None): if not filters: filters = {} @@ -24,6 +25,10 @@ def execute(filters=None): invoice_list = get_invoices(filters) columns, income_accounts, tax_accounts = get_columns(invoice_list) + if not invoice_list: + msgprint(_("No record found")) + return columns, invoice_list + invoice_income_map = get_invoice_income_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) @@ -69,15 +74,17 @@ def get_columns(invoice_list): "Remarks::150", "Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100" ] - income_accounts = webnotes.conn.sql_list("""select distinct income_account - from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) - order by income_account""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + income_accounts = tax_accounts = [] + if invoice_list: + income_accounts = webnotes.conn.sql_list("""select distinct income_account + from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) + order by income_account""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) - tax_accounts = webnotes.conn.sql_list("""select distinct account_head - from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' - and docstatus = 1 and parent in (%s) order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' + and docstatus = 1 and parent in (%s) order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in income_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ @@ -89,19 +96,19 @@ def get_conditions(filters): conditions = "" if filters.get("company"): conditions += " and company=%(company)s" - if filters.get("account"): conditions += " and account = %(account)s" + if filters.get("account"): conditions += " and debit_to = %(account)s" - if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s" - if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s" + if filters.get("from_date"): conditions += " and posting_date >= %(from_date)s" + if filters.get("to_date"): conditions += " and posting_date <= %(to_date)s" return conditions def get_invoices(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select name, posting_date, debit_to, project_name, customer, - remarks, net_total, other_charges_total, grand_total - from `tabSales Invoice` where docstatus = 1 %s - order by posting_date desc, name desc""" % conditions, filters, as_dict=1) + remarks, net_total, other_charges_total, grand_total from `tabSales Invoice` + where docstatus = 1 %s order by posting_date desc, name desc""" % + conditions, filters, as_dict=1) def get_invoice_income_map(invoice_list): income_details = webnotes.conn.sql("""select parent, income_account, sum(amount) as amount From 996191e8b7aee0c4615e3a9e62874f8b26de55c1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 27 May 2013 18:28:36 +0530 Subject: [PATCH 173/295] [purchase invoice] [fix] error message for missing expense account for an item --- accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index c53b6d94fc..95b56dc4ff 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -326,7 +326,7 @@ class DocType(BuyingController): against_accounts.append(stock_not_billed_account) elif not item.expense_head: - msgprint(_("""Expense account is mandatory for item: """) + item.item_code, + msgprint(_("""Expense account is mandatory for item: """) + (item.item_code or item.item_name), raise_exception=1) elif item.expense_head not in against_accounts: From 77a2ee01e27223b4b6f20aee53b5d9f3b68bb628 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 11:56:48 +0530 Subject: [PATCH 174/295] [rename][fixes] do not update customer name of renaming of customer --- selling/doctype/customer/customer.py | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/selling/doctype/customer/customer.py b/selling/doctype/customer/customer.py index 6f54ef9641..72e12b7942 100644 --- a/selling/doctype/customer/customer.py +++ b/selling/doctype/customer/customer.py @@ -219,27 +219,8 @@ class DocType(TransactionBase): def on_rename(self, new, old, merge=False): #update customer_name if not naming series if webnotes.defaults.get_global_default('cust_master_name') == 'Customer Name': - update_fields = [ - ('Customer', 'name'), - ('Address', 'customer'), - ('Contact', 'customer'), - ('Customer Issue', 'customer'), - ('Delivery Note', 'customer'), - ('Opportunity', 'customer'), - ('Installation Note', 'customer'), - ('Maintenance Schedule', 'customer'), - ('Maintenance Visit', 'customer'), - ('Project', 'customer'), - ('Quotation', 'customer'), - ('Sales Invoice', 'customer'), - ('Sales Order', 'customer'), - ('Serial No', 'customer'), - ('Shipping Address', 'customer'), - ('Stock Entry', 'customer'), - ('Support Ticket', 'customer')] - for rec in update_fields: - sql("""update `tab%s` set customer_name = %s - where `%s` = %s""" % (rec[0], "%s" ,rec[1], "%s"), (new, old)) + webnotes.conn.sql("""update `tabCustomer` set customer_name = %s where name = %s""", + (new, old)) for account in webnotes.conn.sql("""select name, account_name from tabAccount where master_name=%s and master_type='Customer'""", old, as_dict=1): From 34d1b1415e6ccb1221f8122506cb6b8623a81457 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 12:32:21 +0530 Subject: [PATCH 175/295] [report] filter added base on territory in sales personwise transaction summary --- accounts/report/accounts_receivable/accounts_receivable.txt | 2 +- .../item_wise_sales_history/item_wise_sales_history.txt | 5 +++-- .../sales_person_wise_transaction_summary.js | 6 ++++++ .../sales_person_wise_transaction_summary.py | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/accounts/report/accounts_receivable/accounts_receivable.txt b/accounts/report/accounts_receivable/accounts_receivable.txt index 7eb344fc08..d01bef23d3 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.txt +++ b/accounts/report/accounts_receivable/accounts_receivable.txt @@ -2,7 +2,7 @@ { "creation": "2013-04-16 11:31:13", "docstatus": 0, - "modified": "2013-04-30 17:54:47", + "modified": "2013-05-24 12:02:52", "modified_by": "Administrator", "owner": "Administrator" }, diff --git a/selling/report/item_wise_sales_history/item_wise_sales_history.txt b/selling/report/item_wise_sales_history/item_wise_sales_history.txt index 6fee050e0d..adbfe69254 100644 --- a/selling/report/item_wise_sales_history/item_wise_sales_history.txt +++ b/selling/report/item_wise_sales_history/item_wise_sales_history.txt @@ -1,13 +1,14 @@ [ { - "creation": "2013-05-03 14:38:34", + "creation": "2013-05-23 17:42:24", "docstatus": 0, - "modified": "2013-05-07 11:19:40", + "modified": "2013-05-24 12:20:17", "modified_by": "Administrator", "owner": "Administrator" }, { "add_total_row": 1, + "disabled": 0, "doctype": "Report", "is_standard": "Yes", "name": "__common__", diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js index 4a8e6787ff..2e1996e9a2 100644 --- a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js @@ -50,5 +50,11 @@ wn.query_reports["Sales Person-wise Transaction Summary"] = { fieldtype: "Link", options: "Customer", }, + { + fieldname:"territory", + label: "Territory", + fieldtype: "Link", + options: "Territory", + }, ] } \ No newline at end of file diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py index 23e8819242..8ec3a1826e 100644 --- a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -54,6 +54,7 @@ def get_conditions(filters, date_field): conditions = "" if filters.get("company"): conditions += " and dt.company = '%s'" % filters["company"] if filters.get("customer"): conditions += " and dt.customer = '%s'" % filters["customer"] + if filters.get("territory"): conditions += " and dt.territory = '%s'" % filters["territory"] if filters.get("from_date"): conditions += " and dt.%s >= '%s'" % \ (date_field, filters["from_date"]) From eca0531068e8ec75c58d9c11764798612b7a9c84 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 16:07:03 +0530 Subject: [PATCH 176/295] [report] purchase order/ received items to be billed --- .../__init__.py | 0 .../purchase_order_items_to_be_billed.txt | 23 +++++++++++++++++++ .../received_items_to_be_billed/__init__.py | 0 .../received_items_to_be_billed.txt | 23 +++++++++++++++++++ .../purchase_order_items_to_be_received.txt | 4 ++-- 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 accounts/report/purchase_order_items_to_be_billed/__init__.py create mode 100644 accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.txt create mode 100644 accounts/report/received_items_to_be_billed/__init__.py create mode 100644 accounts/report/received_items_to_be_billed/received_items_to_be_billed.txt diff --git a/accounts/report/purchase_order_items_to_be_billed/__init__.py b/accounts/report/purchase_order_items_to_be_billed/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..7a9ec367b1 --- /dev/null +++ b/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-28 15:54:16", + "docstatus": 0, + "modified": "2013-05-28 16:02:57", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "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", + "ref_doctype": "Purchase Invoice", + "report_name": "Purchase Order Items To Be Billed", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Purchase Order Items To Be Billed" + } +] \ No newline at end of file diff --git a/accounts/report/received_items_to_be_billed/__init__.py b/accounts/report/received_items_to_be_billed/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..53a1d7a7f5 --- /dev/null +++ b/accounts/report/received_items_to_be_billed/received_items_to_be_billed.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-28 15:57:59", + "docstatus": 0, + "modified": "2013-05-28 16:02:06", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "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`.transaction_date asc", + "ref_doctype": "Purchase Invoice", + "report_name": "Received Items To Be Billed", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Received Items To Be Billed" + } +] \ No newline at end of file diff --git a/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt b/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt index 7a2f6365bf..c588dde3f7 100644 --- a/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt +++ b/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.txt @@ -2,7 +2,7 @@ { "creation": "2013-02-22 18:01:55", "docstatus": 0, - "modified": "2013-05-13 16:11:27", + "modified": "2013-05-28 16:03:15", "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\t`tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order`.`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`.received_qty as \"Received Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive: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`.received_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\t`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`.received_qty as \"Received Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive: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`.received_qty, 0) < ifnull(`tabPurchase Order Item`.qty, 0)\norder by `tabPurchase Order`.transaction_date asc", "ref_doctype": "Purchase Receipt", "report_name": "Purchase Order Items To Be Received", "report_type": "Query Report" From 8b509f56b7b900c1508c27d13aa5095b4c6185ea Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 16:52:30 +0530 Subject: [PATCH 177/295] [utility function] fix total debit/credit due to floating point issue --- accounts/utils.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/accounts/utils.py b/accounts/utils.py index 755546c0e1..382a33762b 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -322,3 +322,20 @@ def get_stock_rbnb_value(company): and exists(select name from `tabPurchase Invoice` where name = pi_item.parent and company = %s)""", company) return flt(total_received_amount[0][0]) - flt(total_billed_amount[0][0]) + + +def fix_total_debit_credit(): + vouchers = webnotes.conn.sql("""select voucher_type, voucher_no, + sum(debit) - sum(credit) as diff + from `tabGL Entry` + group by voucher_type, voucher_no + having sum(ifnull(debit, 0)) != sum(ifnull(credit, 0))""", as_dict=1) + + for d in vouchers: + if abs(d.diff) > 0: + dr_or_cr = d.voucher_type == "Sales Invoice" and "credit" or "debit" + + webnotes.conn.sql("""update `tabGL Entry` set %s = %s + %s + where voucher_type = %s and voucher_no = %s and %s > 0 limit 1""" % + (dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr), + (d.diff, d.voucher_type, d.voucher_no), debug=1) \ No newline at end of file From 9fce94ff96d575a32abdf8d3759a16778bbe4b81 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 17:12:15 +0530 Subject: [PATCH 178/295] [report] reports link added into accounts home page --- accounts/page/accounts_home/accounts_home.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index c3d4cf18ce..25ee92c397 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -197,11 +197,21 @@ wn.module_page["Accounts"] = [ route: "query-report/Delivered Items To Be Billed", doctype: "Sales Invoice" }, + { + "label":wn._("Received Items To Be Billed"), + route: "query-report/Received Items To Be Billed", + doctype: "Purchase Invoice" + }, { "label":wn._("Ordered Items To Be Billed"), route: "query-report/Ordered Items To Be Billed", doctype: "Sales Invoice" }, + { + "label":wn._("Purchase Order Items To Be Billed"), + route: "query-report/Purchase Order Items To Be Billed", + doctype: "Purchase Invoice" + }, { "label":wn._("Bank Clearance Summary"), route: "query-report/Bank Clearance Summary", From 7742e2baed55ace102048cdd7e5642bbe8470a4d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 17:58:36 +0530 Subject: [PATCH 179/295] [report][fix] --- .../received_items_to_be_billed.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 53a1d7a7f5..554e0f0c79 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 16:02:06", + "modified": "2013-05-28 17:34:05", "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`.transaction_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`.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", "ref_doctype": "Purchase Invoice", "report_name": "Received Items To Be Billed", "report_type": "Query Report" From 58c3348ca6b8281d5feca79029b9a73f9fc89196 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 18:06:20 +0530 Subject: [PATCH 180/295] [voucher import tool][fix] fixes for handling exception --- .../voucher_import_tool.py | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/accounts/page/voucher_import_tool/voucher_import_tool.py b/accounts/page/voucher_import_tool/voucher_import_tool.py index 6648ee6f8e..3c920ce04e 100644 --- a/accounts/page/voucher_import_tool/voucher_import_tool.py +++ b/accounts/page/voucher_import_tool/voucher_import_tool.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import webnotes from webnotes import _ -from webnotes.utils import flt, comma_and +from webnotes.utils import flt, comma_and, cstr import webnotes.defaults @webnotes.whitelist() @@ -31,13 +31,12 @@ def get_template(): "3. Naming Series Options: %(naming_options)s" "4. Voucher Type Options: %(voucher_type)s"%(extra_note)s "-------Common Values-----------" -"Company:","%(default_company)s" +"Company:", "--------Data----------" %(columns)s ''' % { "template_type": template_type, "user_fmt": webnotes.defaults.get_global_default('date_format'), - "default_company": webnotes.conn.get_default("company"), "naming_options": naming_options.replace("\n", ", "), "voucher_type": voucher_type.replace("\n", ", "), "extra_note": extra_note, @@ -49,14 +48,28 @@ def get_template(): @webnotes.whitelist() def upload(): - from webnotes.utils.datautils import read_csv_content_from_uploaded_file - rows = read_csv_content_from_uploaded_file() - - common_values = get_common_values(rows) - company_abbr = webnotes.conn.get_value("Company", common_values.company, "abbr") - data, start_idx = get_data(rows, company_abbr) + messages = [] + try: + from webnotes.utils.datautils import read_csv_content_from_uploaded_file + rows = read_csv_content_from_uploaded_file() + common_values = get_common_values(rows) + if not common_values.company: + webnotes.msgprint(_("Company is missing in csv file"), raise_exception=1) + + company_abbr = webnotes.conn.get_value("Company", common_values.company, "abbr") + data, start_idx = get_data(rows, company_abbr) + except Exception, e: + err_msg = webnotes.message_log and "
    ".join(webnotes.message_log) or cstr(e) + messages.append("""

    %s

    """ % (err_msg or "No message")) + webnotes.errprint(webnotes.getTraceback()) + webnotes.message_log = [] + return messages + return import_vouchers(common_values, data, start_idx, rows[0][0]) + + + def map_fields(field_list, source, target): for f in field_list: @@ -70,9 +83,8 @@ def import_vouchers(common_values, data, start_idx, import_type): from webnotes.model.bean import Bean from accounts.utils import get_fiscal_year from webnotes.utils.dateutils import parse_date - messages = [] - + def get_account_details(account): acc_details = webnotes.conn.sql("""select is_pl_account, master_name from tabAccount where name=%s""", account, as_dict=1) @@ -113,8 +125,9 @@ def import_vouchers(common_values, data, start_idx, import_type): if d.ref_number: if not d.ref_date: - raise webnotes.ValidationError, \ - """Ref Date is Mandatory if Ref Number is specified""" + webnotes.msgprint(_("Ref Date is Mandatory if Ref Number is specified"), + raise_exception=1) + d.ref_date = parse_date(d.ref_date) d.company = common_values.company @@ -176,7 +189,7 @@ def import_vouchers(common_values, data, start_idx, import_type): webnotes.conn.commit() except Exception, e: webnotes.conn.rollback() - err_msg = webnotes.message_log and "
    ".join(webnotes.message_log) or unicode(e) + err_msg = webnotes.message_log and "
    ".join(webnotes.message_log) or cstr(e) messages.append("""

    [row #%s] %s failed: %s

    """ % ((start_idx + 1) + i, jv.name or "", err_msg or "No message")) messages.append("

    All transactions rolled back

    ") @@ -240,16 +253,20 @@ def get_data(rows, company_abbr): raise Exception, """Column No(s). %s %s empty. \ Please remove them and try again.""" % (comma_and(empty_columns), len(empty_columns)==1 and "is" or "are") - + columns = [c.replace(" ", "_").lower() for c in rows[i+1] if not c.endswith(" - " + company_abbr)] accounts = [c for c in rows[i+1] if c.endswith(" - " + company_abbr)] - + + if not accounts: + webnotes.msgprint(_("""No Account found in csv file, + May be company abbreviation is not correct"""), raise_exception=1) + if accounts and (len(columns) != rows[i+1].index(accounts[0])): - raise Exception, _("""All account columns should be after \ + webnotes.msgprint(_("""All account columns should be after \ standard columns and on the right. If you entered it properly, next probable reason \ could be wrong account name. - Please rectify it in the file and try again.""") + Please rectify it in the file and try again."""), raise_exception=1) return data, start_row_idx \ No newline at end of file From 0dbd62f163e236f5661b9985770c8327af6bab58 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 May 2013 18:16:44 +0530 Subject: [PATCH 181/295] [voucher import tool][fix] fixes for handling exception --- accounts/page/voucher_import_tool/voucher_import_tool.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/accounts/page/voucher_import_tool/voucher_import_tool.py b/accounts/page/voucher_import_tool/voucher_import_tool.py index 3c920ce04e..7634e5bf84 100644 --- a/accounts/page/voucher_import_tool/voucher_import_tool.py +++ b/accounts/page/voucher_import_tool/voucher_import_tool.py @@ -54,10 +54,11 @@ def upload(): rows = read_csv_content_from_uploaded_file() common_values = get_common_values(rows) - if not common_values.company: - webnotes.msgprint(_("Company is missing in csv file"), raise_exception=1) - company_abbr = webnotes.conn.get_value("Company", common_values.company, "abbr") + + if not company_abbr: + webnotes.msgprint(_("Company is missing or entered incorrect value"), raise_exception=1) + data, start_idx = get_data(rows, company_abbr) except Exception, e: err_msg = webnotes.message_log and "
    ".join(webnotes.message_log) or cstr(e) From df3353228080a510811c541e43f62112995dd7ba Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 May 2013 11:03:11 +0530 Subject: [PATCH 182/295] [fixes] pulling available qty on selection of item in case of pos --- selling/doctype/sales_common/sales_common.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py index 78f842291d..549128f1ea 100644 --- a/selling/doctype/sales_common/sales_common.py +++ b/selling/doctype/sales_common/sales_common.py @@ -132,6 +132,7 @@ class DocType(TransactionBase): where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now() or end_of_life = '0000-00-00') and (is_sales_item = 'Yes' or is_service_item = 'Yes')""", args['item_code'], as_dict=1) + tax = webnotes.conn.sql("""select tax_type, tax_rate from `tabItem Tax` where parent = %s""", args['item_code']) t = {} @@ -167,8 +168,9 @@ class DocType(TransactionBase): ret['export_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) ret['base_ref_rate'] = flt(base_ref_rate) ret['basic_rate'] = flt(base_ref_rate) - + if ret['warehouse'] or ret['reserved_warehouse']: + av_qty = self.get_available_qty({'item_code': args['item_code'], 'warehouse': ret['warehouse'] or ret['reserved_warehouse']}) ret.update(av_qty) @@ -179,7 +181,7 @@ class DocType(TransactionBase): (args['item_code'], obj.doc.customer)) if customer_item_code_row and customer_item_code_row[0][0]: ret['customer_item_code'] = customer_item_code_row[0][0] - + return ret From f6911fb51c4f8efcd6baa3403f0fc58835511400 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 May 2013 11:06:17 +0530 Subject: [PATCH 183/295] Update sales_invoice.py [fixes] pulling available qty on selection of item in case of pos --- accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 6871b1e90f..ab44247d5c 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -351,7 +351,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 From a3773396f37359eaf020829a3f9cece2e5ff910c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 29 May 2013 11:41:13 +0530 Subject: [PATCH 184/295] [naming series] [fix] added Date in make autoname --- setup/doctype/naming_series/naming_series.py | 13 +++---------- setup/page/setup/setup.js | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/setup/doctype/naming_series/naming_series.py b/setup/doctype/naming_series/naming_series.py index 3a6b36d1ff..eb293f256b 100644 --- a/setup/doctype/naming_series/naming_series.py +++ b/setup/doctype/naming_series/naming_series.py @@ -123,16 +123,9 @@ class DocType: def validate_series_name(self, n): import re - if "." in n: - parts = n.split(".") - if len(parts) > 2: - msgprint("Only one dot (.) allowed in " + n, raise_exception=1) - if not re.match("#+$", parts[-1]): - msgprint("Numbering series must be in hashes (e.g. ####)", raise_exception=1) - n = n[0] - if not re.match("^[a-zA-Z0-9-/]*$", n): - msgprint('Special Characters except "-" and "/" not allowed in naming series') - raise Exception + if not re.match("^[a-zA-Z0-9-/.#]*$", n): + msgprint('Special Characters except "-" and "/" not allowed in naming series', + raise_exception=True) def get_options(self, arg=''): sr = webnotes.model.doctype.get_property(self.doc.select_doc_for_series, diff --git a/setup/page/setup/setup.js b/setup/page/setup/setup.js index 1ebd7307a6..788021a42a 100644 --- a/setup/page/setup/setup.js +++ b/setup/page/setup/setup.js @@ -73,7 +73,7 @@ wn.module_page["Setup"] = [ { "route":"Form/Naming Series/Naming Series", doctype: "Naming Series", - label: wn._("Manage numbering series"), + label: wn._("Manage Numbering Series"), "description":wn._("Set multiple numbering series for transactions") }, { From 72edc3d955d7755623e3fa286fd060b07a509f34 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 29 May 2013 12:13:16 +0530 Subject: [PATCH 185/295] [accounts] [auto inventory accounting] changed naming series and message of journal vouchers created for Auto Inventory Accounting --- accounts/utils.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/accounts/utils.py b/accounts/utils.py index 382a33762b..31e622166e 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -273,36 +273,45 @@ def create_stock_in_hand_jv(reverse=False): jv = webnotes.bean([ { "doctype": "Journal Voucher", - "naming_series": "_PATCH-", + "naming_series": "JV-AUTO-", "company": company, "posting_date": today, "fiscal_year": fiscal_year, "voucher_type": "Journal Entry", - "user_remark": "Accounting Entry for Stock: \ - Initial booking of stock received but not billed account" + "user_remark": (_("Auto Inventory Accounting") + ": " + + (_("Disabled") if reverse else _("Enabled")) + ". " + + _("Journal Entry for inventory that is received but not yet invoiced")) }, { "doctype": "Journal Voucher Detail", "parentfield": "entries", "account": get_company_default(company, "stock_received_but_not_billed"), - (stock_rbnb_value > 0 and "credit" or "debit"): abs(stock_rbnb_value) + (stock_rbnb_value > 0 and "credit" or "debit"): abs(stock_rbnb_value) }, { "doctype": "Journal Voucher Detail", "parentfield": "entries", "account": get_company_default(company, "stock_adjustment_account"), - (stock_rbnb_value > 0 and "debit" or "credit"): abs(stock_rbnb_value), + (stock_rbnb_value > 0 and "debit" or "credit"): abs(stock_rbnb_value), "cost_center": get_company_default(company, "stock_adjustment_cost_center") }, ]) jv.insert() - jv.submit() jv_list.append(jv.doc.name) if jv_list: - webnotes.msgprint("""Folowing Journal Vouchers has been created automatically: - %s""" % '\n'.join(jv_list)) + msgprint(_("Following Journal Vouchers have been created automatically") + \ + ":\n%s" % ("\n".join([("%s" % (jv, jv)) for jv in jv_list]),)) + + msgprint(_("""These adjustment vouchers book the difference between \ + the total value of received items and the total value of invoiced items, \ + as a required step to use Auto Inventory Accounting. + This is an approximation to get you started. + You will need to submit these vouchers after checking if the values are correct. + For more details, read: \ + \ + Auto Inventory Accounting""")) webnotes.msgprint("""Please refresh the system to get effect of Auto Inventory Accounting""") From 5b3b123fe8d4116e06f1383707166854d4d9a7f8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 May 2013 17:55:16 +0530 Subject: [PATCH 186/295] 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 187/295] 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 188/295] [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 189/295] [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 190/295] 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 191/295] [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 192/295] [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 7407748b29a60239643be64651559bb060d4b3e1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jun 2013 10:49:08 +0530 Subject: [PATCH 193/295] 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 194/295] [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 195/295] [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 196/295] 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 197/295] 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 198/295] 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 199/295] 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 200/295] 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 201/295] bom exploded items grouped by items --- patches/june_2013/p01_update_bom_exploded_items.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/patches/june_2013/p01_update_bom_exploded_items.py b/patches/june_2013/p01_update_bom_exploded_items.py index 657c2c036a..eff0931e4d 100644 --- a/patches/june_2013/p01_update_bom_exploded_items.py +++ b/patches/june_2013/p01_update_bom_exploded_items.py @@ -21,6 +21,9 @@ def execute(): updated_bom = [] for bom in webnotes.conn.sql("select name from tabBOM where docstatus < 2"): if bom[0] not in updated_bom: - bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) - updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) - webnotes.conn.commit() \ No newline at end of file + try: + bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) + updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) + webnotes.conn.commit() + except: + pass \ No newline at end of file From 1725315d105b32eda172cbe7761bf17ea5ad4067 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 4 Jun 2013 19:35:28 +0530 Subject: [PATCH 202/295] Finalized batch-wise balance history --- .../batch_wise_balance_history/__init__.py | 0 .../batch_wise_balance_history.js | 39 +++++++ .../batch_wise_balance_history.py | 109 ++++++++++++++++++ .../batch_wise_balance_history.txt | 21 ++++ 4 files changed, 169 insertions(+) create mode 100644 stock/report/batch_wise_balance_history/__init__.py create mode 100644 stock/report/batch_wise_balance_history/batch_wise_balance_history.js create mode 100644 stock/report/batch_wise_balance_history/batch_wise_balance_history.py create mode 100644 stock/report/batch_wise_balance_history/batch_wise_balance_history.txt diff --git a/stock/report/batch_wise_balance_history/__init__.py b/stock/report/batch_wise_balance_history/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.js b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js new file mode 100644 index 0000000000..0ba1938a59 --- /dev/null +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js @@ -0,0 +1,39 @@ +wn.query_reports["Batch-Wise Balance History"] = { + "filters": [ + { + "fieldname":"item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + "width": "80" + }, + { + "fieldname":"warehouse", + "label": "Warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": "80" + }, + { + "fieldname":"batch_no", + "label": "Batch", + "fieldtype": "Link", + "options": "Batch", + "width": "80" + }, + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "width": "80", + "default": sys_defaults.year_start_date, + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "width": "80", + "default": wn.datetime.get_today() + } + ] +} \ No newline at end of file diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py new file mode 100644 index 0000000000..ca3e775f72 --- /dev/null +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -0,0 +1,109 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + item_map = get_item_details(filters) + iwb_map = get_item_warehouse_batch_map(filters) + + data = [] + for item in sorted(iwb_map): + for wh in sorted(iwb_map[item]): + for batch in sorted(iwb_map[item][wh]): + qty_dict = iwb_map[item][wh][batch] + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], wh, batch, + qty_dict.opening_qty, qty_dict.in_qty, + qty_dict.out_qty, qty_dict.bal_qty + ]) + + return columns, data + +def get_columns(filters): + """return columns based on filters""" + + columns = ["Item:Link/Item:100"] + ["Item Name::150"] + ["Description::150"] + \ + ["Warehouse:Link/Warehouse:100"] + ["Batch:Link/Batch:100"] + ["Opening Qty::90"] + \ + ["In Qty::80"] + ["Out Qty::80"] + ["Balance Qty::90"] + + return columns + +def get_conditions(filters): + conditions = "" + if filters.get("item_code"): + conditions += " and item_code='%s'" % filters["item_code"] + + if filters.get("warehouse"): + conditions += " and warehouse='%s'" % filters["warehouse"] + + if filters.get("batch_no"): + conditions += " and batch_no='%s'" % filters["batch_no"] + + if not filters.get("from_date"): + webnotes.msgprint("Please enter From Date", raise_exception=1) + + if filters.get("to_date"): + conditions += " and posting_date <= '%s'" % filters["to_date"] + else: + webnotes.msgprint("Please enter To Date", raise_exception=1) + + return conditions + +#get all details +def get_stock_ledger_entries(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select item_code, batch_no, warehouse, + posting_date, actual_qty + from `tabStock Ledger Entry` + where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % + conditions, as_dict=1) + +def get_item_warehouse_batch_map(filters): + sle = get_stock_ledger_entries(filters) + iwb_map = {} + + for d in sle: + iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {})\ + .setdefault(d.batch_no, webnotes._dict({ + "opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0 + })) + qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no] + if d.posting_date < filters["from_date"]: + qty_dict.opening_qty += flt(d.actual_qty) + elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]: + if flt(d.actual_qty) > 0: + qty_dict.in_qty += flt(d.actual_qty) + else: + qty_dict.out_qty += abs(flt(d.actual_qty)) + + qty_dict.bal_qty += flt(d.actual_qty) + + return iwb_map + +def get_item_details(filters): + if filters.get("item_code"): + conditions = " and name = '%s'" % filters["item_code"] + item_map = {} + for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): + item_map.setdefault(d.name, d) + + return item_map \ No newline at end of file diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.txt b/stock/report/batch_wise_balance_history/batch_wise_balance_history.txt new file mode 100644 index 0000000000..9e795b9f31 --- /dev/null +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-04 11:03:47", + "docstatus": 0, + "modified": "2013-06-04 19:32:27", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Stock Ledger Entry", + "report_name": "Batch-Wise Balance History", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Batch-Wise Balance History" + } +] \ No newline at end of file From 6653a957676f609b9b1d78739b78a68c231f9278 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Wed, 5 Jun 2013 11:36:24 +0530 Subject: [PATCH 203/295] Completed Warehouse-Wise Stock Balance --- stock/page/stock_home/stock_home.js | 8 ++ .../warehouse_wise_stock_balance/__init__.py | 0 .../warehouse_wise_stock_balance.js | 32 ++++++ .../warehouse_wise_stock_balance.py | 104 ++++++++++++++++++ .../warehouse_wise_stock_balance.txt | 21 ++++ 5 files changed, 165 insertions(+) create mode 100644 stock/report/warehouse_wise_stock_balance/__init__.py create mode 100644 stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js create mode 100644 stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py create mode 100644 stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index 75e4ee15b0..bc71a4faf5 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -205,6 +205,14 @@ wn.module_page["Stock"] = [ "label":wn._("Requested Items To Be Transferred"), route: "query-report/Requested Items To Be Transferred", }, + { + "label":wn._("Batch-Wise Balance History"), + route: "query-report/Batch-Wise Balance History", + }, + { + "label":wn._("Warehouse-Wise Stock Balance"), + route: "query-report/Warehouse-Wise Stock Balance", + }, ] } ] diff --git a/stock/report/warehouse_wise_stock_balance/__init__.py b/stock/report/warehouse_wise_stock_balance/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js new file mode 100644 index 0000000000..5e1eb3a7b2 --- /dev/null +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js @@ -0,0 +1,32 @@ +wn.query_reports["Warehouse-Wise Stock Balance"] = { + "filters": [ + { + "fieldname":"item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + "width": "80" + }, + { + "fieldname":"warehouse", + "label": "Warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": "80" + }, + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "width": "80", + "default": sys_defaults.year_start_date, + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "width": "80", + "default": wn.datetime.get_today() + } + ] +} \ No newline at end of file diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py new file mode 100644 index 0000000000..324bbe3e8c --- /dev/null +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py @@ -0,0 +1,104 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + item_map = get_item_details(filters) + iwb_map = get_item_warehouse_map(filters) + + data = [] + for item in sorted(iwb_map): + for wh in sorted(iwb_map[item]): + qty_dict = iwb_map[item][wh] + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], wh, + qty_dict.opening_qty, qty_dict.in_qty, + qty_dict.out_qty, qty_dict.bal_qty + ]) + + return columns, data + +def get_columns(filters): + """return columns based on filters""" + + columns = ["Item:Link/Item:100"] + ["Item Name::150"] + ["Description::150"] + \ + ["Warehouse:Link/Warehouse:100"] + ["Opening Qty::90"] + \ + ["In Qty::80"] + ["Out Qty::80"] + ["Balance Qty::90"] + + return columns + +def get_conditions(filters): + conditions = "" + if filters.get("item_code"): + conditions += " and item_code='%s'" % filters["item_code"] + + if filters.get("warehouse"): + conditions += " and warehouse='%s'" % filters["warehouse"] + + if not filters.get("from_date"): + webnotes.msgprint("Please enter From Date", raise_exception=1) + + if filters.get("to_date"): + conditions += " and posting_date <= '%s'" % filters["to_date"] + else: + webnotes.msgprint("Please enter To Date", raise_exception=1) + + return conditions + +#get all details +def get_stock_ledger_entries(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select item_code, warehouse, + posting_date, actual_qty + from `tabStock Ledger Entry` + where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % + conditions, as_dict=1) + +def get_item_warehouse_map(filters): + sle = get_stock_ledger_entries(filters) + iwb_map = {} + + for d in sle: + iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, webnotes._dict({\ + "opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0 + })) + qty_dict = iwb_map[d.item_code][d.warehouse] + if d.posting_date < filters["from_date"]: + qty_dict.opening_qty += flt(d.actual_qty) + elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]: + if flt(d.actual_qty) > 0: + qty_dict.in_qty += flt(d.actual_qty) + else: + qty_dict.out_qty += abs(flt(d.actual_qty)) + + qty_dict.bal_qty += flt(d.actual_qty) + + return iwb_map + +def get_item_details(filters): + if filters.get("item_code"): + conditions = " and name = '%s'" % filters["item_code"] + item_map = {} + for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): + item_map.setdefault(d.name, d) + + return item_map \ No newline at end of file diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt new file mode 100644 index 0000000000..2513587632 --- /dev/null +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-05 11:00:31", + "docstatus": 0, + "modified": "2013-06-05 11:00:31", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Stock Ledger Entry", + "report_name": "Warehouse-Wise Stock Balance", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Warehouse-Wise Stock Balance" + } +] \ No newline at end of file From 196b6b8b68df735321abea0b80abb3f0a1836f45 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 5 Jun 2013 16:12:42 +0530 Subject: [PATCH 204/295] [report] itemwise sales register --- accounts/page/accounts_home/accounts_home.js | 5 ++ .../item_wise_sales_register/__init__.py | 0 .../item_wise_sales_register.js | 39 +++++++++++ .../item_wise_sales_register.py | 67 +++++++++++++++++++ .../item_wise_sales_register.txt | 22 ++++++ 5 files changed, 133 insertions(+) create mode 100644 accounts/report/item_wise_sales_register/__init__.py create mode 100644 accounts/report/item_wise_sales_register/item_wise_sales_register.js create mode 100644 accounts/report/item_wise_sales_register/item_wise_sales_register.py create mode 100644 accounts/report/item_wise_sales_register/item_wise_sales_register.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 03009023c1..7f623d7115 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -252,6 +252,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Item-wise Sales Register", doctype: "Sales Invoice" }, + { + "label":wn._("Item-wise Purchase Register"), + route: "query-report/Item-wise Purchase Register", + doctype: "Purchase Invoice" + }, ] } ] diff --git a/accounts/report/item_wise_sales_register/__init__.py b/accounts/report/item_wise_sales_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/accounts/report/item_wise_sales_register/item_wise_sales_register.js new file mode 100644 index 0000000000..b9ce9595fe --- /dev/null +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -0,0 +1,39 @@ +wn.query_reports["Item-wise Sales Register"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname": "item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Debit", + "master_type": "Customer" + } + } + } + } + ] +} \ No newline at end of file diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/accounts/report/item_wise_sales_register/item_wise_sales_register.py new file mode 100644 index 0000000000..f6e26af350 --- /dev/null +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -0,0 +1,67 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + item_list = get_items(filters) + + data = [] + for d in item_list: + data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, d.customer, + d.debit_to, d.territory, d.project_name, d.company, d.sales_order, d.delivery_note, + d.income_account, d.qty, d.basic_rate, d.amount]) + + return columns, data + + +def get_columns(): + return [ + "Item Code:Link/Item:120", "Item Name::120", "Item Group:Link/Item Group:100", + "Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer:Link/Customer:120", + "Customer Account:Link/Account:120", "Territory:Link/Territory:80", + "Project:Link/Project:80", "Company:Link/Company:100", "Sales Order:Link/Sales Order:100", + "Delivery Note:Link/Delivery Note:100", "Income Account:Link/Account:140", + "Qty:Float:120", "Rate:Currency:120", "Amount:Currency:120" + ] + + +def get_conditions(filters): + conditions = "" + + if filters.get("account"): conditions += " and si.debit_to = %(account)s" + + if filters.get("item_code"): conditions += " and si_item.item_code = %(item_code)s" + + if filters.get("from_date"): conditions += " and si.posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and si.posting_date<=%(to_date)s" + + return conditions + +def get_items(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select si.name, si.posting_date, si.debit_to, si.project_name, + si.customer, si.remarks, si.territory, si_item.item_code, si_item.item_name, + si_item.item_group, si_item.sales_order, si_item.delivery_note, si_item.income_account, + si_item.qty, si_item.basic_rate, si_item.amount + from `tabSales Invoice` si, `tabSales Invoice Item` si_item + where si.name = si_item.parent and si.docstatus = 1 %s + order by si.posting_date desc, si_item.item_code desc""" % conditions, filters, as_dict=1) \ No newline at end of file diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.txt b/accounts/report/item_wise_sales_register/item_wise_sales_register.txt new file mode 100644 index 0000000000..fb0555d459 --- /dev/null +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-13 17:50:55", + "docstatus": 0, + "modified": "2013-05-13 17:50:55", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Invoice", + "report_name": "Item-wise Sales Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Item-wise Sales Register" + } +] \ No newline at end of file From c36e2653f9b29de938dc943a60d908023c73d535 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 5 Jun 2013 16:13:05 +0530 Subject: [PATCH 205/295] [report] itemwise purchase register --- .../item_wise_purchase_register/__init__.py | 0 .../item_wise_purchase_register.js | 39 ++++++++++ .../item_wise_purchase_register.py | 74 +++++++++++++++++++ .../item_wise_purchase_register.txt | 22 ++++++ 4 files changed, 135 insertions(+) create mode 100644 accounts/report/item_wise_purchase_register/__init__.py create mode 100644 accounts/report/item_wise_purchase_register/item_wise_purchase_register.js create mode 100644 accounts/report/item_wise_purchase_register/item_wise_purchase_register.py create mode 100644 accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt diff --git a/accounts/report/item_wise_purchase_register/__init__.py b/accounts/report/item_wise_purchase_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js new file mode 100644 index 0000000000..8323a1af78 --- /dev/null +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js @@ -0,0 +1,39 @@ +wn.query_reports["Item-wise Purchase Register"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname": "item_code", + "label": "Item", + "fieldtype": "Link", + "options": "Item", + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Credit", + "master_type": "Supplier" + } + } + } + } + ] +} \ No newline at end of file diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py new file mode 100644 index 0000000000..ad9d79504b --- /dev/null +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -0,0 +1,74 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + item_list = get_items(filters) + aii_account_map = get_aii_accounts() + webnotes.errprint(aii_account_map) + data = [] + for d in item_list: + expense_head = d.expense_head or aii_account_map.get(d.company) + data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, d.supplier, + d.credit_to, d.project_name, d.company, d.purchase_order, d.purchase_receipt, + expense_head, d.qty, d.rate, d.amount]) + + return columns, data + + +def get_columns(): + return ["Item Code:Link/Item:120", "Item Name::120", "Item Group:Link/Item Group:100", + "Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier:Link/Customer:120", + "Supplier Account:Link/Account:120", "Project:Link/Project:80", "Company:Link/Company:100", + "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100", + "Expense Account:Link/Account:140", "Qty:Float:120", "Rate:Currency:120", + "Amount:Currency:120"] + + +def get_conditions(filters): + conditions = "" + + if filters.get("account"): conditions += " and pi.credit_to = %(account)s" + + if filters.get("item_code"): conditions += " and pi_item.item_code = %(item_code)s" + + if filters.get("from_date"): conditions += " and pi.posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and pi.posting_date<=%(to_date)s" + + return conditions + +def get_items(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select pi.name, pi.posting_date, pi.credit_to, pi.company, + pi.supplier, pi.remarks, pi_item.item_code, pi_item.item_name, pi_item.item_group, + pi_item.project_name, pi_item.purchase_order, pi_item.purchase_receipt, + pi_item.expense_head, pi_item.qty, pi_item.rate, pi_item.amount + from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item + where pi.name = pi_item.parent and pi.docstatus = 1 %s + order by pi.posting_date desc, pi_item.item_code desc""" % conditions, filters, as_dict=1) + +def get_aii_accounts(): + aii_account_map = {} + for d in webnotes.conn.sql("select name, stock_received_but_not_billed from tabCompany", + as_dict=1): + aii_account_map.setdefault(d.name, d.stock_received_but_not_billed) + + return aii_account_map \ No newline at end of file diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt new file mode 100644 index 0000000000..7ded5ff583 --- /dev/null +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-06-05 15:37:30", + "docstatus": 0, + "modified": "2013-06-05 15:37:30", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Invoice", + "report_name": "Item-wise Purchase Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Item-wise Purchase Register" + } +] \ No newline at end of file From 7b48218b6f75bf59b1980d32bea75165e00d4346 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 5 Jun 2013 18:17:28 +0530 Subject: [PATCH 206/295] [Reports][Reorder Level] and [No Sales Order from Customer] --- selling/page/selling_home/selling_home.js | 6 ++ .../__init__.py | 0 ...s_order_from_customers_(since_2_months).py | 69 +++++++++++++ ..._order_from_customers_(since_2_months).txt | 21 ++++ stock/page/stock_home/stock_home.js | 5 + stock/report/item_reorder_level/__init__.py | 0 .../item_reorder_level/item_reorder_level.js | 16 +++ .../item_reorder_level/item_reorder_level.py | 97 +++++++++++++++++++ .../item_reorder_level/item_reorder_level.txt | 21 ++++ 9 files changed, 235 insertions(+) create mode 100644 selling/report/no_sales_order_from_customers_(since_2_months)/__init__.py create mode 100644 selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py create mode 100644 selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt create mode 100644 stock/report/item_reorder_level/__init__.py create mode 100644 stock/report/item_reorder_level/item_reorder_level.js create mode 100644 stock/report/item_reorder_level/item_reorder_level.py create mode 100644 stock/report/item_reorder_level/item_reorder_level.txt diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 682978bd17..1f119e2917 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -149,6 +149,11 @@ wn.module_page["Selling"] = [ right: true, icon: "icon-list", items: [ + { + "label":wn._("No Sales Order from Customers (Since 2 months)"), + route: "query-report/No Sales Order from Customers (Since 2 months)", + doctype: "Sales Order" + }, { "label":wn._("Customer Addresses And Contacts"), route: "query-report/Customer Addresses And Contacts" @@ -165,6 +170,7 @@ wn.module_page["Selling"] = [ "label":wn._("Item-wise Sales History"), route: "query-report/Item-wise Sales History", }, + ] } ] diff --git a/selling/report/no_sales_order_from_customers_(since_2_months)/__init__.py b/selling/report/no_sales_order_from_customers_(since_2_months)/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py b/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py new file mode 100644 index 0000000000..43efb98d06 --- /dev/null +++ b/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py @@ -0,0 +1,69 @@ +# 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() + customers = get_so_details() + + data = [] + for cust in customers: + if cust[8] >= 60: # days_since_last_order + cust.insert(7,get_last_so_amt(cust[0])) + data.append(cust) + + return columns, data + +def get_so_details(): + return webnotes.conn.sql("""select + cust.name, + cust.customer_name, + cust.territory, + cust.customer_group, + count(distinct(so.name)) as 'num_of_order', + sum(net_total) as 'total_order_value', + sum(if(so.status = "Stopped", + so.net_total * so.per_delivered/100, + so.net_total)) as 'total_order_considered', + max(so.transaction_date) as 'last_sales_order_date', + DATEDIFF(CURDATE(),max(so.transaction_date)) as 'days_since_last_order' + from `tabCustomer` cust, `tabSales Order` so + where cust.name = so.customer and so.docstatus = 1 + group by cust.name + order by 'days_since_last_order' desc """,as_list=1) + +def get_last_so_amt(customer): + return webnotes.conn.sql("""select net_total from `tabSales Order` + where customer ='%(customer)s' and docstatus = 1 and + transaction_date = (select max(transaction_date) + from `tabSales Order` + where customer = '%(customer)s') + """%{'customer':customer}) + +def get_columns(): + return [ + "Customer:Link/Customer:120", + "Customer Name:Data:120", + "Territory::120", + "Customer Group::120", + "Number of Order::120", + "Total Order Value:Currency:120", + "Total Order Considered:Currency:160", + "Last Order Amount:Currency:160", + "Last Sales Order Date:Date:160", + "Days Since Last Order::160" + ] \ No newline at end of file diff --git a/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt b/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt new file mode 100644 index 0000000000..cd7b2b7ff9 --- /dev/null +++ b/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-05 11:40:49", + "docstatus": 0, + "modified": "2013-06-05 11:40:49", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Order", + "report_name": "No Sales Order from Customers (Since 2 months)", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "No Sales Order from Customers (Since 2 months)" + } +] \ No newline at end of file diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index 75e4ee15b0..ac3a096c50 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -128,6 +128,11 @@ wn.module_page["Stock"] = [ right: true, icon: "icon-table", items: [ + { + "label":wn._("Item Reorder Level"), + route: "query-report/Item Reorder Level", + doctype: "Item" + }, { "label":wn._("Stock Ledger"), page: "stock-ledger" diff --git a/stock/report/item_reorder_level/__init__.py b/stock/report/item_reorder_level/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/item_reorder_level/item_reorder_level.js b/stock/report/item_reorder_level/item_reorder_level.js new file mode 100644 index 0000000000..30240e587d --- /dev/null +++ b/stock/report/item_reorder_level/item_reorder_level.js @@ -0,0 +1,16 @@ +wn.query_reports["Item Reorder Level"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": sys_defaults.year_start_date + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + } + ] +} \ No newline at end of file diff --git a/stock/report/item_reorder_level/item_reorder_level.py b/stock/report/item_reorder_level/item_reorder_level.py new file mode 100644 index 0000000000..d59696138c --- /dev/null +++ b/stock/report/item_reorder_level/item_reorder_level.py @@ -0,0 +1,97 @@ +# 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 +from webnotes.utils import getdate, flt, cint + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + items = get_item_info() + consumed_item_map = get_consumed_items(filters) + delivered_item_map = get_delivered_items(filters) + + avg_daily_outgoing = 0 + diff = (getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days + if diff <= 0: + webnotes.msgprint("To Date should not be less than eual to From Date",raise_exception=1) + + data = [] + for item in items: + + total_outgoing = consumed_item_map.get(item.name, 0)+delivered_item_map.get(item.name,0) + avg_daily_outgoing = cint(total_outgoing/diff) + reorder_level = (avg_daily_outgoing * item.lead_time_days) + item.min_order_qty + + data.append([item.name, item.item_name, item.description, item.min_order_qty, item.lead_time_days, + consumed_item_map.get(item.name, 0), delivered_item_map.get(item.name,0), total_outgoing, + avg_daily_outgoing, reorder_level]) + + return columns , data + +def get_columns(): + return["Item:Link/Item:120", "Item name:Data:120", "description::160", "minimum inventory level::120", + "lead time days::120", "consumed::120", "delivered::120", "total outgoing::120", + "avg daily outgoing::120", "reorder level::120"] + +def get_item_info(): + return webnotes.conn.sql("""select name, item_name, description, min_order_qty, lead_time_days + from tabItem""",as_dict=1) + +def get_consumed_items(filters): + condition = get_condition(filters) + + cn_items = webnotes.conn.sql("""select se_item.item_code, sum(se_item.actual_qty) as 'consume_qty' + 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, '') = '' %s + group by se_item.item_code""" % (condition), as_dict=1) + + cn_items_map = {} + for item in cn_items: + cn_items_map.setdefault(item.item_code, item.consume_qty) + + return cn_items_map + +def get_delivered_items(filters): + condition = get_condition(filters) + + dn_items = webnotes.conn.sql("""select dn_item.item_code, sum(dn_item.qty) as dn_qty + from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item + where dn.name = dn_item.parent and dn.docstatus = 1 %s group by dn_item.item_code""" % (condition) + , as_dict=1) + + si_items = webnotes.conn.sql("""select si_item.item_name, sum(si_item.qty) as si_qty + 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 %s group by si_item.item_name""" % (condition), as_dict=1) + + dn_item_map = {} + for item in dn_items: + dn_item_map.setdefault(item.item_code, item.dn_qty) + + for item in si_items: + dn_item_map.setdefault(item.item_code, item.si_qty) + + return dn_item_map + +def get_condition(filters): + conditions = "" + if filters.get("from_date") and filters.get("to_date"): + conditions += " and posting_date between '%s' and '%s'" % (filters["from_date"],filters["to_date"]) + else: + webnotes.msgprint("Please set date in from date field",raise_exception=1) + return conditions \ No newline at end of file diff --git a/stock/report/item_reorder_level/item_reorder_level.txt b/stock/report/item_reorder_level/item_reorder_level.txt new file mode 100644 index 0000000000..93c6dfb185 --- /dev/null +++ b/stock/report/item_reorder_level/item_reorder_level.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-05 15:34:51", + "docstatus": 0, + "modified": "2013-06-05 15:50:30", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Item", + "report_name": "Item Reorder Level", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Item Reorder Level" + } +] \ No newline at end of file From e3e15a94c3bdf8a11905355c2499187ef906d2a4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 6 Jun 2013 11:35:49 +0530 Subject: [PATCH 207/295] [system console] [security fix] removed system console --- patches/patch_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/patches/patch_list.py b/patches/patch_list.py index 6a396725dd..d8b84da8f8 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -253,4 +253,5 @@ patch_list = [ "patches.may_2013.p04_reorder_level", "patches.may_2013.p05_update_cancelled_gl_entries", "patches.june_2013.p01_update_bom_exploded_items", + "execute:webnotes.delete_doc('DocType', 'System Console')", ] \ No newline at end of file From 6164b89e9588b1047c3687e10458bc656bef09f6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 6 Jun 2013 17:18:38 +0530 Subject: [PATCH 208/295] [report] listing on home page --- accounts/page/accounts_home/accounts_home.js | 15 +++++---------- .../item_wise_sales_register.py | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 7f623d7115..b920bfdbab 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -127,16 +127,6 @@ 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" @@ -247,6 +237,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Customer Account Head", doctype: "Account" }, + { + "label":wn._("Supplier Account Head"), + route: "query-report/Supplier Account Head", + doctype: "Account" + }, { "label":wn._("Item-wise Sales Register"), route: "query-report/Item-wise Sales Register", diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/accounts/report/item_wise_sales_register/item_wise_sales_register.py index f6e26af350..f3ed2a1242 100644 --- a/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -59,7 +59,7 @@ def get_conditions(filters): def get_items(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select si.name, si.posting_date, si.debit_to, si.project_name, - si.customer, si.remarks, si.territory, si_item.item_code, si_item.item_name, + si.customer, si.remarks, si.territory, si.company, si_item.item_code, si_item.item_name, si_item.item_group, si_item.sales_order, si_item.delivery_note, si_item.income_account, si_item.qty, si_item.basic_rate, si_item.amount from `tabSales Invoice` si, `tabSales Invoice Item` si_item From 657f8a667737e8a693db113f744a463c6e56e933 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Thu, 6 Jun 2013 18:33:26 +0530 Subject: [PATCH 209/295] Completed 'Item Prices' Report --- stock/page/stock_home/stock_home.js | 4 + stock/report/item_prices/__init__.py | 0 stock/report/item_prices/item_prices.js | 3 + stock/report/item_prices/item_prices.py | 106 +++++++++++++++++++++++ stock/report/item_prices/item_prices.txt | 21 +++++ 5 files changed, 134 insertions(+) create mode 100644 stock/report/item_prices/__init__.py create mode 100644 stock/report/item_prices/item_prices.js create mode 100644 stock/report/item_prices/item_prices.py create mode 100644 stock/report/item_prices/item_prices.txt diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index bc71a4faf5..bd28b94291 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -213,6 +213,10 @@ wn.module_page["Stock"] = [ "label":wn._("Warehouse-Wise Stock Balance"), route: "query-report/Warehouse-Wise Stock Balance", }, + { + "label":wn._("Item Prices"), + route: "query-report/Item Prices", + }, ] } ] diff --git a/stock/report/item_prices/__init__.py b/stock/report/item_prices/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/item_prices/item_prices.js b/stock/report/item_prices/item_prices.js new file mode 100644 index 0000000000..3dfa7cad33 --- /dev/null +++ b/stock/report/item_prices/item_prices.js @@ -0,0 +1,3 @@ +wn.query_reports["Item Prices"] = { + "filters": [] +} \ No newline at end of file diff --git a/stock/report/item_prices/item_prices.py b/stock/report/item_prices/item_prices.py new file mode 100644 index 0000000000..ea0be477df --- /dev/null +++ b/stock/report/item_prices/item_prices.py @@ -0,0 +1,106 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + item_map = get_item_details() + pl = get_price_list() + bom_rate = get_item_bom_rate() + val_rate_map = get_valuation_rate() + + data = [] + for item in sorted(item_map): + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], item_map[item]["stock_uom"], + flt(item_map[item]["last_purchase_rate"]), val_rate_map.get(item, 0), + pl.get(item, {}).get("selling"), pl.get(item, {}).get("buying"), + bom_rate.get(item, 0), flt(item_map[item]["standard_rate"]) + ]) + + return columns, data + +def get_columns(filters): + """return columns based on filters""" + + columns = ["Item:Link/Item:100", "Item Name::150", "Description::150", "UOM:Link/UOM:80", + "Last Purchase Rate:Currency:90", "Valuation Rate:Currency:80", "Sales Price List::80", + "Purchase Price List::80", "BOM Rate:Currency:90", "Standard Rate:Currency:100"] + + return columns + +def get_item_details(): + """returns all items details""" + + item_map = {} + + for i in webnotes.conn.sql("select name, item_name, description, \ + stock_uom, standard_rate, last_purchase_rate from tabItem \ + order by item_code", as_dict=1): + item_map.setdefault(i.name, i) + + return item_map + +def get_price_list(): + """Get selling & buying price list of every item""" + + rate = {} + + price_list = webnotes.conn.sql("""select parent, selling, buying, + concat(price_list_name, " - ", ref_currency, " ", ref_rate) as price + from `tabItem Price` where docstatus<2""", as_dict=1) + + for j in price_list: + if j.selling: + rate.setdefault(j.parent, {}).setdefault("selling", []).append(j.price) + if j.buying: + rate.setdefault(j.parent, {}).setdefault("buying", []).append(j.price) + + item_rate_map = {} + + for item in rate: + item_rate_map.setdefault(item, {}).setdefault("selling", + ", ".join(rate[item].get("selling", []))) + item_rate_map[item]["buying"] = ", ".join(rate[item].get("buying", [])) + + return item_rate_map + +def get_item_bom_rate(): + """Get BOM rate of an item from BOM""" + + bom_map = {} + + for b in webnotes.conn.sql("""select item, (total_cost/quantity) as bom_rate + from `tabBOM` where is_active=1 and is_default=1""", as_dict=1): + bom_map.setdefault(b.item, flt(b.bom_rate)) + + return bom_map + +def get_valuation_rate(): + """Get an average valuation rate of an item from all warehouses""" + + val_rate_map = {} + + for d in webnotes.conn.sql("""select item_code, avg(valuation_rate) as val_rate + from tabBin group by item_code""", as_dict=1): + val_rate_map.setdefault(d.item_code, d.val_rate) + + return val_rate_map \ No newline at end of file diff --git a/stock/report/item_prices/item_prices.txt b/stock/report/item_prices/item_prices.txt new file mode 100644 index 0000000000..4c49ca1748 --- /dev/null +++ b/stock/report/item_prices/item_prices.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-05 11:43:30", + "docstatus": 0, + "modified": "2013-06-05 11:43:30", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Stock Ledger Entry", + "report_name": "Item Prices", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Item Prices" + } +] \ No newline at end of file From 1efc8b373446edc0ff2a04a7d328a34ff2ad3f3c Mon Sep 17 00:00:00 2001 From: Saurabh Date: Thu, 6 Jun 2013 19:31:14 +0530 Subject: [PATCH 210/295] [Reprots][No Sales Order From Customer] and [Item Reorder Level] --- selling/page/selling_home/selling_home.js | 10 ++-- .../__init__.py | 0 .../no_sales_order_from_customers.py} | 15 ++--- .../no_sales_order_from_customers.txt} | 8 +-- stock/page/stock_home/stock_home.js | 10 ++-- .../item_reorder_level/item_reorder_level.py | 57 +++++++++++-------- 6 files changed, 52 insertions(+), 48 deletions(-) rename selling/report/{no_sales_order_from_customers_(since_2_months) => no_sales_order_from_customers}/__init__.py (100%) rename selling/report/{no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py => no_sales_order_from_customers/no_sales_order_from_customers.py} (84%) rename selling/report/{no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt => no_sales_order_from_customers/no_sales_order_from_customers.txt} (57%) diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 1f119e2917..603bd3a48f 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -149,11 +149,6 @@ wn.module_page["Selling"] = [ right: true, icon: "icon-list", items: [ - { - "label":wn._("No Sales Order from Customers (Since 2 months)"), - route: "query-report/No Sales Order from Customers (Since 2 months)", - doctype: "Sales Order" - }, { "label":wn._("Customer Addresses And Contacts"), route: "query-report/Customer Addresses And Contacts" @@ -170,6 +165,11 @@ wn.module_page["Selling"] = [ "label":wn._("Item-wise Sales History"), route: "query-report/Item-wise Sales History", }, + { + "label":wn._("No Sales Order from Customers (Since 2 months)"), + route: "query-report/No Sales Order from Customers", + doctype: "Sales Order" + }, ] } diff --git a/selling/report/no_sales_order_from_customers_(since_2_months)/__init__.py b/selling/report/no_sales_order_from_customers/__init__.py similarity index 100% rename from selling/report/no_sales_order_from_customers_(since_2_months)/__init__.py rename to selling/report/no_sales_order_from_customers/__init__.py diff --git a/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py b/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.py similarity index 84% rename from selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py rename to selling/report/no_sales_order_from_customers/no_sales_order_from_customers.py index 43efb98d06..789e1684e9 100644 --- a/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).py +++ b/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.py @@ -16,7 +16,6 @@ import webnotes def execute(filters=None): - columns = get_columns() customers = get_so_details() @@ -25,7 +24,6 @@ def execute(filters=None): if cust[8] >= 60: # days_since_last_order cust.insert(7,get_last_so_amt(cust[0])) data.append(cust) - return columns, data def get_so_details(): @@ -40,19 +38,18 @@ def get_so_details(): so.net_total * so.per_delivered/100, so.net_total)) as 'total_order_considered', max(so.transaction_date) as 'last_sales_order_date', - DATEDIFF(CURDATE(),max(so.transaction_date)) as 'days_since_last_order' + DATEDIFF(CURDATE(), max(so.transaction_date)) as 'days_since_last_order' from `tabCustomer` cust, `tabSales Order` so where cust.name = so.customer and so.docstatus = 1 group by cust.name order by 'days_since_last_order' desc """,as_list=1) def get_last_so_amt(customer): - return webnotes.conn.sql("""select net_total from `tabSales Order` - where customer ='%(customer)s' and docstatus = 1 and - transaction_date = (select max(transaction_date) - from `tabSales Order` - where customer = '%(customer)s') - """%{'customer':customer}) + res = webnotes.conn.sql("""select net_total from `tabSales Order` + where customer ='%(customer)s' and docstatus = 1 order by transaction_date desc + limit 1""" % {'customer':customer}) + + return res and res[0][0] or 0 def get_columns(): return [ diff --git a/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt b/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.txt similarity index 57% rename from selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt rename to selling/report/no_sales_order_from_customers/no_sales_order_from_customers.txt index cd7b2b7ff9..32b38ae815 100644 --- a/selling/report/no_sales_order_from_customers_(since_2_months)/no_sales_order_from_customers_(since_2_months).txt +++ b/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-06-05 11:40:49", + "creation": "2013-06-06 19:15:50", "docstatus": 0, - "modified": "2013-06-05 11:40:49", + "modified": "2013-06-06 19:15:51", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,11 +11,11 @@ "is_standard": "Yes", "name": "__common__", "ref_doctype": "Sales Order", - "report_name": "No Sales Order from Customers (Since 2 months)", + "report_name": "No Sales Order from Customers", "report_type": "Script Report" }, { "doctype": "Report", - "name": "No Sales Order from Customers (Since 2 months)" + "name": "No Sales Order from Customers" } ] \ No newline at end of file diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index ac3a096c50..ab3d3253bb 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -128,11 +128,6 @@ wn.module_page["Stock"] = [ right: true, icon: "icon-table", items: [ - { - "label":wn._("Item Reorder Level"), - route: "query-report/Item Reorder Level", - doctype: "Item" - }, { "label":wn._("Stock Ledger"), page: "stock-ledger" @@ -210,6 +205,11 @@ wn.module_page["Stock"] = [ "label":wn._("Requested Items To Be Transferred"), route: "query-report/Requested Items To Be Transferred", }, + { + "label":wn._("Item Reorder Level"), + route: "query-report/Item Reorder Level", + doctype: "Item" + }, ] } ] diff --git a/stock/report/item_reorder_level/item_reorder_level.py b/stock/report/item_reorder_level/item_reorder_level.py index d59696138c..10ee182701 100644 --- a/stock/report/item_reorder_level/item_reorder_level.py +++ b/stock/report/item_reorder_level/item_reorder_level.py @@ -15,27 +15,30 @@ # along with this program. If not, see . import webnotes -from webnotes.utils import getdate, flt, cint +from webnotes.utils import getdate, flt def execute(filters=None): if not filters: filters = {} + float_preceision = webnotes.conn.get_default("float_preceision") + + condition =get_condition(filters) + + avg_daily_outgoing = 0 + diff = ((getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days)+1 + if diff <= 0: + webnotes.msgprint("To Date should not be less than eual to From Date",raise_exception=1) columns = get_columns() items = get_item_info() - consumed_item_map = get_consumed_items(filters) - delivered_item_map = get_delivered_items(filters) - - avg_daily_outgoing = 0 - diff = (getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days - if diff <= 0: - webnotes.msgprint("To Date should not be less than eual to From Date",raise_exception=1) + consumed_item_map = get_consumed_items(condition) + delivered_item_map = get_delivered_items(condition) data = [] for item in items: total_outgoing = consumed_item_map.get(item.name, 0)+delivered_item_map.get(item.name,0) - avg_daily_outgoing = cint(total_outgoing/diff) - reorder_level = (avg_daily_outgoing * item.lead_time_days) + item.min_order_qty + avg_daily_outgoing = flt(total_outgoing/diff, float_preceision) + reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.min_order_qty) data.append([item.name, item.item_name, item.description, item.min_order_qty, item.lead_time_days, consumed_item_map.get(item.name, 0), delivered_item_map.get(item.name,0), total_outgoing, @@ -44,20 +47,24 @@ def execute(filters=None): return columns , data def get_columns(): - return["Item:Link/Item:120", "Item name:Data:120", "description::160", "minimum inventory level::120", - "lead time days::120", "consumed::120", "delivered::120", "total outgoing::120", - "avg daily outgoing::120", "reorder level::120"] + return[ + "Item:Link/Item:120", "Item name:Data:120", "Description::160", + "Minimum Inventory Level:Float:160", "Lead Time Days:Float:120", "Consumed:Float:120", + "Delivered:Float:120", "Total Outgoing:Float:120", "Avg Daily Outgoing:Float:160", + "Reorder Level:Float:120" + ] def get_item_info(): - return webnotes.conn.sql("""select name, item_name, description, min_order_qty, lead_time_days - from tabItem""",as_dict=1) + return webnotes.conn.sql("""select name, item_name, description, min_order_qty, + lead_time_days from tabItem""", as_dict=1) -def get_consumed_items(filters): - condition = get_condition(filters) +def get_consumed_items(condition): - cn_items = webnotes.conn.sql("""select se_item.item_code, sum(se_item.actual_qty) as 'consume_qty' + cn_items = webnotes.conn.sql("""select se_item.item_code, + sum(se_item.actual_qty) as 'consume_qty' 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, '') = '' %s + where se.name = se_item.parent and se.docstatus = 1 + and ifnull(se_item.t_warehouse, '') = '' %s group by se_item.item_code""" % (condition), as_dict=1) cn_items_map = {} @@ -66,18 +73,18 @@ def get_consumed_items(filters): return cn_items_map -def get_delivered_items(filters): - condition = get_condition(filters) +def get_delivered_items(condition): dn_items = webnotes.conn.sql("""select dn_item.item_code, sum(dn_item.qty) as dn_qty from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item - where dn.name = dn_item.parent and dn.docstatus = 1 %s group by dn_item.item_code""" % (condition) - , as_dict=1) + where dn.name = dn_item.parent and dn.docstatus = 1 %s + group by dn_item.item_code""" % (condition), as_dict=1) si_items = webnotes.conn.sql("""select si_item.item_name, sum(si_item.qty) as si_qty 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 %s group by si_item.item_name""" % (condition), as_dict=1) + where si.name = si_item.parent and si.docstatus = 1 and + ifnull(si.update_stock, 0) = 1 and ifnull(si.is_pos, 0) = 1 %s + group by si_item.item_name""" % (condition), as_dict=1) dn_item_map = {} for item in dn_items: From 426b57e1b41feb6f273838097e35e524907364ac Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Fri, 7 Jun 2013 11:52:15 +0530 Subject: [PATCH 211/295] Deleted item_prices.js --- stock/report/item_prices/item_prices.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 stock/report/item_prices/item_prices.js diff --git a/stock/report/item_prices/item_prices.js b/stock/report/item_prices/item_prices.js deleted file mode 100644 index 3dfa7cad33..0000000000 --- a/stock/report/item_prices/item_prices.js +++ /dev/null @@ -1,3 +0,0 @@ -wn.query_reports["Item Prices"] = { - "filters": [] -} \ No newline at end of file From 5b1e899250637b0813c9f0822b24a62249b7fb08 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 7 Jun 2013 13:34:16 +0530 Subject: [PATCH 212/295] [Reports] [Customers Not Buying Sinse Long Time] and [Itemwise Recommended Reorder level] --- selling/page/selling_home/selling_home.js | 4 ++-- .../__init__.py | 0 .../customers_not_buying_since_long_time.js | 10 ++++++++++ .../customers_not_buying_since_long_time.py} | 11 ++++++++++- .../customers_not_buying_since_long_time.txt} | 8 ++++---- stock/page/stock_home/stock_home.js | 4 ++-- .../__init__.py | 0 .../itemwise_recommended_reorder_level.js} | 2 +- .../itemwise_recommended_reorder_level.py} | 2 +- .../itemwise_recommended_reorder_level.txt} | 8 ++++---- 10 files changed, 34 insertions(+), 15 deletions(-) rename selling/report/{no_sales_order_from_customers => customers_not_buying_since_long_time}/__init__.py (100%) create mode 100644 selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.js rename selling/report/{no_sales_order_from_customers/no_sales_order_from_customers.py => customers_not_buying_since_long_time/customers_not_buying_since_long_time.py} (85%) rename selling/report/{no_sales_order_from_customers/no_sales_order_from_customers.txt => customers_not_buying_since_long_time/customers_not_buying_since_long_time.txt} (59%) rename stock/report/{item_reorder_level => itemwise_recommended_reorder_level}/__init__.py (100%) rename stock/report/{item_reorder_level/item_reorder_level.js => itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js} (81%) rename stock/report/{item_reorder_level/item_reorder_level.py => itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py} (98%) rename stock/report/{item_reorder_level/item_reorder_level.txt => itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.txt} (59%) diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 603bd3a48f..9c18fda681 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -166,8 +166,8 @@ wn.module_page["Selling"] = [ route: "query-report/Item-wise Sales History", }, { - "label":wn._("No Sales Order from Customers (Since 2 months)"), - route: "query-report/No Sales Order from Customers", + "label":wn._("Customers Not Buying Since Long Time"), + route: "query-report/Customers Not Buying Since Long Time", doctype: "Sales Order" }, diff --git a/selling/report/no_sales_order_from_customers/__init__.py b/selling/report/customers_not_buying_since_long_time/__init__.py similarity index 100% rename from selling/report/no_sales_order_from_customers/__init__.py rename to selling/report/customers_not_buying_since_long_time/__init__.py diff --git a/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.js b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.js new file mode 100644 index 0000000000..65d63484a5 --- /dev/null +++ b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.js @@ -0,0 +1,10 @@ +wn.query_reports["Customers Not Buying Since Long Time"] = { + "filters": [ + { + "fieldname":"days_since_last_order", + "label": "Days Since Last Order", + "fieldtype": "Int", + "default": 60 + } + ] +} \ No newline at end of file diff --git a/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.py b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.py similarity index 85% rename from selling/report/no_sales_order_from_customers/no_sales_order_from_customers.py rename to selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.py index 789e1684e9..d13315c151 100644 --- a/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.py +++ b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.py @@ -13,15 +13,24 @@ # # 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 getdate, cint def execute(filters=None): + if not filters: filters ={} + + days_since_last_order = filters.get("days_since_last_order") + if not days_since_last_order or days_since_last_order <= 0: + webnotes.msgprint("Please mention legal value in days since last order field",raise_exception=1) + columns = get_columns() customers = get_so_details() data = [] for cust in customers: - if cust[8] >= 60: # days_since_last_order + if cust[8] >= days_since_last_order: cust.insert(7,get_last_so_amt(cust[0])) data.append(cust) return columns, data diff --git a/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.txt b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.txt similarity index 59% rename from selling/report/no_sales_order_from_customers/no_sales_order_from_customers.txt rename to selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.txt index 32b38ae815..4d94377aa9 100644 --- a/selling/report/no_sales_order_from_customers/no_sales_order_from_customers.txt +++ b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-06-06 19:15:50", + "creation": "2013-06-07 12:27:07", "docstatus": 0, - "modified": "2013-06-06 19:15:51", + "modified": "2013-06-07 12:27:07", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,11 +11,11 @@ "is_standard": "Yes", "name": "__common__", "ref_doctype": "Sales Order", - "report_name": "No Sales Order from Customers", + "report_name": "Customers Not Buying Since Long Time ", "report_type": "Script Report" }, { "doctype": "Report", - "name": "No Sales Order from Customers" + "name": "Customers Not Buying Since Long Time" } ] \ No newline at end of file diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index ab3d3253bb..7e67cab3da 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -206,8 +206,8 @@ wn.module_page["Stock"] = [ route: "query-report/Requested Items To Be Transferred", }, { - "label":wn._("Item Reorder Level"), - route: "query-report/Item Reorder Level", + "label":wn._("Itemwise Recommended Reorder Level"), + route: "query-report/Itemwise Recommended Reorder Level", doctype: "Item" }, ] diff --git a/stock/report/item_reorder_level/__init__.py b/stock/report/itemwise_recommended_reorder_level/__init__.py similarity index 100% rename from stock/report/item_reorder_level/__init__.py rename to stock/report/itemwise_recommended_reorder_level/__init__.py diff --git a/stock/report/item_reorder_level/item_reorder_level.js b/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js similarity index 81% rename from stock/report/item_reorder_level/item_reorder_level.js rename to stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js index 30240e587d..b8aa378828 100644 --- a/stock/report/item_reorder_level/item_reorder_level.js +++ b/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js @@ -1,4 +1,4 @@ -wn.query_reports["Item Reorder Level"] = { +wn.query_reports["Itemwise Recommended Reorder Level"] = { "filters": [ { "fieldname":"from_date", diff --git a/stock/report/item_reorder_level/item_reorder_level.py b/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py similarity index 98% rename from stock/report/item_reorder_level/item_reorder_level.py rename to stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index 10ee182701..588132f961 100644 --- a/stock/report/item_reorder_level/item_reorder_level.py +++ b/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -61,7 +61,7 @@ def get_item_info(): def get_consumed_items(condition): cn_items = webnotes.conn.sql("""select se_item.item_code, - sum(se_item.actual_qty) as 'consume_qty' + sum(se_item.actual_qty) as 'consume_qty' 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, '') = '' %s diff --git a/stock/report/item_reorder_level/item_reorder_level.txt b/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.txt similarity index 59% rename from stock/report/item_reorder_level/item_reorder_level.txt rename to stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.txt index 93c6dfb185..2763f21dfe 100644 --- a/stock/report/item_reorder_level/item_reorder_level.txt +++ b/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-06-05 15:34:51", + "creation": "2013-06-07 12:47:22", "docstatus": 0, - "modified": "2013-06-05 15:50:30", + "modified": "2013-06-07 13:03:54", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,11 +11,11 @@ "is_standard": "Yes", "name": "__common__", "ref_doctype": "Item", - "report_name": "Item Reorder Level", + "report_name": "Itemwise Recommended Reorder Level", "report_type": "Script Report" }, { "doctype": "Report", - "name": "Item Reorder Level" + "name": "Itemwise Recommended Reorder Level" } ] \ No newline at end of file From d9bf6424470b542439c7eef443ef9694cf3f89a6 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 7 Jun 2013 15:28:43 +0530 Subject: [PATCH 213/295] [Report][Changes made in Customers Not Buying Since Long Time.py] --- .../customers_not_buying_since_long_time.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.py b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.py index d13315c151..08809a7619 100644 --- a/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.py +++ b/selling/report/customers_not_buying_since_long_time/customers_not_buying_since_long_time.py @@ -22,8 +22,8 @@ def execute(filters=None): if not filters: filters ={} days_since_last_order = filters.get("days_since_last_order") - if not days_since_last_order or days_since_last_order <= 0: - webnotes.msgprint("Please mention legal value in days since last order field",raise_exception=1) + if cint(days_since_last_order) <= 0: + webnotes.msgprint("Please mention positive value in 'Days Since Last Order' field",raise_exception=1) columns = get_columns() customers = get_so_details() From 4bb0eee41117cf6ea17b7e79428bf7cf58c90011 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 7 Jun 2013 17:06:39 +0530 Subject: [PATCH 214/295] [fixes] outstanding for jv --- accounts/doctype/gl_entry/gl_entry.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py index 112e449f97..a2ef25a777 100644 --- a/accounts/doctype/gl_entry/gl_entry.py +++ b/accounts/doctype/gl_entry/gl_entry.py @@ -160,13 +160,13 @@ class DocType: def update_outstanding_amt(self): # get final outstanding amt bal = flt(sql("""select sum(debit) - sum(credit) from `tabGL Entry` - where against_voucher=%s and against_voucher_type=%s - and ifnull(is_cancelled,'No') = 'No'""", - (self.doc.against_voucher, self.doc.against_voucher_type))[0][0] or 0.0) - + where against_voucher=%s and against_voucher_type=%s and account = %s + and ifnull(is_cancelled,'No') = 'No'""", (self.doc.against_voucher, + self.doc.against_voucher_type, self.doc.account))[0][0] or 0.0) + 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 From 66f10cd14fce583b608c0d2b9e9ace9f7d680a67 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 7 Jun 2013 18:05:16 +0530 Subject: [PATCH 215/295] Quotation Trend --- selling/page/selling_home/selling_home.js | 5 + selling/report/quotation_trends/__init__.py | 0 .../quotation_trends/quotation_trends.js | 40 ++++++++ .../quotation_trends/quotation_trends.py | 96 +++++++++++++++++++ .../quotation_trends/quotation_trends.txt | 22 +++++ 5 files changed, 163 insertions(+) create mode 100644 selling/report/quotation_trends/__init__.py create mode 100644 selling/report/quotation_trends/quotation_trends.js create mode 100644 selling/report/quotation_trends/quotation_trends.py create mode 100644 selling/report/quotation_trends/quotation_trends.txt diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 9c18fda681..15f9b86162 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -170,6 +170,11 @@ wn.module_page["Selling"] = [ route: "query-report/Customers Not Buying Since Long Time", doctype: "Sales Order" }, + { + "label":wn._("Quotation Trend"), + route: "query-report/Quotation Trends", + doctype: "Sales Order" + }, ] } diff --git a/selling/report/quotation_trends/__init__.py b/selling/report/quotation_trends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/quotation_trends/quotation_trends.js b/selling/report/quotation_trends/quotation_trends.js new file mode 100644 index 0000000000..e55e252c0b --- /dev/null +++ b/selling/report/quotation_trends/quotation_trends.js @@ -0,0 +1,40 @@ +wn.query_reports["Quotation Trends"] = { + "filters": [ + { + "fieldname":"period", + "label": "Period", + "fieldtype": "Select", + "options": "Monthly"+NEWLINE+"Quarterly"+NEWLINE+"Half-yearly"+NEWLINE+"Yearly", + "default": "Monthly" + }, + { + "fieldname":"based_on", + "label": "Based On", + "fieldtype": "Select", + "options": "Item"+NEWLINE+"Item Group"+NEWLINE+"Customer"+NEWLINE+"Customer Group"+NEWLINE+"Territory"+NEWLINE+"Project", + "default": "Item" + }, + { + "fieldname":"group_by", + "label": "Group By", + "fieldtype": "Select", + "options": "Item"+NEWLINE+"Customer", + "default": "Customer" + }, + { + "fieldname":"fiscal_year", + "label": "Fiscal Year", + "fieldtype": "Link", + "options":'Fiscal Year', + "default": "Fiscal Year" + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": "Company" + }, + + ] +} \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py new file mode 100644 index 0000000000..5b9fc04fb3 --- /dev/null +++ b/selling/report/quotation_trends/quotation_trends.py @@ -0,0 +1,96 @@ +# 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 getdate, cint + +def execute(filters=None): + if not filters: filters ={} + + period = filters.get("period") + based_on = filters.get("based_on") + group_by = filters.get("group_by") + + columns = get_columns(filters, period, based_on, group_by) + data = [] + + return columns, data + +def get_columns(filters, period, based_on, group_by): + columns = [] + pwc = [] + bon = [] + gby = [] + + if not (period and based_on): + webnotes.msgprint("Value missing in 'Period' or 'Based On'",raise_exception=1) + elif based_on == group_by: + webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'") + else: + pwc = period_wise_column(filters, period, pwc) + bon = base_wise_column(based_on, bon) + gby = gruoup_wise_column(group_by) + + if gby: + columns = bon + gby + pwc + else: + columns = bon + pwc + return columns + + +def period_wise_column(filters, period, pwc): + + if period == "Monthly": + month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] + for month in range(0,len(month_name)): + pwc.append(month_name[month]+' (Qty):Float:120') + pwc.append(month_name[month]+' (Amt):Currency:120') + + elif period == "Quarterly": + pwc = ["Q1(qty):Float:120", "Q1(amt):Currency:120", "Q2(qty):Float:120", "Q2(amt):Currency:120", + "Q3(qty):Float:120", "Q3(amt):Currency:120", "Q4(qty):Float:120", "Q4(amt):Currency:120" + ] + + elif period == "Half-yearly": + pwc = ["Fisrt Half(qty):Float:120", "Fisrt Half(amt):Currency:120", "Second Half(qty):Float:120", + "Second Half(amt):Currency:120" + ] + else: + pwc = [filters.get("fiscal_year")+"(qty):Float:120", filters.get("fiscal_year")+"(amt):Currency:120"] + + return pwc + +def base_wise_column(based_on, bon): + if based_on == "Item": + bon = ["Item:Link/Item:120", "Item Name:Data:120"] + elif based_on == "Item Group": + bon = ["Item Group:Link/Item Group:120"] + elif based_on == "Customer": + bon = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"] + elif based_on == "Customer Group": + bon = ["Customer Group:Link/Customer Group"] + elif based_on == "Territory": + bon = ["Territory:Link/Territory:120"] + else: + bon = ["Project:Link/Project:120"] + return bon + +def gruoup_wise_column(group_by): + if group_by: + return [group_by+":Link/"+group_by+":120"] + else: + return [] \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.txt b/selling/report/quotation_trends/quotation_trends.txt new file mode 100644 index 0000000000..a135c34560 --- /dev/null +++ b/selling/report/quotation_trends/quotation_trends.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-06-07 16:01:16", + "docstatus": 0, + "modified": "2013-06-07 16:01:16", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Quotation", + "report_name": "Quotation Trends", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Quotation Trends" + } +] \ No newline at end of file From b2dc1aeca99c26a577ace74802288520e141b932 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 10 Jun 2013 12:34:31 +0530 Subject: [PATCH 216/295] [lead] [next contact] [fix] create event --- selling/doctype/lead/lead.py | 59 ++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py index 571cdfd516..d80f84362b 100644 --- a/selling/doctype/lead/lead.py +++ b/selling/doctype/lead/lead.py @@ -17,8 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes import _ -from webnotes.utils import cstr, validate_email_add -from webnotes.model.doc import Document, addchild +from webnotes.utils import cstr, validate_email_add, cint from webnotes import session, msgprint sql = webnotes.conn.sql @@ -54,11 +53,17 @@ class DocType(SellingController): if not validate_email_add(self.doc.email_id): msgprint('Please enter valid email id.') raise Exception + + self._prev = webnotes._dict({ + "contact_date": webnotes.conn.get_value("Lead", self.doc.name, "contact_date") if \ + (not cint(self.doc.fields.get("__islocal"))) else None, + "contact_by": webnotes.conn.get_value("Lead", self.doc.name, "contact_by") if \ + (not cint(self.doc.fields.get("__islocal"))) else None, + }) def on_update(self): - if self.doc.contact_date: - self.add_calendar_event() + self.add_calendar_event() self.check_email_id_is_unique() @@ -73,25 +78,33 @@ class DocType(SellingController): ", ".join(items), raise_exception=True) def add_calendar_event(self): - # delete any earlier event by this lead - sql("delete from tabEvent where ref_type='Lead' and ref_name=%s", self.doc.name) - - # create new event - ev = Document('Event') - ev.owner = self.doc.lead_owner - ev.description = ('Contact ' + cstr(self.doc.lead_name)) + \ - (self.doc.contact_by and ('. By : ' + cstr(self.doc.contact_by)) or '') + \ - (self.doc.remark and ('.To Discuss : ' + cstr(self.doc.remark)) or '') - ev.event_date = self.doc.contact_date - ev.event_hour = '10:00' - ev.event_type = 'Private' - ev.ref_type = 'Lead' - ev.ref_name = self.doc.name - ev.save(1) - - event_user = addchild(ev, 'event_individuals', 'Event User') - event_user.person = self.doc.contact_by - event_user.save() + if self.doc.contact_by != cstr(self._prev.contact_by) or \ + self.doc.contact_date != cstr(self._prev.contact_date): + # delete any earlier event by this lead + for name in webnotes.conn.sql_list("""select name from `tabEvent` + where ref_type="Lead" and ref_name=%s""", self.doc.name): + webnotes.delete_doc("Event", name) + + if self.doc.contact_date: + webnotes.bean([ + { + "doctype": "Event", + "owner": self.doc.lead_owner or self.doc.owner, + "subject": ('Contact ' + cstr(self.doc.lead_name)), + "description": ('Contact ' + cstr(self.doc.lead_name)) + \ + (self.doc.contact_by and ('. By : ' + cstr(self.doc.contact_by)) or '') + \ + (self.doc.remark and ('.To Discuss : ' + cstr(self.doc.remark)) or ''), + "starts_on": self.doc.contact_date + " 10:00:00", + "event_type": "Private", + "ref_type": "Lead", + "ref_name": self.doc.name + }, + { + "doctype": "Event User", + "parentfield": "event_individuals", + "person": self.doc.contact_by + } + ]).insert() def get_sender(self, comm): return webnotes.conn.get_value('Sales Email Settings',None,'email_id') From e53a81dceacbd20375276c068c2f05666ca8d72c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 10 Jun 2013 15:15:40 +0530 Subject: [PATCH 217/295] [event] [lead, opportunity, project] [fix] create events --- .../p04_fix_event_for_lead_oppty_project.py | 21 ++++++ patches/patch_list.py | 1 + projects/doctype/project/project.py | 70 ++++++++----------- selling/doctype/lead/lead.py | 58 +++++---------- selling/doctype/opportunity/opportunity.py | 65 ++++++++--------- .../maintenance_schedule.py | 39 ++++++----- utilities/transaction_base.py | 36 +++++++++- 7 files changed, 157 insertions(+), 133 deletions(-) create mode 100644 patches/june_2013/p04_fix_event_for_lead_oppty_project.py diff --git a/patches/june_2013/p04_fix_event_for_lead_oppty_project.py b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py new file mode 100644 index 0000000000..a6449c528a --- /dev/null +++ b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py @@ -0,0 +1,21 @@ +import webnotes + +def execute(): + # delete orphaned Event User + webnotes.conn.sql("""delete from `tabEvent User` + where not exists(select name from `tabEvent` where `tabEvent`.name = `tabEvent User`.parent)""") + + for dt in ["Lead", "Opportunity", "Project"]: + for ref_name in webnotes.conn.sql_list("""select ref_name + from `tabEvent` where ref_type=%s and ifnull(starts_on, '')='' """, dt): + if webnotes.conn.exists(dt, ref_name): + controller = webnotes.get_obj(dt, ref_name) + if dt == "Project": + controller.add_calendar_event() + else: + controller.delete_events() + controller._add_calendar_event() + else: + # remove events where ref doc doesn't exist + webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` + where ref_type=%s and ref_name=%s""", (dt, ref_name))) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index d8b84da8f8..6b88955a10 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -254,4 +254,5 @@ patch_list = [ "patches.may_2013.p05_update_cancelled_gl_entries", "patches.june_2013.p01_update_bom_exploded_items", "execute:webnotes.delete_doc('DocType', 'System Console')", + "patches.june_2013.p04_fix_event_for_lead_oppty_project", ] \ No newline at end of file diff --git a/projects/doctype/project/project.py b/projects/doctype/project/project.py index 1de551c753..b519224ab9 100644 --- a/projects/doctype/project/project.py +++ b/projects/doctype/project/project.py @@ -18,24 +18,17 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt, getdate -from webnotes.model import db_exists -from webnotes.model.doc import Document -from webnotes.model.bean import copy_doclist from webnotes import msgprint -sql = webnotes.conn.sql - - - class DocType: - def __init__(self, doc, doclist=[]): + def __init__(self, doc, doclist=None): self.doc = doc self.doclist = doclist # Get Customer Details along with its primary contact details # ============================================================== def get_customer_details(self): - details =sql("select address, territory, customer_group,customer_name from `tabCustomer` where name=%s and docstatus!=2",(self.doc.customer),as_dict=1) + details =webnotes.conn.sql("select address, territory, customer_group,customer_name from `tabCustomer` where name=%s and docstatus!=2",(self.doc.customer),as_dict=1) if details: ret = { 'customer_address' : details and details[0]['address'] or '', @@ -44,7 +37,7 @@ class DocType: 'customer_name' : details and details[0]['customer_name'] or '' } #get primary contact details(this is done separately coz. , if join query used & no primary contact thn it would not be able to fetch customer details) - contact_det = sql("select contact_name, phone, email_id from `tabContact` where customer_name='%s' and is_customer=1 and is_primary_contact=1 and docstatus!=2" %(self.doc.customer), as_dict = 1) + contact_det = webnotes.conn.sql("select contact_name, phone, email_id from `tabContact` where customer_name='%s' and is_customer=1 and is_primary_contact=1 and docstatus!=2" %(self.doc.customer), as_dict = 1) ret['contact_person'] = contact_det and contact_det[0]['contact_name'] or '' ret['contact_no'] = contact_det and contact_det[0]['phone'] or '' ret['email_id'] = contact_det and contact_det[0]['email_id'] or '' @@ -52,21 +45,7 @@ class DocType: else: msgprint("Customer : %s does not exist in system." % (self.doc.customer)) raise Exception - - # Get customer's contact person details - # ============================================================== - def get_contact_details(self): - contact = sql("select contact_no, email_id from `tabContact` where contact_name = '%s' and customer_name = '%s' and docstatus != 2" %(self.doc,contact_person,self.doc.customer), as_dict=1) - if contact: - ret = { - 'contact_no' : contact and contact[0]['contact_no'] or '', - 'email_id' : contact and contact[0]['email_id'] or '' - } - return ret - else: - msgprint("Contact Person : %s does not exist in the system." % (self.doc,contact_person)) - raise Exception - + #calculate gross profit #============================================= def get_gross_profit(self): @@ -86,20 +65,29 @@ class DocType: raise Exception def on_update(self): - # update milestones - webnotes.conn.sql("""delete from tabEvent where ref_type='Project' and ref_name=%s""", - self.doc.name) - for d in self.doclist: - if d.doctype=='Project Milestone' and d.docstatus!=2: - self.add_calendar_event(d.milestone, d.milestone_date) + self.add_calendar_event() - def add_calendar_event(self, milestone, date): - """ Add calendar event for task in calendar of Allocated person""" - event = Document('Event') - event.description = milestone + ' for ' + self.doc.name - event.event_date = date - event.event_hour = '10:00' - event.event_type = 'Public' - event.ref_type = 'Project' - event.ref_name = self.doc.name - event.save(1) + def add_calendar_event(self): + # delete any earlier event for this project + self.delete_events() + + # add events + for milestone in self.doclist.get({"parentfield": "project_milestones"}): + description = milestone.milestone + " for " + self.doc.name + webnotes.bean({ + "doctype": "Event", + "owner": self.doc.owner, + "subject": description, + "description": description, + "starts_on": milestone.milestone_date + " 10:00:00", + "event_type": "Private", + "ref_type": self.doc.doctype, + "ref_name": self.doc.name + }).insert() + + def on_trash(self): + self.delete_events() + + def delete_events(self): + webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` + where ref_type=%s and ref_name=%s""", (self.doc.doctype, self.doc.name))) \ No newline at end of file diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py index d80f84362b..0e27261a26 100644 --- a/selling/doctype/lead/lead.py +++ b/selling/doctype/lead/lead.py @@ -29,6 +29,13 @@ class DocType(SellingController): self.doc = doc self.doclist = doclist + self._prev = webnotes._dict({ + "contact_date": webnotes.conn.get_value("Lead", self.doc.name, "contact_date") if \ + (not cint(self.doc.fields.get("__islocal"))) else None, + "contact_by": webnotes.conn.get_value("Lead", self.doc.name, "contact_by") if \ + (not cint(self.doc.fields.get("__islocal"))) else None, + }) + def onload(self): self.add_communication_list() @@ -53,19 +60,19 @@ class DocType(SellingController): if not validate_email_add(self.doc.email_id): msgprint('Please enter valid email id.') raise Exception - - self._prev = webnotes._dict({ - "contact_date": webnotes.conn.get_value("Lead", self.doc.name, "contact_date") if \ - (not cint(self.doc.fields.get("__islocal"))) else None, - "contact_by": webnotes.conn.get_value("Lead", self.doc.name, "contact_by") if \ - (not cint(self.doc.fields.get("__islocal"))) else None, - }) - def on_update(self): - self.add_calendar_event() - self.check_email_id_is_unique() + self.add_calendar_event() + + def add_calendar_event(self, opts=None): + super(DocType, self).add_calendar_event({ + "owner": self.doc.lead_owner, + "subject": ('Contact ' + cstr(self.doc.lead_name)), + "description": ('Contact ' + cstr(self.doc.lead_name)) + \ + (self.doc.contact_by and ('. By : ' + cstr(self.doc.contact_by)) or '') + \ + (self.doc.remark and ('.To Discuss : ' + cstr(self.doc.remark)) or '') + }) def check_email_id_is_unique(self): if self.doc.email_id: @@ -76,35 +83,6 @@ class DocType(SellingController): items = [e[0] for e in email_list if e[0]!=self.doc.name] webnotes.msgprint(_("""Email Id must be unique, already exists for: """) + \ ", ".join(items), raise_exception=True) - - def add_calendar_event(self): - if self.doc.contact_by != cstr(self._prev.contact_by) or \ - self.doc.contact_date != cstr(self._prev.contact_date): - # delete any earlier event by this lead - for name in webnotes.conn.sql_list("""select name from `tabEvent` - where ref_type="Lead" and ref_name=%s""", self.doc.name): - webnotes.delete_doc("Event", name) - - if self.doc.contact_date: - webnotes.bean([ - { - "doctype": "Event", - "owner": self.doc.lead_owner or self.doc.owner, - "subject": ('Contact ' + cstr(self.doc.lead_name)), - "description": ('Contact ' + cstr(self.doc.lead_name)) + \ - (self.doc.contact_by and ('. By : ' + cstr(self.doc.contact_by)) or '') + \ - (self.doc.remark and ('.To Discuss : ' + cstr(self.doc.remark)) or ''), - "starts_on": self.doc.contact_date + " 10:00:00", - "event_type": "Private", - "ref_type": "Lead", - "ref_name": self.doc.name - }, - { - "doctype": "Event User", - "parentfield": "event_individuals", - "person": self.doc.contact_by - } - ]).insert() def get_sender(self, comm): return webnotes.conn.get_value('Sales Email Settings',None,'email_id') @@ -113,3 +91,5 @@ class DocType(SellingController): webnotes.conn.sql("""update tabCommunication set lead=null where lead=%s""", self.doc.name) webnotes.conn.sql("""update `tabSupport Ticket` set lead='' where lead=%s""", self.doc.name) + + self.delete_events() \ No newline at end of file diff --git a/selling/doctype/opportunity/opportunity.py b/selling/doctype/opportunity/opportunity.py index 75a7cd270d..0540ac9881 100644 --- a/selling/doctype/opportunity/opportunity.py +++ b/selling/doctype/opportunity/opportunity.py @@ -17,9 +17,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import add_days, cstr, getdate -from webnotes.model import db_exists -from webnotes.model.doc import Document, addchild +from webnotes.utils import add_days, cstr, getdate, cint from webnotes.model.bean import getlist from webnotes import msgprint @@ -34,6 +32,13 @@ class DocType(TransactionBase): self.fname = 'enq_details' self.tname = 'Opportunity Item' + self._prev = webnotes._dict({ + "contact_date": webnotes.conn.get_value("Opportunity", self.doc.name, "contact_date") if \ + (not cint(self.doc.fields.get("__islocal"))) else None, + "contact_by": webnotes.conn.get_value("Opportunity", self.doc.name, "contact_by") if \ + (not cint(self.doc.fields.get("__islocal"))) else None, + }) + def onload(self): self.add_communication_list() @@ -84,48 +89,34 @@ class DocType(TransactionBase): def on_update(self): # Add to calendar if self.doc.contact_date and self.doc.contact_date_ref != self.doc.contact_date: - if self.doc.contact_by: - self.add_calendar_event() webnotes.conn.set(self.doc, 'contact_date_ref',self.doc.contact_date) - webnotes.conn.set(self.doc, 'status', 'Draft') - def add_calendar_event(self): - desc='' - user_lst =[] + self.add_calendar_event() + + def add_calendar_event(self, opts=None): + if not opts: + opts = webnotes._dict() + + opts.description = "" + if self.doc.customer: if self.doc.contact_person: - desc = 'Contact '+cstr(self.doc.contact_person) + opts.description = 'Contact '+cstr(self.doc.contact_person) else: - desc = 'Contact customer '+cstr(self.doc.customer) + opts.description = 'Contact customer '+cstr(self.doc.customer) elif self.doc.lead: if self.doc.contact_display: - desc = 'Contact '+cstr(self.doc.contact_display) + opts.description = 'Contact '+cstr(self.doc.contact_display) else: - desc = 'Contact lead '+cstr(self.doc.lead) - desc = desc+ '. By : ' + cstr(self.doc.contact_by) + opts.description = 'Contact lead '+cstr(self.doc.lead) + + opts.subject = opts.description + opts.description += '. By : ' + cstr(self.doc.contact_by) if self.doc.to_discuss: - desc = desc+' To Discuss : ' + cstr(self.doc.to_discuss) + opts.description += ' To Discuss : ' + cstr(self.doc.to_discuss) - ev = Document('Event') - ev.description = desc - ev.event_date = self.doc.contact_date - ev.event_hour = '10:00' - ev.event_type = 'Private' - ev.ref_type = 'Opportunity' - ev.ref_name = self.doc.name - ev.save(1) - - user_lst.append(self.doc.owner) - - chk = sql("select t1.name from `tabProfile` t1, `tabSales Person` t2 where t2.email_id = t1.name and t2.name=%s",self.doc.contact_by) - if chk: - user_lst.append(chk[0][0]) - - for d in user_lst: - ch = addchild(ev, 'event_individuals', 'Event User') - ch.person = d - ch.save(1) + super(DocType, self).add_calendar_event(opts) def set_last_contact_date(self): if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date: @@ -159,6 +150,9 @@ class DocType(TransactionBase): self.set_last_contact_date() self.validate_item_details() self.validate_lead_cust() + + if not self.doc.status: + self.doc.status = "Draft" def on_submit(self): webnotes.conn.set(self.doc, 'status', 'Submitted') @@ -180,3 +174,6 @@ class DocType(TransactionBase): webnotes.conn.set(self.doc, 'status', 'Opportunity Lost') webnotes.conn.set(self.doc, 'order_lost_reason', arg) return 'true' + + def on_trash(self): + self.delete_events() \ No newline at end of file diff --git a/support/doctype/maintenance_schedule/maintenance_schedule.py b/support/doctype/maintenance_schedule/maintenance_schedule.py index 60962b1d52..06c5a47aff 100644 --- a/support/doctype/maintenance_schedule/maintenance_schedule.py +++ b/support/doctype/maintenance_schedule/maintenance_schedule.py @@ -18,7 +18,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import add_days, cstr, getdate -from webnotes.model.doc import Document, addchild +from webnotes.model.doc import addchild from webnotes.model.bean import getlist from webnotes.model.code import get_obj from webnotes import msgprint @@ -100,23 +100,21 @@ class DocType(TransactionBase): for key in scheduled_date: if email_map[d.incharge_name]: - self.add_calender_event(key["scheduled_date"],email_map[d.incharge_name],d.item_code) + description = "Reference: %s, Item Code: %s and Customer: %s" % \ + (self.doc.name, d.item_code, self.doc.customer) + webnotes.bean({ + "doctype": "Event", + "owner": email_map[d.incharge_name] or self.doc.owner, + "subject": description, + "description": description, + "starts_on": key["scheduled_date"] + " 10:00:00", + "event_type": "Private", + "ref_type": self.doc.doctype, + "ref_name": self.doc.name + }).insert() + webnotes.conn.set(self.doc, 'status', 'Submitted') - - def add_calender_event(self,scheduled_date,incharge_email,item_code): - """ Add calendar event for Maintenece Schedule in calendar of Allocated person""" - event = Document('Event') - event.owner = incharge_email - event.description = "Reference:%s, Item Code:%s and Customer: %s" %(self.doc.name, item_code, self.doc.customer) - event.event_date = scheduled_date - event.event_hour = '10:00' - event.event_type = 'Private' - event.ref_type = 'Maintenance Schedule' - event.ref_name = self.doc.name - event.save(1) - - #get schedule dates #---------------------- def create_schedule_list(self, start_date, end_date, no_of_visit): @@ -329,8 +327,13 @@ class DocType(TransactionBase): if d.serial_no: self.update_amc_date(d.serial_no, '') webnotes.conn.set(self.doc, 'status', 'Cancelled') - sql("delete from `tabEvent` where ref_type='Maintenance Schedule' and ref_name='%s' " %(self.doc.name)) + self.delete_events() + def on_trash(self): - sql("delete from `tabEvent` where ref_type='Maintenance Schedule' and ref_name='%s' " %(self.doc.name)) + self.delete_events() + + def delete_events(self): + webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` + where ref_type=%s and ref_name=%s""", (self.doc.doctype, self.doc.name))) diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 5d7d1a84b1..4c70eba2d7 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -268,4 +268,38 @@ class TransactionBase(DocListController): def validate_posting_time(self): if not self.doc.posting_time: self.doc.posting_time = now_datetime().strftime('%H:%M:%S') - \ No newline at end of file + + def add_calendar_event(self, opts): + if self.doc.contact_by != cstr(self._prev.contact_by) or \ + self.doc.contact_date != cstr(self._prev.contact_date): + + self.delete_events() + self._add_calendar_event(opts) + + def delete_events(self): + webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` + where ref_type=%s and ref_name=%s""", (self.doc.doctype, self.doc.name))) + + def _add_calendar_event(self, opts): + opts = webnotes._dict(opts) + + if self.doc.contact_date: + event_doclist = [{ + "doctype": "Event", + "owner": opts.owner or self.doc.owner, + "subject": opts.subject, + "description": opts.description, + "starts_on": self.doc.contact_date + " 10:00:00", + "event_type": "Private", + "ref_type": self.doc.doctype, + "ref_name": self.doc.name + }] + + if webnotes.conn.exists("Profile", self.doc.contact_by): + event_doclist.append({ + "doctype": "Event User", + "parentfield": "event_individuals", + "person": self.doc.contact_by + }) + + webnotes.bean(event_doclist).insert() From dec9a165ac2c31c51a4bacd4eb763e4790e502c1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 10 Jun 2013 15:26:54 +0530 Subject: [PATCH 218/295] [patch] [fix] events for project, lead, oppty --- patches/june_2013/p04_fix_event_for_lead_oppty_project.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/patches/june_2013/p04_fix_event_for_lead_oppty_project.py b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py index a6449c528a..6929eedcdd 100644 --- a/patches/june_2013/p04_fix_event_for_lead_oppty_project.py +++ b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py @@ -9,12 +9,7 @@ def execute(): for ref_name in webnotes.conn.sql_list("""select ref_name from `tabEvent` where ref_type=%s and ifnull(starts_on, '')='' """, dt): if webnotes.conn.exists(dt, ref_name): - controller = webnotes.get_obj(dt, ref_name) - if dt == "Project": - controller.add_calendar_event() - else: - controller.delete_events() - controller._add_calendar_event() + webnotes.get_obj(dt, ref_name).add_calendar_event() else: # remove events where ref doc doesn't exist webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` From 670199b9c62180044b387a8b9a205986f19e53c3 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 10 Jun 2013 15:38:01 +0530 Subject: [PATCH 219/295] [patch] [fix] event for project, oppty, lead --- patches/june_2013/p04_fix_event_for_lead_oppty_project.py | 5 ++++- selling/doctype/lead/lead.py | 4 ++-- selling/doctype/opportunity/opportunity.py | 4 ++-- utilities/transaction_base.py | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/patches/june_2013/p04_fix_event_for_lead_oppty_project.py b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py index 6929eedcdd..3f66d8bfbb 100644 --- a/patches/june_2013/p04_fix_event_for_lead_oppty_project.py +++ b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py @@ -9,7 +9,10 @@ def execute(): for ref_name in webnotes.conn.sql_list("""select ref_name from `tabEvent` where ref_type=%s and ifnull(starts_on, '')='' """, dt): if webnotes.conn.exists(dt, ref_name): - webnotes.get_obj(dt, ref_name).add_calendar_event() + if dt in ["Lead", "Opportunity"]: + webnotes.get_obj(dt, ref_name).add_calendar_event(force=True) + else: + webnotes.get_obj(dt, ref_name).add_calendar_event() else: # remove events where ref doc doesn't exist webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py index 0e27261a26..a54343a907 100644 --- a/selling/doctype/lead/lead.py +++ b/selling/doctype/lead/lead.py @@ -65,14 +65,14 @@ class DocType(SellingController): self.check_email_id_is_unique() self.add_calendar_event() - def add_calendar_event(self, opts=None): + def add_calendar_event(self, opts=None, force=False): super(DocType, self).add_calendar_event({ "owner": self.doc.lead_owner, "subject": ('Contact ' + cstr(self.doc.lead_name)), "description": ('Contact ' + cstr(self.doc.lead_name)) + \ (self.doc.contact_by and ('. By : ' + cstr(self.doc.contact_by)) or '') + \ (self.doc.remark and ('.To Discuss : ' + cstr(self.doc.remark)) or '') - }) + }, force) def check_email_id_is_unique(self): if self.doc.email_id: diff --git a/selling/doctype/opportunity/opportunity.py b/selling/doctype/opportunity/opportunity.py index 0540ac9881..9fb061b228 100644 --- a/selling/doctype/opportunity/opportunity.py +++ b/selling/doctype/opportunity/opportunity.py @@ -93,7 +93,7 @@ class DocType(TransactionBase): self.add_calendar_event() - def add_calendar_event(self, opts=None): + def add_calendar_event(self, opts=None, force=False): if not opts: opts = webnotes._dict() @@ -116,7 +116,7 @@ class DocType(TransactionBase): if self.doc.to_discuss: opts.description += ' To Discuss : ' + cstr(self.doc.to_discuss) - super(DocType, self).add_calendar_event(opts) + super(DocType, self).add_calendar_event(opts, force) def set_last_contact_date(self): if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date: diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 4c70eba2d7..f9af912737 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -269,9 +269,9 @@ class TransactionBase(DocListController): if not self.doc.posting_time: self.doc.posting_time = now_datetime().strftime('%H:%M:%S') - def add_calendar_event(self, opts): + def add_calendar_event(self, opts, force=False): if self.doc.contact_by != cstr(self._prev.contact_by) or \ - self.doc.contact_date != cstr(self._prev.contact_date): + self.doc.contact_date != cstr(self._prev.contact_date) or force: self.delete_events() self._add_calendar_event(opts) From f09a9f68e4a7834649e9df9b41f03778b38a72bd Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 10 Jun 2013 15:57:52 +0530 Subject: [PATCH 220/295] [project] [event] fix in creation of event --- projects/doctype/project/project.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/projects/doctype/project/project.py b/projects/doctype/project/project.py index b519224ab9..94b6787312 100644 --- a/projects/doctype/project/project.py +++ b/projects/doctype/project/project.py @@ -73,17 +73,18 @@ class DocType: # add events for milestone in self.doclist.get({"parentfield": "project_milestones"}): - description = milestone.milestone + " for " + self.doc.name - webnotes.bean({ - "doctype": "Event", - "owner": self.doc.owner, - "subject": description, - "description": description, - "starts_on": milestone.milestone_date + " 10:00:00", - "event_type": "Private", - "ref_type": self.doc.doctype, - "ref_name": self.doc.name - }).insert() + if milestone.milestone_date: + description = (milestone.milestone or "Milestone") + " for " + self.doc.name + webnotes.bean({ + "doctype": "Event", + "owner": self.doc.owner, + "subject": description, + "description": description, + "starts_on": milestone.milestone_date + " 10:00:00", + "event_type": "Private", + "ref_type": self.doc.doctype, + "ref_name": self.doc.name + }).insert() def on_trash(self): self.delete_events() From 0175fbd47026daa45333829637285a0e74bfcadb Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 10 Jun 2013 16:27:29 +0530 Subject: [PATCH 221/295] [buying] [purchase taxes] get cost center from taxes master --- buying/doctype/purchase_common/purchase_common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/buying/doctype/purchase_common/purchase_common.py b/buying/doctype/purchase_common/purchase_common.py index d5b563b3c5..2b6ca27acc 100644 --- a/buying/doctype/purchase_common/purchase_common.py +++ b/buying/doctype/purchase_common/purchase_common.py @@ -432,6 +432,7 @@ class DocType(BuyingController): d.account_head = other['account_head'] d.rate = flt(other['rate']) d.tax_amount = flt(other['tax_amount']) + d.cost_center = other["cost_center"] d.idx = idx idx += 1 return obj.doclist From 89e2ac4f477ee6f5c1de170e253188212437df2c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 10 Jun 2013 17:58:33 +0530 Subject: [PATCH 222/295] [voucher import tool] [fix] validate account columns only for voucher import of type Multiple Accounts --- .../voucher_import_tool.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/accounts/page/voucher_import_tool/voucher_import_tool.py b/accounts/page/voucher_import_tool/voucher_import_tool.py index 7634e5bf84..a3790a8fe1 100644 --- a/accounts/page/voucher_import_tool/voucher_import_tool.py +++ b/accounts/page/voucher_import_tool/voucher_import_tool.py @@ -59,7 +59,7 @@ def upload(): if not company_abbr: webnotes.msgprint(_("Company is missing or entered incorrect value"), raise_exception=1) - data, start_idx = get_data(rows, company_abbr) + data, start_idx = get_data(rows, company_abbr, rows[0][0]) except Exception, e: err_msg = webnotes.message_log and "
    ".join(webnotes.message_log) or cstr(e) messages.append("""

    %s

    """ % (err_msg or "No message")) @@ -213,10 +213,11 @@ def get_common_values(rows): return common_values -def get_data(rows, company_abbr): +def get_data(rows, company_abbr, import_type): start_row = 0 data = [] start_row_idx = 0 + accounts = None for i in xrange(len(rows)): r = rows[i] if r[0]: @@ -257,17 +258,19 @@ def get_data(rows, company_abbr): columns = [c.replace(" ", "_").lower() for c in rows[i+1] if not c.endswith(" - " + company_abbr)] - accounts = [c for c in rows[i+1] if c.endswith(" - " + company_abbr)] + + if import_type == "Voucher Import: Multiple Accounts": + accounts = [c for c in rows[i+1] if c.endswith(" - " + company_abbr)] - if not accounts: - webnotes.msgprint(_("""No Account found in csv file, - May be company abbreviation is not correct"""), raise_exception=1) + if not accounts: + webnotes.msgprint(_("""No Account found in csv file, + May be company abbreviation is not correct"""), raise_exception=1) - if accounts and (len(columns) != rows[i+1].index(accounts[0])): - webnotes.msgprint(_("""All account columns should be after \ - standard columns and on the right. - If you entered it properly, next probable reason \ - could be wrong account name. - Please rectify it in the file and try again."""), raise_exception=1) + if accounts and (len(columns) != rows[i+1].index(accounts[0])): + webnotes.msgprint(_("""All account columns should be after \ + standard columns and on the right. + If you entered it properly, next probable reason \ + could be wrong account name. + Please rectify it in the file and try again."""), raise_exception=1) return data, start_row_idx \ No newline at end of file From f75ebb5a1bc940db2d5d47e4da060a8841d26cc1 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 11 Jun 2013 10:23:10 +0530 Subject: [PATCH 223/295] Report partially completed - Territory Target Variance(Item-Group Wise) --- selling/page/selling_home/selling_home.js | 4 + .../__init__.py | 0 ...itory_target_variance_(item_group_wise).js | 25 ++++++ ...itory_target_variance_(item_group_wise).py | 89 +++++++++++++++++++ ...tory_target_variance_(item_group_wise).txt | 21 +++++ 5 files changed, 139 insertions(+) create mode 100644 selling/report/territory_target_variance_(item_group_wise)/__init__.py create mode 100644 selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js create mode 100644 selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).py create mode 100644 selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 682978bd17..7d0162d2ab 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -165,6 +165,10 @@ wn.module_page["Selling"] = [ "label":wn._("Item-wise Sales History"), route: "query-report/Item-wise Sales History", }, + { + "label":wn._("Territory Target Variance (Item Group-Wise)"), + route: "query-report/Territory Target Variance (Item Group-Wise)", + }, ] } ] diff --git a/selling/report/territory_target_variance_(item_group_wise)/__init__.py b/selling/report/territory_target_variance_(item_group_wise)/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js b/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js new file mode 100644 index 0000000000..58718a4e0b --- /dev/null +++ b/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js @@ -0,0 +1,25 @@ +wn.query_reports["Territory Target Variance (Item Group-Wise)"] = { + "filters": [ + { + fieldname: "fiscal_year", + label: "Fiscal Year", + fieldtype: "Link", + options: "Fiscal Year", + default: sys_defaults.fiscal_year + }, + { + fieldname: "period", + label: "Period", + fieldtype: "Select", + options: "Monthly\nQuarterly\nHalf-Yearly\nYearly", + default: "Monthly" + }, + { + fieldname: "target_on", + label: "Target On", + fieldtype: "Select", + options: "Quantity\nAmount", + default: "Quantity" + }, + ] +} \ No newline at end of file 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 new file mode 100644 index 0000000000..790c6f02ad --- /dev/null +++ b/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).py @@ -0,0 +1,89 @@ +# 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 +import calendar +from webnotes import msgprint +from webnotes.utils import cint, cstr, add_months + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + + data = [] + + return columns, data + +def get_columns(filters): + """return columns based on filters""" + + if not filters.get("period"): + msgprint("Please select the Period", raise_exception=1) + + mo = cint(cstr(webnotes.conn.get_value("Fiscal Year", filters["fiscal_year"], "year_start_date")).split("-")[1]) + period_months = [] + if (filters["period"] == "Monthly" or "Yearly"): + for x in range(0,12): + period_months.append(mo) + if (mo!=12): + mo += 1 + else: + mo = 1 + + columns = ["Territory:Link/Territory:80"] + ["Item Group:Link/Item Group:80"] + + period = [] + + if (filters["period"] == "Monthly" or "Yearly"): + for i in (0,12): + period.append("Target (" + "i" + ")::80") + period.append("Achieved (" + "i" + ")::80") + period.append("Variance (" + "i" + ")::80") + + columns = columns + [(p) for p in period] + \ + ["Total Target::80"] + ["Total Achieved::80"] + ["Total Variance::80"] + + return columns + +def get_conditions(filters): + conditions = "" + + if filters.get("fiscal_year"): + conditions += " and posting_date <= '%s'" % filters["fiscal_year"] + else: + webnotes.msgprint("Please enter Fiscal Year", raise_exception=1) + + if filters.get("target_on"): + conditions += " and posting_date <= '%s'" % filters["target_on"] + else: + webnotes.msgprint("Please select Target On", raise_exception=1) + + return conditions + + +#get territory details +def get_territory_details(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select item_code, batch_no, warehouse, + posting_date, actual_qty + from `tabStock Ledger Entry` + where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % + conditions, as_dict=1) + +def get_month_abbr(month_number): + return 0 \ No newline at end of file diff --git a/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt b/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt new file mode 100644 index 0000000000..7fff64a861 --- /dev/null +++ b/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-07 15:13:13", + "docstatus": 0, + "modified": "2013-06-07 15:13:13", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Order", + "report_name": "Territory Target Variance (Item Group-Wise)", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Territory Target Variance (Item Group-Wise)" + } +] \ No newline at end of file From 85e1026325253f15d133a6b2fd3accd35726433f Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 11 Jun 2013 11:17:56 +0530 Subject: [PATCH 224/295] Fixed selling_home.js conflicted during merge --- selling/page/selling_home/selling_home.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 5996625577..e3663c93e4 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -159,16 +159,18 @@ wn.module_page["Selling"] = [ }, { "label":wn._("Sales Person-wise Transaction Summary"), - route: "query-report/Sales Person-wise Transaction Summary", + route: "query-report/Sales Person-wise Transaction Summary" }, { "label":wn._("Item-wise Sales History"), - route: "query-report/Item-wise Sales History", + route: "query-report/Item-wise Sales History" }, { "label":wn._("Territory Target Variance (Item Group-Wise)"), route: "query-report/Territory Target Variance (Item Group-Wise)", + doctype: "Sales Order" }, + { "label":wn._("Customers Not Buying Since Long Time"), route: "query-report/Customers Not Buying Since Long Time", doctype: "Sales Order" From 49af8754d558c4acf867775391528b96cc75bbad Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 11 Jun 2013 12:40:46 +0530 Subject: [PATCH 225/295] [auto inventory accounting] [general ledger entry] calculate valuation amount without rounding --- accounts/doctype/purchase_invoice/purchase_invoice.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index 95b56dc4ff..32a46cf58f 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -466,9 +466,8 @@ class DocType(BuyingController): # expense will be booked in sales invoice stock_item_and_auto_inventory_accounting = True - valuation_amt = (flt(item.amount, self.precision.item.amount) + - flt(item.item_tax_amount, self.precision.item.item_tax_amount) + - flt(item.rm_supp_cost, self.precision.item.rm_supp_cost)) + valuation_amt = (flt(item.amount) + flt(item.item_tax_amount) + + flt(item.rm_supp_cost)) gl_entries.append( self.get_gl_dict({ From 433047b2e284a707165fc1ae623d402f44a4cd64 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 11 Jun 2013 14:28:42 +0530 Subject: [PATCH 226/295] [customer] [permlevel] changed fields/perms with permlevel 2 to permlevel 1 --- selling/doctype/customer/customer.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/selling/doctype/customer/customer.txt b/selling/doctype/customer/customer.txt index a221daa599..d7cecdd1bf 100644 --- a/selling/doctype/customer/customer.txt +++ b/selling/doctype/customer/customer.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-23 19:57:18", + "creation": "2013-06-11 14:26:44", "docstatus": 0, - "modified": "2013-01-29 16:28:03", + "modified": "2013-06-11 14:27:57", "modified_by": "Administrator", "owner": "Administrator" }, @@ -270,7 +270,7 @@ "label": "Credit Days", "oldfieldname": "credit_days", "oldfieldtype": "Int", - "permlevel": 2 + "permlevel": 1 }, { "doctype": "DocField", @@ -280,7 +280,7 @@ "oldfieldname": "credit_limit", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "permlevel": 2 + "permlevel": 1 }, { "doctype": "DocField", @@ -339,7 +339,7 @@ }, { "doctype": "DocPerm", - "permlevel": 2, + "permlevel": 1, "role": "Sales User" }, { @@ -355,7 +355,7 @@ }, { "doctype": "DocPerm", - "permlevel": 2, + "permlevel": 1, "role": "Sales Master Manager", "write": 1 } From 2e0cc41fc6634403d392d3c41fe203e1e2e9dacf Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 11 Jun 2013 17:33:37 +0530 Subject: [PATCH 227/295] [chart of accounts/cost center] [add account/cost center] fix in permission issue --- accounts/utils.py | 2 -- buying/doctype/supplier/supplier.py | 19 ++++++++++++------- selling/doctype/customer/customer.py | 16 +++++++++------- setup/doctype/company/company.py | 6 ++++-- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/accounts/utils.py b/accounts/utils.py index 31e622166e..eb240e796c 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -125,7 +125,6 @@ def add_ac(args=None): ac.doc.doctype = "Account" ac.doc.old_parent = "" ac.doc.freeze_account = "No" - ac.ignore_permissions = 1 ac.insert() return ac.doc.name @@ -138,7 +137,6 @@ def add_cc(args=None): cc = webnotes.bean(args) cc.doc.doctype = "Cost Center" cc.doc.old_parent = "" - cc.ignore_permissions = 1 cc.insert() return cc.doc.name diff --git a/buying/doctype/supplier/supplier.py b/buying/doctype/supplier/supplier.py index d41b86c32c..f506439bad 100644 --- a/buying/doctype/supplier/supplier.py +++ b/buying/doctype/supplier/supplier.py @@ -24,7 +24,6 @@ from webnotes.model.doc import make_autoname sql = webnotes.conn.sql -from accounts.utils import add_ac from utilities.transaction_base import TransactionBase class DocType(TransactionBase): @@ -71,14 +70,16 @@ class DocType(TransactionBase): return g def add_account(self, ac, par, abbr): - ac = add_ac({ + ac_bean = webnotes.bean({ + "doctype": "Account", 'account_name':ac, 'parent_account':par, 'group_or_ledger':'Group', 'company':self.doc.company, - 'account_type':'', - 'tax_rate':'0' + "freeze_account": "No", }) + ac_bean.ignore_permissions = True + ac_bean.insert() msgprint(_("Created Group ") + ac) @@ -109,8 +110,8 @@ class DocType(TransactionBase): parent_account = self.get_parent_account(abbr) if not sql("select name from tabAccount where name=%s", (self.doc.name + " - " + abbr)): - - ac = add_ac({ + ac_bean = webnotes.bean({ + "doctype": "Account", 'account_name': self.doc.name, 'parent_account': parent_account, 'group_or_ledger':'Ledger', @@ -119,8 +120,12 @@ class DocType(TransactionBase): 'tax_rate': '0', 'master_type': 'Supplier', 'master_name': self.doc.name, + "freeze_account": "No" }) - msgprint(_("Created Account Head: ") + ac) + ac_bean.ignore_permissions = True + ac_bean.insert() + + msgprint(_("Created Account Head: ") + ac_bean.doc.name) else: self.check_parent_account(parent_account, abbr) else : diff --git a/selling/doctype/customer/customer.py b/selling/doctype/customer/customer.py index 72e12b7942..65ac865304 100644 --- a/selling/doctype/customer/customer.py +++ b/selling/doctype/customer/customer.py @@ -115,18 +115,20 @@ class DocType(TransactionBase): if not webnotes.conn.exists("Account", (self.doc.name + " - " + abbr)): parent_account = self.get_receivables_group() # create - from accounts.utils import add_ac - ac = add_ac({ - 'account_name':self.doc.name, + ac_bean = webnotes.bean({ + "doctype": "Account", + 'account_name': self.doc.name, 'parent_account': parent_account, 'group_or_ledger':'Ledger', 'company':self.doc.company, - 'account_type':'', - 'tax_rate':'0', 'master_type':'Customer', - 'master_name':self.doc.name + 'master_name':self.doc.name, + "freeze_account": "No" }) - msgprint("Account Head: %s created" % ac) + ac_bean.ignore_permissions = True + ac_bean.insert() + + msgprint("Account Head: %s created" % ac_bean.doc.name) else : msgprint("Please Select Company under which you want to create account head") diff --git a/setup/doctype/company/company.py b/setup/doctype/company/company.py index 9863d7d28b..2564503833 100644 --- a/setup/doctype/company/company.py +++ b/setup/doctype/company/company.py @@ -222,7 +222,6 @@ class DocType: # Create default cost center # --------------------------------------------------- def create_default_cost_center(self): - from accounts.utils import add_cc cc_list = [ { 'cost_center_name':'Root', @@ -244,7 +243,10 @@ class DocType: } ] for cc in cc_list: - add_cc(cc) + cc.update({"doctype": "Cost Center"}) + cc_bean = webnotes.bean(cc) + cc_bean.ignore_permissions = True + cc_bean.insert() webnotes.conn.set_value("Company", self.doc.name, "cost_center", "Default CC Ledger - " + self.doc.abbr) From 0865f606735715b80ee4abb359cc30dfebf5083a Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 11 Jun 2013 17:48:59 +0530 Subject: [PATCH 228/295] [chart of accounts/cost center] [fix] check permission before enabling add child --- accounts/page/accounts_browser/accounts_browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/page/accounts_browser/accounts_browser.js b/accounts/page/accounts_browser/accounts_browser.js index 2e8cc820a6..3541fcbf1f 100644 --- a/accounts/page/accounts_browser/accounts_browser.js +++ b/accounts/page/accounts_browser/accounts_browser.js @@ -131,7 +131,7 @@ erpnext.AccountsChart = Class.extend({ if (wn.model.can_read(this.ctype) !== -1) { node_links.push('Edit'); } - if (data.expandable) { + if (data.expandable && wn.boot.profile.in_create.indexOf(this.ctype) !== -1) { node_links.push('Add Child'); } else if (this.ctype === 'Account' && wn.boot.profile.can_read.indexOf("GL Entry") !== -1) { node_links.push('View Ledger'); From bcbd9a48008e80326d584260a23037fc305341c3 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 11 Jun 2013 18:06:58 +0530 Subject: [PATCH 229/295] [Report][Quotation Trend] --- .../trend_analyzer/trend_analyzer.py | 6 +- .../quotation_trends/quotation_trends.js | 6 +- .../quotation_trends/quotation_trends.py | 134 ++++++++++++++---- 3 files changed, 110 insertions(+), 36 deletions(-) diff --git a/accounts/search_criteria/trend_analyzer/trend_analyzer.py b/accounts/search_criteria/trend_analyzer/trend_analyzer.py index 87e1e8ed09..9bdde392b4 100644 --- a/accounts/search_criteria/trend_analyzer/trend_analyzer.py +++ b/accounts/search_criteria/trend_analyzer/trend_analyzer.py @@ -155,10 +155,8 @@ for r in res: for d in range(len(colnames) - cr): r.append(flt(main_det[0][d])) out.append(r) - if group_by: flag = 1 - # check for root nodes if based_on in ['Item Group','Customer Group','Territory']: is_grp = sql("select is_group from `tab%s` where name = '%s'" % (based_on, cstr(r[col_idx[based_on]]).strip())) is_grp = is_grp and cstr(is_grp[0][0]) or '' @@ -167,11 +165,11 @@ for r in res: if flag == 1: det = [x[0] for x in sql("SELECT DISTINCT %s FROM %s where %s" % (sel_col, add_tab, add_cond % {'value':cstr(r[col_idx[based_on]]).strip()}))] - for des in range(len(det)): t_row = ['' for i in range(len(colnames))] t_row[col_idx[group_by]] = cstr(det[des]) gr_det = sql("SELECT %s FROM %s WHERE %s = '%s' and %s" % (query_val, add_tab, sel_col, cstr(det[des]), add_cond % {'value':cstr(r[col_idx[based_on]]).strip()})) + webnotes.errprint(cstr(r[col_idx[based_on]]).strip()) for d in range(len(col_names)): t_row[col_idx[col_names[d]]] = flt(gr_det[0][d]) - out.append(t_row) + out.append(t_row) \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.js b/selling/report/quotation_trends/quotation_trends.js index e55e252c0b..e166fa66ce 100644 --- a/selling/report/quotation_trends/quotation_trends.js +++ b/selling/report/quotation_trends/quotation_trends.js @@ -19,21 +19,21 @@ wn.query_reports["Quotation Trends"] = { "label": "Group By", "fieldtype": "Select", "options": "Item"+NEWLINE+"Customer", - "default": "Customer" + "default": "" }, { "fieldname":"fiscal_year", "label": "Fiscal Year", "fieldtype": "Link", "options":'Fiscal Year', - "default": "Fiscal Year" + "default": sys_defaults.fiscal_year }, { "fieldname":"company", "label": "Company", "fieldtype": "Link", "options": "Company", - "default": "Company" + "default": sys_defaults.company }, ] diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py index 5b9fc04fb3..6a81d97f16 100644 --- a/selling/report/quotation_trends/quotation_trends.py +++ b/selling/report/quotation_trends/quotation_trends.py @@ -16,78 +16,154 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import getdate, cint +from webnotes.utils import cint, add_days, add_months, cstr def execute(filters=None): if not filters: filters ={} - - period = filters.get("period") - based_on = filters.get("based_on") - group_by = filters.get("group_by") - columns = get_columns(filters, period, based_on, group_by) - data = [] + # Global data + ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + + columns, query_bon, query_pwc, group_by = get_columns(filters, year_start_date, start_month) + + ptab = "tabQuotation" + ctab = "tabQuotation Item" + data = get_data(filters, ptab, ctab, query_bon, query_pwc, group_by) return columns, data -def get_columns(filters, period, based_on, group_by): - columns = [] - pwc = [] - bon = [] - gby = [] +def get_columns(filters, year_start_date, start_month): + columns, pwc, bon, gby = [], [], [], [] + query_bon, query_pwc = '', '' + + period = filters.get("period") + based_on = filters.get("based_on") + grby = filters.get("group_by") if not (period and based_on): - webnotes.msgprint("Value missing in 'Period' or 'Based On'",raise_exception=1) - elif based_on == group_by: - webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'") + webnotes.msgprint("Value missing in 'Period' or 'Based On'", raise_exception=1) + elif based_on == grby: + webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'", raise_exception=1) else: - pwc = period_wise_column(filters, period, pwc) - bon = base_wise_column(based_on, bon) - gby = gruoup_wise_column(group_by) + bon,query_bon,group_by = base_wise_column(based_on, bon) + pwc,query_pwc = period_wise_column_and_query(filters, period, pwc, year_start_date,start_month) + gby = gruoup_wise_column(grby) if gby: - columns = bon + gby + pwc + columns = bon + gby + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] else: - columns = bon + pwc - return columns + columns = bon + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] + return columns, query_bon, query_pwc, group_by -def period_wise_column(filters, period, pwc): +def get_data(filters, ptab, ctab, query_bon, query_pwc, group_by): + query_details = query_bon + query_pwc + 'SUM(t2.qty), SUM(t1.grand_total) ' + query_pwc = query_pwc + 'SUM(t2.qty), SUM(t1.grand_total)' + if not filters.get("group_by"): + data = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' and t1.docstatus = 1 + group by %s + """%(query_details, ptab, ctab, filters.get("company"), filters.get("fiscal_year"), group_by), as_list=1) + + # No coma is included between %s and t2.item_code cause it's already bounded with query_bon + if filters.get("group_by") == 'Item': + data = webnotes.conn.sql(""" select %s t2.item_code, %s from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' and t1.docstatus = 1 + group by %s, %s"""%( query_bon, query_pwc, ptab, ctab, filters.get("company"), filters.get("fiscal_year"), + group_by,'t2.item_code'), as_list=1) + + if filters.get("group_by") == 'Customer': + data = webnotes.conn.sql(""" select %s t1.customer_name, %s from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' and t1.docstatus = 1 + group by %s, %s"""%(query_bon, query_pwc, ptab, ctab, filters.get("company"), filters.get("fiscal_year"), group_by, 't1.customer_name'), as_list=1) + + return data + +def period_wise_column_and_query(filters, period, pwc, year_start_date, start_month): + query_details = '' if period == "Monthly": month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] - for month in range(0,len(month_name)): + for month in range(start_month-1,len(month_name)): pwc.append(month_name[month]+' (Qty):Float:120') pwc.append(month_name[month]+' (Amt):Currency:120') + query_details += """Sum(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t2.qty ELSE NULL END), + SUM(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + """%{"mon_num": cstr(month+1)} + for month in range(0, start_month-1): + pwc.append(month_name[month]+' (Qty):Float:120') + pwc.append(month_name[month]+' (Amt):Currency:120') + query_details += """Sum(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t2.qty ELSE NULL END), + SUM(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + """%{"mon_num": cstr(month+1)} + elif period == "Quarterly": pwc = ["Q1(qty):Float:120", "Q1(amt):Currency:120", "Q2(qty):Float:120", "Q2(amt):Currency:120", - "Q3(qty):Float:120", "Q3(amt):Currency:120", "Q4(qty):Float:120", "Q4(amt):Currency:120" - ] + "Q3(qty):Float:120", "Q3(amt):Currency:120", "Q4(qty):Float:120", "Q4(amt):Currency:120"] + + first_qsd, second_qsd, third_qsd, fourth_qsd = year_start_date, add_months(year_start_date,3), add_months(year_start_date,6), add_months(year_start_date,9) + first_qed, second_qed, third_qed, fourth_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1), add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) + + bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] + for d in bet_dates: + query_details += """ + SUM(CASE WHEN t1.transaction_date BETWEEN '%(sd)s' AND '%(ed)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.transaction_date BETWEEN '%(sd)s' AND '%(ed)s' THEN t1.grand_total ELSE NULL END), + """%{"sd": d[0],"ed": d[1]} elif period == "Half-yearly": pwc = ["Fisrt Half(qty):Float:120", "Fisrt Half(amt):Currency:120", "Second Half(qty):Float:120", - "Second Half(amt):Currency:120" - ] + "Second Half(amt):Currency:120"] + + first_half_start = year_start_date + first_half_end = add_days(add_months(first_half_start,6),-1) + second_half_start = add_days(first_half_end,1) + second_half_end = add_days(add_months(second_half_start,6),-1) + + query_details = """ SUM(CASE WHEN t1.transaction_date BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.transaction_date BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t1.grand_total ELSE NULL END), + SUM(CASE WHEN t1.transaction_date BETWEEN '%(shs)s' AND '%(she)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.transaction_date BETWEEN '%(shs)s' AND '%(she)s' THEN t1.grand_total ELSE NULL END), + """%{"fhs": first_half_start, "fhe": first_half_end,"shs": second_half_start, "she": second_half_end} + else: pwc = [filters.get("fiscal_year")+"(qty):Float:120", filters.get("fiscal_year")+"(amt):Currency:120"] + query_details = " SUM(t2.qty), SUM(t1.grand_total)," - return pwc + return pwc, query_details def base_wise_column(based_on, bon): if based_on == "Item": bon = ["Item:Link/Item:120", "Item Name:Data:120"] + query_details = "t2.item_code, t2.item_name," + group_by = 't2.item_code' + elif based_on == "Item Group": bon = ["Item Group:Link/Item Group:120"] + query_details = "t2.item_group," + group_by = 't2.item_group' + elif based_on == "Customer": bon = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"] + query_details = "t1.customer_name, t1.territory, " + group_by = 't1.customer_name' + elif based_on == "Customer Group": bon = ["Customer Group:Link/Customer Group"] + query_details = "t1.customer_group, " + group_by = 't1.customer_group' + elif based_on == "Territory": bon = ["Territory:Link/Territory:120"] + query_details = "t1.territory, " + group_by = 't1.territory' + else: bon = ["Project:Link/Project:120"] - return bon + return bon, query_details, group_by def gruoup_wise_column(group_by): if group_by: From f9966f44cdb553dff236cba71b04336e16a34796 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 12 Jun 2013 17:55:19 +0530 Subject: [PATCH 230/295] [Report][Quotation Trend Complete] --- .../trend_analyzer/trend_analyzer.py | 4 +- .../quotation_trends/quotation_trends.py | 128 ++++++++++++------ .../quotation_trends/quotation_trends.txt | 4 +- 3 files changed, 93 insertions(+), 43 deletions(-) diff --git a/accounts/search_criteria/trend_analyzer/trend_analyzer.py b/accounts/search_criteria/trend_analyzer/trend_analyzer.py index 9bdde392b4..6bc4cf6f69 100644 --- a/accounts/search_criteria/trend_analyzer/trend_analyzer.py +++ b/accounts/search_criteria/trend_analyzer/trend_analyzer.py @@ -155,8 +155,10 @@ for r in res: for d in range(len(colnames) - cr): r.append(flt(main_det[0][d])) out.append(r) + if group_by: flag = 1 + # check for root nodes if based_on in ['Item Group','Customer Group','Territory']: is_grp = sql("select is_group from `tab%s` where name = '%s'" % (based_on, cstr(r[col_idx[based_on]]).strip())) is_grp = is_grp and cstr(is_grp[0][0]) or '' @@ -165,11 +167,11 @@ for r in res: if flag == 1: det = [x[0] for x in sql("SELECT DISTINCT %s FROM %s where %s" % (sel_col, add_tab, add_cond % {'value':cstr(r[col_idx[based_on]]).strip()}))] + for des in range(len(det)): t_row = ['' for i in range(len(colnames))] t_row[col_idx[group_by]] = cstr(det[des]) gr_det = sql("SELECT %s FROM %s WHERE %s = '%s' and %s" % (query_val, add_tab, sel_col, cstr(det[des]), add_cond % {'value':cstr(r[col_idx[based_on]]).strip()})) - webnotes.errprint(cstr(r[col_idx[based_on]]).strip()) for d in range(len(col_names)): t_row[col_idx[col_names[d]]] = flt(gr_det[0][d]) out.append(t_row) \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py index 6a81d97f16..7f944e0f23 100644 --- a/selling/report/quotation_trends/quotation_trends.py +++ b/selling/report/quotation_trends/quotation_trends.py @@ -22,19 +22,18 @@ def execute(filters=None): if not filters: filters ={} # Global data + trans = "Quotation" + tab = ["tabQuotation","tabQuotation Item"] ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] year_start_date = ysd.strftime('%Y-%m-%d') start_month = cint(year_start_date.split('-')[1]) - columns, query_bon, query_pwc, group_by = get_columns(filters, year_start_date, start_month) - - ptab = "tabQuotation" - ctab = "tabQuotation Item" - data = get_data(filters, ptab, ctab, query_bon, query_pwc, group_by) + columns, query_bon, query_pwc, group_by, gby = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, group_by, gby) return columns, data -def get_columns(filters, year_start_date, start_month): +def get_columns(filters, year_start_date, start_month, trans): columns, pwc, bon, gby = [], [], [], [] query_bon, query_pwc = '', '' @@ -44,11 +43,13 @@ def get_columns(filters, year_start_date, start_month): if not (period and based_on): webnotes.msgprint("Value missing in 'Period' or 'Based On'", raise_exception=1) + elif based_on == grby: webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'", raise_exception=1) + else: bon,query_bon,group_by = base_wise_column(based_on, bon) - pwc,query_pwc = period_wise_column_and_query(filters, period, pwc, year_start_date,start_month) + pwc,query_pwc = period_wise_column_and_query(filters, period, pwc, year_start_date,start_month, trans) gby = gruoup_wise_column(grby) if gby: @@ -56,49 +57,95 @@ def get_columns(filters, year_start_date, start_month): else: columns = bon + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - return columns, query_bon, query_pwc, group_by + return columns, query_bon, query_pwc, group_by,gby -def get_data(filters, ptab, ctab, query_bon, query_pwc, group_by): +def get_data(columns, filters, tab, query_bon, query_pwc, group_by,gby): + query_details = query_bon + query_pwc + 'SUM(t2.qty), SUM(t1.grand_total) ' query_pwc = query_pwc + 'SUM(t2.qty), SUM(t1.grand_total)' - if not filters.get("group_by"): + data = [] + inc = '' + + if filters.get("group_by"): + sel_col = '' + if filters.get("group_by") == 'Item': + sel_col = 't2.item_code' + + elif filters.get("group_by") == 'Customer': + sel_col = 't1.customer' + + if filters.get('based_on') in ['Item','Customer']: + inc = 2 + else : + inc = 1 + + ind = columns.index(gby[0]) + + data1 = webnotes.conn.sql(""" select %s %s from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 group by %s """%(query_bon, query_pwc, tab[0], tab[1], + filters.get("company"), filters.get("fiscal_year"), group_by), as_list=1) + + for d in range(len(data1)): + + dt = data1[d] + dt.insert(ind,'') + data.append(dt) + + row = webnotes.conn.sql("""select DISTINCT(%s) from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 and %s = '%s' """%(sel_col, tab[0], tab[1], filters.get("company"), + filters.get("fiscal_year"), group_by, data1[d][0]),as_list=1) + + for i in range(len(row)): + des = ['' for q in range(len(columns))] + + row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 and %s = '%s' and %s ='%s' """%(sel_col, query_pwc, tab[0], tab[1], + filters.get("company"), filters.get("fiscal_year"), sel_col, row[i][0], group_by, + data1[d][0]),as_list=1) + + des[ind] = row[i] + for j in range(1,len(columns)-inc): + des[j+inc] = row1[0][j] + data.append(des) + else: + data = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' and t1.docstatus = 1 - group by %s - """%(query_details, ptab, ctab, filters.get("company"), filters.get("fiscal_year"), group_by), as_list=1) + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 group by %s + """%(query_details, tab[0], tab[1], filters.get("company"), filters.get("fiscal_year"), group_by), as_list=1) - # No coma is included between %s and t2.item_code cause it's already bounded with query_bon - if filters.get("group_by") == 'Item': - data = webnotes.conn.sql(""" select %s t2.item_code, %s from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' and t1.docstatus = 1 - group by %s, %s"""%( query_bon, query_pwc, ptab, ctab, filters.get("company"), filters.get("fiscal_year"), - group_by,'t2.item_code'), as_list=1) - - if filters.get("group_by") == 'Customer': - data = webnotes.conn.sql(""" select %s t1.customer_name, %s from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' and t1.docstatus = 1 - group by %s, %s"""%(query_bon, query_pwc, ptab, ctab, filters.get("company"), filters.get("fiscal_year"), group_by, 't1.customer_name'), as_list=1) - return data -def period_wise_column_and_query(filters, period, pwc, year_start_date, start_month): +def period_wise_column_and_query(filters, period, pwc, year_start_date, start_month, trans): query_details = '' + if trans in ['Purchase Receipt', 'Delivery Note', 'Purchase Invoice', 'Sales Invoice']: + trans_date = 'posting_date' + else: + trans_date = 'transaction_date' if period == "Monthly": month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] + for month in range(start_month-1,len(month_name)): pwc.append(month_name[month]+' (Qty):Float:120') pwc.append(month_name[month]+' (Amt):Currency:120') - query_details += """Sum(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t2.qty ELSE NULL END), - SUM(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t1.grand_total ELSE NULL END), - """%{"mon_num": cstr(month+1)} + + query_details += """ + Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), + SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date,"mon_num": cstr(month+1)} for month in range(0, start_month-1): pwc.append(month_name[month]+' (Qty):Float:120') pwc.append(month_name[month]+' (Amt):Currency:120') - query_details += """Sum(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t2.qty ELSE NULL END), - SUM(CASE WHEN MONTH(t1.transaction_date)= %(mon_num)s THEN t1.grand_total ELSE NULL END), - """%{"mon_num": cstr(month+1)} + + query_details += """ + Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), + SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date, "mon_num": cstr(month+1)} elif period == "Quarterly": pwc = ["Q1(qty):Float:120", "Q1(amt):Currency:120", "Q2(qty):Float:120", "Q2(amt):Currency:120", @@ -110,9 +157,9 @@ def period_wise_column_and_query(filters, period, pwc, year_start_date, start_mo bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] for d in bet_dates: query_details += """ - SUM(CASE WHEN t1.transaction_date BETWEEN '%(sd)s' AND '%(ed)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.transaction_date BETWEEN '%(sd)s' AND '%(ed)s' THEN t1.grand_total ELSE NULL END), - """%{"sd": d[0],"ed": d[1]} + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date, "sd": d[0],"ed": d[1]} elif period == "Half-yearly": pwc = ["Fisrt Half(qty):Float:120", "Fisrt Half(amt):Currency:120", "Second Half(qty):Float:120", @@ -123,11 +170,12 @@ def period_wise_column_and_query(filters, period, pwc, year_start_date, start_mo second_half_start = add_days(first_half_end,1) second_half_end = add_days(add_months(second_half_start,6),-1) - query_details = """ SUM(CASE WHEN t1.transaction_date BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.transaction_date BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t1.grand_total ELSE NULL END), - SUM(CASE WHEN t1.transaction_date BETWEEN '%(shs)s' AND '%(she)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.transaction_date BETWEEN '%(shs)s' AND '%(she)s' THEN t1.grand_total ELSE NULL END), - """%{"fhs": first_half_start, "fhe": first_half_end,"shs": second_half_start, "she": second_half_end} + query_details = """ + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t1.grand_total ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date, "fhs": first_half_start, "fhe": first_half_end,"shs": second_half_start, "she": second_half_end} else: pwc = [filters.get("fiscal_year")+"(qty):Float:120", filters.get("fiscal_year")+"(amt):Currency:120"] diff --git a/selling/report/quotation_trends/quotation_trends.txt b/selling/report/quotation_trends/quotation_trends.txt index a135c34560..eebffcf84b 100644 --- a/selling/report/quotation_trends/quotation_trends.txt +++ b/selling/report/quotation_trends/quotation_trends.txt @@ -2,12 +2,12 @@ { "creation": "2013-06-07 16:01:16", "docstatus": 0, - "modified": "2013-06-07 16:01:16", + "modified": "2013-06-12 16:31:23", "modified_by": "Administrator", "owner": "Administrator" }, { - "add_total_row": 1, + "add_total_row": 0, "doctype": "Report", "is_standard": "Yes", "name": "__common__", From 0326f5414e4ede793ec7f8bdad7c51e795c69864 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Thu, 13 Jun 2013 19:17:56 +0530 Subject: [PATCH 231/295] [reports][purchase and sales trends] --- .../purchase_invoice_trends/__init__.py | 0 .../purchase_invoice_trends.js | 5 + .../purchase_invoice_trends.py | 37 +++ .../purchase_invoice_trends.txt | 21 ++ .../report/sales_invoice_trends/__init__.py | 0 .../sales_invoice_trends.js | 5 + .../sales_invoice_trends.py | 37 +++ .../sales_invoice_trends.txt | 21 ++ .../report/purchase_order_trends/__init__.py | 0 .../purchase_order_trends.js | 5 + .../purchase_order_trends.py | 37 +++ .../purchase_order_trends.txt | 21 ++ controllers/trends.py | 245 ++++++++++++++++++ public/js/purchase_trends_filters.js | 39 +++ public/js/sales_trends_filters.js | 39 +++ .../quotation_trends/quotation_trends.js | 43 +-- .../quotation_trends/quotation_trends.py | 197 +------------- selling/report/sales_order_trends/__init__.py | 0 .../sales_order_trends/sales_order_trends.js | 5 + .../sales_order_trends/sales_order_trends.py | 37 +++ .../sales_order_trends/sales_order_trends.txt | 21 ++ stock/report/delivery_note_trends/__init__.py | 0 .../delivery_note_trends.js | 5 + .../delivery_note_trends.py | 37 +++ .../delivery_note_trends.txt | 21 ++ .../purchase_receipt_trends/__init__.py | 0 .../purchase_receipt_trends.js | 5 + .../purchase_receipt_trends.py | 37 +++ .../purchase_receipt_trends.txt | 21 ++ 29 files changed, 712 insertions(+), 229 deletions(-) create mode 100644 accounts/report/purchase_invoice_trends/__init__.py create mode 100644 accounts/report/purchase_invoice_trends/purchase_invoice_trends.js create mode 100644 accounts/report/purchase_invoice_trends/purchase_invoice_trends.py create mode 100644 accounts/report/purchase_invoice_trends/purchase_invoice_trends.txt create mode 100644 accounts/report/sales_invoice_trends/__init__.py create mode 100644 accounts/report/sales_invoice_trends/sales_invoice_trends.js create mode 100644 accounts/report/sales_invoice_trends/sales_invoice_trends.py create mode 100644 accounts/report/sales_invoice_trends/sales_invoice_trends.txt create mode 100644 buying/report/purchase_order_trends/__init__.py create mode 100644 buying/report/purchase_order_trends/purchase_order_trends.js create mode 100644 buying/report/purchase_order_trends/purchase_order_trends.py create mode 100644 buying/report/purchase_order_trends/purchase_order_trends.txt create mode 100644 controllers/trends.py create mode 100644 public/js/purchase_trends_filters.js create mode 100644 public/js/sales_trends_filters.js create mode 100644 selling/report/sales_order_trends/__init__.py create mode 100644 selling/report/sales_order_trends/sales_order_trends.js create mode 100644 selling/report/sales_order_trends/sales_order_trends.py create mode 100644 selling/report/sales_order_trends/sales_order_trends.txt create mode 100644 stock/report/delivery_note_trends/__init__.py create mode 100644 stock/report/delivery_note_trends/delivery_note_trends.js create mode 100644 stock/report/delivery_note_trends/delivery_note_trends.py create mode 100644 stock/report/delivery_note_trends/delivery_note_trends.txt create mode 100644 stock/report/purchase_receipt_trends/__init__.py create mode 100644 stock/report/purchase_receipt_trends/purchase_receipt_trends.js create mode 100644 stock/report/purchase_receipt_trends/purchase_receipt_trends.py create mode 100644 stock/report/purchase_receipt_trends/purchase_receipt_trends.txt diff --git a/accounts/report/purchase_invoice_trends/__init__.py b/accounts/report/purchase_invoice_trends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js new file mode 100644 index 0000000000..bb18ce4cbe --- /dev/null +++ b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js @@ -0,0 +1,5 @@ +wn.require("app/js/purchase_trends_filters.js"); + +wn.query_reports["Purchase Invoice Trends"] = { + filters: get_filters() + } \ No newline at end of file diff --git a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py new file mode 100644 index 0000000000..e74ad2129b --- /dev/null +++ b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py @@ -0,0 +1,37 @@ +# 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 cint, add_days, add_months, cstr +from controllers.trends import get_columns,get_data + +def execute(filters=None): + if not filters: filters ={} + data = [] + + trans = "Purchase Invoice" + tab = ["tabPurchase Invoice","tabPurchase Invoice Item"] + ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + + columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + + if data == '': + webnotes.msgprint("Data Not Available") + return columns, data \ No newline at end of file diff --git a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.txt b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.txt new file mode 100644 index 0000000000..1d5c2d5f45 --- /dev/null +++ b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-13 18:46:55", + "docstatus": 0, + "modified": "2013-06-13 18:46:55", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Invoice", + "report_name": "Purchase Invoice Trends", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Purchase Invoice Trends" + } +] \ No newline at end of file diff --git a/accounts/report/sales_invoice_trends/__init__.py b/accounts/report/sales_invoice_trends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/sales_invoice_trends/sales_invoice_trends.js b/accounts/report/sales_invoice_trends/sales_invoice_trends.js new file mode 100644 index 0000000000..6f20015d21 --- /dev/null +++ b/accounts/report/sales_invoice_trends/sales_invoice_trends.js @@ -0,0 +1,5 @@ +wn.require("app/js/sales_trends_filters.js"); + +wn.query_reports["Sales Invoice Trends"] = { + filters: get_filters() + } \ No newline at end of file diff --git a/accounts/report/sales_invoice_trends/sales_invoice_trends.py b/accounts/report/sales_invoice_trends/sales_invoice_trends.py new file mode 100644 index 0000000000..48606f04ed --- /dev/null +++ b/accounts/report/sales_invoice_trends/sales_invoice_trends.py @@ -0,0 +1,37 @@ +# 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 cint, add_days, add_months, cstr +from controllers.trends import get_columns,get_data + +def execute(filters=None): + if not filters: filters ={} + data = [] + + trans = "Sales Invoice" + tab = ["tabSales Invoice","tabSales Invoice Item"] + ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + + columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + + if data == '': + webnotes.msgprint("Data Not Available") + return columns, data \ No newline at end of file diff --git a/accounts/report/sales_invoice_trends/sales_invoice_trends.txt b/accounts/report/sales_invoice_trends/sales_invoice_trends.txt new file mode 100644 index 0000000000..279ac1269f --- /dev/null +++ b/accounts/report/sales_invoice_trends/sales_invoice_trends.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-13 18:44:21", + "docstatus": 0, + "modified": "2013-06-13 18:44:21", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Invoice", + "report_name": "Sales Invoice Trends", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Sales Invoice Trends" + } +] \ No newline at end of file diff --git a/buying/report/purchase_order_trends/__init__.py b/buying/report/purchase_order_trends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/buying/report/purchase_order_trends/purchase_order_trends.js b/buying/report/purchase_order_trends/purchase_order_trends.js new file mode 100644 index 0000000000..c6373db6c2 --- /dev/null +++ b/buying/report/purchase_order_trends/purchase_order_trends.js @@ -0,0 +1,5 @@ +wn.require("app/js/purchase_trends_filters.js"); + +wn.query_reports["Purchase Order Trends"] = { + filters: get_filters() + } \ No newline at end of file diff --git a/buying/report/purchase_order_trends/purchase_order_trends.py b/buying/report/purchase_order_trends/purchase_order_trends.py new file mode 100644 index 0000000000..cdf79b2fad --- /dev/null +++ b/buying/report/purchase_order_trends/purchase_order_trends.py @@ -0,0 +1,37 @@ +# 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 cint, add_days, add_months, cstr +from controllers.trends import get_columns,get_data + +def execute(filters=None): + if not filters: filters ={} + data = [] + + trans = "Purchase Order" + tab = ["tabPurchase Order","tabPurchase Order Item"] + ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + + columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + + if data == '': + webnotes.msgprint("Data Not Available") + return columns, data \ No newline at end of file diff --git a/buying/report/purchase_order_trends/purchase_order_trends.txt b/buying/report/purchase_order_trends/purchase_order_trends.txt new file mode 100644 index 0000000000..658dd4aab1 --- /dev/null +++ b/buying/report/purchase_order_trends/purchase_order_trends.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-13 18:45:01", + "docstatus": 0, + "modified": "2013-06-13 18:45:01", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Order", + "report_name": "Purchase Order Trends", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Purchase Order Trends" + } +] \ No newline at end of file diff --git a/controllers/trends.py b/controllers/trends.py new file mode 100644 index 0000000000..6c33235b9f --- /dev/null +++ b/controllers/trends.py @@ -0,0 +1,245 @@ +# 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 cint, add_days, add_months, cstr + +def get_columns(filters, year_start_date, start_month, trans): + columns, pwc, bon, grbc = [], [], [], [] + query_bon, query_pwc = '', '' + + period = filters.get("period") + based_on = filters.get("based_on") + grby = filters.get("group_by") + + if not (period and based_on): + webnotes.msgprint("Value missing in 'Period' or 'Based On'", raise_exception=1) + + elif based_on == grby: + webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'", raise_exception=1) + + else: + bon, query_bon, basedon, sup_tab = bon_columns_qdata(based_on, bon, trans) + pwc, query_pwc = pw_column_qdata(filters, period, pwc, year_start_date,start_month, trans) + grbc = grp_column(grby) + + if grbc: + columns = bon + grbc + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] + else: + columns = bon + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] + + return columns, query_bon, query_pwc, basedon, grbc, sup_tab + +def get_data(columns, filters, tab, query_bon, query_pwc, basedon, grbc, sup_tab): + + query_details = query_bon + query_pwc + 'SUM(t2.qty), SUM(t1.grand_total) ' + query_pwc = query_pwc + 'SUM(t2.qty), SUM(t1.grand_total)' + data = [] + inc, cond= '','' + + if query_bon in ["t1.project_name,", "t2.project_name,"]: + cond = 'and '+ query_bon[:-1] +' IS Not NULL' + + if filters.get("group_by"): + sel_col = '' + + if filters.get("group_by") == 'Item': + sel_col = 't2.item_code' + + elif filters.get("group_by") == 'Customer': + sel_col = 't1.customer' + + elif filters.get("group_by") == 'Supplier': + sel_col = 't1.supplier' + + if filters.get('based_on') in ['Item','Customer','Supplier']: + inc = 2 + else : + inc = 1 + + ind = columns.index(grbc[0]) + + data1 = webnotes.conn.sql(""" select %s %s from `%s` t1, `%s` t2 %s + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 %s group by %s + """%(query_bon, query_pwc, tab[0], tab[1], sup_tab,filters.get("company"), filters.get("fiscal_year"), + cond, basedon), as_list=1) + + for d in range(len(data1)): + #to add blanck column + dt = data1[d] + dt.insert(ind,'') + data.append(dt) + + #to get distinct value of col specified by group_by in filter + row = webnotes.conn.sql("""select DISTINCT(%s) from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 and %s = '%s' + """%(sel_col, tab[0], tab[1], filters.get("company"), filters.get("fiscal_year"), + basedon, data1[d][0]),as_list=1) + + for i in range(len(row)): + des = ['' for q in range(len(columns))] + + #get data for each group_by filter + row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 and %s = '%s' and %s ='%s' + """%(sel_col, query_pwc, tab[0], tab[1], filters.get("company"), filters.get("fiscal_year"), + sel_col, row[i][0], basedon, data1[d][0]),as_list=1) + + des[ind] = row[i] + for j in range(1,len(columns)-inc): + des[j+inc] = row1[0][j] + data.append(des) + else: + + data = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 %s + where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' + and t1.docstatus = 1 %s group by %s + """%(query_details, tab[0], tab[1], sup_tab, filters.get("company"), + filters.get("fiscal_year"), cond,basedon), as_list=1) + + return data + +def pw_column_qdata(filters, period, pwc, year_start_date, start_month, trans): + query_details = '' + if trans in ['Purchase Receipt', 'Delivery Note', 'Purchase Invoice', 'Sales Invoice']: + trans_date = 'posting_date' + else: + trans_date = 'transaction_date' + + if period == "Monthly": + month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] + + for month in range(start_month-1,len(month_name)): + pwc.append(month_name[month]+' (Qty):Float:120') + pwc.append(month_name[month]+' (Amt):Currency:120') + + query_details += """ + Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), + SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date,"mon_num": cstr(month+1)} + + for month in range(0, start_month-1): + pwc.append(month_name[month]+' (Qty):Float:120') + pwc.append(month_name[month]+' (Amt):Currency:120') + + query_details += """ + Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), + SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date, "mon_num": cstr(month+1)} + + elif period == "Quarterly": + pwc = ["Q1(qty):Float:120", "Q1(amt):Currency:120", "Q2(qty):Float:120", "Q2(amt):Currency:120", + "Q3(qty):Float:120", "Q3(amt):Currency:120", "Q4(qty):Float:120", "Q4(amt):Currency:120"] + + first_qsd, second_qsd, third_qsd, fourth_qsd = year_start_date, add_months(year_start_date,3), add_months(year_start_date,6), add_months(year_start_date,9) + first_qed, second_qed, third_qed, fourth_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1), add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) + + bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] + for d in bet_dates: + query_details += """ + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date, "sd": d[0],"ed": d[1]} + + elif period == "Half-yearly": + pwc = ["Fisrt Half(qty):Float:120", "Fisrt Half(amt):Currency:120", "Second Half(qty):Float:120", + "Second Half(amt):Currency:120"] + + first_half_start = year_start_date + first_half_end = add_days(add_months(first_half_start,6),-1) + second_half_start = add_days(first_half_end,1) + second_half_end = add_days(add_months(second_half_start,6),-1) + + query_details = """ + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t1.grand_total ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t2.qty ELSE NULL END), + SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t1.grand_total ELSE NULL END), + """%{"trans": trans_date, "fhs": first_half_start, "fhe": first_half_end,"shs": second_half_start, + "she": second_half_end} + + else: + pwc = [filters.get("fiscal_year")+"(qty):Float:120", filters.get("fiscal_year")+"(amt):Currency:120"] + query_details = " SUM(t2.qty), SUM(t1.grand_total)," + + return pwc, query_details + +def bon_columns_qdata(based_on, bon, trans): + sup_tab = '' + + if based_on == "Item": + bon = ["Item:Link/Item:120", "Item Name:Data:120"] + query_details = "t2.item_code, t2.item_name," + basedon = 't2.item_code' + + elif based_on == "Item Group": + bon = ["Item Group:Link/Item Group:120"] + query_details = "t2.item_group," + basedon = 't2.item_group' + + elif based_on == "Customer": + bon = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"] + query_details = "t1.customer_name, t1.territory, " + basedon = 't1.customer_name' + + elif based_on == "Customer Group": + bon = ["Customer Group:Link/Customer Group"] + query_details = "t1.customer_group," + basedon = 't1.customer_group' + + elif based_on == 'Supplier': + bon = ["Supplier:Link/Supplier:120", "Supplier Type:Link/Supplier Type:120"] + query_details = "t1.supplier, t3.supplier_type," + basedon = 't1.supplier' + sup_tab = ',`tabSupplier` t3' + + elif based_on == 'Supplier Type': + bon = ["Supplier Type:Link/Supplier Type:120"] + query_details = "t3.supplier_type," + basedon = 't3.supplier_type' + sup_tab = ',`tabSupplier` t3' + + elif based_on == "Territory": + bon = ["Territory:Link/Territory:120"] + query_details = "t1.territory," + basedon = 't1.territory' + + elif based_on == "Project": + + if trans in ['Sales Invoice', 'Delivery Note', 'Sales Order']: + bon = ["Project:Link/Project:120"] + query_details = "t1.project_name," + basedon = 't1.project_name' + + elif trans in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: + bon = ["Project:Link/Project:120"] + query_details = "t2.project_name," + basedon = 't2.project_name' + + else: + webnotes.msgprint("No Information Available", raise_exception=1) + + return bon, query_details, basedon, sup_tab + +def grp_column(group_by): + if group_by: + return [group_by+":Link/"+group_by+":120"] + else: + return [] \ No newline at end of file diff --git a/public/js/purchase_trends_filters.js b/public/js/purchase_trends_filters.js new file mode 100644 index 0000000000..05f67c92a4 --- /dev/null +++ b/public/js/purchase_trends_filters.js @@ -0,0 +1,39 @@ +var get_filter = function(){ + return [ + { + "fieldname":"period", + "label": "Period", + "fieldtype": "Select", + "options": ["Monthly", "Quarterly", "Half-yearly", "Yearly"].join("\n"), + "default": "Monthly" + }, + { + "fieldname":"based_on", + "label": "Based On", + "fieldtype": "Select", + "options": ["Item", "Item Group", "Supplier", "Supplier Type", "Project"].join("\n"), + "default": "Item" + }, + { + "fieldname":"group_by", + "label": "Group By", + "fieldtype": "Select", + "options": ["Item", "Supplier"].join("\n"), + "default": "" + }, + { + "fieldname":"fiscal_year", + "label": "Fiscal Year", + "fieldtype": "Link", + "options":'Fiscal Year', + "default": sys_defaults.fiscal_year + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + }, + ]; +} \ No newline at end of file diff --git a/public/js/sales_trends_filters.js b/public/js/sales_trends_filters.js new file mode 100644 index 0000000000..14dcbe3cf6 --- /dev/null +++ b/public/js/sales_trends_filters.js @@ -0,0 +1,39 @@ +var get_filters = function(){ + return[ + { + "fieldname":"period", + "label": "Period", + "fieldtype": "Select", + "options": ["Monthly", "Quarterly", "Half-yearly", "Yearly"].join("\n"), + "default": "Monthly" + }, + { + "fieldname":"based_on", + "label": "Based On", + "fieldtype": "Select", + "options": ["Item", "Item Group", "Customer", "Customer Group", "Territory", "Project"].join("\n"), + "default": "Item" + }, + { + "fieldname":"group_by", + "label": "Group By", + "fieldtype": "Select", + "options": ["Item", "Customer"].join("\n"), + "default": "" + }, + { + "fieldname":"fiscal_year", + "label": "Fiscal Year", + "fieldtype": "Link", + "options":'Fiscal Year', + "default": sys_defaults.fiscal_year + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + }, + ]; +} \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.js b/selling/report/quotation_trends/quotation_trends.js index e166fa66ce..2f9f23666c 100644 --- a/selling/report/quotation_trends/quotation_trends.js +++ b/selling/report/quotation_trends/quotation_trends.js @@ -1,40 +1,5 @@ +wn.require("app/js/sales_trends_filters.js"); + wn.query_reports["Quotation Trends"] = { - "filters": [ - { - "fieldname":"period", - "label": "Period", - "fieldtype": "Select", - "options": "Monthly"+NEWLINE+"Quarterly"+NEWLINE+"Half-yearly"+NEWLINE+"Yearly", - "default": "Monthly" - }, - { - "fieldname":"based_on", - "label": "Based On", - "fieldtype": "Select", - "options": "Item"+NEWLINE+"Item Group"+NEWLINE+"Customer"+NEWLINE+"Customer Group"+NEWLINE+"Territory"+NEWLINE+"Project", - "default": "Item" - }, - { - "fieldname":"group_by", - "label": "Group By", - "fieldtype": "Select", - "options": "Item"+NEWLINE+"Customer", - "default": "" - }, - { - "fieldname":"fiscal_year", - "label": "Fiscal Year", - "fieldtype": "Link", - "options":'Fiscal Year', - "default": sys_defaults.fiscal_year - }, - { - "fieldname":"company", - "label": "Company", - "fieldtype": "Link", - "options": "Company", - "default": sys_defaults.company - }, - - ] -} \ No newline at end of file + filters: get_filters() + } \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py index 7f944e0f23..527e0addc5 100644 --- a/selling/report/quotation_trends/quotation_trends.py +++ b/selling/report/quotation_trends/quotation_trends.py @@ -17,204 +17,21 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cint, add_days, add_months, cstr +from controllers.trends import get_columns,get_data def execute(filters=None): if not filters: filters ={} + data = [] - # Global data trans = "Quotation" tab = ["tabQuotation","tabQuotation Item"] ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] year_start_date = ysd.strftime('%Y-%m-%d') start_month = cint(year_start_date.split('-')[1]) - columns, query_bon, query_pwc, group_by, gby = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, group_by, gby) + columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) - return columns, data - -def get_columns(filters, year_start_date, start_month, trans): - columns, pwc, bon, gby = [], [], [], [] - query_bon, query_pwc = '', '' - - period = filters.get("period") - based_on = filters.get("based_on") - grby = filters.get("group_by") - - if not (period and based_on): - webnotes.msgprint("Value missing in 'Period' or 'Based On'", raise_exception=1) - - elif based_on == grby: - webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'", raise_exception=1) - - else: - bon,query_bon,group_by = base_wise_column(based_on, bon) - pwc,query_pwc = period_wise_column_and_query(filters, period, pwc, year_start_date,start_month, trans) - gby = gruoup_wise_column(grby) - - if gby: - columns = bon + gby + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - else: - columns = bon + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - - return columns, query_bon, query_pwc, group_by,gby - -def get_data(columns, filters, tab, query_bon, query_pwc, group_by,gby): - - query_details = query_bon + query_pwc + 'SUM(t2.qty), SUM(t1.grand_total) ' - query_pwc = query_pwc + 'SUM(t2.qty), SUM(t1.grand_total)' - data = [] - inc = '' - - if filters.get("group_by"): - sel_col = '' - if filters.get("group_by") == 'Item': - sel_col = 't2.item_code' - - elif filters.get("group_by") == 'Customer': - sel_col = 't1.customer' - - if filters.get('based_on') in ['Item','Customer']: - inc = 2 - else : - inc = 1 - - ind = columns.index(gby[0]) - - data1 = webnotes.conn.sql(""" select %s %s from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 group by %s """%(query_bon, query_pwc, tab[0], tab[1], - filters.get("company"), filters.get("fiscal_year"), group_by), as_list=1) - - for d in range(len(data1)): - - dt = data1[d] - dt.insert(ind,'') - data.append(dt) - - row = webnotes.conn.sql("""select DISTINCT(%s) from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 and %s = '%s' """%(sel_col, tab[0], tab[1], filters.get("company"), - filters.get("fiscal_year"), group_by, data1[d][0]),as_list=1) - - for i in range(len(row)): - des = ['' for q in range(len(columns))] - - row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 and %s = '%s' and %s ='%s' """%(sel_col, query_pwc, tab[0], tab[1], - filters.get("company"), filters.get("fiscal_year"), sel_col, row[i][0], group_by, - data1[d][0]),as_list=1) - - des[ind] = row[i] - for j in range(1,len(columns)-inc): - des[j+inc] = row1[0][j] - data.append(des) - else: - - data = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 group by %s - """%(query_details, tab[0], tab[1], filters.get("company"), filters.get("fiscal_year"), group_by), as_list=1) - - return data - -def period_wise_column_and_query(filters, period, pwc, year_start_date, start_month, trans): - query_details = '' - if trans in ['Purchase Receipt', 'Delivery Note', 'Purchase Invoice', 'Sales Invoice']: - trans_date = 'posting_date' - else: - trans_date = 'transaction_date' - - if period == "Monthly": - month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] - - for month in range(start_month-1,len(month_name)): - pwc.append(month_name[month]+' (Qty):Float:120') - pwc.append(month_name[month]+' (Amt):Currency:120') - - query_details += """ - Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), - SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), - """%{"trans": trans_date,"mon_num": cstr(month+1)} - - for month in range(0, start_month-1): - pwc.append(month_name[month]+' (Qty):Float:120') - pwc.append(month_name[month]+' (Amt):Currency:120') - - query_details += """ - Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), - SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), - """%{"trans": trans_date, "mon_num": cstr(month+1)} - - elif period == "Quarterly": - pwc = ["Q1(qty):Float:120", "Q1(amt):Currency:120", "Q2(qty):Float:120", "Q2(amt):Currency:120", - "Q3(qty):Float:120", "Q3(amt):Currency:120", "Q4(qty):Float:120", "Q4(amt):Currency:120"] - - first_qsd, second_qsd, third_qsd, fourth_qsd = year_start_date, add_months(year_start_date,3), add_months(year_start_date,6), add_months(year_start_date,9) - first_qed, second_qed, third_qed, fourth_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1), add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) - - bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] - for d in bet_dates: - query_details += """ - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t1.grand_total ELSE NULL END), - """%{"trans": trans_date, "sd": d[0],"ed": d[1]} - - elif period == "Half-yearly": - pwc = ["Fisrt Half(qty):Float:120", "Fisrt Half(amt):Currency:120", "Second Half(qty):Float:120", - "Second Half(amt):Currency:120"] - - first_half_start = year_start_date - first_half_end = add_days(add_months(first_half_start,6),-1) - second_half_start = add_days(first_half_end,1) - second_half_end = add_days(add_months(second_half_start,6),-1) - - query_details = """ - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t1.grand_total ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t1.grand_total ELSE NULL END), - """%{"trans": trans_date, "fhs": first_half_start, "fhe": first_half_end,"shs": second_half_start, "she": second_half_end} - - else: - pwc = [filters.get("fiscal_year")+"(qty):Float:120", filters.get("fiscal_year")+"(amt):Currency:120"] - query_details = " SUM(t2.qty), SUM(t1.grand_total)," - - return pwc, query_details - -def base_wise_column(based_on, bon): - if based_on == "Item": - bon = ["Item:Link/Item:120", "Item Name:Data:120"] - query_details = "t2.item_code, t2.item_name," - group_by = 't2.item_code' - - elif based_on == "Item Group": - bon = ["Item Group:Link/Item Group:120"] - query_details = "t2.item_group," - group_by = 't2.item_group' - - elif based_on == "Customer": - bon = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"] - query_details = "t1.customer_name, t1.territory, " - group_by = 't1.customer_name' - - elif based_on == "Customer Group": - bon = ["Customer Group:Link/Customer Group"] - query_details = "t1.customer_group, " - group_by = 't1.customer_group' - - elif based_on == "Territory": - bon = ["Territory:Link/Territory:120"] - query_details = "t1.territory, " - group_by = 't1.territory' - - else: - bon = ["Project:Link/Project:120"] - return bon, query_details, group_by - -def gruoup_wise_column(group_by): - if group_by: - return [group_by+":Link/"+group_by+":120"] - else: - return [] \ No newline at end of file + if data == '': + webnotes.msgprint("Data Not Available") + return columns, data \ No newline at end of file diff --git a/selling/report/sales_order_trends/__init__.py b/selling/report/sales_order_trends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/sales_order_trends/sales_order_trends.js b/selling/report/sales_order_trends/sales_order_trends.js new file mode 100644 index 0000000000..29c124403f --- /dev/null +++ b/selling/report/sales_order_trends/sales_order_trends.js @@ -0,0 +1,5 @@ +wn.require("app/js/sales_trends_filters.js"); + +wn.query_reports["Sales Order"] = { + filters: get_filters() + } \ No newline at end of file diff --git a/selling/report/sales_order_trends/sales_order_trends.py b/selling/report/sales_order_trends/sales_order_trends.py new file mode 100644 index 0000000000..047a8a9e9f --- /dev/null +++ b/selling/report/sales_order_trends/sales_order_trends.py @@ -0,0 +1,37 @@ +# 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 cint, add_days, add_months, cstr +from controllers.trends import get_columns,get_data + +def execute(filters=None): + if not filters: filters ={} + data = [] + + trans = "Sales Order" + tab = ["tabSales Order","tabSales Order Item"] + ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + + columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + + if data == '': + webnotes.msgprint("Data Not Available") + return columns, data \ No newline at end of file diff --git a/selling/report/sales_order_trends/sales_order_trends.txt b/selling/report/sales_order_trends/sales_order_trends.txt new file mode 100644 index 0000000000..16ee9ca46b --- /dev/null +++ b/selling/report/sales_order_trends/sales_order_trends.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-13 18:43:30", + "docstatus": 0, + "modified": "2013-06-13 18:43:30", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Order", + "report_name": "Sales Order Trends", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Sales Order Trends" + } +] \ No newline at end of file diff --git a/stock/report/delivery_note_trends/__init__.py b/stock/report/delivery_note_trends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/delivery_note_trends/delivery_note_trends.js b/stock/report/delivery_note_trends/delivery_note_trends.js new file mode 100644 index 0000000000..3ec5e594d7 --- /dev/null +++ b/stock/report/delivery_note_trends/delivery_note_trends.js @@ -0,0 +1,5 @@ +wn.require("app/js/sales_trends_filters.js"); + +wn.query_reports["Delivery Note Trends"] = { + filters: get_filters() + } \ No newline at end of file diff --git a/stock/report/delivery_note_trends/delivery_note_trends.py b/stock/report/delivery_note_trends/delivery_note_trends.py new file mode 100644 index 0000000000..685cede509 --- /dev/null +++ b/stock/report/delivery_note_trends/delivery_note_trends.py @@ -0,0 +1,37 @@ +# 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 cint, add_days, add_months, cstr +from controllers.trends import get_columns,get_data + +def execute(filters=None): + if not filters: filters ={} + data = [] + + trans = "Delivery Note" + tab = ["tabDelivery Note","tabDelivery Note Item"] + ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + + columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + + if data == '': + webnotes.msgprint("Data Not Available") + return columns, data \ No newline at end of file diff --git a/stock/report/delivery_note_trends/delivery_note_trends.txt b/stock/report/delivery_note_trends/delivery_note_trends.txt new file mode 100644 index 0000000000..bb8720074a --- /dev/null +++ b/stock/report/delivery_note_trends/delivery_note_trends.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-13 18:42:11", + "docstatus": 0, + "modified": "2013-06-13 18:42:11", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Delivery Note", + "report_name": "Delivery Note Trends", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Delivery Note Trends" + } +] \ No newline at end of file diff --git a/stock/report/purchase_receipt_trends/__init__.py b/stock/report/purchase_receipt_trends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/report/purchase_receipt_trends/purchase_receipt_trends.js b/stock/report/purchase_receipt_trends/purchase_receipt_trends.js new file mode 100644 index 0000000000..ecfa5a473e --- /dev/null +++ b/stock/report/purchase_receipt_trends/purchase_receipt_trends.js @@ -0,0 +1,5 @@ +wn.require("app/js/purchase_trends_filters.js"); + +wn.query_reports["Purchase Receipt Trends"] = { + filters: get_filters() + } \ No newline at end of file diff --git a/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py new file mode 100644 index 0000000000..aaa18a0a0d --- /dev/null +++ b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -0,0 +1,37 @@ +# 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 cint, add_days, add_months, cstr +from controllers.trends import get_columns,get_data + +def execute(filters=None): + if not filters: filters ={} + data = [] + + trans = "Purchase Receipt" + tab = ["tabPurchase Receipt","tabPurchase Receipt Item"] + ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + + columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) + data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + + if data == '': + webnotes.msgprint("Data Not Available") + return columns, data \ No newline at end of file diff --git a/stock/report/purchase_receipt_trends/purchase_receipt_trends.txt b/stock/report/purchase_receipt_trends/purchase_receipt_trends.txt new file mode 100644 index 0000000000..179c524f96 --- /dev/null +++ b/stock/report/purchase_receipt_trends/purchase_receipt_trends.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-13 18:45:44", + "docstatus": 0, + "modified": "2013-06-13 18:45:44", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Receipt", + "report_name": "Purchase Receipt Trends", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Purchase Receipt Trends" + } +] \ No newline at end of file From 1848b71a0a3354134c2b76233176aec0a057d789 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 14 Jun 2013 15:03:45 +0530 Subject: [PATCH 232/295] [Reports][Sales and Purchase Trends][Completed] --- accounts/page/accounts_home/accounts_home.js | 10 ++ .../purchase_invoice_trends.py | 13 +- .../sales_invoice_trends.py | 13 +- buying/page/buying_home/buying_home.js | 5 + .../purchase_order_trends.py | 13 +- controllers/trends.py | 138 ++++++++++-------- public/js/purchase_trends_filters.js | 2 +- selling/page/selling_home/selling_home.js | 5 + .../quotation_trends/quotation_trends.py | 13 +- .../sales_order_trends/sales_order_trends.js | 2 +- .../sales_order_trends/sales_order_trends.py | 15 +- stock/page/stock_home/stock_home.js | 10 ++ .../delivery_note_trends.py | 15 +- .../purchase_receipt_trends.py | 13 +- 14 files changed, 146 insertions(+), 121 deletions(-) diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 7f623d7115..63fe9695cc 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -257,6 +257,16 @@ wn.module_page["Accounts"] = [ route: "query-report/Item-wise Purchase Register", doctype: "Purchase Invoice" }, + { + "label":wn._("Purchase Invoice Trends"), + route: "query-report/Purchase Invoice Trends", + doctype: "Purchase Invoice" + }, + { + "label":wn._("Sales Invoice Trends"), + route: "query-report/Sales Invoice Trends", + doctype: "Sales Invoice" + }, ] } ] diff --git a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py index e74ad2129b..a38c37cdb5 100644 --- a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py +++ b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr from controllers.trends import get_columns,get_data def execute(filters=None): @@ -25,13 +24,11 @@ def execute(filters=None): trans = "Purchase Invoice" tab = ["tabPurchase Invoice","tabPurchase Invoice Item"] - ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) - - columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + details = get_columns(filters, trans) + data = get_data(filters, tab, details) + if data == '': webnotes.msgprint("Data Not Available") - return columns, data \ No newline at end of file + + return details["columns"], data \ No newline at end of file diff --git a/accounts/report/sales_invoice_trends/sales_invoice_trends.py b/accounts/report/sales_invoice_trends/sales_invoice_trends.py index 48606f04ed..3839900b46 100644 --- a/accounts/report/sales_invoice_trends/sales_invoice_trends.py +++ b/accounts/report/sales_invoice_trends/sales_invoice_trends.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr from controllers.trends import get_columns,get_data def execute(filters=None): @@ -25,13 +24,11 @@ def execute(filters=None): trans = "Sales Invoice" tab = ["tabSales Invoice","tabSales Invoice Item"] - ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) - - columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + details = get_columns(filters, trans) + data = get_data(filters, tab, details) + if data == '': webnotes.msgprint("Data Not Available") - return columns, data \ No newline at end of file + + return details["columns"], data \ No newline at end of file diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js index 2070fd4e33..dfcd71e17d 100644 --- a/buying/page/buying_home/buying_home.js +++ b/buying/page/buying_home/buying_home.js @@ -115,6 +115,11 @@ wn.module_page["Buying"] = [ "label":wn._("Requested Items To Be Ordered"), route: "query-report/Requested Items To Be Ordered", }, + { + "label":wn._("Purchase Order Trends"), + route: "query-report/Purchase Order Trends", + doctype: "Purchase Order" + }, ] } ] diff --git a/buying/report/purchase_order_trends/purchase_order_trends.py b/buying/report/purchase_order_trends/purchase_order_trends.py index cdf79b2fad..063bef43f2 100644 --- a/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/buying/report/purchase_order_trends/purchase_order_trends.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr from controllers.trends import get_columns,get_data def execute(filters=None): @@ -25,13 +24,11 @@ def execute(filters=None): trans = "Purchase Order" tab = ["tabPurchase Order","tabPurchase Order Item"] - ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) - - columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + details = get_columns(filters, trans) + data = get_data(filters, tab, details) + if data == '': webnotes.msgprint("Data Not Available") - return columns, data \ No newline at end of file + + return details["columns"], data \ No newline at end of file diff --git a/controllers/trends.py b/controllers/trends.py index 6c33235b9f..dee9ec8b4f 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -18,51 +18,45 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cint, add_days, add_months, cstr -def get_columns(filters, year_start_date, start_month, trans): - columns, pwc, bon, grbc = [], [], [], [] - query_bon, query_pwc = '', '' +def get_columns(filters, trans): - period = filters.get("period") - based_on = filters.get("based_on") - grby = filters.get("group_by") - - if not (period and based_on): + if not (filters.get("period") and filters.get("based_on")): webnotes.msgprint("Value missing in 'Period' or 'Based On'", raise_exception=1) - elif based_on == grby: + elif filters.get("based_on") == filters.get("group_by"): webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'", raise_exception=1) else: - bon, query_bon, basedon, sup_tab = bon_columns_qdata(based_on, bon, trans) - pwc, query_pwc = pw_column_qdata(filters, period, pwc, year_start_date,start_month, trans) - grbc = grp_column(grby) - - if grbc: - columns = bon + grbc + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - else: + bon, query_bon, basedon, sup_tab = basedon_wise_colums_query(filters.get("based_on"), trans) + pwc, query_pwc = period_wise_colums_query(filters, trans) + grbc = group_wise_column(filters.get("group_by")) + columns = bon + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] + if grbc: + columns = bon + grbc + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - return columns, query_bon, query_pwc, basedon, grbc, sup_tab + details = {"query_bon": query_bon, "query_pwc": query_pwc, "columns": columns, "basedon": basedon, + "grbc": grbc, "sup_tab": sup_tab} -def get_data(columns, filters, tab, query_bon, query_pwc, basedon, grbc, sup_tab): + return details + +def get_data(filters, tab, details): - query_details = query_bon + query_pwc + 'SUM(t2.qty), SUM(t1.grand_total) ' - query_pwc = query_pwc + 'SUM(t2.qty), SUM(t1.grand_total)' data = [] inc, cond= '','' + query_details = details["query_bon"] + details["query_pwc"] - if query_bon in ["t1.project_name,", "t2.project_name,"]: - cond = 'and '+ query_bon[:-1] +' IS Not NULL' + if details["query_bon"] in ["t1.project_name,", "t2.project_name,"]: + cond = 'and '+ details["query_bon"][:-1] +' IS Not NULL' if filters.get("group_by"): sel_col = '' + ind = details["columns"].index(details["grbc"][0]) if filters.get("group_by") == 'Item': sel_col = 't2.item_code' - elif filters.get("group_by") == 'Customer': sel_col = 't1.customer' - elif filters.get("group_by") == 'Supplier': sel_col = 't1.supplier' @@ -71,13 +65,14 @@ def get_data(columns, filters, tab, query_bon, query_pwc, basedon, grbc, sup_ta else : inc = 1 - ind = columns.index(grbc[0]) - - data1 = webnotes.conn.sql(""" select %s %s from `%s` t1, `%s` t2 %s - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 %s group by %s - """%(query_bon, query_pwc, tab[0], tab[1], sup_tab,filters.get("company"), filters.get("fiscal_year"), - cond, basedon), as_list=1) + data1 = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 %s + where t2.parent = t1.name and t1.company = %s + and t1.fiscal_year = %s and t1.docstatus = 1 %s + group by %s + """ % (query_details, tab[0], tab[1], details["sup_tab"], "%s", + "%s", cond, details["basedon"]), (filters.get("company"), + filters["fiscal_year"]), + as_list=1) for d in range(len(data1)): #to add blanck column @@ -86,44 +81,64 @@ def get_data(columns, filters, tab, query_bon, query_pwc, basedon, grbc, sup_ta data.append(dt) #to get distinct value of col specified by group_by in filter - row = webnotes.conn.sql("""select DISTINCT(%s) from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 and %s = '%s' - """%(sel_col, tab[0], tab[1], filters.get("company"), filters.get("fiscal_year"), - basedon, data1[d][0]),as_list=1) - + row = webnotes.conn.sql("""select DISTINCT(%s) from `%s` t1, `%s` t2 %s + where t2.parent = t1.name and t1.company = %s + and t1.fiscal_year = %s and t1.docstatus = 1 + and %s = %s + """%(sel_col, tab[0], tab[1], details["sup_tab"], "%s", + "%s", details["basedon"], "%s"), + (filters.get("company"), filters.get("fiscal_year"), + data1[d][0]), + as_list=1) + for i in range(len(row)): - des = ['' for q in range(len(columns))] + des = ['' for q in range(len(details["columns"]))] #get data for each group_by filter - row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 and %s = '%s' and %s ='%s' - """%(sel_col, query_pwc, tab[0], tab[1], filters.get("company"), filters.get("fiscal_year"), - sel_col, row[i][0], basedon, data1[d][0]),as_list=1) + row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 %s + where t2.parent = t1.name and t1.company = %s + and t1.fiscal_year = %s and t1.docstatus = 1 + and %s = %s and %s = %s + """%(sel_col, details["query_pwc"], tab[0], tab[1], details["sup_tab"], + "%s", "%s", sel_col, "%s", details["basedon"], "%s"), + (filters.get("company"), filters.get("fiscal_year"), row[i][0], + data1[d][0]), + as_list=1) des[ind] = row[i] - for j in range(1,len(columns)-inc): + for j in range(1,len(details["columns"])-inc): des[j+inc] = row1[0][j] data.append(des) else: data = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 %s - where t2.parent = t1.name and t1.company = '%s' and t1.fiscal_year = '%s' - and t1.docstatus = 1 %s group by %s - """%(query_details, tab[0], tab[1], sup_tab, filters.get("company"), - filters.get("fiscal_year"), cond,basedon), as_list=1) + where t2.parent = t1.name and t1.company = %s + and t1.fiscal_year = %s and t1.docstatus = 1 + %s group by %s + """%(query_details, tab[0], tab[1], details["sup_tab"], "%s", + "%s", cond,details["basedon"]), (filters.get("company"), + filters.get("fiscal_year")), + as_list=1) return data -def pw_column_qdata(filters, period, pwc, year_start_date, start_month, trans): +def period_wise_colums_query(filters, trans): + query_details = '' + pwc = [] + ysd = webnotes.conn.sql("""select year_start_date from `tabFiscal Year` + where name = '%s' + """%filters.get("fiscal_year"))[0][0] + + year_start_date = ysd.strftime('%Y-%m-%d') + start_month = cint(year_start_date.split('-')[1]) + if trans in ['Purchase Receipt', 'Delivery Note', 'Purchase Invoice', 'Sales Invoice']: trans_date = 'posting_date' else: trans_date = 'transaction_date' - if period == "Monthly": + if filters.get("period") == "Monthly": month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] for month in range(start_month-1,len(month_name)): @@ -144,23 +159,23 @@ def pw_column_qdata(filters, period, pwc, year_start_date, start_month, trans): SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), """%{"trans": trans_date, "mon_num": cstr(month+1)} - elif period == "Quarterly": - pwc = ["Q1(qty):Float:120", "Q1(amt):Currency:120", "Q2(qty):Float:120", "Q2(amt):Currency:120", - "Q3(qty):Float:120", "Q3(amt):Currency:120", "Q4(qty):Float:120", "Q4(amt):Currency:120"] + elif filters.get("period") == "Quarterly": + pwc = ["Q1 (Qty):Float:120", "Q1 (Amt):Currency:120", "Q2 (Qty):Float:120", "Q2 (Amt):Currency:120", + "Q3 (Qty):Float:120", "Q3 (Amt):Currency:120", "Q4 (Qty):Float:120", "Q4 (Amt):Currency:120"] first_qsd, second_qsd, third_qsd, fourth_qsd = year_start_date, add_months(year_start_date,3), add_months(year_start_date,6), add_months(year_start_date,9) first_qed, second_qed, third_qed, fourth_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1), add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) - bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] + for d in bet_dates: query_details += """ SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t2.qty ELSE NULL END), SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t1.grand_total ELSE NULL END), """%{"trans": trans_date, "sd": d[0],"ed": d[1]} - elif period == "Half-yearly": - pwc = ["Fisrt Half(qty):Float:120", "Fisrt Half(amt):Currency:120", "Second Half(qty):Float:120", - "Second Half(amt):Currency:120"] + elif filters.get("period") == "Half-yearly": + pwc = ["Fisrt Half (Qty):Float:120", "Fisrt Half (Amt):Currency:120", "Second Half (Qty):Float:120", + "Second Half (Amt):Currency:120"] first_half_start = year_start_date first_half_end = add_days(add_months(first_half_start,6),-1) @@ -176,12 +191,13 @@ def pw_column_qdata(filters, period, pwc, year_start_date, start_month, trans): "she": second_half_end} else: - pwc = [filters.get("fiscal_year")+"(qty):Float:120", filters.get("fiscal_year")+"(amt):Currency:120"] + pwc = [filters.get("fiscal_year")+" (Qty):Float:120", filters.get("fiscal_year")+" (Amt):Currency:120"] query_details = " SUM(t2.qty), SUM(t1.grand_total)," + query_details += 'SUM(t2.qty), SUM(t1.grand_total)' return pwc, query_details -def bon_columns_qdata(based_on, bon, trans): +def basedon_wise_colums_query(based_on, trans): sup_tab = '' if based_on == "Item": @@ -234,11 +250,11 @@ def bon_columns_qdata(based_on, bon, trans): basedon = 't2.project_name' else: - webnotes.msgprint("No Information Available", raise_exception=1) + webnotes.msgprint("Information Not Available", raise_exception=1) return bon, query_details, basedon, sup_tab -def grp_column(group_by): +def group_wise_column(group_by): if group_by: return [group_by+":Link/"+group_by+":120"] else: diff --git a/public/js/purchase_trends_filters.js b/public/js/purchase_trends_filters.js index 05f67c92a4..e994a47ebd 100644 --- a/public/js/purchase_trends_filters.js +++ b/public/js/purchase_trends_filters.js @@ -1,4 +1,4 @@ -var get_filter = function(){ +var get_filters = function(){ return [ { "fieldname":"period", diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 15f9b86162..ff8cff8892 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -173,6 +173,11 @@ wn.module_page["Selling"] = [ { "label":wn._("Quotation Trend"), route: "query-report/Quotation Trends", + doctype: "Quotation" + }, + { + "label":wn._("Sales Order Trend"), + route: "query-report/Sales Order Trends", doctype: "Sales Order" }, diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py index 527e0addc5..548a4a8253 100644 --- a/selling/report/quotation_trends/quotation_trends.py +++ b/selling/report/quotation_trends/quotation_trends.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr from controllers.trends import get_columns,get_data def execute(filters=None): @@ -25,13 +24,11 @@ def execute(filters=None): trans = "Quotation" tab = ["tabQuotation","tabQuotation Item"] - ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) - - columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + details = get_columns(filters, trans) + data = get_data(filters, tab, details) + if data == '': webnotes.msgprint("Data Not Available") - return columns, data \ No newline at end of file + + return details["columns"], data \ No newline at end of file diff --git a/selling/report/sales_order_trends/sales_order_trends.js b/selling/report/sales_order_trends/sales_order_trends.js index 29c124403f..458fee6ed3 100644 --- a/selling/report/sales_order_trends/sales_order_trends.js +++ b/selling/report/sales_order_trends/sales_order_trends.js @@ -1,5 +1,5 @@ wn.require("app/js/sales_trends_filters.js"); -wn.query_reports["Sales Order"] = { +wn.query_reports["Sales Order Trends"] = { filters: get_filters() } \ No newline at end of file diff --git a/selling/report/sales_order_trends/sales_order_trends.py b/selling/report/sales_order_trends/sales_order_trends.py index 047a8a9e9f..e3d9d9f523 100644 --- a/selling/report/sales_order_trends/sales_order_trends.py +++ b/selling/report/sales_order_trends/sales_order_trends.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr from controllers.trends import get_columns,get_data def execute(filters=None): @@ -25,13 +24,11 @@ def execute(filters=None): trans = "Sales Order" tab = ["tabSales Order","tabSales Order Item"] - ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) - - columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) - + + details = get_columns(filters, trans) + data = get_data(filters, tab, details) + if data == '': webnotes.msgprint("Data Not Available") - return columns, data \ No newline at end of file + + return details["columns"], data \ No newline at end of file diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js index d8c63aab66..532eb66406 100644 --- a/stock/page/stock_home/stock_home.js +++ b/stock/page/stock_home/stock_home.js @@ -223,6 +223,16 @@ wn.module_page["Stock"] = [ route: "query-report/Itemwise Recommended Reorder Level", doctype: "Item" }, + { + "label":wn._("Delivery Note Trends"), + route: "query-report/Delivery Note Trends", + doctype: "Delivery Note" + }, + { + "label":wn._("Purchase Receipt Trends"), + route: "query-report/Purchase Receipt Trends", + doctype: "Purchase Receipt" + }, ] } ] diff --git a/stock/report/delivery_note_trends/delivery_note_trends.py b/stock/report/delivery_note_trends/delivery_note_trends.py index 685cede509..a161c65eee 100644 --- a/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/stock/report/delivery_note_trends/delivery_note_trends.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr from controllers.trends import get_columns,get_data def execute(filters=None): @@ -25,13 +24,11 @@ def execute(filters=None): trans = "Delivery Note" tab = ["tabDelivery Note","tabDelivery Note Item"] - ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) - - columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) - + + details = get_columns(filters, trans) + data = get_data(filters, tab, details) + if data == '': webnotes.msgprint("Data Not Available") - return columns, data \ No newline at end of file + + return details["columns"], data \ No newline at end of file diff --git a/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index aaa18a0a0d..abce01eedd 100644 --- a/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr from controllers.trends import get_columns,get_data def execute(filters=None): @@ -25,13 +24,11 @@ def execute(filters=None): trans = "Purchase Receipt" tab = ["tabPurchase Receipt","tabPurchase Receipt Item"] - ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = '%s'"%filters.get("fiscal_year"))[0][0] - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) - - columns, query_bon, query_pwc, basedon, grbc, sup_tab = get_columns(filters, year_start_date, start_month, trans) - data = get_data(columns,filters, tab, query_bon, query_pwc, basedon, grbc ,sup_tab) + details = get_columns(filters, trans) + data = get_data(filters, tab, details) + if data == '': webnotes.msgprint("Data Not Available") - return columns, data \ No newline at end of file + + return details["columns"], data \ No newline at end of file From feb8669cbde3cb784b8ad5169834921dd83f4138 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 14 Jun 2013 17:40:48 +0530 Subject: [PATCH 233/295] [Reports][Validation changes in Purchase and Sales trends] --- .../purchase_invoice_trends/purchase_invoice_trends.py | 6 +++--- accounts/report/purchase_register/purchase_register.py | 1 + .../report/sales_invoice_trends/sales_invoice_trends.py | 4 ++-- .../report/purchase_order_trends/purchase_order_trends.py | 4 ++-- selling/report/quotation_trends/quotation_trends.py | 4 ++-- selling/report/sales_order_trends/sales_order_trends.py | 4 ++-- stock/report/delivery_note_trends/delivery_note_trends.py | 4 ++-- .../purchase_receipt_trends/purchase_receipt_trends.py | 4 ++-- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py index a38c37cdb5..b2f376b471 100644 --- a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py +++ b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py @@ -27,8 +27,8 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - - if data == '': - webnotes.msgprint("Data Not Available") + + if not data : + webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 7c4b38671d..548b561344 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -107,6 +107,7 @@ def get_invoices(filters): from `tabPurchase Invoice` where docstatus = 1 %s order by posting_date desc, name desc""" % conditions, filters, as_dict=1) + def get_invoice_expense_map(invoice_list): expense_details = webnotes.conn.sql("""select parent, expense_head, sum(amount) as amount from `tabPurchase Invoice Item` where parent in (%s) group by parent, expense_head""" % diff --git a/accounts/report/sales_invoice_trends/sales_invoice_trends.py b/accounts/report/sales_invoice_trends/sales_invoice_trends.py index 3839900b46..11d6665ff3 100644 --- a/accounts/report/sales_invoice_trends/sales_invoice_trends.py +++ b/accounts/report/sales_invoice_trends/sales_invoice_trends.py @@ -28,7 +28,7 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - if data == '': - webnotes.msgprint("Data Not Available") + if not data : + webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file diff --git a/buying/report/purchase_order_trends/purchase_order_trends.py b/buying/report/purchase_order_trends/purchase_order_trends.py index 063bef43f2..301124fd3b 100644 --- a/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/buying/report/purchase_order_trends/purchase_order_trends.py @@ -28,7 +28,7 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - if data == '': - webnotes.msgprint("Data Not Available") + if not data : + webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py index 548a4a8253..e341752cd0 100644 --- a/selling/report/quotation_trends/quotation_trends.py +++ b/selling/report/quotation_trends/quotation_trends.py @@ -28,7 +28,7 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - if data == '': - webnotes.msgprint("Data Not Available") + if not data: + webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file diff --git a/selling/report/sales_order_trends/sales_order_trends.py b/selling/report/sales_order_trends/sales_order_trends.py index e3d9d9f523..d556a58d37 100644 --- a/selling/report/sales_order_trends/sales_order_trends.py +++ b/selling/report/sales_order_trends/sales_order_trends.py @@ -28,7 +28,7 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - if data == '': - webnotes.msgprint("Data Not Available") + if not data : + webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file diff --git a/stock/report/delivery_note_trends/delivery_note_trends.py b/stock/report/delivery_note_trends/delivery_note_trends.py index a161c65eee..369b6a36b1 100644 --- a/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/stock/report/delivery_note_trends/delivery_note_trends.py @@ -28,7 +28,7 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - if data == '': - webnotes.msgprint("Data Not Available") + if not data : + webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file diff --git a/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index abce01eedd..bd089fafa2 100644 --- a/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -28,7 +28,7 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - if data == '': - webnotes.msgprint("Data Not Available") + if not data : + webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file From ad6180ef6d325bb6fc09ee9136e3a36e75483f74 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 17 Jun 2013 11:57:04 +0530 Subject: [PATCH 234/295] [material request on re-order level] [fixes] if error occurs, send email to the System Manager --- .../purchase_common/purchase_common.py | 25 +++--- startup/schedule_handlers.py | 4 +- stock/doctype/stock_entry/test_stock_entry.py | 3 + stock/utils.py | 90 +++++++++++++------ 4 files changed, 81 insertions(+), 41 deletions(-) diff --git a/buying/doctype/purchase_common/purchase_common.py b/buying/doctype/purchase_common/purchase_common.py index 2b6ca27acc..7cc5c22ea8 100644 --- a/buying/doctype/purchase_common/purchase_common.py +++ b/buying/doctype/purchase_common/purchase_common.py @@ -185,21 +185,22 @@ class DocType(BuyingController): if d.fields.has_key(x): d.fields[x] = f_lst[x] - item = sql("select is_stock_item, is_purchase_item, is_sub_contracted_item from tabItem where name=%s and (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now())", d.item_code) + item = sql("select is_stock_item, is_purchase_item, is_sub_contracted_item, end_of_life from tabItem where name=%s", + d.item_code) if not item: - msgprint("Item %s does not exist in Item Master." % cstr(d.item_code)) - raise Exception + msgprint("Item %s does not exist in Item Master." % cstr(d.item_code), raise_exception=True) + + from stock.utils import validate_end_of_life + validate_end_of_life(d.item_code, item[0][3]) # validate stock item if item[0][0]=='Yes' and d.qty and not d.warehouse: - msgprint("Warehouse is mandatory for %s, since it is a stock item" % - d.item_code, raise_exception=1) + msgprint("Warehouse is mandatory for %s, since it is a stock item" % + d.item_code, raise_exception=1) # validate purchase item if item[0][1] != 'Yes' and item[0][2] != 'Yes': - msgprint("Item %s is not a purchase item or sub-contracted item. Please check" % (d.item_code)) - raise Exception - + msgprint("Item %s is not a purchase item or sub-contracted item. Please check" % (d.item_code), raise_exception=True) if d.fields.has_key('prevdoc_docname') and d.prevdoc_docname: # check warehouse, uom in previous doc and in current doc are same. @@ -215,13 +216,13 @@ class DocType(BuyingController): # 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 + 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=True) # Check if UOM has been modified. if not cstr(data[0]['uom']) == cstr(d.uom) and not cstr(d.prevdoc_doctype) == 'Material Request': - msgprint("Please check UOM %s of Item %s which is not present in %s %s ." % (d.uom, d.item_code, d.prevdoc_doctype, d.prevdoc_docname)) - raise Exception + msgprint("Please check UOM %s of Item %s which is not present in %s %s ." % \ + (d.uom, d.item_code, d.prevdoc_doctype, d.prevdoc_docname), raise_exception=True) # list criteria that should not repeat if item is stock item e = [d.schedule_date, d.item_code, d.description, d.warehouse, d.uom, d.fields.has_key('prevdoc_docname') and d.prevdoc_docname or '', d.fields.has_key('prevdoc_detail_docname') and d.prevdoc_detail_docname or '', d.fields.has_key('batch_no') and d.batch_no or ''] diff --git a/startup/schedule_handlers.py b/startup/schedule_handlers.py index cc0d1f4fea..99cc05b041 100644 --- a/startup/schedule_handlers.py +++ b/startup/schedule_handlers.py @@ -53,7 +53,7 @@ def execute_daily(): # daily backup from setup.doctype.backup_manager.backup_manager import take_backups_daily - take_backups_daily() + run_fn(take_backups_daily) # check reorder level from stock.utils import reorder_item @@ -61,7 +61,7 @@ def execute_daily(): def execute_weekly(): from setup.doctype.backup_manager.backup_manager import take_backups_weekly - take_backups_weekly() + run_fn(take_backups_weekly) def execute_monthly(): pass diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index c3ce2d7f40..2ab2b1dad8 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -21,6 +21,9 @@ class TestStockEntry(unittest.TestCase): st2.insert() st2.submit() + from stock.utils import reorder_item + reorder_item() + mr_name = webnotes.conn.sql("""select parent from `tabMaterial Request Item` where item_code='_Test Item'""") diff --git a/stock/utils.py b/stock/utils.py index 5e7e53bb01..53ad69b157 100644 --- a/stock/utils.py +++ b/stock/utils.py @@ -19,13 +19,14 @@ from webnotes import msgprint, _ import json from webnotes.utils import flt, cstr, nowdate, add_days, cint from webnotes.defaults import get_global_default +from webnotes.utils.email_lib import sendmail def validate_end_of_life(item_code, end_of_life=None, verbose=1): if not end_of_life: end_of_life = webnotes.conn.get_value("Item", item_code, "end_of_life") from webnotes.utils import getdate, now_datetime, formatdate - if end_of_life and getdate(end_of_life) > now_datetime().date(): + if end_of_life and getdate(end_of_life) <= now_datetime().date(): msg = (_("Item") + " %(item_code)s: " + _("reached its end of life on") + \ " %(date)s. " + _("Please check") + ": %(end_of_life_label)s " + \ "in Item master") % { @@ -205,7 +206,11 @@ def reorder_item(): if webnotes.auto_indent: material_requests = {} bin_list = webnotes.conn.sql("""select item_code, warehouse, projected_qty - from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != ''""", + from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != '' + and exists (select name from `tabItem` + where `tabItem`.name = `tabBin`.item_code and + is_stock_item='Yes' and (is_purchase_item='Yes' or is_sub_contracted_item='Yes') and + (ifnull(end_of_life, '')='') or end_of_life > now())""", as_dict=True) for bin in bin_list: #check if re-order is required @@ -220,7 +225,7 @@ def reorder_item(): ["re_order_level", "re_order_qty"]) material_request_type = "Purchase" - if reorder_level and flt(bin.projected_qty) < flt(reorder_level): + if flt(reorder_level) and flt(bin.projected_qty) < flt(reorder_level): if flt(reorder_level) - flt(bin.projected_qty) > flt(reorder_qty): reorder_qty = flt(reorder_level) - flt(bin.projected_qty) @@ -242,10 +247,14 @@ def create_material_request(material_requests): """ Create indent on reaching reorder level """ mr_list = [] defaults = webnotes.defaults.get_defaults() + exceptions_list = [] for request_type in material_requests: for company in material_requests[request_type]: - items = material_requests[request_type][company] - if items: + try: + items = material_requests[request_type][company] + if not items: + continue + mr = [{ "doctype": "Material Request", "company": company, @@ -257,27 +266,34 @@ def create_material_request(material_requests): quantity reaches re-order level when the following record was created""") }] - for d in items: - item = webnotes.doc("Item", d.item_code) - mr.append({ - "doctype": "Material Request Item", - "parenttype": "Material Request", - "parentfield": "indent_details", - "item_code": d.item_code, - "schedule_date": add_days(nowdate(),cint(item.lead_time_days)), - "uom": item.stock_uom, - "warehouse": d.warehouse, - "item_name": item.item_name, - "description": item.description, - "item_group": item.item_group, - "qty": d.reorder_qty, - "brand": item.brand, - }) + for d in items: + item = webnotes.doc("Item", d.item_code) + mr.append({ + "doctype": "Material Request Item", + "parenttype": "Material Request", + "parentfield": "indent_details", + "item_code": d.item_code, + "schedule_date": add_days(nowdate(),cint(item.lead_time_days)), + "uom": item.stock_uom, + "warehouse": d.warehouse, + "item_name": item.item_name, + "description": item.description, + "item_group": item.item_group, + "qty": d.reorder_qty, + "brand": item.brand, + }) - mr_bean = webnotes.bean(mr) - mr_bean.insert() - mr_bean.submit() - mr_list.append(mr_bean) + mr_bean = webnotes.bean(mr) + mr_bean.insert() + mr_bean.submit() + mr_list.append(mr_bean) + + except: + if webnotes.message_log: + exceptions_list.append([] + webnotes.message_log) + webnotes.message_log = [] + else: + exceptions_list.append(webnotes.getTraceback()) if mr_list: if not hasattr(webnotes, "reorder_email_notify"): @@ -286,11 +302,13 @@ def create_material_request(material_requests): if(webnotes.reorder_email_notify): send_email_notification(mr_list) + + if exceptions_list: + notify_errors(exceptions_list) def send_email_notification(mr_list): """ Notify user about auto creation of indent""" - from webnotes.utils.email_lib import sendmail email_list = webnotes.conn.sql_list("""select distinct r.parent from tabUserRole r, tabProfile p where p.name = r.parent and p.enabled = 1 and p.docstatus < 2 @@ -307,4 +325,22 @@ def send_email_notification(mr_list): cstr(item.qty) + "" + cstr(item.uom) + "" msg += "" - sendmail(email_list, subject='Auto Material Request Generation Notification', msg = msg) \ No newline at end of file + sendmail(email_list, subject='Auto Material Request Generation Notification', msg = msg) + +def notify_errors(exceptions_list): + subject = "[Important] [ERPNext] Error(s) while creating Material Requests based on Re-order Levels" + msg = """Dear System Manager, + + An error occured for certain Items while creating Material Requests based on Re-order level. + + Please rectify these issues: + --- + + %s + + --- + Regards, + Administrator""" % ("\n\n".join(["\n".join(msg) for msg in exceptions_list]),) + + from webnotes.profile import get_system_managers + sendmail(get_system_managers(), subject=subject, msg=msg) From 11d311376eb4b18572a7ae2dedfe9adb617002a5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 17 Jun 2013 12:51:36 +0530 Subject: [PATCH 235/295] [event] [fix] commonified delete_events method, which is executed before auto-creation of events. Fixed delete issue --- .../p04_fix_event_for_lead_oppty_project.py | 5 +++-- projects/doctype/project/project.py | 9 +++------ .../maintenance_schedule/maintenance_schedule.py | 12 +++--------- utilities/transaction_base.py | 5 +++++ 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/patches/june_2013/p04_fix_event_for_lead_oppty_project.py b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py index 3f66d8bfbb..971e4c7459 100644 --- a/patches/june_2013/p04_fix_event_for_lead_oppty_project.py +++ b/patches/june_2013/p04_fix_event_for_lead_oppty_project.py @@ -1,6 +1,8 @@ import webnotes def execute(): + from utilities.transaction_base import delete_events + # delete orphaned Event User webnotes.conn.sql("""delete from `tabEvent User` where not exists(select name from `tabEvent` where `tabEvent`.name = `tabEvent User`.parent)""") @@ -15,5 +17,4 @@ def execute(): webnotes.get_obj(dt, ref_name).add_calendar_event() else: # remove events where ref doc doesn't exist - webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` - where ref_type=%s and ref_name=%s""", (dt, ref_name))) \ No newline at end of file + delete_events(dt, ref_name) \ No newline at end of file diff --git a/projects/doctype/project/project.py b/projects/doctype/project/project.py index 94b6787312..84a216c349 100644 --- a/projects/doctype/project/project.py +++ b/projects/doctype/project/project.py @@ -19,6 +19,7 @@ import webnotes from webnotes.utils import flt, getdate from webnotes import msgprint +from utilities.transaction_base import delete_events class DocType: def __init__(self, doc, doclist=None): @@ -69,7 +70,7 @@ class DocType: def add_calendar_event(self): # delete any earlier event for this project - self.delete_events() + delete_events(self.doc.doctype, self.doc.name) # add events for milestone in self.doclist.get({"parentfield": "project_milestones"}): @@ -87,8 +88,4 @@ class DocType: }).insert() def on_trash(self): - self.delete_events() - - def delete_events(self): - webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` - where ref_type=%s and ref_name=%s""", (self.doc.doctype, self.doc.name))) \ No newline at end of file + delete_events(self.doc.doctype, self.doc.name) \ No newline at end of file diff --git a/support/doctype/maintenance_schedule/maintenance_schedule.py b/support/doctype/maintenance_schedule/maintenance_schedule.py index 06c5a47aff..baed6a9dbd 100644 --- a/support/doctype/maintenance_schedule/maintenance_schedule.py +++ b/support/doctype/maintenance_schedule/maintenance_schedule.py @@ -26,7 +26,7 @@ from webnotes import msgprint sql = webnotes.conn.sql -from utilities.transaction_base import TransactionBase +from utilities.transaction_base import TransactionBase, delete_events class DocType(TransactionBase): def __init__(self, doc, doclist=[]): @@ -327,13 +327,7 @@ class DocType(TransactionBase): if d.serial_no: self.update_amc_date(d.serial_no, '') webnotes.conn.set(self.doc, 'status', 'Cancelled') - self.delete_events() + delete_events(self.doc.doctype, self.doc.name) def on_trash(self): - self.delete_events() - - def delete_events(self): - webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` - where ref_type=%s and ref_name=%s""", (self.doc.doctype, self.doc.name))) - - + delete_events(self.doc.doctype, self.doc.name) diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index f9af912737..03321853a7 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -303,3 +303,8 @@ class TransactionBase(DocListController): }) webnotes.bean(event_doclist).insert() + + +def delete_events(ref_type, ref_name): + webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent` + where ref_type=%s and ref_name=%s""", (ref_type, ref_name)), for_reload=True) \ No newline at end of file From f841a7bc144074d496f0c935524a0d87ce1fc2c0 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Mon, 17 Jun 2013 19:25:48 +0530 Subject: [PATCH 236/295] Completed territory_target_variance(item_group_wise) report --- ...itory_target_variance_(item_group_wise).py | 193 +++++++++++++----- 1 file changed, 145 insertions(+), 48 deletions(-) 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 790c6f02ad..844d4f3e05 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 @@ -17,73 +17,170 @@ from __future__ import unicode_literals import webnotes import calendar -from webnotes import msgprint -from webnotes.utils import cint, cstr, add_months +from webnotes import _, msgprint +from webnotes.utils import cint, cstr, add_months, flt +import time +import calendar def execute(filters=None): if not filters: filters = {} columns = get_columns(filters) + period_month_ranges = get_period_month_ranges(filters) + + target_on = "Quantity" if (filters.get("target_on")=="Quantity") else "Amount" + tim_map = get_territory_item_month_map(filters, target_on) data = [] - - return columns, data + + for territory, territory_items in tim_map.items(): + for item_group, monthwise_data in territory_items.items(): + row = [territory, item_group] + totals = [0, 0, 0] + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + 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)) + period_data[i] += value + totals[i] += value + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + row += totals + data.append(row) + + return columns, sorted(data, key=lambda x: (x[0], x[1])) def get_columns(filters): - """return columns based on filters""" - - if not filters.get("period"): - msgprint("Please select the Period", raise_exception=1) + for fieldname in ["fiscal_year", "period", "target_on"]: + if not filters.get(fieldname): + label = (" ".join(fieldname.split("_"))).title() + msgprint(_("Please specify") + ": " + label, + raise_exception=True) - mo = cint(cstr(webnotes.conn.get_value("Fiscal Year", filters["fiscal_year"], "year_start_date")).split("-")[1]) - period_months = [] - if (filters["period"] == "Monthly" or "Yearly"): - for x in range(0,12): - period_months.append(mo) - if (mo!=12): - mo += 1 + columns = ["Territory:Link/Territory:80", "Item Group:Link/Item Group:80"] + + group_months = False if filters["period"] == "Monthly" else True + + for from_date, to_date in get_period_date_ranges(filters): + for label in ["Target (%s)", "Achieved (%s)", "Variance (%s)"]: + if group_months: + columns.append(label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))) else: - mo = 1 + columns.append(label % from_date.strftime("%b")) - columns = ["Territory:Link/Territory:80"] + ["Item Group:Link/Item Group:80"] + return columns + ["Total Target::80", "Total Achieved::80", "Total Variance::80"] - period = [] +def get_period_date_ranges(filters): + from dateutil.relativedelta import relativedelta - if (filters["period"] == "Monthly" or "Yearly"): - for i in (0,12): - period.append("Target (" + "i" + ")::80") - period.append("Achieved (" + "i" + ")::80") - period.append("Variance (" + "i" + ")::80") + year_start_date, year_end_date = get_year_start_end_date(filters) - columns = columns + [(p) for p in period] + \ - ["Total Target::80"] + ["Total Achieved::80"] + ["Total Variance::80"] + increment = { + "Monthly": 1, + "Quarterly": 3, + "Half-Yearly": 6, + "Yearly": 12 + }.get(filters["period"]) - return columns + period_date_ranges = [] + for i in xrange(1, 13, increment): + period_end_date = year_start_date + relativedelta(months=increment, + days=-1) + period_date_ranges.append([year_start_date, period_end_date]) + year_start_date = period_end_date + relativedelta(days=1) -def get_conditions(filters): - conditions = "" - - if filters.get("fiscal_year"): - conditions += " and posting_date <= '%s'" % filters["fiscal_year"] - else: - webnotes.msgprint("Please enter Fiscal Year", raise_exception=1) - - if filters.get("target_on"): - conditions += " and posting_date <= '%s'" % filters["target_on"] - else: - webnotes.msgprint("Please select Target On", raise_exception=1) + return period_date_ranges - return conditions +def get_period_month_ranges(filters): + from dateutil.relativedelta import relativedelta + period_month_ranges = [] + + for start_date, end_date in get_period_date_ranges(filters): + months_in_this_period = [] + while start_date <= end_date: + months_in_this_period.append(start_date.strftime("%B")) + start_date += relativedelta(months=1) + period_month_ranges.append(months_in_this_period) + + return period_month_ranges -#get territory details +#Get territory & item group details def get_territory_details(filters): - conditions = get_conditions(filters) - return webnotes.conn.sql("""select item_code, batch_no, warehouse, - posting_date, actual_qty - from `tabStock Ledger Entry` - where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % - conditions, as_dict=1) + 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""" % + ('%s'), (filters.get("fiscal_year")), as_dict=1) -def get_month_abbr(month_number): - return 0 \ No newline at end of file +#Get target distribution details of item group +def get_target_distribution_details(filters): + target_details = {} + abc = [] + 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 """ % ('%s'), (filters.get("fiscal_year")), as_dict=1): + target_details.setdefault(d.month, d) + + return target_details + +#Get achieved details from sales order +def get_achieved_details(filters): + start_date, end_date = get_year_start_end_date(filters) + achieved_details = {} + + for d in webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, \ + MONTHNAME(so.transaction_date) as month_name \ + from `tabSales Order Item` soi, `tabSales Order` so \ + where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and \ + so.transaction_date<=%s""" % ('%s', '%s'), \ + (start_date, end_date), as_dict=1): + achieved_details.setdefault(d.month_name, d) + + return achieved_details + +def get_territory_item_month_map(filters, target_on): + territory_details = get_territory_details(filters) + tdd = get_target_distribution_details(filters) + achieved_details = get_achieved_details(filters) + + ti_map = {} + + for td in territory_details: + for month in tdd: + ti_map.setdefault(td.name, {}).setdefault(td.item_group, {})\ + .setdefault(month, webnotes._dict({ + "target": 0.0, "achieved": 0.0, "variance": 0.0 + })) + + tav_dict = ti_map[td.name][td.item_group][month] + + for ad in achieved_details: + if (target_on == "Quantity"): + tav_dict.target = td.target_qty*(tdd[month]["percentage_allocation"]/100) + if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: + tav_dict.achieved += achieved_details[month]["qty"] + + if (target_on == "Amount"): + tav_dict.target = td.target_amount*(tdd[month]["percentage_allocation"]/100) + if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: + tav_dict.achieved += achieved_details[month]["amount"] + + return ti_map + +def get_year_start_end_date(filters): + return webnotes.conn.sql("""select year_start_date, + subdate(adddate(year_start_date, interval 1 year), interval 1 day) + as year_end_date + from `tabFiscal Year` + where name=%s""", filters["fiscal_year"])[0] + +def get_item_group(item_name): + """Get Item Group of an item""" + + return webnotes.conn.sql_list("select item_group from `tabItem` where name=%s""" % + ('%s'), (item_name)) \ No newline at end of file From 24208f5c4a985d1a762251d166808c4f7e85ee89 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 18 Jun 2013 15:45:08 +0530 Subject: [PATCH 237/295] [Report][Item-Wise Purchase Receipt] --- buying/page/buying_home/buying_home.js | 4 ++++ .../item_wise_last_purchase_rate/__init__.py | 0 .../item_wise_last_purchase_rate.txt | 22 +++++++++++++++++++ controllers/trends.py | 6 ++--- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 buying/report/item_wise_last_purchase_rate/__init__.py create mode 100644 buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js index dfcd71e17d..0e078fefc9 100644 --- a/buying/page/buying_home/buying_home.js +++ b/buying/page/buying_home/buying_home.js @@ -120,6 +120,10 @@ wn.module_page["Buying"] = [ route: "query-report/Purchase Order Trends", doctype: "Purchase Order" }, + { + "label":wn._("Item-wise Last Purchase Rate"), + route: "query-report/Item-wise Last Purchase Rate", + } ] } ] diff --git a/buying/report/item_wise_last_purchase_rate/__init__.py b/buying/report/item_wise_last_purchase_rate/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt b/buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt new file mode 100644 index 0000000000..db99e724ce --- /dev/null +++ b/buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-06-18 11:24:36", + "docstatus": 0, + "modified": "2013-06-18 15:28:57", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select * from (select \n result.item_code as \"Item Code:Link/Item:120\",\n result.item_name as \"Item Name::120\",\n result.description as \"Description::150\",\n result.posting_date as \"Date::150\",\n result.purchase_ref_rate as \"Price List Rate::180\", \n result.discount_rate as \"Discount::120\", \n result.purchase_rate as \"Rate::120\"\nfrom (\n (select \n po_item.item_code,\n po_item.item_name,\n po_item.description,\n po.transaction_date as posting_date,\n po_item.purchase_ref_rate, \n po_item.discount_rate, \n po_item.purchase_rate\n from `tabPurchase Order` po, `tabPurchase Order Item` po_item\n where po.name = po_item.parent and po.docstatus = 1)\n union\n (select \n pr_item.item_code,\n pr_item.item_name,\n pr_item.description,\n pr.posting_date,\n pr_item.purchase_ref_rate,\n pr_item.discount_rate,\n pr_item.purchase_rate\n from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item\n where pr.name = pr_item.parent and pr.docstatus = 1)\n) result\norder by result.item_code asc, result.posting_date desc) result_wrapper\ngroup by `Item Code:Link/Item:120`\n", + "ref_doctype": "Purchase Order", + "report_name": "Item-wise Last Purchase Rate", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Item-wise Last Purchase Rate" + } +] \ No newline at end of file diff --git a/controllers/trends.py b/controllers/trends.py index dee9ec8b4f..8905591193 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -83,7 +83,7 @@ def get_data(filters, tab, details): #to get distinct value of col specified by group_by in filter row = webnotes.conn.sql("""select DISTINCT(%s) from `%s` t1, `%s` t2 %s where t2.parent = t1.name and t1.company = %s - and t1.fiscal_year = %s and t1.docstatus = 1 + and t1.fiscal_year = %s and t1.docstatus = 1 and %s = %s """%(sel_col, tab[0], tab[1], details["sup_tab"], "%s", "%s", details["basedon"], "%s"), @@ -224,13 +224,13 @@ def basedon_wise_colums_query(based_on, trans): bon = ["Supplier:Link/Supplier:120", "Supplier Type:Link/Supplier Type:120"] query_details = "t1.supplier, t3.supplier_type," basedon = 't1.supplier' - sup_tab = ',`tabSupplier` t3' + sup_tab = '`tabSupplier` t3', elif based_on == 'Supplier Type': bon = ["Supplier Type:Link/Supplier Type:120"] query_details = "t3.supplier_type," basedon = 't3.supplier_type' - sup_tab = ',`tabSupplier` t3' + sup_tab ='`tabSupplier` t3', elif based_on == "Territory": bon = ["Territory:Link/Territory:120"] From 3c6c3cf5ee902124f3a7e576a4865b206d4749c4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 18 Jun 2013 18:26:30 +0530 Subject: [PATCH 238/295] [sales order] [shipping address] allow to edit shipping address --- selling/doctype/sales_order/sales_order.js | 23 ++++++ selling/doctype/sales_order/sales_order.txt | 78 ++------------------- 2 files changed, 27 insertions(+), 74 deletions(-) diff --git a/selling/doctype/sales_order/sales_order.js b/selling/doctype/sales_order/sales_order.js index b792754384..f272b2e378 100644 --- a/selling/doctype/sales_order/sales_order.js +++ b/selling/doctype/sales_order/sales_order.js @@ -130,6 +130,29 @@ cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); } +cur_frm.fields_dict.shipping_address_name.get_query = cur_frm.fields_dict['customer_address'].get_query; + +cur_frm.cscript.shipping_address_name = function() { + if(cur_frm.doc.shipping_address_name) { + wn.model.with_doc("Address", cur_frm.doc.shipping_address_name, function(name) { + var address = wn.model.get_doc("Address", name); + + var out = $.map(["address_line1", "address_line2", "city"], + function(f) { return address[f]; }); + + var state_pincode = $.map(["state", "pincode"], function(f) { return address[f]; }).join(" "); + if(state_pincode) out.push(state_pincode); + + if(address["country"]) out.push(address["country"]); + + out.concat($.map([["Phone:", address["phone"]], ["Fax:", address["fax"]]], + function(val) { return val[1] ? val.join(" ") : null; })); + + cur_frm.set_value("shipping_address", out.join("\n")); + }); + } +}; + cur_frm.cscript.pull_quotation_details = function(doc,dt,dn) { var callback = function(r,rt){ var doc = locals[cur_frm.doctype][cur_frm.docname]; diff --git a/selling/doctype/sales_order/sales_order.txt b/selling/doctype/sales_order/sales_order.txt index ba0b1de07c..a8989701e7 100644 --- a/selling/doctype/sales_order/sales_order.txt +++ b/selling/doctype/sales_order/sales_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 14:48:34", + "creation": "2013-06-18 12:39:59", "docstatus": 0, - "modified": "2013-01-29 17:14:58", + "modified": "2013-06-18 17:49:11", "modified_by": "Administrator", "owner": "Administrator" }, @@ -32,6 +32,7 @@ "parent": "Sales Order", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1 }, { @@ -202,7 +203,7 @@ "label": "Shipping Address Name", "options": "Address", "print_hide": 1, - "read_only": 1 + "read_only": 0 }, { "doctype": "DocField", @@ -950,104 +951,33 @@ "options": "Sales Team", "print_hide": 1 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Sales Manager", - "submit": 0, - "write": 0 - }, { "amend": 1, "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Sales Manager", - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "match": "", - "permlevel": 0, "report": 1, "role": "Sales User", "submit": 1, "write": 1 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Sales User", - "submit": 0, - "write": 0 - }, { "amend": 1, "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Maintenance Manager", - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "role": "Maintenance Manager", - "submit": 0 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, "report": 1, "role": "Maintenance User", "submit": 1, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "role": "Maintenance User", - "submit": 0 - }, - { - "doctype": "DocPerm", - "permlevel": 0, "role": "Accounts User" }, { "doctype": "DocPerm", "match": "customer", - "permlevel": 0, "role": "Customer" } ] \ No newline at end of file From 4ddc2618e8611f39665cddb49a6d93e646a80317 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 18 Jun 2013 18:46:02 +0530 Subject: [PATCH 239/295] [Report] Completed Sales Person Target Variance (Item-Group wise) --- accounts/page/accounts_home/accounts_home.js | 5 + .../report/budget_variance_report/__init__.py | 0 .../budget_variance_report.js | 25 +++ .../budget_variance_report.py | 184 ++++++++++++++++++ .../budget_variance_report.txt | 21 ++ selling/page/selling_home/selling_home.js | 5 + .../__init__.py | 0 ...erson_target_variance_(item_group_wise).js | 25 +++ ...erson_target_variance_(item_group_wise).py | 184 ++++++++++++++++++ ...rson_target_variance_(item_group_wise).txt | 21 ++ ...itory_target_variance_(item_group_wise).py | 26 ++- 11 files changed, 482 insertions(+), 14 deletions(-) create mode 100644 accounts/report/budget_variance_report/__init__.py create mode 100644 accounts/report/budget_variance_report/budget_variance_report.js create mode 100644 accounts/report/budget_variance_report/budget_variance_report.py create mode 100644 accounts/report/budget_variance_report/budget_variance_report.txt create mode 100644 selling/report/sales_person_target_variance_(item_group_wise)/__init__.py create mode 100644 selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js create mode 100644 selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).py create mode 100644 selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index b920bfdbab..b4846746a4 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -252,6 +252,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Item-wise Purchase Register", doctype: "Purchase Invoice" }, + { + "label":wn._("Budget Variance Report"), + route: "query-report/Budget Variance Report", + doctype: "Cost Center" + }, ] } ] diff --git a/accounts/report/budget_variance_report/__init__.py b/accounts/report/budget_variance_report/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/budget_variance_report/budget_variance_report.js b/accounts/report/budget_variance_report/budget_variance_report.js new file mode 100644 index 0000000000..a0516050ce --- /dev/null +++ b/accounts/report/budget_variance_report/budget_variance_report.js @@ -0,0 +1,25 @@ +wn.query_reports["Budget Variance Report"] = { + "filters": [ + { + fieldname: "fiscal_year", + label: "Fiscal Year", + fieldtype: "Link", + options: "Fiscal Year", + default: sys_defaults.fiscal_year + }, + { + fieldname: "period", + label: "Period", + fieldtype: "Select", + options: "Monthly\nQuarterly\nHalf-Yearly\nYearly", + default: "Monthly" + }, + { + fieldname: "company", + label: "Company", + fieldtype: "Link", + options: "Company", + default: sys_defaults.company + }, + ] +} \ No newline at end of file diff --git a/accounts/report/budget_variance_report/budget_variance_report.py b/accounts/report/budget_variance_report/budget_variance_report.py new file mode 100644 index 0000000000..ac38d9f34d --- /dev/null +++ b/accounts/report/budget_variance_report/budget_variance_report.py @@ -0,0 +1,184 @@ +# 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 +import calendar +from webnotes import _, msgprint +from webnotes.utils import flt +import time + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + period_month_ranges = get_period_month_ranges(filters) + # tim_map = get_territory_item_month_map(filters) + + data = [] + + # for territory, territory_items in tim_map.items(): + # for item_group, monthwise_data in territory_items.items(): + # row = [territory, item_group] + # totals = [0, 0, 0] + # for relevant_months in period_month_ranges: + # period_data = [0, 0, 0] + # 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)) + # period_data[i] += value + # totals[i] += value + # period_data[2] = period_data[0] - period_data[1] + # row += period_data + # totals[2] = totals[0] - totals[1] + # row += totals + # data.append(row) + + return columns, sorted(data, key=lambda x: (x[0], x[1])) + +def get_columns(filters): + for fieldname in ["fiscal_year", "period", "company"]: + if not filters.get(fieldname): + label = (" ".join(fieldname.split("_"))).title() + msgprint(_("Please specify") + ": " + label, + raise_exception=True) + + columns = ["Cost Center:Link/Cost Center:80"] + + group_months = False if filters["period"] == "Monthly" else True + + for from_date, to_date in get_period_date_ranges(filters): + for label in ["Target (%s)", "Actual (%s)", "Variance (%s)"]: + if group_months: + columns.append(label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))) + else: + columns.append(label % from_date.strftime("%b")) + + return columns + ["Total Target::80", "Total Actual::80", "Total Variance::80"] + +def get_period_date_ranges(filters): + from dateutil.relativedelta import relativedelta + + year_start_date, year_end_date = get_year_start_end_date(filters) + + increment = { + "Monthly": 1, + "Quarterly": 3, + "Half-Yearly": 6, + "Yearly": 12 + }.get(filters["period"]) + + period_date_ranges = [] + for i in xrange(1, 13, increment): + period_end_date = year_start_date + relativedelta(months=increment, + days=-1) + period_date_ranges.append([year_start_date, period_end_date]) + year_start_date = period_end_date + relativedelta(days=1) + + return period_date_ranges + +def get_period_month_ranges(filters): + from dateutil.relativedelta import relativedelta + period_month_ranges = [] + + for start_date, end_date in get_period_date_ranges(filters): + months_in_this_period = [] + while start_date <= end_date: + months_in_this_period.append(start_date.strftime("%B")) + start_date += relativedelta(months=1) + period_month_ranges.append(months_in_this_period) + + return period_month_ranges + + +#Get cost center details +def get_costcenter_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""" % + ('%s'), (filters.get("fiscal_year")), as_dict=1) + +#Get target distribution details of item group +def get_target_distribution_details(filters): + target_details = {} + abc = [] + 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 """ % ('%s'), (filters.get("fiscal_year")), as_dict=1): + target_details.setdefault(d.month, d) + + return target_details + +#Get achieved details from sales order +def get_achieved_details(filters): + start_date, end_date = get_year_start_end_date(filters) + achieved_details = {} + + for d in webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, \ + MONTHNAME(so.transaction_date) as month_name \ + from `tabSales Order Item` soi, `tabSales Order` so \ + where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and \ + so.transaction_date<=%s""" % ('%s', '%s'), \ + (start_date, end_date), as_dict=1): + achieved_details.setdefault(d.month_name, d) + + return achieved_details + +def get_territory_item_month_map(filters): + territory_details = get_territory_details(filters) + tdd = get_target_distribution_details(filters) + achieved_details = get_achieved_details(filters) + + tim_map = {} + + for td in territory_details: + for month in tdd: + tim_map.setdefault(td.name, {}).setdefault(td.item_group, {})\ + .setdefault(month, webnotes._dict({ + "target": 0.0, "achieved": 0.0, "variance": 0.0 + })) + + tav_dict = tim_map[td.name][td.item_group][month] + + for ad in achieved_details: + if (filters["target_on"] == "Quantity"): + tav_dict.target = td.target_qty*(tdd[month]["percentage_allocation"]/100) + if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: + tav_dict.achieved += achieved_details[month]["qty"] + + if (filters["target_on"] == "Amount"): + tav_dict.target = td.target_amount*(tdd[month]["percentage_allocation"]/100) + if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: + tav_dict.achieved += achieved_details[month]["amount"] + + return tim_map + +def get_year_start_end_date(filters): + return webnotes.conn.sql("""select year_start_date, + subdate(adddate(year_start_date, interval 1 year), interval 1 day) + as year_end_date + from `tabFiscal Year` + where name=%s""", filters["fiscal_year"])[0] + +def get_item_group(item_name): + """Get Item Group of an item""" + + return webnotes.conn.sql_list("select item_group from `tabItem` where name=%s""" % + ('%s'), (item_name)) \ No newline at end of file diff --git a/accounts/report/budget_variance_report/budget_variance_report.txt b/accounts/report/budget_variance_report/budget_variance_report.txt new file mode 100644 index 0000000000..b89cb4545f --- /dev/null +++ b/accounts/report/budget_variance_report/budget_variance_report.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-18 12:56:36", + "docstatus": 0, + "modified": "2013-06-18 12:56:36", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Cost Center", + "report_name": "Budget Variance Report", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Budget Variance Report" + } +] \ No newline at end of file diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index e3663c93e4..0792f0a8e6 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -170,6 +170,11 @@ wn.module_page["Selling"] = [ route: "query-report/Territory Target Variance (Item Group-Wise)", doctype: "Sales Order" }, + { + "label":wn._("Sales Person Target Variance (Item Group-Wise)"), + route: "query-report/Sales Person Target Variance (Item Group-Wise)", + doctype: "Sales Order" + }, { "label":wn._("Customers Not Buying Since Long Time"), route: "query-report/Customers Not Buying Since Long Time", diff --git a/selling/report/sales_person_target_variance_(item_group_wise)/__init__.py b/selling/report/sales_person_target_variance_(item_group_wise)/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js b/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js new file mode 100644 index 0000000000..09f0d55aed --- /dev/null +++ b/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js @@ -0,0 +1,25 @@ +wn.query_reports["Sales Person Target Variance (Item Group-Wise)"] = { + "filters": [ + { + fieldname: "fiscal_year", + label: "Fiscal Year", + fieldtype: "Link", + options: "Fiscal Year", + default: sys_defaults.fiscal_year + }, + { + fieldname: "period", + label: "Period", + fieldtype: "Select", + options: "Monthly\nQuarterly\nHalf-Yearly\nYearly", + default: "Monthly" + }, + { + fieldname: "target_on", + label: "Target On", + fieldtype: "Select", + options: "Quantity\nAmount", + default: "Quantity" + }, + ] +} \ No newline at end of file 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 new file mode 100644 index 0000000000..3163829131 --- /dev/null +++ b/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).py @@ -0,0 +1,184 @@ +# 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 +import calendar +from webnotes import _, msgprint +from webnotes.utils import flt +import time + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns(filters) + period_month_ranges = get_period_month_ranges(filters) + sim_map = get_salesperson_item_month_map(filters) + + data = [] + + for salesperson, salesperson_items in sim_map.items(): + for item_group, monthwise_data in salesperson_items.items(): + row = [salesperson, item_group] + totals = [0, 0, 0] + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + 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)) + period_data[i] += value + totals[i] += value + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + row += totals + data.append(row) + + return columns, sorted(data, key=lambda x: (x[0], x[1])) + +def get_columns(filters): + for fieldname in ["fiscal_year", "period", "target_on"]: + if not filters.get(fieldname): + label = (" ".join(fieldname.split("_"))).title() + msgprint(_("Please specify") + ": " + label, + raise_exception=True) + + columns = ["Sales Person:Link/Sales Person:80", "Item Group:Link/Item Group:80"] + + group_months = False if filters["period"] == "Monthly" else True + + for from_date, to_date in get_period_date_ranges(filters): + for label in ["Target (%s)", "Achieved (%s)", "Variance (%s)"]: + if group_months: + columns.append(label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))) + else: + columns.append(label % from_date.strftime("%b")) + + return columns + ["Total Target::80", "Total Achieved::80", "Total Variance::80"] + +def get_period_date_ranges(filters): + from dateutil.relativedelta import relativedelta + + year_start_date, year_end_date = get_year_start_end_date(filters) + + increment = { + "Monthly": 1, + "Quarterly": 3, + "Half-Yearly": 6, + "Yearly": 12 + }.get(filters["period"]) + + period_date_ranges = [] + for i in xrange(1, 13, increment): + period_end_date = year_start_date + relativedelta(months=increment, + days=-1) + period_date_ranges.append([year_start_date, period_end_date]) + year_start_date = period_end_date + relativedelta(days=1) + + return period_date_ranges + +def get_period_month_ranges(filters): + from dateutil.relativedelta import relativedelta + period_month_ranges = [] + + for start_date, end_date in get_period_date_ranges(filters): + months_in_this_period = [] + while start_date <= end_date: + months_in_this_period.append(start_date.strftime("%B")) + start_date += relativedelta(months=1) + period_month_ranges.append(months_in_this_period) + + return period_month_ranges + + +#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""" % + ('%s'), (filters.get("fiscal_year")), as_dict=1) + +#Get target distribution details of item group +def get_target_distribution_details(filters): + target_details = {} + abc = [] + 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 """ % ('%s'), (filters.get("fiscal_year")), as_dict=1): + target_details.setdefault(d.month, d) + + return target_details + +#Get achieved details from sales order +def get_achieved_details(filters): + start_date, end_date = get_year_start_end_date(filters) + achieved_details = {} + + for d in webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, \ + MONTHNAME(so.transaction_date) as month_name \ + from `tabSales Order Item` soi, `tabSales Order` so \ + where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and \ + so.transaction_date<=%s""" % ('%s', '%s'), \ + (start_date, end_date), as_dict=1): + achieved_details.setdefault(d.month_name, d) + + return achieved_details + +def get_salesperson_item_month_map(filters): + 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: + sim_map.setdefault(sd.name, {}).setdefault(sd.item_group, {})\ + .setdefault(month, webnotes._dict({ + "target": 0.0, "achieved": 0.0, "variance": 0.0 + })) + + tav_dict = sim_map[sd.name][sd.item_group][month] + + for ad in achieved_details: + if (filters["target_on"] == "Quantity"): + tav_dict.target = sd.target_qty*(tdd[month]["percentage_allocation"]/100) + if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == sd.item_group: + tav_dict.achieved += achieved_details[month]["qty"] + + if (filters["target_on"] == "Amount"): + tav_dict.target = sd.target_amount*(tdd[month]["percentage_allocation"]/100) + if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == sd.item_group: + tav_dict.achieved += achieved_details[month]["amount"] + + return sim_map + +def get_year_start_end_date(filters): + return webnotes.conn.sql("""select year_start_date, + subdate(adddate(year_start_date, interval 1 year), interval 1 day) + as year_end_date + from `tabFiscal Year` + where name=%s""", filters["fiscal_year"])[0] + +def get_item_group(item_name): + """Get Item Group of an item""" + + return webnotes.conn.sql_list("select item_group from `tabItem` where name=%s""" % + ('%s'), (item_name)) \ No newline at end of file diff --git a/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt b/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt new file mode 100644 index 0000000000..955cdec477 --- /dev/null +++ b/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-18 12:09:40", + "docstatus": 0, + "modified": "2013-06-18 12:09:40", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Order", + "report_name": "Sales Person Target Variance (Item Group-Wise)", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Sales Person Target Variance (Item Group-Wise)" + } +] \ No newline at end of file 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 844d4f3e05..fe5e628c65 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 @@ -18,18 +18,15 @@ from __future__ import unicode_literals import webnotes import calendar from webnotes import _, msgprint -from webnotes.utils import cint, cstr, add_months, flt +from webnotes.utils import flt import time -import calendar def execute(filters=None): if not filters: filters = {} columns = get_columns(filters) period_month_ranges = get_period_month_ranges(filters) - - target_on = "Quantity" if (filters.get("target_on")=="Quantity") else "Amount" - tim_map = get_territory_item_month_map(filters, target_on) + tim_map = get_territory_item_month_map(filters) data = [] @@ -110,8 +107,9 @@ def get_period_month_ranges(filters): #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 + 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""" % ('%s'), (filters.get("fiscal_year")), as_dict=1) @@ -143,34 +141,34 @@ def get_achieved_details(filters): return achieved_details -def get_territory_item_month_map(filters, target_on): +def get_territory_item_month_map(filters): territory_details = get_territory_details(filters) tdd = get_target_distribution_details(filters) achieved_details = get_achieved_details(filters) - ti_map = {} + tim_map = {} for td in territory_details: for month in tdd: - ti_map.setdefault(td.name, {}).setdefault(td.item_group, {})\ + tim_map.setdefault(td.name, {}).setdefault(td.item_group, {})\ .setdefault(month, webnotes._dict({ "target": 0.0, "achieved": 0.0, "variance": 0.0 })) - tav_dict = ti_map[td.name][td.item_group][month] + tav_dict = tim_map[td.name][td.item_group][month] for ad in achieved_details: - if (target_on == "Quantity"): + if (filters["target_on"] == "Quantity"): tav_dict.target = td.target_qty*(tdd[month]["percentage_allocation"]/100) if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: tav_dict.achieved += achieved_details[month]["qty"] - if (target_on == "Amount"): + if (filters["target_on"] == "Amount"): tav_dict.target = td.target_amount*(tdd[month]["percentage_allocation"]/100) if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: tav_dict.achieved += achieved_details[month]["amount"] - return ti_map + return tim_map def get_year_start_end_date(filters): return webnotes.conn.sql("""select year_start_date, From 5b6d03e479603275684169f8f14c337fa811623e Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 19 Jun 2013 12:34:22 +0530 Subject: [PATCH 240/295] [Report][Trend Analyzer] --- controllers/trends.py | 75 ++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/controllers/trends.py b/controllers/trends.py index 8905591193..b00afe8d90 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -66,13 +66,13 @@ def get_data(filters, tab, details): inc = 1 data1 = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 %s - where t2.parent = t1.name and t1.company = %s - and t1.fiscal_year = %s and t1.docstatus = 1 %s - group by %s - """ % (query_details, tab[0], tab[1], details["sup_tab"], "%s", - "%s", cond, details["basedon"]), (filters.get("company"), - filters["fiscal_year"]), - as_list=1) + where t2.parent = t1.name and t1.company = %s + and t1.fiscal_year = %s and t1.docstatus = 1 %s + group by %s + """ % (query_details, tab[0], tab[1], details["sup_tab"], "%s", + "%s", cond, details["basedon"]), (filters.get("company"), + filters["fiscal_year"]), + as_list=1) for d in range(len(data1)): #to add blanck column @@ -82,28 +82,23 @@ def get_data(filters, tab, details): #to get distinct value of col specified by group_by in filter row = webnotes.conn.sql("""select DISTINCT(%s) from `%s` t1, `%s` t2 %s - where t2.parent = t1.name and t1.company = %s - and t1.fiscal_year = %s and t1.docstatus = 1 - and %s = %s - """%(sel_col, tab[0], tab[1], details["sup_tab"], "%s", - "%s", details["basedon"], "%s"), - (filters.get("company"), filters.get("fiscal_year"), - data1[d][0]), - as_list=1) + where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s + and t1.docstatus = 1 and %s = %s + """%(sel_col, tab[0], tab[1], details["sup_tab"], "%s", "%s", details["basedon"], "%s"), + (filters.get("company"), filters.get("fiscal_year"), data1[d][0]), + as_list=1) for i in range(len(row)): des = ['' for q in range(len(details["columns"]))] #get data for each group_by filter row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 %s - where t2.parent = t1.name and t1.company = %s - and t1.fiscal_year = %s and t1.docstatus = 1 - and %s = %s and %s = %s - """%(sel_col, details["query_pwc"], tab[0], tab[1], details["sup_tab"], - "%s", "%s", sel_col, "%s", details["basedon"], "%s"), - (filters.get("company"), filters.get("fiscal_year"), row[i][0], - data1[d][0]), - as_list=1) + where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s + and t1.docstatus = 1 and %s = %s and %s = %s + """%(sel_col, details["query_pwc"], tab[0], tab[1], details["sup_tab"], + "%s", "%s", sel_col, "%s", details["basedon"], "%s"), + (filters.get("company"), filters.get("fiscal_year"), row[i][0], data1[d][0]), + as_list=1) des[ind] = row[i] for j in range(1,len(details["columns"])-inc): @@ -112,13 +107,13 @@ def get_data(filters, tab, details): else: data = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 %s - where t2.parent = t1.name and t1.company = %s - and t1.fiscal_year = %s and t1.docstatus = 1 - %s group by %s - """%(query_details, tab[0], tab[1], details["sup_tab"], "%s", - "%s", cond,details["basedon"]), (filters.get("company"), - filters.get("fiscal_year")), - as_list=1) + where t2.parent = t1.name and t1.company = %s + and t1.fiscal_year = %s and t1.docstatus = 1 %s + group by %s + """%(query_details, tab[0], tab[1], details["sup_tab"], "%s", + "%s", cond,details["basedon"]), (filters.get("company"), + filters.get("fiscal_year")), + as_list=1) return data @@ -146,8 +141,8 @@ def period_wise_colums_query(filters, trans): pwc.append(month_name[month]+' (Amt):Currency:120') query_details += """ - Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), - SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + Sum(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t2.qty, NULL)), + SUM(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t1.grand_total, NULL)), """%{"trans": trans_date,"mon_num": cstr(month+1)} for month in range(0, start_month-1): @@ -155,8 +150,8 @@ def period_wise_colums_query(filters, trans): pwc.append(month_name[month]+' (Amt):Currency:120') query_details += """ - Sum(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t2.qty ELSE NULL END), - SUM(CASE WHEN MONTH(t1.%(trans)s)= %(mon_num)s THEN t1.grand_total ELSE NULL END), + Sum(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t2.qty, NULL)), + SUM(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t1.grand_total, NULL)), """%{"trans": trans_date, "mon_num": cstr(month+1)} elif filters.get("period") == "Quarterly": @@ -169,8 +164,8 @@ def period_wise_colums_query(filters, trans): for d in bet_dates: query_details += """ - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s' THEN t1.grand_total ELSE NULL END), + SUM(IF(t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s', t2.qty, NULL)), + SUM(IF(t1.%(trans)s BETWEEN '%(sd)s' AND '%(ed)s', t1.grand_total, NULL)), """%{"trans": trans_date, "sd": d[0],"ed": d[1]} elif filters.get("period") == "Half-yearly": @@ -183,10 +178,10 @@ def period_wise_colums_query(filters, trans): second_half_end = add_days(add_months(second_half_start,6),-1) query_details = """ - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s' THEN t1.grand_total ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t2.qty ELSE NULL END), - SUM(CASE WHEN t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s' THEN t1.grand_total ELSE NULL END), + SUM(IF(t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s', t2.qty, NULL)), + SUM(IF(t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s', t1.grand_total, NULL)), + SUM(IF(t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s', t2.qty, NULL)), + SUM(IF(t1.%(trans)s BETWEEN '%(shs)s' AND '%(she)s', t1.grand_total, NULL)), """%{"trans": trans_date, "fhs": first_half_start, "fhe": first_half_end,"shs": second_half_start, "she": second_half_end} From bfe9fe7c3b0481e20ae3028b3e69c4b658662a42 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 19 Jun 2013 12:41:28 +0530 Subject: [PATCH 241/295] [Report][Spliting of Trend Analyzer Completed] --- controllers/trends.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/controllers/trends.py b/controllers/trends.py index b00afe8d90..76db44705f 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -27,15 +27,15 @@ def get_columns(filters, trans): webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'", raise_exception=1) else: - bon, query_bon, basedon, sup_tab = basedon_wise_colums_query(filters.get("based_on"), trans) + bonc, query_bon, based, sup_tab = basedon_wise_colums_query(filters.get("based_on"), trans) pwc, query_pwc = period_wise_colums_query(filters, trans) grbc = group_wise_column(filters.get("group_by")) - columns = bon + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] + columns = bonc + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] if grbc: - columns = bon + grbc + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] + columns = bonc + grbc + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - details = {"query_bon": query_bon, "query_pwc": query_pwc, "columns": columns, "basedon": basedon, + details = {"query_bon": query_bon, "query_pwc": query_pwc, "columns": columns, "basedon": based, "grbc": grbc, "sup_tab": sup_tab} return details @@ -121,9 +121,8 @@ def period_wise_colums_query(filters, trans): query_details = '' pwc = [] - ysd = webnotes.conn.sql("""select year_start_date from `tabFiscal Year` - where name = '%s' - """%filters.get("fiscal_year"))[0][0] + ysd = webnotes.conn.sql("""select year_start_date from `tabFiscal Year` where name = '%s' + """%filters.get("fiscal_year"))[0][0] year_start_date = ysd.strftime('%Y-%m-%d') start_month = cint(year_start_date.split('-')[1]) @@ -198,56 +197,56 @@ def basedon_wise_colums_query(based_on, trans): if based_on == "Item": bon = ["Item:Link/Item:120", "Item Name:Data:120"] query_details = "t2.item_code, t2.item_name," - basedon = 't2.item_code' + based = 't2.item_code' elif based_on == "Item Group": bon = ["Item Group:Link/Item Group:120"] query_details = "t2.item_group," - basedon = 't2.item_group' + based = 't2.item_group' elif based_on == "Customer": bon = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"] query_details = "t1.customer_name, t1.territory, " - basedon = 't1.customer_name' + based = 't1.customer_name' elif based_on == "Customer Group": bon = ["Customer Group:Link/Customer Group"] query_details = "t1.customer_group," - basedon = 't1.customer_group' + based = 't1.customer_group' elif based_on == 'Supplier': bon = ["Supplier:Link/Supplier:120", "Supplier Type:Link/Supplier Type:120"] query_details = "t1.supplier, t3.supplier_type," - basedon = 't1.supplier' + based = 't1.supplier' sup_tab = '`tabSupplier` t3', elif based_on == 'Supplier Type': bon = ["Supplier Type:Link/Supplier Type:120"] query_details = "t3.supplier_type," - basedon = 't3.supplier_type' + based = 't3.supplier_type' sup_tab ='`tabSupplier` t3', elif based_on == "Territory": bon = ["Territory:Link/Territory:120"] query_details = "t1.territory," - basedon = 't1.territory' + based = 't1.territory' elif based_on == "Project": if trans in ['Sales Invoice', 'Delivery Note', 'Sales Order']: bon = ["Project:Link/Project:120"] query_details = "t1.project_name," - basedon = 't1.project_name' + based = 't1.project_name' elif trans in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: bon = ["Project:Link/Project:120"] query_details = "t2.project_name," - basedon = 't2.project_name' + based = 't2.project_name' else: webnotes.msgprint("Information Not Available", raise_exception=1) - return bon, query_details, basedon, sup_tab + return bon, query_details, based, sup_tab def group_wise_column(group_by): if group_by: From 21f7623d2ec814f052a2d17226389b5b98288d9c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 19 Jun 2013 14:13:01 +0530 Subject: [PATCH 242/295] [fix][report] in item prices --- stock/report/item_prices/item_prices.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stock/report/item_prices/item_prices.py b/stock/report/item_prices/item_prices.py index ea0be477df..86ae085ce7 100644 --- a/stock/report/item_prices/item_prices.py +++ b/stock/report/item_prices/item_prices.py @@ -69,10 +69,11 @@ def get_price_list(): from `tabItem Price` where docstatus<2""", as_dict=1) for j in price_list: - if j.selling: - rate.setdefault(j.parent, {}).setdefault("selling", []).append(j.price) - if j.buying: - rate.setdefault(j.parent, {}).setdefault("buying", []).append(j.price) + if j.price: + if j.selling: + rate.setdefault(j.parent, {}).setdefault("selling", []).append(j.price) + if j.buying: + rate.setdefault(j.parent, {}).setdefault("buying", []).append(j.price) item_rate_map = {} From d4f2199269740cc62775b781478172ed13fdf334 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 19 Jun 2013 14:44:44 +0530 Subject: [PATCH 243/295] [Reports][Trend Analyzer completed] --- controllers/trends.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/controllers/trends.py b/controllers/trends.py index 76db44705f..4edb7845ce 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -17,6 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cint, add_days, add_months, cstr +from datetime import datetime def get_columns(filters, trans): @@ -117,7 +118,12 @@ def get_data(filters, tab, details): return data +def get_mon(date): + """convert srting formated date into date and retrieve month abbrevation""" + return (datetime.strptime(date, '%Y-%m-%d')).strftime("%b") + def period_wise_colums_query(filters, trans): + from datetime import datetime query_details = '' pwc = [] @@ -154,12 +160,15 @@ def period_wise_colums_query(filters, trans): """%{"trans": trans_date, "mon_num": cstr(month+1)} elif filters.get("period") == "Quarterly": - pwc = ["Q1 (Qty):Float:120", "Q1 (Amt):Currency:120", "Q2 (Qty):Float:120", "Q2 (Amt):Currency:120", - "Q3 (Qty):Float:120", "Q3 (Amt):Currency:120", "Q4 (Qty):Float:120", "Q4 (Amt):Currency:120"] first_qsd, second_qsd, third_qsd, fourth_qsd = year_start_date, add_months(year_start_date,3), add_months(year_start_date,6), add_months(year_start_date,9) first_qed, second_qed, third_qed, fourth_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1), add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] + + pwc = [get_mon(first_qsd)+"-"+get_mon(first_qed)+" (Qty):Float:120", get_mon(first_qsd)+"-"+get_mon(first_qed)+"(Amt):Currency:120", + get_mon(second_qsd)+"-"+get_mon(second_qed)+" (Qty):Float:120", get_mon(second_qsd)+"-"+get_mon(second_qed)+" (Amt):Currency:120", + get_mon(third_qsd)+"-"+get_mon(third_qed)+" (Qty):Float:120", get_mon(third_qsd)+"-"+get_mon(third_qed)+" (Amt):Currency:120", + get_mon(fourth_qsd)+"-"+get_mon(fourth_qed)+" (Qty):Float:120", get_mon(fourth_qsd)+"-"+get_mon(fourth_qed)+" (Amt):Currency:120"] for d in bet_dates: query_details += """ @@ -168,14 +177,15 @@ def period_wise_colums_query(filters, trans): """%{"trans": trans_date, "sd": d[0],"ed": d[1]} elif filters.get("period") == "Half-yearly": - pwc = ["Fisrt Half (Qty):Float:120", "Fisrt Half (Amt):Currency:120", "Second Half (Qty):Float:120", - "Second Half (Amt):Currency:120"] first_half_start = year_start_date first_half_end = add_days(add_months(first_half_start,6),-1) second_half_start = add_days(first_half_end,1) second_half_end = add_days(add_months(second_half_start,6),-1) + pwc = [get_mon(first_half_start)+"-"+get_mon(first_half_end)+"(Qty):Float:120", get_mon(first_half_start)+"-"+get_mon(first_half_end)+" (Amt):Currency:120", + get_mon(second_half_start)+"-"+get_mon(second_half_end)+" (Qty):Float:120", get_mon(second_half_start)+"-"+get_mon(second_half_end)+" (Amt):Currency:120"] + query_details = """ SUM(IF(t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s', t2.qty, NULL)), SUM(IF(t1.%(trans)s BETWEEN '%(fhs)s' AND '%(fhe)s', t1.grand_total, NULL)), From bc2d9e89a78ccd38f0bc067ccbadcf3b181d429e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 19 Jun 2013 14:58:01 +0530 Subject: [PATCH 244/295] [validation][buying] stock and non-stock items are now allowed in same document --- controllers/buying_controller.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 3deda0284b..f02e8482f4 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -54,16 +54,7 @@ class BuyingController(StockController): raise_exception=WrongWarehouseCompany) def validate_stock_or_nonstock_items(self): - items = [d.item_code for d in self.doclist.get({"parentfield": self.fname})] - if self.stock_items: - nonstock_items = list(set(items) - set(self.stock_items)) - if nonstock_items: - webnotes.msgprint(_("Stock and non-stock items can not be entered in the same ") + - self.doc.doctype + _(""". You should make separate documents for them. - Stock Items: """) + ", ".join(self.stock_items) + _(""" - Non-stock Items: """) + ", ".join(nonstock_items), raise_exception=1) - - elif items and not self.stock_items: + if not self.stock_items: tax_for_valuation = [d.account_head for d in self.doclist.get({"parentfield": "purchase_tax_details"}) if d.category in ["Valuation", "Valuation and Total"]] From 92571c692939c17537cdb27bedea89343a038fb1 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Wed, 19 Jun 2013 18:15:20 +0530 Subject: [PATCH 245/295] [Report] Completed Budget variance Report and corrections in Territory Target Varinace & Sales Person Target Variance Report --- .../budget_variance_report.py | 128 ++++++++---------- .../item_wise_purchase_register.py | 1 - ...erson_target_variance_(item_group_wise).py | 31 ++--- ...itory_target_variance_(item_group_wise).py | 30 ++-- 4 files changed, 85 insertions(+), 105 deletions(-) diff --git a/accounts/report/budget_variance_report/budget_variance_report.py b/accounts/report/budget_variance_report/budget_variance_report.py index ac38d9f34d..99e303bd20 100644 --- a/accounts/report/budget_variance_report/budget_variance_report.py +++ b/accounts/report/budget_variance_report/budget_variance_report.py @@ -26,27 +26,27 @@ def execute(filters=None): columns = get_columns(filters) period_month_ranges = get_period_month_ranges(filters) - # tim_map = get_territory_item_month_map(filters) + cam_map = get_costcenter_account_month_map(filters) data = [] - # for territory, territory_items in tim_map.items(): - # for item_group, monthwise_data in territory_items.items(): - # row = [territory, item_group] - # totals = [0, 0, 0] - # for relevant_months in period_month_ranges: - # period_data = [0, 0, 0] - # 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)) - # period_data[i] += value - # totals[i] += value - # period_data[2] = period_data[0] - period_data[1] - # row += period_data - # totals[2] = totals[0] - totals[1] - # row += totals - # data.append(row) + for cost_center, cost_center_items in cam_map.items(): + for account, monthwise_data in cost_center_items.items(): + row = [cost_center, account] + totals = [0, 0, 0] + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + month_data = monthwise_data.get(month, {}) + for i, fieldname in enumerate(["target", "actual", "variance"]): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + row += totals + data.append(row) return columns, sorted(data, key=lambda x: (x[0], x[1])) @@ -57,7 +57,7 @@ def get_columns(filters): msgprint(_("Please specify") + ": " + label, raise_exception=True) - columns = ["Cost Center:Link/Cost Center:80"] + columns = ["Cost Center:Link/Cost Center:100", "Account:Link/Account:100"] group_months = False if filters["period"] == "Monthly" else True @@ -105,80 +105,64 @@ def get_period_month_ranges(filters): return period_month_ranges -#Get cost center details -def get_costcenter_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""" % - ('%s'), (filters.get("fiscal_year")), as_dict=1) +#Get cost center & target details +def get_costcenter_target_details(filters): + return webnotes.conn.sql("""select cc.name, cc.distribution_id, + 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_name=%s and ifnull(cc.distribution_id, '')!='' + order by cc.name""" % ('%s', '%s'), + (filters.get("fiscal_year"), filters.get("company")), as_dict=1) -#Get target distribution details of item group +#Get target distribution details of accounts of cost center def get_target_distribution_details(filters): target_details = {} - abc = [] + 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 """ % ('%s'), (filters.get("fiscal_year")), as_dict=1): + `tabCost Center` cc where bdd.parent=bd.name and cc.distribution_id=bd.name and \ + bd.fiscal_year=%s""" % ('%s'), (filters.get("fiscal_year")), as_dict=1): target_details.setdefault(d.month, d) return target_details -#Get achieved details from sales order -def get_achieved_details(filters): - start_date, end_date = get_year_start_end_date(filters) - achieved_details = {} +#Get actual details from gl entry +def get_actual_details(filters): + return webnotes.conn.sql("""select gl.account, gl.debit, gl.credit, + gl.cost_center, MONTHNAME(gl.posting_date) as month_name + from `tabGL Entry` gl, `tabBudget Detail` bd + where gl.fiscal_year=%s and company=%s and is_cancelled='No' + and bd.account=gl.account""" % ('%s', '%s'), + (filters.get("fiscal_year"), filters.get("company")), as_dict=1) - for d in webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, \ - MONTHNAME(so.transaction_date) as month_name \ - from `tabSales Order Item` soi, `tabSales Order` so \ - where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and \ - so.transaction_date<=%s""" % ('%s', '%s'), \ - (start_date, end_date), as_dict=1): - achieved_details.setdefault(d.month_name, d) - - return achieved_details - -def get_territory_item_month_map(filters): - territory_details = get_territory_details(filters) +def get_costcenter_account_month_map(filters): + costcenter_target_details = get_costcenter_target_details(filters) tdd = get_target_distribution_details(filters) - achieved_details = get_achieved_details(filters) + actual_details = get_actual_details(filters) - tim_map = {} + cam_map = {} - for td in territory_details: + for ccd in costcenter_target_details: for month in tdd: - tim_map.setdefault(td.name, {}).setdefault(td.item_group, {})\ + cam_map.setdefault(ccd.name, {}).setdefault(ccd.account, {})\ .setdefault(month, webnotes._dict({ - "target": 0.0, "achieved": 0.0, "variance": 0.0 + "target": 0.0, "actual": 0.0, "variance": 0.0 })) - tav_dict = tim_map[td.name][td.item_group][month] + tav_dict = cam_map[ccd.name][ccd.account][month] + tav_dict.target = ccd.budget_allocated*(tdd[month]["percentage_allocation"]/100) - for ad in achieved_details: - if (filters["target_on"] == "Quantity"): - tav_dict.target = td.target_qty*(tdd[month]["percentage_allocation"]/100) - if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: - tav_dict.achieved += achieved_details[month]["qty"] - - if (filters["target_on"] == "Amount"): - tav_dict.target = td.target_amount*(tdd[month]["percentage_allocation"]/100) - if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: - tav_dict.achieved += achieved_details[month]["amount"] - - return tim_map + for ad in actual_details: + if ad.month_name == month and ad.account == ccd.account \ + and ad.cost_center == ccd.name: + tav_dict.actual += ad.debit - ad.credit + + return cam_map def get_year_start_end_date(filters): return webnotes.conn.sql("""select year_start_date, subdate(adddate(year_start_date, interval 1 year), interval 1 day) as year_end_date from `tabFiscal Year` - where name=%s""", filters["fiscal_year"])[0] - -def get_item_group(item_name): - """Get Item Group of an item""" - - return webnotes.conn.sql_list("select item_group from `tabItem` where name=%s""" % - ('%s'), (item_name)) \ No newline at end of file + where name=%s""", filters["fiscal_year"])[0] \ No newline at end of file diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index ad9d79504b..f7afb3dfb6 100644 --- a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -23,7 +23,6 @@ def execute(filters=None): columns = get_columns() item_list = get_items(filters) aii_account_map = get_aii_accounts() - webnotes.errprint(aii_account_map) data = [] for d in item_list: expense_head = d.expense_head or aii_account_map.get(d.company) 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 3163829131..8f5931d826 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 @@ -117,7 +117,7 @@ def get_salesperson_details(filters): #Get target distribution details of item group def get_target_distribution_details(filters): target_details = {} - abc = [] + 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 \ @@ -129,17 +129,14 @@ def get_target_distribution_details(filters): #Get achieved details from sales order def get_achieved_details(filters): start_date, end_date = get_year_start_end_date(filters) - achieved_details = {} - - for d in webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, \ - MONTHNAME(so.transaction_date) as month_name \ - from `tabSales Order Item` soi, `tabSales Order` so \ - where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and \ - so.transaction_date<=%s""" % ('%s', '%s'), \ - (start_date, end_date), as_dict=1): - achieved_details.setdefault(d.month_name, d) - - return achieved_details + + return webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, + st.sales_person, MONTHNAME(so.transaction_date) as month_name + from `tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st + where soi.parent=so.name and so.docstatus=1 and + st.parent=so.name and so.transaction_date>=%s and + so.transaction_date<=%s""" % ('%s', '%s'), + (start_date, end_date), as_dict=1) def get_salesperson_item_month_map(filters): salesperson_details = get_salesperson_details(filters) @@ -160,13 +157,15 @@ def get_salesperson_item_month_map(filters): for ad in achieved_details: if (filters["target_on"] == "Quantity"): tav_dict.target = sd.target_qty*(tdd[month]["percentage_allocation"]/100) - if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == sd.item_group: - tav_dict.achieved += achieved_details[month]["qty"] + if ad.month_name == month and ''.join(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 = sd.target_amount*(tdd[month]["percentage_allocation"]/100) - if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == sd.item_group: - tav_dict.achieved += achieved_details[month]["amount"] + if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == sd.item_group \ + and ad.sales_person == sd.name: + tav_dict.achieved += ad.amount return sim_map 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 fe5e628c65..079f8e82a2 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 @@ -117,11 +117,11 @@ def get_territory_details(filters): #Get target distribution details of item group def get_target_distribution_details(filters): target_details = {} - abc = [] + 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 """ % ('%s'), (filters.get("fiscal_year")), as_dict=1): + bd.fiscal_year=%s""" % ('%s'), (filters.get("fiscal_year")), as_dict=1): target_details.setdefault(d.month, d) return target_details @@ -129,17 +129,13 @@ def get_target_distribution_details(filters): #Get achieved details from sales order def get_achieved_details(filters): start_date, end_date = get_year_start_end_date(filters) - achieved_details = {} - for d in webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, \ - MONTHNAME(so.transaction_date) as month_name \ - from `tabSales Order Item` soi, `tabSales Order` so \ - where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and \ - so.transaction_date<=%s""" % ('%s', '%s'), \ - (start_date, end_date), as_dict=1): - achieved_details.setdefault(d.month_name, d) - - return achieved_details + return webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, + so.territory, MONTHNAME(so.transaction_date) as month_name + from `tabSales Order Item` soi, `tabSales Order` so + where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and + so.transaction_date<=%s""" % ('%s', '%s'), + (start_date, end_date), as_dict=1) def get_territory_item_month_map(filters): territory_details = get_territory_details(filters) @@ -160,13 +156,15 @@ def get_territory_item_month_map(filters): for ad in achieved_details: if (filters["target_on"] == "Quantity"): tav_dict.target = td.target_qty*(tdd[month]["percentage_allocation"]/100) - if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: - tav_dict.achieved += achieved_details[month]["qty"] + if ad.month_name == month and ''.join(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 = td.target_amount*(tdd[month]["percentage_allocation"]/100) - if ad == month and ''.join(get_item_group(achieved_details[month]["item_code"])) == td.item_group: - tav_dict.achieved += achieved_details[month]["amount"] + if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == td.item_group \ + and ad.territory == td.name: + tav_dict.achieved += ad.amount return tim_map From b8ebbcafa997de794ac296d4130e26f8207c59ff Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 20 Jun 2013 13:03:10 +0530 Subject: [PATCH 246/295] [trend analyzer] cleanup --- controllers/trends.py | 105 ++++++++-------- .../quotation_trends/quotation_trends.py | 5 +- stock/doctype/serial_no/serial_no.txt | 116 ++++++++---------- 3 files changed, 100 insertions(+), 126 deletions(-) diff --git a/controllers/trends.py b/controllers/trends.py index 4edb7845ce..c0636cba45 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -16,33 +16,39 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, add_days, add_months, cstr -from datetime import datetime +from webnotes.utils import add_days, add_months, cstr, getdate +from webnotes import _ def get_columns(filters, trans): + validate_filters(filters) + + # based_on_cols, based_on_select, based_on_group_by, addl_tables + bonc, query_bon, based, sup_tab = basedon_wise_colums_query(filters.get("based_on"), trans) + # period_cols, period_select + pwc, query_pwc = period_wise_colums_query(filters, trans) + + # group_by_cols + grbc = group_wise_column(filters.get("group_by")) - if not (filters.get("period") and filters.get("based_on")): - webnotes.msgprint("Value missing in 'Period' or 'Based On'", raise_exception=1) + columns = bonc + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] + if grbc: + columns = bonc + grbc + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - elif filters.get("based_on") == filters.get("group_by"): - webnotes.msgprint("Plese select different values in 'Based On' and 'Group By'", raise_exception=1) - - else: - bonc, query_bon, based, sup_tab = basedon_wise_colums_query(filters.get("based_on"), trans) - pwc, query_pwc = period_wise_colums_query(filters, trans) - grbc = group_wise_column(filters.get("group_by")) - - columns = bonc + pwc + ["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - if grbc: - columns = bonc + grbc + pwc +["TOTAL(Qty):Float:120", "TOTAL(Amt):Currency:120"] - - details = {"query_bon": query_bon, "query_pwc": query_pwc, "columns": columns, "basedon": based, - "grbc": grbc, "sup_tab": sup_tab} + # conditions + details = {"query_bon": query_bon, "query_pwc": query_pwc, "columns": columns, + "basedon": based, "grbc": grbc, "sup_tab": sup_tab} return details -def get_data(filters, tab, details): +def validate_filters(filters): + for f in ["Fiscal Year", "Based On", "Period", "Company"]: + if not filters.get(f.lower().replace(" ", "_")): + webnotes.msgprint(f + _(" is mandatory"), raise_exception=1) + if filters.get("based_on") == filters.get("group_by"): + webnotes.msgprint("'Based On' and 'Group By' can not be same", raise_exception=1) + +def get_data(filters, tab, details): data = [] inc, cond= '','' query_details = details["query_bon"] + details["query_pwc"] @@ -96,17 +102,17 @@ def get_data(filters, tab, details): row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 and %s = %s and %s = %s - """%(sel_col, details["query_pwc"], tab[0], tab[1], details["sup_tab"], + """ % (sel_col, details["query_pwc"], tab[0], tab[1], details["sup_tab"], "%s", "%s", sel_col, "%s", details["basedon"], "%s"), - (filters.get("company"), filters.get("fiscal_year"), row[i][0], data1[d][0]), - as_list=1) + (filters.get("company"), filters.get("fiscal_year"), + row[i][0], data1[d][0]), as_list=1) des[ind] = row[i] for j in range(1,len(details["columns"])-inc): des[j+inc] = row1[0][j] + data.append(des) else: - data = webnotes.conn.sql(""" select %s from `%s` t1, `%s` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 %s @@ -118,20 +124,13 @@ def get_data(filters, tab, details): return data -def get_mon(date): - """convert srting formated date into date and retrieve month abbrevation""" - return (datetime.strptime(date, '%Y-%m-%d')).strftime("%b") +def get_mon(dt): + return getdate(dt).strftime("%b") def period_wise_colums_query(filters, trans): - from datetime import datetime - query_details = '' pwc = [] - ysd = webnotes.conn.sql("""select year_start_date from `tabFiscal Year` where name = '%s' - """%filters.get("fiscal_year"))[0][0] - - year_start_date = ysd.strftime('%Y-%m-%d') - start_month = cint(year_start_date.split('-')[1]) + ysd = webnotes.conn.get_value("Fiscal year", filters.get("fiscal_year"), "year_start_date") if trans in ['Purchase Receipt', 'Delivery Note', 'Purchase Invoice', 'Sales Invoice']: trans_date = 'posting_date' @@ -141,27 +140,13 @@ def period_wise_colums_query(filters, trans): if filters.get("period") == "Monthly": month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] - for month in range(start_month-1,len(month_name)): - pwc.append(month_name[month]+' (Qty):Float:120') - pwc.append(month_name[month]+' (Amt):Currency:120') - - query_details += """ - Sum(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t2.qty, NULL)), - SUM(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t1.grand_total, NULL)), - """%{"trans": trans_date,"mon_num": cstr(month+1)} - - for month in range(0, start_month-1): - pwc.append(month_name[month]+' (Qty):Float:120') - pwc.append(month_name[month]+' (Amt):Currency:120') - - query_details += """ - Sum(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t2.qty, NULL)), - SUM(IF(MONTH(t1.%(trans)s)= %(mon_num)s, t1.grand_total, NULL)), - """%{"trans": trans_date, "mon_num": cstr(month+1)} + for month_idx in range(ysd.month-1,len(month_name)) + range(0, ysd.month-1): + query_details = get_monthly_conditions(month_name, month_idx, trans_date, + pwc, query_details) elif filters.get("period") == "Quarterly": - first_qsd, second_qsd, third_qsd, fourth_qsd = year_start_date, add_months(year_start_date,3), add_months(year_start_date,6), add_months(year_start_date,9) + first_qsd, second_qsd, third_qsd, fourth_qsd = ysd, add_months(ysd,3), add_months(ysd,6), add_months(ysd,9) first_qed, second_qed, third_qed, fourth_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1), add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] @@ -178,7 +163,7 @@ def period_wise_colums_query(filters, trans): elif filters.get("period") == "Half-yearly": - first_half_start = year_start_date + first_half_start = ysd first_half_end = add_days(add_months(first_half_start,6),-1) second_half_start = add_days(first_half_end,1) second_half_end = add_days(add_months(second_half_start,6),-1) @@ -200,6 +185,17 @@ def period_wise_colums_query(filters, trans): query_details += 'SUM(t2.qty), SUM(t1.grand_total)' return pwc, query_details + +def get_monthly_conditions(month_list, month_idx, trans_date, pwc, query_details): + pwc += [month_list[month_idx] + ' (Qty):Float:120', + month_list[month_idx] + ' (Amt):Currency:120'] + + query_details += """ + Sum(IF(MONTH(t1.%(trans_date)s)= %(mon_num)s, t2.qty, NULL)), + SUM(IF(MONTH(t1.%(trans_date)s)= %(mon_num)s, t1.grand_total, NULL)), + """ % {"trans_date": trans_date, "mon_num": cstr(month_idx+1)} + + return query_details def basedon_wise_colums_query(based_on, trans): sup_tab = '' @@ -242,19 +238,16 @@ def basedon_wise_colums_query(based_on, trans): based = 't1.territory' elif based_on == "Project": - if trans in ['Sales Invoice', 'Delivery Note', 'Sales Order']: bon = ["Project:Link/Project:120"] query_details = "t1.project_name," based = 't1.project_name' - elif trans in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: bon = ["Project:Link/Project:120"] query_details = "t2.project_name," based = 't2.project_name' - else: - webnotes.msgprint("Information Not Available", raise_exception=1) + webnotes.msgprint("Project-wise data is not available for Quotation", raise_exception=1) return bon, query_details, based, sup_tab diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py index e341752cd0..c6a54c7588 100644 --- a/selling/report/quotation_trends/quotation_trends.py +++ b/selling/report/quotation_trends/quotation_trends.py @@ -16,7 +16,7 @@ from __future__ import unicode_literals import webnotes -from controllers.trends import get_columns,get_data +from controllers.trends import get_columns, get_data def execute(filters=None): if not filters: filters ={} @@ -27,8 +27,5 @@ def execute(filters=None): details = get_columns(filters, trans) data = get_data(filters, tab, details) - - if not data: - webnotes.msgprint("Data not found for selected criterias") return details["columns"], data \ No newline at end of file diff --git a/stock/doctype/serial_no/serial_no.txt b/stock/doctype/serial_no/serial_no.txt index 8e891b8172..33160c785f 100644 --- a/stock/doctype/serial_no/serial_no.txt +++ b/stock/doctype/serial_no/serial_no.txt @@ -1,14 +1,14 @@ [ { - "creation": "2013-01-29 19:25:41", + "creation": "2013-05-16 10:59:15", "docstatus": 0, - "modified": "2013-01-29 16:27:57", + "modified": "2013-06-20 11:23:01", "modified_by": "Administrator", "owner": "Administrator" }, { "allow_attach": 1, - "allow_rename": 1, + "allow_rename": 0, "autoname": "field:serial_no", "description": "Distinct unit of an Item", "doctype": "DocType", @@ -31,7 +31,9 @@ "parent": "Serial No", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, + "report": 1, "submit": 0 }, { @@ -43,12 +45,14 @@ "fieldname": "details", "fieldtype": "Section Break", "label": "Details", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break0", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "read_only": 0 }, { "default": "In Store", @@ -75,6 +79,7 @@ "no_copy": 1, "oldfieldname": "serial_no", "oldfieldtype": "Data", + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -88,13 +93,15 @@ "oldfieldname": "item_code", "oldfieldtype": "Link", "options": "Item", + "read_only": 0, "reqd": 1, "search_index": 0 }, { "doctype": "DocField", "fieldname": "column_break1", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "read_only": 0 }, { "doctype": "DocField", @@ -146,12 +153,14 @@ "doctype": "DocField", "fieldname": "purchase_details", "fieldtype": "Section Break", - "label": "Purchase Details" + "label": "Purchase Details", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break2", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -160,7 +169,8 @@ "fieldtype": "Select", "label": "Purchase Document Type", "no_copy": 1, - "options": "\nPurchase Receipt\nStock Entry" + "options": "\nPurchase Receipt\nStock Entry", + "read_only": 0 }, { "doctype": "DocField", @@ -168,7 +178,8 @@ "fieldtype": "Data", "hidden": 0, "label": "Purchase Document No", - "no_copy": 1 + "no_copy": 1, + "read_only": 0 }, { "doctype": "DocField", @@ -179,6 +190,7 @@ "no_copy": 1, "oldfieldname": "purchase_date", "oldfieldtype": "Date", + "read_only": 0, "reqd": 0, "search_index": 0 }, @@ -188,6 +200,7 @@ "fieldtype": "Time", "label": "Incoming Time", "no_copy": 1, + "read_only": 0, "reqd": 1 }, { @@ -200,6 +213,7 @@ "oldfieldname": "purchase_rate", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "read_only": 0, "reqd": 1, "search_index": 0 }, @@ -207,6 +221,7 @@ "doctype": "DocField", "fieldname": "column_break3", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -220,6 +235,7 @@ "oldfieldname": "warehouse", "oldfieldtype": "Link", "options": "Warehouse", + "read_only": 0, "reqd": 0, "search_index": 1 }, @@ -230,7 +246,8 @@ "in_filter": 1, "label": "Supplier", "no_copy": 1, - "options": "Supplier" + "options": "Supplier", + "read_only": 0 }, { "doctype": "DocField", @@ -254,12 +271,14 @@ "fieldname": "delivery_details", "fieldtype": "Section Break", "label": "Delivery Details", - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break4", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -318,12 +337,14 @@ "oldfieldname": "is_cancelled", "oldfieldtype": "Select", "options": "\nYes\nNo", + "read_only": 0, "report_hide": 1 }, { "doctype": "DocField", "fieldname": "column_break5", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -378,12 +399,14 @@ "doctype": "DocField", "fieldname": "warranty_amc_details", "fieldtype": "Section Break", - "label": "Warranty / AMC Details" + "label": "Warranty / AMC Details", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "column_break6", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -396,6 +419,7 @@ "oldfieldname": "maintenance_status", "oldfieldtype": "Select", "options": "\nUnder Warranty\nOut of Warranty\nUnder AMC\nOut of AMC", + "read_only": 0, "search_index": 1, "width": "150px" }, @@ -406,12 +430,14 @@ "label": "Warranty Period (Days)", "oldfieldname": "warranty_period", "oldfieldtype": "Int", + "read_only": 0, "width": "150px" }, { "doctype": "DocField", "fieldname": "column_break7", "fieldtype": "Column Break", + "read_only": 0, "width": "50%" }, { @@ -422,6 +448,7 @@ "label": "Warranty Expiry Date", "oldfieldname": "warranty_expiry_date", "oldfieldtype": "Date", + "read_only": 0, "width": "150px" }, { @@ -432,6 +459,7 @@ "label": "AMC Expiry Date", "oldfieldname": "amc_expiry_date", "oldfieldtype": "Date", + "read_only": 0, "search_index": 0, "width": "150px" }, @@ -439,13 +467,15 @@ "doctype": "DocField", "fieldname": "more_info", "fieldtype": "Section Break", - "label": "More Info" + "label": "More Info", + "read_only": 0 }, { "doctype": "DocField", "fieldname": "serial_no_details", "fieldtype": "Text Editor", - "label": "Serial No Details" + "label": "Serial No Details", + "read_only": 0 }, { "doctype": "DocField", @@ -454,6 +484,7 @@ "in_filter": 1, "label": "Company", "options": "link:Company", + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -464,6 +495,7 @@ "in_filter": 1, "label": "Fiscal Year", "options": "link:Fiscal Year", + "read_only": 0, "reqd": 1, "search_index": 1 }, @@ -487,54 +519,10 @@ "read_only": 1, "report_hide": 1 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Material Manager", - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Material Manager", - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Material User", - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "role": "Material User", - "write": 0 - }, { "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, "role": "System Manager", "write": 1 }, @@ -542,8 +530,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "report": 1, "role": "Material Master Manager", "write": 1 }, @@ -552,17 +538,15 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "role": "System Manager" + "role": "Material Manager", + "write": 0 }, { "amend": 0, "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "role": "Sales Master Manager" + "role": "Material User", + "write": 0 } ] \ No newline at end of file From af5df668471891b2dcd85464a5d8ad430bdf444d Mon Sep 17 00:00:00 2001 From: Saurabh Date: Thu, 20 Jun 2013 15:00:53 +0530 Subject: [PATCH 247/295] to avoid merge conflict --- controllers/trends.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/controllers/trends.py b/controllers/trends.py index 4edb7845ce..08babb7332 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -74,7 +74,6 @@ def get_data(filters, tab, details): "%s", cond, details["basedon"]), (filters.get("company"), filters["fiscal_year"]), as_list=1) - for d in range(len(data1)): #to add blanck column dt = data1[d] @@ -88,11 +87,10 @@ def get_data(filters, tab, details): """%(sel_col, tab[0], tab[1], details["sup_tab"], "%s", "%s", details["basedon"], "%s"), (filters.get("company"), filters.get("fiscal_year"), data1[d][0]), as_list=1) - for i in range(len(row)): des = ['' for q in range(len(details["columns"]))] - #get data for each group_by filter + #get data for group_by filter row1 = webnotes.conn.sql(""" select %s , %s from `%s` t1, `%s` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 and %s = %s and %s = %s @@ -100,7 +98,6 @@ def get_data(filters, tab, details): "%s", "%s", sel_col, "%s", details["basedon"], "%s"), (filters.get("company"), filters.get("fiscal_year"), row[i][0], data1[d][0]), as_list=1) - des[ind] = row[i] for j in range(1,len(details["columns"])-inc): des[j+inc] = row1[0][j] @@ -123,7 +120,6 @@ def get_mon(date): return (datetime.strptime(date, '%Y-%m-%d')).strftime("%b") def period_wise_colums_query(filters, trans): - from datetime import datetime query_details = '' pwc = [] @@ -161,8 +157,12 @@ def period_wise_colums_query(filters, trans): elif filters.get("period") == "Quarterly": - first_qsd, second_qsd, third_qsd, fourth_qsd = year_start_date, add_months(year_start_date,3), add_months(year_start_date,6), add_months(year_start_date,9) - first_qed, second_qed, third_qed, fourth_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1), add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) + first_qsd, second_qsd = year_start_date, add_months(year_start_date,3) + third_qsd, fourth_qsd = add_months(year_start_date,6), add_months(year_start_date,9) + + first_qed, second_qed = add_days(add_months(first_qsd,3),-1), add_days(add_months(second_qsd,3),-1) + third_qed, fourth_qed = add_days(add_months(third_qsd,3),-1), add_days(add_months(fourth_qsd,3),-1) + bet_dates = [[first_qsd,first_qed],[second_qsd,second_qed],[third_qsd,third_qed],[fourth_qsd,fourth_qed]] pwc = [get_mon(first_qsd)+"-"+get_mon(first_qed)+" (Qty):Float:120", get_mon(first_qsd)+"-"+get_mon(first_qed)+"(Amt):Currency:120", From 0930b94264bc09adc3fd269e1ae083991f263e82 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 20 Jun 2013 16:35:09 +0530 Subject: [PATCH 248/295] [target report] cleanup --- accounts/utils.py | 16 +++++--- ...itory_target_variance_(item_group_wise).py | 37 +++++++------------ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/accounts/utils.py b/accounts/utils.py index eb240e796c..fa93cb0628 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -26,17 +26,23 @@ from utilities import build_filter_conditions class FiscalYearError(webnotes.ValidationError): pass -def get_fiscal_year(date, verbose=1): - return get_fiscal_years(date, verbose=1)[0] +def get_fiscal_year(date=None, fiscal_year=None, verbose=1): + return get_fiscal_years(date, fiscal_year, verbose=1)[0] -def get_fiscal_years(date, verbose=1): +def get_fiscal_years(date=None, fiscal_year=None, verbose=1): # if year start date is 2012-04-01, year end date should be 2013-03-31 (hence subdate) + cond = "" + if fiscal_year: + cond = "name = '%s'" % fiscal_year + else: + cond = "'%s' >= year_start_date and '%s' < adddate(year_start_date, interval 1 year)" % \ + (date, date) fy = webnotes.conn.sql("""select name, year_start_date, subdate(adddate(year_start_date, interval 1 year), interval 1 day) as year_end_date from `tabFiscal Year` - where %s >= year_start_date and %s < adddate(year_start_date, interval 1 year) - order by year_start_date desc""", (date, date)) + where %s + order by year_start_date desc""" % cond) if not fy: error_msg = """%s not in any Fiscal Year""" % formatdate(date) 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 079f8e82a2..022d19ecf6 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 @@ -20,6 +20,7 @@ import calendar from webnotes import _, msgprint from webnotes.utils import flt import time +from accounts.utils import get_fiscal_year def execute(filters=None): if not filters: filters = {} @@ -54,8 +55,7 @@ def get_columns(filters): for fieldname in ["fiscal_year", "period", "target_on"]: if not filters.get(fieldname): label = (" ".join(fieldname.split("_"))).title() - msgprint(_("Please specify") + ": " + label, - raise_exception=True) + msgprint(_("Please specify") + ": " + label, raise_exception=True) columns = ["Territory:Link/Territory:80", "Item Group:Link/Item Group:80"] @@ -72,8 +72,8 @@ def get_columns(filters): def get_period_date_ranges(filters): from dateutil.relativedelta import relativedelta - - year_start_date, year_end_date = get_year_start_end_date(filters) + year_start_date, year_end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:] + increment = { "Monthly": 1, @@ -111,8 +111,8 @@ def get_territory_details(filters): 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""" % - ('%s'), (filters.get("fiscal_year")), as_dict=1) + ifnull(t.distribution_id, '')!='' order by t.name""", + filters.get("fiscal_year"), as_dict=1) #Get target distribution details of item group def get_target_distribution_details(filters): @@ -128,7 +128,7 @@ def get_target_distribution_details(filters): #Get achieved details from sales order def get_achieved_details(filters): - start_date, end_date = get_year_start_end_date(filters) + start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:] return webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, so.territory, MONTHNAME(so.transaction_date) as month_name @@ -148,35 +148,26 @@ def get_territory_item_month_map(filters): for month in tdd: tim_map.setdefault(td.name, {}).setdefault(td.item_group, {})\ .setdefault(month, webnotes._dict({ - "target": 0.0, "achieved": 0.0, "variance": 0.0 + "target": 0.0, "achieved": 0.0 })) tav_dict = tim_map[td.name][td.item_group][month] for ad in achieved_details: if (filters["target_on"] == "Quantity"): - tav_dict.target = td.target_qty*(tdd[month]["percentage_allocation"]/100) - if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == td.item_group \ + tav_dict.target = flt(td.target_qty) * (tdd[month]["percentage_allocation"]/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 = td.target_amount*(tdd[month]["percentage_allocation"]/100) - if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == td.item_group \ + tav_dict.target = flt(td.target_amount) * \ + (tdd[month]["percentage_allocation"]/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 return tim_map -def get_year_start_end_date(filters): - return webnotes.conn.sql("""select year_start_date, - subdate(adddate(year_start_date, interval 1 year), interval 1 day) - as year_end_date - from `tabFiscal Year` - where name=%s""", filters["fiscal_year"])[0] - def get_item_group(item_name): - """Get Item Group of an item""" - - return webnotes.conn.sql_list("select item_group from `tabItem` where name=%s""" % - ('%s'), (item_name)) \ No newline at end of file + return webnotes.conn.get_value("Item", item_name, "item_group") \ No newline at end of file From b7784eac365876dff9c93f95b6b761dd8826a99c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 20 Jun 2013 17:19:51 +0530 Subject: [PATCH 249/295] [fix][report] in item prices --- stock/report/item_prices/item_prices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock/report/item_prices/item_prices.py b/stock/report/item_prices/item_prices.py index 86ae085ce7..c8d0b69db5 100644 --- a/stock/report/item_prices/item_prices.py +++ b/stock/report/item_prices/item_prices.py @@ -101,7 +101,7 @@ def get_valuation_rate(): val_rate_map = {} for d in webnotes.conn.sql("""select item_code, avg(valuation_rate) as val_rate - from tabBin group by item_code""", as_dict=1): + from tabBin where actual_qty > 0 group by item_code""", as_dict=1): val_rate_map.setdefault(d.item_code, d.val_rate) return val_rate_map \ No newline at end of file From 71253bea0f4bd055605aa88363233c78702abca5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 20 Jun 2013 17:32:00 +0530 Subject: [PATCH 250/295] [fix][report] in item prices --- stock/report/item_prices/item_prices.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stock/report/item_prices/item_prices.py b/stock/report/item_prices/item_prices.py index c8d0b69db5..3bdb746510 100644 --- a/stock/report/item_prices/item_prices.py +++ b/stock/report/item_prices/item_prices.py @@ -100,7 +100,8 @@ def get_valuation_rate(): val_rate_map = {} - for d in webnotes.conn.sql("""select item_code, avg(valuation_rate) as val_rate + for d in webnotes.conn.sql("""select item_code, + sum(actual_qty*valuation_rate)/sum(actual_qty) as val_rate from tabBin where actual_qty > 0 group by item_code""", as_dict=1): val_rate_map.setdefault(d.item_code, d.val_rate) From 2b02f1410a3e726c4d34da84f8c2391d8048c4a3 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 21 Jun 2013 10:46:26 +0530 Subject: [PATCH 251/295] Trends Analyzer cleanup finished --- controllers/trends.py | 94 ++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/controllers/trends.py b/controllers/trends.py index bbab01eb2a..acbc744ae6 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -23,18 +23,20 @@ def get_columns(filters, trans): validate_filters(filters) # get conditions for based_on filter cond - based_on_cols, based_on_select, based_on_group_by, addl_tables = based_wise_colums_query(filters.get("based_on"), trans) + based_on_details = based_wise_colums_query(filters.get("based_on"), trans) # get conditions for periodic filter cond period_cols, period_select = period_wise_colums_query(filters, trans) # get conditions for grouping filter cond group_by_cols = group_wise_column(filters.get("group_by")) - columns = based_on_cols + period_cols + ["Total(Qty):Float:120", "Total(Amt):Currency:120"] + columns = based_on_details["based_on_cols"] + period_cols + ["Total(Qty):Float:120", "Total(Amt):Currency:120"] if group_by_cols: - columns = based_on_cols + group_by_cols + period_cols +["Total(Qty):Float:120", "Total(Amt):Currency:120"] + columns = based_on_details["based_on_cols"] + group_by_cols + period_cols + \ + ["Total(Qty):Float:120", "Total(Amt):Currency:120"] - conditions = {"based_on_select": based_on_select, "period_wise_select": period_select, "columns": columns, - "group_by": based_on_group_by, "grbc": group_by_cols, "sup_tab": addl_tables, "trans": trans} + conditions = {"based_on_select": based_on_details["based_on_select"], "period_wise_select": period_select, + "columns": columns, "group_by": based_on_details["based_on_group_by"], "grbc": group_by_cols, "trans": trans, + "addl_tables": based_on_details["addl_tables"]} return conditions @@ -73,7 +75,7 @@ def get_data(filters, conditions): where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 %s group by %s - """ % (query_details, conditions["trans"], conditions["trans"], conditions["sup_tab"], "%s", + """ % (query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", "%s", cond, conditions["group_by"]), (filters.get("company"), filters["fiscal_year"]),as_list=1) @@ -87,7 +89,7 @@ def get_data(filters, conditions): row = webnotes.conn.sql("""select DISTINCT(%s) from `tab%s` t1, `tab%s Item` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 and %s = %s - """%(sel_col, conditions["trans"], conditions["trans"], conditions["sup_tab"], "%s", "%s", + """%(sel_col, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", "%s", conditions["group_by"], "%s"), (filters.get("company"), filters.get("fiscal_year"), data1[d][0]), as_list=1) @@ -99,7 +101,7 @@ def get_data(filters, conditions): where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 and %s = %s and %s = %s """ % (sel_col, conditions["period_wise_select"], conditions["trans"], conditions["trans"], - conditions["sup_tab"], "%s", "%s", sel_col, "%s", conditions["group_by"], "%s"), + conditions["addl_tables"], "%s", "%s", sel_col, "%s", conditions["group_by"], "%s"), (filters.get("company"), filters.get("fiscal_year"), row[i][0], data1[d][0]), as_list=1) des[ind] = row[i] @@ -108,12 +110,12 @@ def get_data(filters, conditions): data.append(des) else: - webnotes.errprint(["hii", conditions["sup_tab"]]) + webnotes.errprint(["hii", conditions["addl_tables"]]) data = webnotes.conn.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 %s group by %s - """%(query_details, conditions["trans"], conditions["trans"], conditions["sup_tab"], "%s", + """%(query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", "%s", cond,conditions["group_by"]), (filters.get("company"), filters.get("fiscal_year")), as_list=1) @@ -191,58 +193,66 @@ def get_period_month_ranges(period, fiscal_year): return period_month_ranges def based_wise_colums_query(based_on, trans): - sup_tab = '' + based_on_details = {} + # based_on_cols, based_on_select, based_on_group_by, addl_tables if based_on == "Item": - bon = ["Item:Link/Item:120", "Item Name:Data:120"] - query_details = "t2.item_code, t2.item_name," - based = 't2.item_code' + based_on_details["based_on_cols"] = ["Item:Link/Item:120", "Item Name:Data:120"] + based_on_details["based_on_select"] = "t2.item_code, t2.item_name," + based_on_details["based_on_group_by"] = 't2.item_code' + based_on_details["addl_tables"] = '' elif based_on == "Item Group": - bon = ["Item Group:Link/Item Group:120"] - query_details = "t2.item_group," - based = 't2.item_group' + based_on_details["based_on_cols"] = ["Item Group:Link/Item Group:120"] + based_on_details["based_on_select"] = "t2.item_group," + based_on_details["based_on_group_by"] = 't2.item_group' + based_on_details["addl_tables"] = '' elif based_on == "Customer": - bon = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"] - query_details = "t1.customer_name, t1.territory, " - based = 't1.customer_name' + based_on_details["based_on_cols"] = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"] + based_on_details["based_on_select"] = "t1.customer_name, t1.territory, " + based_on_details["based_on_group_by"] = 't1.customer_name' + based_on_details["addl_tables"] = '' elif based_on == "Customer Group": - bon = ["Customer Group:Link/Customer Group"] - query_details = "t1.customer_group," - based = 't1.customer_group' - + based_on_details["based_on_cols"] = ["Customer Group:Link/Customer Group"] + based_on_details["based_on_select"] = "t1.customer_group," + based_on_details["based_on_group_by"] = 't1.customer_group' + based_on_details["addl_tables"] = '' + elif based_on == 'Supplier': - bon = ["Supplier:Link/Supplier:120", "Supplier Type:Link/Supplier Type:120"] - query_details = "t1.supplier, t3.supplier_type," - based = 't1.supplier' - sup_tab = ',`tabSupplier` t3' + based_on_details["based_on_cols"] = ["Supplier:Link/Supplier:120", "Supplier Type:Link/Supplier Type:140"] + based_on_details["based_on_select"] = "t1.supplier, t3.supplier_type," + based_on_details["based_on_group_by"] = 't1.supplier' + based_on_details["addl_tables"] = ',`tabSupplier` t3' elif based_on == 'Supplier Type': - bon = ["Supplier Type:Link/Supplier Type:120"] - query_details = "t3.supplier_type," - based = 't3.supplier_type' - sup_tab =',`tabSupplier` t3' + based_on_details["based_on_cols"] = ["Supplier Type:Link/Supplier Type:140"] + based_on_details["based_on_select"] = "t3.supplier_type," + based_on_details["based_on_group_by"] = 't3.supplier_type' + based_on_details["addl_tables"] =',`tabSupplier` t3' elif based_on == "Territory": - bon = ["Territory:Link/Territory:120"] - query_details = "t1.territory," - based = 't1.territory' + based_on_details["based_on_cols"] = ["Territory:Link/Territory:120"] + based_on_details["based_on_select"] = "t1.territory," + based_on_details["based_on_group_by"] = 't1.territory' + based_on_details["addl_tables"] = '' elif based_on == "Project": if trans in ['Sales Invoice', 'Delivery Note', 'Sales Order']: - bon = ["Project:Link/Project:120"] - query_details = "t1.project_name," - based = 't1.project_name' + based_on_details["based_on_cols"] = ["Project:Link/Project:120"] + based_on_details["based_on_select"] = "t1.project_name," + based_on_details["based_on_group_by"] = 't1.project_name' + based_on_details["addl_tables"] = '' elif trans in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: - bon = ["Project:Link/Project:120"] - query_details = "t2.project_name," - based = 't2.project_name' + based_on_details["based_on_cols"] = ["Project:Link/Project:120"] + based_on_details["based_on_select"] = "t2.project_name," + based_on_details["based_on_group_by"] = 't2.project_name' + based_on_details["addl_tables"] = '' else: webnotes.msgprint("Project-wise data is not available for Quotation", raise_exception=1) - return bon, query_details, based, sup_tab + return based_on_details def group_wise_column(group_by): if group_by: From 8df49ca772c046f9d4e091bba36096bb1ec43512 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 21 Jun 2013 12:05:35 +0530 Subject: [PATCH 252/295] [buying] [fix] on change of currency, set conversion rate = 1 if currency=company currency --- buying/doctype/purchase_common/purchase_common.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index dacee80e55..9e291f5f17 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -128,23 +128,27 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ }, currency: function() { - this.set_dynamic_labels(); + if(this.frm.doc.currency === this.get_company_currency()) + this.frm.set_value("conversion_rate", 1.0); + + this.price_list_currency(); }, company: function() { - this.set_dynamic_labels(); + if(this.frm.fields_dict.currency) + this.set_dynamic_labels(); }, price_list_currency: function() { this.frm.toggle_reqd("plc_conversion_rate", !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); - this.set_dynamic_labels(); - if(this.frm.doc.price_list_currency === this.get_company_currency()) this.frm.set_value("plc_conversion_rate", 1.0); else if(this.frm.doc.price_list_currency === this.frm.doc.currency) this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate || 1.0); + + this.set_dynamic_labels(); }, set_dynamic_labels: function(doc, dt, dn) { From 1ded5c618ee73166dfcba4671cd9b041baa1cd4f Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Fri, 21 Jun 2013 12:47:51 +0530 Subject: [PATCH 253/295] [Report]Cleanup of territory target variance, sales person target variance & budget variance reports --- .../budget_variance_report.py | 59 +++------------ selling/page/selling_home/selling_home.js | 4 +- .../__init__.py | 0 ...person_target_variance_item_group_wise.js} | 2 +- ...person_target_variance_item_group_wise.py} | 74 ++++--------------- ...erson_target_variance_item_group_wise.txt} | 8 +- .../__init__.py | 0 ...ritory_target_variance_item_group_wise.js} | 2 +- ...ritory_target_variance_item_group_wise.py} | 74 ++++--------------- ...itory_target_variance_item_group_wise.txt} | 8 +- stock/report/item_prices/item_prices.py | 62 +++++++++++++--- 11 files changed, 106 insertions(+), 187 deletions(-) rename selling/report/{sales_person_target_variance_(item_group_wise) => sales_person_target_variance_item_group_wise}/__init__.py (100%) rename selling/report/{sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js => sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.js} (86%) rename selling/report/{sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).py => sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py} (67%) rename selling/report/{sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt => sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.txt} (57%) rename selling/report/{territory_target_variance_(item_group_wise) => territory_target_variance_item_group_wise}/__init__.py (100%) rename selling/report/{territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js => territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.js} (87%) rename selling/report/{territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).py => territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py} (66%) rename selling/report/{territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt => territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.txt} (57%) diff --git a/accounts/report/budget_variance_report/budget_variance_report.py b/accounts/report/budget_variance_report/budget_variance_report.py index 99e303bd20..b4959aa667 100644 --- a/accounts/report/budget_variance_report/budget_variance_report.py +++ b/accounts/report/budget_variance_report/budget_variance_report.py @@ -16,18 +16,21 @@ from __future__ import unicode_literals import webnotes -import calendar from webnotes import _, msgprint 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 def execute(filters=None): if not filters: filters = {} columns = get_columns(filters) - period_month_ranges = get_period_month_ranges(filters) + period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) cam_map = get_costcenter_account_month_map(filters) + precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2 + data = [] for cost_center, cost_center_items in cam_map.items(): @@ -39,7 +42,7 @@ def execute(filters=None): for month in relevant_months: month_data = monthwise_data.get(month, {}) for i, fieldname in enumerate(["target", "actual", "variance"]): - value = flt(month_data.get(fieldname)) + value = flt(month_data.get(fieldname), precision) period_data[i] += value totals[i] += value period_data[2] = period_data[0] - period_data[1] @@ -61,7 +64,7 @@ def get_columns(filters): group_months = False if filters["period"] == "Monthly" else True - for from_date, to_date in get_period_date_ranges(filters): + 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"))) @@ -70,41 +73,6 @@ def get_columns(filters): return columns + ["Total Target::80", "Total Actual::80", "Total Variance::80"] -def get_period_date_ranges(filters): - from dateutil.relativedelta import relativedelta - - year_start_date, year_end_date = get_year_start_end_date(filters) - - increment = { - "Monthly": 1, - "Quarterly": 3, - "Half-Yearly": 6, - "Yearly": 12 - }.get(filters["period"]) - - period_date_ranges = [] - for i in xrange(1, 13, increment): - period_end_date = year_start_date + relativedelta(months=increment, - days=-1) - period_date_ranges.append([year_start_date, period_end_date]) - year_start_date = period_end_date + relativedelta(days=1) - - return period_date_ranges - -def get_period_month_ranges(filters): - from dateutil.relativedelta import relativedelta - period_month_ranges = [] - - for start_date, end_date in get_period_date_ranges(filters): - months_in_this_period = [] - while start_date <= end_date: - months_in_this_period.append(start_date.strftime("%B")) - start_date += relativedelta(months=1) - period_month_ranges.append(months_in_this_period) - - return period_month_ranges - - #Get cost center & target details def get_costcenter_target_details(filters): return webnotes.conn.sql("""select cc.name, cc.distribution_id, @@ -122,7 +90,7 @@ def get_target_distribution_details(filters): 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""" % ('%s'), (filters.get("fiscal_year")), as_dict=1): + bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): target_details.setdefault(d.month, d) return target_details @@ -151,18 +119,11 @@ def get_costcenter_account_month_map(filters): })) tav_dict = cam_map[ccd.name][ccd.account][month] - tav_dict.target = ccd.budget_allocated*(tdd[month]["percentage_allocation"]/100) + tav_dict.target = flt(ccd.budget_allocated)*(tdd[month]["percentage_allocation"]/100) for ad in actual_details: if ad.month_name == month and ad.account == ccd.account \ and ad.cost_center == ccd.name: tav_dict.actual += ad.debit - ad.credit - return cam_map - -def get_year_start_end_date(filters): - return webnotes.conn.sql("""select year_start_date, - subdate(adddate(year_start_date, interval 1 year), interval 1 day) - as year_end_date - from `tabFiscal Year` - where name=%s""", filters["fiscal_year"])[0] \ No newline at end of file + return cam_map \ No newline at end of file diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 87e12db904..7a0848e931 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -167,12 +167,12 @@ wn.module_page["Selling"] = [ }, { "label":wn._("Territory Target Variance (Item Group-Wise)"), - route: "query-report/Territory Target Variance (Item Group-Wise)", + route: "query-report/Territory Target Variance Item Group-Wise", doctype: "Sales Order" }, { "label":wn._("Sales Person Target Variance (Item Group-Wise)"), - route: "query-report/Sales Person Target Variance (Item Group-Wise)", + route: "query-report/Sales Person Target Variance Item Group-Wise", doctype: "Sales Order" }, { diff --git a/selling/report/sales_person_target_variance_(item_group_wise)/__init__.py b/selling/report/sales_person_target_variance_item_group_wise/__init__.py similarity index 100% rename from selling/report/sales_person_target_variance_(item_group_wise)/__init__.py rename to selling/report/sales_person_target_variance_item_group_wise/__init__.py diff --git a/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.js similarity index 86% rename from selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js rename to selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.js index 09f0d55aed..c7a5d668c4 100644 --- a/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).js +++ b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.js @@ -1,4 +1,4 @@ -wn.query_reports["Sales Person Target Variance (Item Group-Wise)"] = { +wn.query_reports["Sales Person Target Variance Item Group-Wise"] = { "filters": [ { fieldname: "fiscal_year", 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 similarity index 67% rename from selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).py rename to selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py index 8f5931d826..6e39143f2e 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 @@ -16,18 +16,21 @@ from __future__ import unicode_literals import webnotes -import calendar from webnotes import _, msgprint 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 def execute(filters=None): if not filters: filters = {} columns = get_columns(filters) - period_month_ranges = get_period_month_ranges(filters) + 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(): @@ -39,7 +42,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)) + value = flt(month_data.get(fieldname), precision) period_data[i] += value totals[i] += value period_data[2] = period_data[0] - period_data[1] @@ -61,7 +64,7 @@ def get_columns(filters): group_months = False if filters["period"] == "Monthly" else True - for from_date, to_date in get_period_date_ranges(filters): + 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"))) @@ -70,49 +73,14 @@ def get_columns(filters): return columns + ["Total Target::80", "Total Achieved::80", "Total Variance::80"] -def get_period_date_ranges(filters): - from dateutil.relativedelta import relativedelta - - year_start_date, year_end_date = get_year_start_end_date(filters) - - increment = { - "Monthly": 1, - "Quarterly": 3, - "Half-Yearly": 6, - "Yearly": 12 - }.get(filters["period"]) - - period_date_ranges = [] - for i in xrange(1, 13, increment): - period_end_date = year_start_date + relativedelta(months=increment, - days=-1) - period_date_ranges.append([year_start_date, period_end_date]) - year_start_date = period_end_date + relativedelta(days=1) - - return period_date_ranges - -def get_period_month_ranges(filters): - from dateutil.relativedelta import relativedelta - period_month_ranges = [] - - for start_date, end_date in get_period_date_ranges(filters): - months_in_this_period = [] - while start_date <= end_date: - months_in_this_period.append(start_date.strftime("%B")) - start_date += relativedelta(months=1) - period_month_ranges.append(months_in_this_period) - - return period_month_ranges - - #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""" % - ('%s'), (filters.get("fiscal_year")), as_dict=1) + ifnull(sp.distribution_id, '')!='' order by sp.name""", + (filters["fiscal_year"]), as_dict=1) #Get target distribution details of item group def get_target_distribution_details(filters): @@ -121,14 +89,14 @@ def get_target_distribution_details(filters): 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 """ % ('%s'), (filters.get("fiscal_year")), as_dict=1): + bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): target_details.setdefault(d.month, d) return target_details #Get achieved details from sales order def get_achieved_details(filters): - start_date, end_date = get_year_start_end_date(filters) + start_date, end_date = get_fiscal_year(filters)[1:] return webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, st.sales_person, MONTHNAME(so.transaction_date) as month_name @@ -156,28 +124,18 @@ def get_salesperson_item_month_map(filters): for ad in achieved_details: if (filters["target_on"] == "Quantity"): - tav_dict.target = sd.target_qty*(tdd[month]["percentage_allocation"]/100) - if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == sd.item_group \ + tav_dict.target = flt(sd.target_qty)*(tdd[month]["percentage_allocation"]/100) + 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 = sd.target_amount*(tdd[month]["percentage_allocation"]/100) - if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == sd.item_group \ + tav_dict.target = flt(sd.target_amount)*(tdd[month]["percentage_allocation"]/100) + 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 return sim_map -def get_year_start_end_date(filters): - return webnotes.conn.sql("""select year_start_date, - subdate(adddate(year_start_date, interval 1 year), interval 1 day) - as year_end_date - from `tabFiscal Year` - where name=%s""", filters["fiscal_year"])[0] - def get_item_group(item_name): - """Get Item Group of an item""" - - return webnotes.conn.sql_list("select item_group from `tabItem` where name=%s""" % - ('%s'), (item_name)) \ No newline at end of file + return webnotes.conn.get_value("Item", item_name, item_group) \ No newline at end of file diff --git a/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.txt similarity index 57% rename from selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt rename to selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.txt index 955cdec477..e587c2c084 100644 --- a/selling/report/sales_person_target_variance_(item_group_wise)/sales_person_target_variance_(item_group_wise).txt +++ b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-06-18 12:09:40", + "creation": "2013-06-21 12:14:15", "docstatus": 0, - "modified": "2013-06-18 12:09:40", + "modified": "2013-06-21 12:14:15", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,11 +11,11 @@ "is_standard": "Yes", "name": "__common__", "ref_doctype": "Sales Order", - "report_name": "Sales Person Target Variance (Item Group-Wise)", + "report_name": "Sales Person Target Variance Item Group-Wise", "report_type": "Script Report" }, { "doctype": "Report", - "name": "Sales Person Target Variance (Item Group-Wise)" + "name": "Sales Person Target Variance Item Group-Wise" } ] \ No newline at end of file diff --git a/selling/report/territory_target_variance_(item_group_wise)/__init__.py b/selling/report/territory_target_variance_item_group_wise/__init__.py similarity index 100% rename from selling/report/territory_target_variance_(item_group_wise)/__init__.py rename to selling/report/territory_target_variance_item_group_wise/__init__.py diff --git a/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.js similarity index 87% rename from selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js rename to selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.js index 58718a4e0b..146b17d02b 100644 --- a/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).js +++ b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.js @@ -1,4 +1,4 @@ -wn.query_reports["Territory Target Variance (Item Group-Wise)"] = { +wn.query_reports["Territory Target Variance Item Group-Wise"] = { "filters": [ { fieldname: "fiscal_year", 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 similarity index 66% rename from selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).py rename to selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py index 079f8e82a2..e7ddc6e6f9 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 @@ -16,18 +16,21 @@ from __future__ import unicode_literals import webnotes -import calendar from webnotes import _, msgprint 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 def execute(filters=None): if not filters: filters = {} columns = get_columns(filters) - period_month_ranges = get_period_month_ranges(filters) + period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) tim_map = get_territory_item_month_map(filters) + precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2 + data = [] for territory, territory_items in tim_map.items(): @@ -39,7 +42,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)) + value = flt(month_data.get(fieldname), precision) period_data[i] += value totals[i] += value period_data[2] = period_data[0] - period_data[1] @@ -61,7 +64,7 @@ def get_columns(filters): group_months = False if filters["period"] == "Monthly" else True - for from_date, to_date in get_period_date_ranges(filters): + 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"))) @@ -70,49 +73,14 @@ def get_columns(filters): return columns + ["Total Target::80", "Total Achieved::80", "Total Variance::80"] -def get_period_date_ranges(filters): - from dateutil.relativedelta import relativedelta - - year_start_date, year_end_date = get_year_start_end_date(filters) - - increment = { - "Monthly": 1, - "Quarterly": 3, - "Half-Yearly": 6, - "Yearly": 12 - }.get(filters["period"]) - - period_date_ranges = [] - for i in xrange(1, 13, increment): - period_end_date = year_start_date + relativedelta(months=increment, - days=-1) - period_date_ranges.append([year_start_date, period_end_date]) - year_start_date = period_end_date + relativedelta(days=1) - - return period_date_ranges - -def get_period_month_ranges(filters): - from dateutil.relativedelta import relativedelta - period_month_ranges = [] - - for start_date, end_date in get_period_date_ranges(filters): - months_in_this_period = [] - while start_date <= end_date: - months_in_this_period.append(start_date.strftime("%B")) - start_date += relativedelta(months=1) - period_month_ranges.append(months_in_this_period) - - return period_month_ranges - - #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""" % - ('%s'), (filters.get("fiscal_year")), as_dict=1) + ifnull(t.distribution_id, '')!='' order by t.name""", + (filters["fiscal_year"]), as_dict=1) #Get target distribution details of item group def get_target_distribution_details(filters): @@ -121,14 +89,14 @@ def get_target_distribution_details(filters): 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""" % ('%s'), (filters.get("fiscal_year")), as_dict=1): + bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): target_details.setdefault(d.month, d) return target_details #Get achieved details from sales order def get_achieved_details(filters): - start_date, end_date = get_year_start_end_date(filters) + start_date, end_date = get_fiscal_year(filters)[1:] return webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, so.territory, MONTHNAME(so.transaction_date) as month_name @@ -155,28 +123,18 @@ def get_territory_item_month_map(filters): for ad in achieved_details: if (filters["target_on"] == "Quantity"): - tav_dict.target = td.target_qty*(tdd[month]["percentage_allocation"]/100) - if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == td.item_group \ + tav_dict.target = flt(td.target_qty)*(tdd[month]["percentage_allocation"]/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 = td.target_amount*(tdd[month]["percentage_allocation"]/100) - if ad.month_name == month and ''.join(get_item_group(ad.item_code)) == td.item_group \ + tav_dict.target = flt(td.target_amount)*(tdd[month]["percentage_allocation"]/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 return tim_map -def get_year_start_end_date(filters): - return webnotes.conn.sql("""select year_start_date, - subdate(adddate(year_start_date, interval 1 year), interval 1 day) - as year_end_date - from `tabFiscal Year` - where name=%s""", filters["fiscal_year"])[0] - def get_item_group(item_name): - """Get Item Group of an item""" - - return webnotes.conn.sql_list("select item_group from `tabItem` where name=%s""" % - ('%s'), (item_name)) \ No newline at end of file + return webnotes.conn.get_value("Item", item_name, item_group) \ No newline at end of file diff --git a/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.txt similarity index 57% rename from selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt rename to selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.txt index 7fff64a861..6e16b64d4e 100644 --- a/selling/report/territory_target_variance_(item_group_wise)/territory_target_variance_(item_group_wise).txt +++ b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-06-07 15:13:13", + "creation": "2013-06-21 12:15:00", "docstatus": 0, - "modified": "2013-06-07 15:13:13", + "modified": "2013-06-21 12:15:00", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,11 +11,11 @@ "is_standard": "Yes", "name": "__common__", "ref_doctype": "Sales Order", - "report_name": "Territory Target Variance (Item Group-Wise)", + "report_name": "Territory Target Variance Item Group-Wise", "report_type": "Script Report" }, { "doctype": "Report", - "name": "Territory Target Variance (Item Group-Wise)" + "name": "Territory Target Variance Item Group-Wise" } ] \ No newline at end of file diff --git a/stock/report/item_prices/item_prices.py b/stock/report/item_prices/item_prices.py index 86ae085ce7..7a3877bcca 100644 --- a/stock/report/item_prices/item_prices.py +++ b/stock/report/item_prices/item_prices.py @@ -24,16 +24,22 @@ def execute(filters=None): columns = get_columns(filters) item_map = get_item_details() pl = get_price_list() + last_purchase_rate = get_last_purchase_rate() bom_rate = get_item_bom_rate() val_rate_map = get_valuation_rate() + + precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2 data = [] for item in sorted(item_map): data.append([item, item_map[item]["item_name"], item_map[item]["description"], item_map[item]["stock_uom"], - flt(item_map[item]["last_purchase_rate"]), val_rate_map.get(item, 0), - pl.get(item, {}).get("selling"), pl.get(item, {}).get("buying"), - bom_rate.get(item, 0), flt(item_map[item]["standard_rate"]) + flt(last_purchase_rate.get(item, 0), precision), + flt(val_rate_map.get(item, 0), precision), + pl.get(item, {}).get("selling"), + pl.get(item, {}).get("buying"), + flt(bom_rate.get(item, 0), precision), + flt(item_map[item]["standard_rate"]) ]) return columns, data @@ -53,7 +59,7 @@ def get_item_details(): item_map = {} for i in webnotes.conn.sql("select name, item_name, description, \ - stock_uom, standard_rate, last_purchase_rate from tabItem \ + stock_uom, standard_rate from tabItem \ order by item_code", as_dict=1): item_map.setdefault(i.name, i) @@ -84,24 +90,60 @@ def get_price_list(): return item_rate_map +def get_last_purchase_rate(): + + item_last_purchase_rate_map = {} + + query = """select * from (select + result.item_code, + result.purchase_rate + from ( + (select + po_item.item_code, + po_item.item_name, + po.transaction_date as posting_date, + po_item.purchase_ref_rate, + po_item.discount_rate, + po_item.purchase_rate + from `tabPurchase Order` po, `tabPurchase Order Item` po_item + where po.name = po_item.parent and po.docstatus = 1) + union + (select + pr_item.item_code, + pr_item.item_name, + pr.posting_date, + pr_item.purchase_ref_rate, + pr_item.discount_rate, + pr_item.purchase_rate + from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item + where pr.name = pr_item.parent and pr.docstatus = 1) + ) result + order by result.item_code asc, result.posting_date desc) result_wrapper + group by item_code""" + + for d in webnotes.conn.sql(query, as_dict=1): + item_last_purchase_rate_map.setdefault(d.item_code, d.purchase_rate) + + return item_last_purchase_rate_map + def get_item_bom_rate(): """Get BOM rate of an item from BOM""" - bom_map = {} + item_bom_map = {} for b in webnotes.conn.sql("""select item, (total_cost/quantity) as bom_rate from `tabBOM` where is_active=1 and is_default=1""", as_dict=1): - bom_map.setdefault(b.item, flt(b.bom_rate)) + item_bom_map.setdefault(b.item, flt(b.bom_rate)) - return bom_map + return item_bom_map def get_valuation_rate(): """Get an average valuation rate of an item from all warehouses""" - val_rate_map = {} + item_val_rate_map = {} for d in webnotes.conn.sql("""select item_code, avg(valuation_rate) as val_rate from tabBin group by item_code""", as_dict=1): - val_rate_map.setdefault(d.item_code, d.val_rate) + item_val_rate_map.setdefault(d.item_code, flt(d.val_rate)) - return val_rate_map \ No newline at end of file + return item_val_rate_map \ No newline at end of file From f78d16cca1d5a8e2606e5601f5f642c21ea4c28b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 21 Jun 2013 15:13:49 +0530 Subject: [PATCH 254/295] [general ledger] group by voucher option --- .../page/general_ledger/general_ledger.js | 63 ++++++++++++++++++- patches/september_2012/repost_stock.py | 2 +- startup/report_data_map.py | 5 +- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index a462b70e93..dfba26cf6d 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -63,11 +63,21 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ open_btn: true, doctype: "dataContext.voucher_type" }}, + {id: "against_voucher_type", name: "Against Voucher Type", + field: "against_voucher_type", width: 120}, + {id: "against_voucher", name: "Against Voucher", + field: "against_voucher", width: 160, + link_formatter: { + filter_input: "against_voucher", + open_btn: true, + doctype: "dataContext.against_voucher_type" + }}, {id: "remarks", name: "Remarks", field: "remarks", width: 200, formatter: this.text_formatter}, ]; }, + filters: [ {fieldtype:"Select", label: "Company", link:"Company", default_value: "Select Company...", filter: function(val, item, opts) { @@ -96,6 +106,7 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ return dateutil.str_to_obj(val) >= dateutil.str_to_obj(item.posting_date); }}, {fieldtype: "Check", label: "Group by Ledger"}, + {fieldtype: "Check", label: "Group by Voucher"}, {fieldtype:"Button", label: "Refresh", icon:"icon-refresh icon-white", cssClass:"btn-info"}, {fieldtype:"Button", label: "Reset Filters"} ], @@ -116,9 +127,14 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ me.filter_inputs.group_by_ledger .parent().toggle(!!(me.account_by_name[$(this).val()] && me.account_by_name[$(this).val()].group_or_ledger==="Group")); + + me.filter_inputs.group_by_voucher + .parent().toggle(!!(me.account_by_name[$(this).val()] + && me.account_by_name[$(this).val()].group_or_ledger==="Ledger")); }); this.trigger_refresh_on_change(["group_by_ledger"]); + this.trigger_refresh_on_change(["group_by_voucher"]); }, setup_account_filter: function(company_filter) { var me = this; @@ -139,12 +155,14 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ init_filter_values: function() { this._super(); this.filter_inputs.group_by_ledger.parent().toggle(false); + this.filter_inputs.group_by_voucher.parent().toggle(false); this.filter_inputs.company.change(); this.filter_inputs.account.change(); }, apply_filters_from_route: function() { this._super(); this.filter_inputs.group_by_ledger.parent().toggle(false); + this.filter_inputs.group_by_voucher.parent().toggle(false); this.filter_inputs.company.change(); this.filter_inputs.account.change(); }, @@ -196,13 +214,21 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ if(!grouped_ledgers[item.account]) { grouped_ledgers[item.account] = { entries: [], + entries_group_by_voucher: {}, opening: me.make_summary_row("Opening", item.account), totals: me.make_summary_row("Totals", item.account), closing: me.make_summary_row("Closing (Opening + Totals)", item.account) }; } - + + if(!grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no]) { + grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] = { + entries: [], + totals: me.make_summary_row("Totals", item.voucher_no) + } + } + if(date < from_date || item.is_opening=="Yes") { opening.debit += item.debit; opening.credit += item.credit; @@ -215,6 +241,11 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ grouped_ledgers[item.account].totals.debit += item.debit; grouped_ledgers[item.account].totals.credit += item.credit; + + grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] + .totals.debit += item.debit; + grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] + .totals.credit += item.credit; } if(item.account) { item.against_account = me.voucher_accounts[item.voucher_type + ":" @@ -224,6 +255,9 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ if(me.apply_filters(item) && item.is_opening=="No") { out.push(item); grouped_ledgers[item.account].entries.push(item); + + grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] + .entries.push(item); } } }); @@ -241,6 +275,11 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ out = this.group_data_by_ledger(grouped_ledgers); } + if(this.account_by_name[this.account].group_or_ledger==="Ledger" + && this.group_by_voucher) { + out = this.group_data_by_voucher(grouped_ledgers); + } + opening = me.get_balance(me.account_by_name[me.account].debit_or_credit, opening) closing = me.get_balance(me.account_by_name[me.account].debit_or_credit, closing) @@ -283,6 +322,28 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ return [{id: "_blank_first", _no_format: true, debit: "", credit: ""}].concat(out); }, + group_data_by_voucher: function(grouped_ledgers) { + var me = this; + var out = [] + $.each(Object.keys(grouped_ledgers).sort(), function(i, account) { + if(grouped_ledgers[account].entries.length) { + $.each(Object.keys(grouped_ledgers[account].entries_group_by_voucher).sort(), + function(j, voucher) { + if(grouped_ledgers[account].entries_group_by_voucher[voucher] + .entries.length) { + out = out.concat(grouped_ledgers[account] + .entries_group_by_voucher[voucher].entries) + .concat([ + grouped_ledgers[account].entries_group_by_voucher[voucher] + .totals, {id: "_blank" + j, _no_format: true, + debit: "", credit: ""}]); + } + }); + } + }); + return [{id: "_blank_first", _no_format: true, debit: "", credit: ""}].concat(out); + }, + get_balance: function(debit_or_credit, balance) { if(debit_or_credit == "Debit") { balance.debit -= balance.credit; balance.credit = 0; diff --git a/patches/september_2012/repost_stock.py b/patches/september_2012/repost_stock.py index 972070137a..deb218637e 100644 --- a/patches/september_2012/repost_stock.py +++ b/patches/september_2012/repost_stock.py @@ -22,7 +22,7 @@ def execute(): i=0 for d in res: try: - update_entries_after({ "item_code": d[0], "warehouse": d[1] }) + update_entries_after({ "item_code": d[0], "warehouse": d[1]}) except: pass i += 1 diff --git a/startup/report_data_map.py b/startup/report_data_map.py index 501db4f6ce..ae3a53fb40 100644 --- a/startup/report_data_map.py +++ b/startup/report_data_map.py @@ -47,8 +47,9 @@ data_map = { "order_by": "lft" }, "GL Entry": { - "columns": ["name", "account", "posting_date", "cost_center", "debit", "credit", "is_opening", - "company", "voucher_type", "voucher_no", "remarks"], + "columns": ["name", "account", "posting_date", "cost_center", "debit", "credit", + "is_opening", "company", "voucher_type", "voucher_no", "remarks", + "against_voucher_type", "against_voucher"], "conditions": ["ifnull(is_cancelled, 'No')='No'"], "order_by": "posting_date, account", "links": { From 36cb3caaa774cd6a04c1e68f76ebaab55eaae411 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 21 Jun 2013 16:25:28 +0530 Subject: [PATCH 255/295] [Pending so Items for purchase receipt] --- controllers/trends.py | 11 +++++----- selling/page/selling_home/selling_home.js | 4 ++++ .../__init__.py | 0 .../pending_so_items_for_purchase_request.txt | 22 +++++++++++++++++++ 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 selling/report/pending_so_items_for_purchase_request/__init__.py create mode 100644 selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt diff --git a/controllers/trends.py b/controllers/trends.py index acbc744ae6..2cdccb182d 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -35,7 +35,7 @@ def get_columns(filters, trans): ["Total(Qty):Float:120", "Total(Amt):Currency:120"] conditions = {"based_on_select": based_on_details["based_on_select"], "period_wise_select": period_select, - "columns": columns, "group_by": based_on_details["based_on_group_by"], "grbc": group_by_cols, "trans": trans, + "columns": columns, "group_by": based_on_details["based_on_group_by"], "grbc": group_by_cols, "trans": trans, "addl_tables": based_on_details["addl_tables"]} return conditions @@ -72,8 +72,8 @@ def get_data(filters, conditions): else : inc = 1 data1 = webnotes.conn.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s - where t2.parent = t1.name and t1.company = %s - and t1.fiscal_year = %s and t1.docstatus = 1 %s + where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and + t1.docstatus = 1 %s group by %s """ % (query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", "%s", cond, conditions["group_by"]), (filters.get("company"), @@ -110,10 +110,9 @@ def get_data(filters, conditions): data.append(des) else: - webnotes.errprint(["hii", conditions["addl_tables"]]) data = webnotes.conn.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s - where t2.parent = t1.name and t1.company = %s - and t1.fiscal_year = %s and t1.docstatus = 1 %s + where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and + t1.docstatus = 1 %s group by %s """%(query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", "%s", cond,conditions["group_by"]), (filters.get("company"), diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 87e12db904..91649e39c8 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -190,6 +190,10 @@ wn.module_page["Selling"] = [ route: "query-report/Sales Order Trends", doctype: "Sales Order" }, + { + "label":wn._("Pending SO Items For Purchase Request"), + route: "query-report/Pending SO Items For Purchase Request" + }, ] } ] diff --git a/selling/report/pending_so_items_for_purchase_request/__init__.py b/selling/report/pending_so_items_for_purchase_request/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt b/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt new file mode 100644 index 0000000000..080e4556c1 --- /dev/null +++ b/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-06-21 12:07:36", + "docstatus": 0, + "modified": "2013-06-21 14:47:37", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select \n so_item.item_code as \"Item Code:Link/Item:120\",\n so_item.item_name as \"Item Name::120\",\n so_item.description as \"Description::120\",\n so.`name` as \"S.O. No.:Link/Sales Order:120\",\n so.`transaction_date` as \"S.O. Date:Date:120\",\n mr.name as \"Request ID:Link/Material Request:120\",\n so.customer as \"Customer:Link/Customer:120\",\n so.territory as \"Terretory:Link/Territory:120\",\n so_item.qty as \"S.O. Qty:Float:100 \",\n SUM(mr_item.qty) as \"Requested Qty:Float:100\"\nfrom\n `tabSales Order` so, `tabSales Order Item` so_item, \n `tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n so_item.`parent` = so.`name` and so.docstatus = 1 \n and so.status != \"Stopped\" and mr_item.parent = mr.name\n and mr.status != \"Stopped\" and mr.docstatus = 1\n and mr_item.sales_order_no = so.name\ngroup by so.name\norder by so_item.item_code asc, so.`transaction_date`", + "ref_doctype": "Sales Order", + "report_name": "Pending SO Items For Purchase Request", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Pending SO Items For Purchase Request" + } +] \ No newline at end of file From 4555d3bcf4d0a96b5e3f3e21d090e20c7ffd8ecc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 21 Jun 2013 16:27:34 +0530 Subject: [PATCH 256/295] [report][trends] --- controllers/trends.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/controllers/trends.py b/controllers/trends.py index acbc744ae6..528f5e4d0e 100644 --- a/controllers/trends.py +++ b/controllers/trends.py @@ -89,9 +89,10 @@ def get_data(filters, conditions): row = webnotes.conn.sql("""select DISTINCT(%s) from `tab%s` t1, `tab%s Item` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 and %s = %s - """%(sel_col, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", "%s", - conditions["group_by"], "%s"), (filters.get("company"), filters.get("fiscal_year"), - data1[d][0]), as_list=1) + """ % + (sel_col, conditions["trans"], conditions["trans"], conditions["addl_tables"], + "%s", "%s", conditions["group_by"], "%s"), + (filters.get("company"), filters.get("fiscal_year"), data1[d][0]), as_list=1) for i in range(len(row)): des = ['' for q in range(len(conditions["columns"]))] @@ -100,9 +101,12 @@ def get_data(filters, conditions): row1 = webnotes.conn.sql(""" select %s , %s from `tab%s` t1, `tab%s Item` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 and %s = %s and %s = %s - """ % (sel_col, conditions["period_wise_select"], conditions["trans"], conditions["trans"], - conditions["addl_tables"], "%s", "%s", sel_col, "%s", conditions["group_by"], "%s"), - (filters.get("company"), filters.get("fiscal_year"), row[i][0], data1[d][0]), as_list=1) + """ % + (sel_col, conditions["period_wise_select"], conditions["trans"], + conditions["trans"], conditions["addl_tables"], "%s", "%s", sel_col, + "%s", conditions["group_by"], "%s"), + (filters.get("company"), filters.get("fiscal_year"), row[i][0], + data1[d][0]), as_list=1) des[ind] = row[i] for j in range(1,len(conditions["columns"])-inc): @@ -110,14 +114,14 @@ def get_data(filters, conditions): data.append(des) else: - webnotes.errprint(["hii", conditions["addl_tables"]]) data = webnotes.conn.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and t1.docstatus = 1 %s group by %s - """%(query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", - "%s", cond,conditions["group_by"]), (filters.get("company"), - filters.get("fiscal_year")), as_list=1) + """ % + (query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], + "%s", "%s", cond,conditions["group_by"]), + (filters.get("company"), filters.get("fiscal_year")), as_list=1) return data @@ -139,7 +143,8 @@ def period_wise_colums_query(filters, trans): get_period_wise_columns(dt, filters.get("period"), pwc) query_details = get_period_wise_query(dt, trans_date, query_details) else: - pwc = [filters.get("fiscal_year")+" (Qty):Float:120", filters.get("fiscal_year")+" (Amt):Currency:120"] + pwc = [filters.get("fiscal_year") + " (Qty):Float:120", + filters.get("fiscal_year") + " (Amt):Currency:120"] query_details = " SUM(t2.qty), SUM(t1.grand_total)," query_details += 'SUM(t2.qty), SUM(t1.grand_total)' @@ -154,10 +159,9 @@ def get_period_wise_columns(bet_dates, period, pwc): get_mon(bet_dates[0]) + "-" + get_mon(bet_dates[1]) + " (Amt):Currency:120"] def get_period_wise_query(bet_dates, trans_date, query_details): - query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.qty, NULL)), SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t1.grand_total, NULL)), - """%{"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1]} + """ % {"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1]} return query_details def get_period_date_ranges(period, fiscal_year): From 99fd7493344aaf584cbefb4e49af2a09017e779a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 21 Jun 2013 16:56:32 +0530 Subject: [PATCH 257/295] [report][cleanup] pending so items for purchase request --- .../pending_so_items_for_purchase_request.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt b/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt index 080e4556c1..e0c450a254 100644 --- a/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt +++ b/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-06-21 12:07:36", + "creation": "2013-06-21 16:46:45", "docstatus": 0, - "modified": "2013-06-21 14:47:37", + "modified": "2013-06-21 16:55:54", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,7 +10,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select \n so_item.item_code as \"Item Code:Link/Item:120\",\n so_item.item_name as \"Item Name::120\",\n so_item.description as \"Description::120\",\n so.`name` as \"S.O. No.:Link/Sales Order:120\",\n so.`transaction_date` as \"S.O. Date:Date:120\",\n mr.name as \"Request ID:Link/Material Request:120\",\n so.customer as \"Customer:Link/Customer:120\",\n so.territory as \"Terretory:Link/Territory:120\",\n so_item.qty as \"S.O. Qty:Float:100 \",\n SUM(mr_item.qty) as \"Requested Qty:Float:100\"\nfrom\n `tabSales Order` so, `tabSales Order Item` so_item, \n `tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n so_item.`parent` = so.`name` and so.docstatus = 1 \n and so.status != \"Stopped\" and mr_item.parent = mr.name\n and mr.status != \"Stopped\" and mr.docstatus = 1\n and mr_item.sales_order_no = so.name\ngroup by so.name\norder by so_item.item_code asc, so.`transaction_date`", + "query": "select \n so_item.item_code as \"Item Code:Link/Item:120\",\n so_item.item_name as \"Item Name::120\",\n so_item.description as \"Description::120\",\n so.`name` as \"S.O. No.:Link/Sales Order:120\",\n so.`transaction_date` as \"Date:Date:120\",\n mr.name as \"Material Request:Link/Material Request:120\",\n so.customer as \"Customer:Link/Customer:120\",\n so.territory as \"Terretory:Link/Territory:120\",\n sum(so_item.qty) as \"SO Qty:Float:100 \",\n sum(mr_item.qty) as \"Requested Qty:Float:100\"\nfrom\n `tabSales Order` so, `tabSales Order Item` so_item, \n `tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n so_item.`parent` = so.`name` and mr_item.sales_order_no = so.name\n and mr_item.parent = mr.name \n and so.docstatus = 1 and so.status != \"Stopped\" \n and mr.docstatus = 1 and mr.status != \"Stopped\"\ngroup by so.name, so_item.item_code\norder by so.name desc, so_item.item_code asc", "ref_doctype": "Sales Order", "report_name": "Pending SO Items For Purchase Request", "report_type": "Query Report" From b0f69cdd886220025344b95329f23e130d3eab91 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 21 Jun 2013 16:57:53 +0530 Subject: [PATCH 258/295] [report][deleted] itemwise last purchase rate --- .../item_wise_last_purchase_rate/__init__.py | 0 .../item_wise_last_purchase_rate.txt | 22 ------------------- 2 files changed, 22 deletions(-) delete mode 100644 buying/report/item_wise_last_purchase_rate/__init__.py delete mode 100644 buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt diff --git a/buying/report/item_wise_last_purchase_rate/__init__.py b/buying/report/item_wise_last_purchase_rate/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt b/buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt deleted file mode 100644 index db99e724ce..0000000000 --- a/buying/report/item_wise_last_purchase_rate/item_wise_last_purchase_rate.txt +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "creation": "2013-06-18 11:24:36", - "docstatus": 0, - "modified": "2013-06-18 15:28:57", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Report", - "is_standard": "Yes", - "name": "__common__", - "query": "select * from (select \n result.item_code as \"Item Code:Link/Item:120\",\n result.item_name as \"Item Name::120\",\n result.description as \"Description::150\",\n result.posting_date as \"Date::150\",\n result.purchase_ref_rate as \"Price List Rate::180\", \n result.discount_rate as \"Discount::120\", \n result.purchase_rate as \"Rate::120\"\nfrom (\n (select \n po_item.item_code,\n po_item.item_name,\n po_item.description,\n po.transaction_date as posting_date,\n po_item.purchase_ref_rate, \n po_item.discount_rate, \n po_item.purchase_rate\n from `tabPurchase Order` po, `tabPurchase Order Item` po_item\n where po.name = po_item.parent and po.docstatus = 1)\n union\n (select \n pr_item.item_code,\n pr_item.item_name,\n pr_item.description,\n pr.posting_date,\n pr_item.purchase_ref_rate,\n pr_item.discount_rate,\n pr_item.purchase_rate\n from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item\n where pr.name = pr_item.parent and pr.docstatus = 1)\n) result\norder by result.item_code asc, result.posting_date desc) result_wrapper\ngroup by `Item Code:Link/Item:120`\n", - "ref_doctype": "Purchase Order", - "report_name": "Item-wise Last Purchase Rate", - "report_type": "Query Report" - }, - { - "doctype": "Report", - "name": "Item-wise Last Purchase Rate" - } -] \ No newline at end of file From 65f038c9263f3ed3359399ba274249a303b4f23f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Jun 2013 11:59:37 +0530 Subject: [PATCH 259/295] [fixes] pull previous doc details --- accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- stock/doctype/delivery_note/delivery_note.js | 5 +---- stock/doctype/purchase_receipt/purchase_receipt.js | 8 +++----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index 92f17487bc..49292c2712 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -166,7 +166,7 @@ cur_frm.cscript.recalculate = function(doc, cdt, cdn) { cur_frm.cscript.get_items = function(doc, dt, dn) { var callback = function(r,rt) { unhide_field(['supplier_address', 'contact_person']); - refresh_many(['credit_to','supplier','supplier_address','contact_person','supplier_name', 'address_display', 'contact_display','contact_mobile', 'contact_email','entries', 'purchase_receipt_main', 'purchase_order_main', 'purchase_tax_details']); + cur_frm.refresh(); } $c_obj(make_doclist(dt,dn),'pull_details','',callback); } diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index 0a31dfe80e..6467f62ca2 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -112,10 +112,7 @@ cur_frm.cscript.get_items = function(doc,dt,dn) { if(doc.sales_order_no) { unhide_field(['customer_address','contact_person','territory','customer_group']); } - - refresh_many(['delivery_note_details', 'customer', 'customer_address', - 'contact_person', 'customer_name', 'address_display', 'contact_display', - 'contact_mobile', 'contact_email', 'territory', 'customer_group']); + cur_frm.refresh(); } } $c_obj(make_doclist(doc.doctype, doc.name),'pull_sales_order_details','',callback); diff --git a/stock/doctype/purchase_receipt/purchase_receipt.js b/stock/doctype/purchase_receipt/purchase_receipt.js index 82e494cfb7..419ccb4e6a 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/stock/doctype/purchase_receipt/purchase_receipt.js @@ -102,11 +102,9 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) { // Get Purchase Order Button // ----------------- cur_frm.cscript.pull_purchase_order_details = function(doc, dt, dn) { - var callback = function(r,rt) { - //unhide_field(['supplier_address','contact_person','supplier_name','address_display', 'contact_display', 'contact_mobile','contact_email']); - refresh_many(['supplier','supplier_address','contact_person', 'supplier_name', 'address_display', 'contact_display','contact_mobile', 'contact_email', 'purchase_receipt_details', 'purchase_tax_details']); - } - $c_obj(make_doclist(dt,dn),'get_po_details','',callback); + $c_obj(make_doclist(dt,dn),'get_po_details','',function(r,rt) { + cur_frm.refresh(); + }); } From 7edb191661f7e9b0395c788f216ee016ee0bc0d6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 24 Jun 2013 12:11:15 +0530 Subject: [PATCH 260/295] [complete setup] [usability] refresh after success --- public/js/complete_setup.js | 17 +++++++++++------ setup/doctype/setup_control/setup_control.py | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/public/js/complete_setup.js b/public/js/complete_setup.js index f0b21c8516..37c2fba52e 100644 --- a/public/js/complete_setup.js +++ b/public/js/complete_setup.js @@ -75,12 +75,17 @@ $.extend(erpnext.complete_setup, { if(!data) return; $(this).set_working(); $c_obj('Setup Control','setup_account',data,function(r, rt){ - sys_defaults = r.message; - user_fullname = r.message.user_fullname; - wn.boot.user_info[user].fullname = user_fullname; - d.hide(); - $('header').toggle(true); - wn.container.wntoolbar.set_user_name(); + if(!r.exc) { + sys_defaults = r.message; + user_fullname = r.message.user_fullname; + wn.boot.user_info[user].fullname = user_fullname; + d.hide(); + $('header').toggle(true); + wn.container.wntoolbar.set_user_name(); + + setTimeout(function() { window.location.reload(); }, 3000); + } + }); }; diff --git a/setup/doctype/setup_control/setup_control.py b/setup/doctype/setup_control/setup_control.py index 5bfe4a7d94..bf8b87f3ad 100644 --- a/setup/doctype/setup_control/setup_control.py +++ b/setup/doctype/setup_control/setup_control.py @@ -97,7 +97,7 @@ class DocType: self.create_email_digest() webnotes.clear_cache() - msgprint("Company setup is complete. Please refresh the page before continuing.") + msgprint("Company setup is complete. This page will be refreshed in a moment.") import webnotes.utils user_fullname = (args.get('first_name') or '') + (args.get('last_name') From 4cd977a2a9314e91be4dba35de00dbee5a9d897a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Jun 2013 12:20:35 +0530 Subject: [PATCH 261/295] [fixes] pull previous doc details --- accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- accounts/doctype/sales_invoice/sales_invoice.js | 2 +- selling/doctype/sales_order/sales_order.js | 2 +- stock/doctype/delivery_note/delivery_note.js | 2 +- stock/doctype/purchase_receipt/purchase_receipt.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index 49292c2712..8b7af8b9dc 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -166,7 +166,7 @@ cur_frm.cscript.recalculate = function(doc, cdt, cdn) { cur_frm.cscript.get_items = function(doc, dt, dn) { var callback = function(r,rt) { unhide_field(['supplier_address', 'contact_person']); - cur_frm.refresh(); + cur_frm.refresh_fields(); } $c_obj(make_doclist(dt,dn),'pull_details','',callback); } diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index be6ec3d001..f5301c1f1e 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -275,7 +275,7 @@ cur_frm.cscript.is_opening = function(doc, dt, dn) { cur_frm.cscript.get_items = function(doc, dt, dn) { var callback = function(r,rt) { unhide_field(['customer_address','contact_person', 'territory','customer_group']); - cur_frm.refresh(); + cur_frm.refresh_fields(); } get_server_fields('pull_details','','',doc, dt, dn,1,callback); } diff --git a/selling/doctype/sales_order/sales_order.js b/selling/doctype/sales_order/sales_order.js index f272b2e378..d50f06d848 100644 --- a/selling/doctype/sales_order/sales_order.js +++ b/selling/doctype/sales_order/sales_order.js @@ -163,7 +163,7 @@ cur_frm.cscript.pull_quotation_details = function(doc,dt,dn) { 'contact_person', 'territory', 'customer_group']); if(doc.customer) get_server_fields('get_shipping_address', doc.customer, '', doc, dt, dn, 0); } - cur_frm.refresh(); + cur_frm.refresh_fields(); } } diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index 6467f62ca2..5992d016b8 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -112,7 +112,7 @@ cur_frm.cscript.get_items = function(doc,dt,dn) { if(doc.sales_order_no) { unhide_field(['customer_address','contact_person','territory','customer_group']); } - cur_frm.refresh(); + cur_frm.refresh_fields(); } } $c_obj(make_doclist(doc.doctype, doc.name),'pull_sales_order_details','',callback); diff --git a/stock/doctype/purchase_receipt/purchase_receipt.js b/stock/doctype/purchase_receipt/purchase_receipt.js index 419ccb4e6a..609baa8a1e 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/stock/doctype/purchase_receipt/purchase_receipt.js @@ -103,7 +103,7 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) { // ----------------- cur_frm.cscript.pull_purchase_order_details = function(doc, dt, dn) { $c_obj(make_doclist(dt,dn),'get_po_details','',function(r,rt) { - cur_frm.refresh(); + cur_frm.refresh_fields(); }); } From 8b96ba1da65f8d71697e0f001b11da810ee0e9bc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Jun 2013 13:54:16 +0530 Subject: [PATCH 262/295] [general ledger] group by voucher --- .../page/general_ledger/general_ledger.js | 33 +++++++------------ startup/report_data_map.py | 3 +- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index dfba26cf6d..ffab56831a 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -63,15 +63,6 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ open_btn: true, doctype: "dataContext.voucher_type" }}, - {id: "against_voucher_type", name: "Against Voucher Type", - field: "against_voucher_type", width: 120}, - {id: "against_voucher", name: "Against Voucher", - field: "against_voucher", width: 160, - link_formatter: { - filter_input: "against_voucher", - open_btn: true, - doctype: "dataContext.against_voucher_type" - }}, {id: "remarks", name: "Remarks", field: "remarks", width: 200, formatter: this.text_formatter}, @@ -224,8 +215,8 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ if(!grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no]) { grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] = { - entries: [], - totals: me.make_summary_row("Totals", item.voucher_no) + row: {}, + totals: {"debit": 0, "credit": 0} } } @@ -256,8 +247,10 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ out.push(item); grouped_ledgers[item.account].entries.push(item); - grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] - .entries.push(item); + if(grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no].row){ + grouped_ledgers[item.account]. + entries_group_by_voucher[item.voucher_no].row = item; + } } } }); @@ -329,14 +322,12 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ if(grouped_ledgers[account].entries.length) { $.each(Object.keys(grouped_ledgers[account].entries_group_by_voucher).sort(), function(j, voucher) { - if(grouped_ledgers[account].entries_group_by_voucher[voucher] - .entries.length) { - out = out.concat(grouped_ledgers[account] - .entries_group_by_voucher[voucher].entries) - .concat([ - grouped_ledgers[account].entries_group_by_voucher[voucher] - .totals, {id: "_blank" + j, _no_format: true, - debit: "", credit: ""}]); + voucher_dict = grouped_ledgers[account].entries_group_by_voucher[voucher]; + if(voucher_dict.totals.debit || voucher_dict.totals.credit) { + voucher_dict.row.debit = voucher_dict.totals.debit; + voucher_dict.row.credit = voucher_dict.totals.credit; + voucher_dict.row.id = "entry" + voucher + out = out.concat(voucher_dict.row); } }); } diff --git a/startup/report_data_map.py b/startup/report_data_map.py index ae3a53fb40..068e470045 100644 --- a/startup/report_data_map.py +++ b/startup/report_data_map.py @@ -48,8 +48,7 @@ data_map = { }, "GL Entry": { "columns": ["name", "account", "posting_date", "cost_center", "debit", "credit", - "is_opening", "company", "voucher_type", "voucher_no", "remarks", - "against_voucher_type", "against_voucher"], + "is_opening", "company", "voucher_type", "voucher_no", "remarks"], "conditions": ["ifnull(is_cancelled, 'No')='No'"], "order_by": "posting_date, account", "links": { From 82366d3bb8a8cc99dfa757f8c9876986ec836f27 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Jun 2013 14:31:56 +0530 Subject: [PATCH 263/295] [report] deleted old reports based on search_criteria --- accounts/page/accounts_home/accounts_home.js | 5 - accounts/search_criteria/__init__.py | 1 - .../accounts_payable/__init__.py | 1 - .../accounts_payable/accounts_payable.js | 37 -- .../accounts_payable/accounts_payable.py | 183 ------ .../accounts_payable/accounts_payable.sql | 16 - .../accounts_payable/accounts_payable.txt | 26 - .../accounts_receivable/__init__.py | 1 - .../accounts_receivable.js | 35 -- .../accounts_receivable.py | 168 ------ .../accounts_receivable.sql | 16 - .../accounts_receivable.txt | 25 - .../bank_clearance_report/__init__.py | 1 - .../bank_clearance_report.js | 50 -- .../bank_clearance_report.txt | 32 -- .../bank_reconciliation_statement/__init__.py | 1 - .../bank_reconciliation_statement.js | 34 -- .../bank_reconciliation_statement.py | 66 --- .../bank_reconciliation_statement.txt | 29 - .../budget_variance_report/__init__.py | 1 - .../budget_variance_report.js | 31 -- .../budget_variance_report.py | 184 ------- .../budget_variance_report.sql | 10 - .../budget_variance_report.txt | 26 - .../__init__.py | 1 - .../business_associate_commission_report.js | 26 - .../business_associate_commission_report.py | 27 - .../business_associate_commission_report.txt | 29 - .../collection_report/__init__.py | 1 - .../collection_report/collection_report.js | 40 -- .../collection_report/collection_report.py | 107 ---- .../collection_report/collection_report.sql | 13 - .../collection_report/collection_report.txt | 26 - .../cost_center_wise_expense/__init__.py | 1 - .../cost_center_wise_expense.js | 39 -- .../cost_center_wise_expense.py | 30 - .../cost_center_wise_expense.sql | 20 - .../cost_center_wise_expense.txt | 26 - .../creditors_ledger/__init__.py | 1 - .../creditors_ledger/creditors_ledger.js | 40 -- .../creditors_ledger/creditors_ledger.py | 134 ----- .../creditors_ledger/creditors_ledger.sql | 15 - .../creditors_ledger/creditors_ledger.txt | 27 - .../debtors_ledger/__init__.py | 1 - .../debtors_ledger/debtors_ledger.js | 40 -- .../debtors_ledger/debtors_ledger.py | 133 ----- .../debtors_ledger/debtors_ledger.sql | 15 - .../debtors_ledger/debtors_ledger.txt | 27 - .../general_ledger/__init__.py | 1 - .../general_ledger/general_ledger.js | 39 -- .../general_ledger/general_ledger.py | 141 ----- .../general_ledger/general_ledger.sql | 13 - .../general_ledger/general_ledger.txt | 25 - .../invoices_overdue/__init__.py | 1 - .../invoices_overdue/invoices_overdue.txt | 27 - .../itemwise_purchase_register/__init__.py | 1 - .../itemwise_purchase_register.js | 43 -- .../itemwise_purchase_register.txt | 29 - .../itemwise_sales_register/__init__.py | 1 - .../itemwise_sales_register.js | 43 -- .../itemwise_sales_register.txt | 27 - .../monthly_transaction_summary/__init__.py | 1 - .../monthly_transaction_summary.js | 29 - .../monthly_transaction_summary.py | 168 ------ .../monthly_transaction_summary.sql | 1 - .../monthly_transaction_summary.txt | 25 - .../payment_receipt_report/__init__.py | 1 - .../payment_receipt_report.py | 41 -- .../payment_receipt_report.sql | 1 - .../payment_receipt_report.txt | 26 - .../purchase_register/__init__.py | 1 - .../purchase_register/purchase_register.py | 97 ---- .../purchase_register/purchase_register.txt | 24 - .../sales_register/__init__.py | 1 - .../sales_register/sales_register.js | 32 -- .../sales_register/sales_register.py | 93 ---- .../sales_register/sales_register.txt | 30 - .../trend_analyzer/__init__.py | 1 - .../trend_analyzer/trend_analyzer.js | 153 ------ .../trend_analyzer/trend_analyzer.py | 177 ------ .../trend_analyzer/trend_analyzer.txt | 26 - .../voucher_wise_tax_details/__init__.py | 1 - .../voucher_wise_tax_details.js | 63 --- .../voucher_wise_tax_details.py | 39 -- .../voucher_wise_tax_details.txt | 27 - .../yearly_transaction_summary/__init__.py | 1 - .../yearly_transaction_summary.js | 28 - .../yearly_transaction_summary.py | 131 ----- .../yearly_transaction_summary.sql | 1 - .../yearly_transaction_summary.txt | 25 - buying/page/buying_home/buying_home.js | 5 - buying/search_criteria/__init__.py | 1 - .../itemwise_purchase_details/__init__.py | 1 - .../itemwise_purchase_details.js | 21 - .../itemwise_purchase_details.txt | 27 - .../pending_po_items_to_bill/__init__.py | 1 - .../pending_po_items_to_bill.js | 20 - .../pending_po_items_to_bill.txt | 30 - .../pending_po_items_to_receive/__init__.py | 1 - .../pending_po_items_to_receive.js | 20 - .../pending_po_items_to_receive.txt | 30 - .../purchase_in_transit/__init__.py | 1 - .../purchase_in_transit.js | 48 -- .../purchase_in_transit.py | 24 - .../purchase_in_transit.txt | 32 -- hr/search_criteria/__init__.py | 1 - .../employee_appraisals/__init__.py | 1 - .../employee_appraisals.txt | 26 - .../employee_information/__init__.py | 1 - .../employee_information.py | 23 - .../employee_information.txt | 26 - .../employees_birthday/__init__.py | 1 - .../employees_birthday/employees_birthday.txt | 26 - hr/search_criteria/expense_claims/__init__.py | 1 - .../expense_claims/expense_claims.txt | 26 - .../monthly_attendance_details/__init__.py | 1 - .../monthly_attendance_details.js | 45 -- .../monthly_attendance_details.py | 93 ---- .../monthly_attendance_details.sql | 1 - .../monthly_attendance_details.txt | 26 - .../monthly_salary_register/__init__.py | 1 - .../monthly_salary_register.py | 99 ---- .../monthly_salary_register.txt | 26 - .../__init__.py | 1 - .../new_or_left_employees_for_a_month.js | 66 --- .../new_or_left_employees_for_a_month.py | 60 -- .../new_or_left_employees_for_a_month.txt | 26 - .../salary_register/__init__.py | 1 - .../salary_register/salary_register.js | 25 - .../salary_register/salary_register.py | 77 --- .../salary_register/salary_register.txt | 26 - hr/search_criteria/salary_slips/__init__.py | 1 - .../salary_slips/salary_slips.txt | 26 - .../salary_structure_details/__init__.py | 1 - .../salary_structure_details.txt | 26 - manufacturing/search_criteria/__init__.py | 1 - .../__init__.py | 1 - .../consumption_against_production.txt | 30 - .../itemwise_production_report/__init__.py | 1 - .../itemwise_production_report.js | 32 -- .../itemwise_production_report.txt | 27 - .../production_orders_in_process/__init__.py | 1 - .../production_orders_in_process.txt | 27 - .../p05_remove_search_criteria_reports.py | 6 +- patches/patch_list.py | 1 + projects/search_criteria/__init__.py | 1 - .../__init__.py | 1 - .../projectwise_delivered_qty_and_costs.js | 32 -- .../projectwise_delivered_qty_and_costs.txt | 28 - .../__init__.py | 1 - .../projectwise_pending_qty_and_costs.js | 31 -- .../projectwise_pending_qty_and_costs.txt | 29 - .../projectwise_purchase_details/__init__.py | 1 - .../projectwise_purchase_details.js | 108 ---- .../projectwise_purchase_details.py | 40 -- .../projectwise_purchase_details.txt | 24 - .../projectwise_sales_details/__init__.py | 1 - .../projectwise_sales_details.js | 112 ---- .../projectwise_sales_details.py | 37 -- .../projectwise_sales_details.txt | 24 - .../projectwise_sales_orders/__init__.py | 1 - .../projectwise_sales_orders.txt | 27 - .../timesheet_report/__init__.py | 1 - .../timesheet_report/timesheet_report.js | 23 - .../timesheet_report/timesheet_report.txt | 27 - selling/page/selling_home/selling_home.js | 5 - selling/search_criteria/__init__.py | 1 - .../delivered_items_to_be_install/__init__.py | 1 - .../delivered_items_to_be_install.js | 20 - .../delivered_items_to_be_install.txt | 30 - .../draft_sales_orders/__init__.py | 1 - .../draft_sales_orders/draft_sales_orders.js | 22 - .../draft_sales_orders/draft_sales_orders.txt | 29 - .../follow_up_report/__init__.py | 1 - .../follow_up_report/follow_up_report.js | 63 --- .../follow_up_report/follow_up_report.py | 33 -- .../follow_up_report/follow_up_report.txt | 27 - .../itemwise_delivery_details/__init__.py | 1 - .../itemwise_delivery_details.js | 21 - .../itemwise_delivery_details.py | 34 -- .../itemwise_delivery_details.txt | 27 - .../itemwise_sales_details/__init__.py | 1 - .../itemwise_sales_details.js | 27 - .../itemwise_sales_details.py | 36 -- .../itemwise_sales_details.txt | 27 - .../lead_to_follow_up/__init__.py | 1 - .../lead_to_follow_up/lead_to_follow_up.js | 21 - .../lead_to_follow_up/lead_to_follow_up.txt | 27 - .../lead_to_follow_up/sales_order_overdue.js | 27 - .../opportunity_to_follow_up/__init__.py | 1 - .../opportunity_to_follow_up.txt | 27 - .../__init__.py | 1 - ...s_orderwise_booking_&_delivery_summary.txt | 30 - .../__init__.py | 1 - .../sales_orderwise_pending_amount_to_bill.js | 20 - ...sales_orderwise_pending_amount_to_bill.txt | 31 -- .../__init__.py | 1 - ...sales_orderwise_pending_qty_to_deliver.txt | 31 -- .../__init__.py | 1 - ...persons_target_variance_item_group_wise.js | 57 -- ...persons_target_variance_item_group_wise.py | 145 ----- ...ersons_target_variance_item_group_wise.txt | 26 - .../__init__.py | 1 - .../sales_personwise_transaction_summary.js | 53 -- .../sales_personwise_transaction_summary.py | 30 - .../sales_personwise_transaction_summary.txt | 26 - .../__init__.py | 1 - .../serial_no_amc_expiring_this_month.txt | 27 - .../__init__.py | 1 - ...serial_no_warranty_expiring_this_month.txt | 27 - .../target_variance_report/__init__.py | 1 - .../target_variance_report.js | 43 -- .../target_variance_report.py | 127 ----- .../target_variance_report.txt | 27 - .../__init__.py | 1 - ...itories_target_variance_item_group_wise.js | 61 -- ...itories_target_variance_item_group_wise.py | 144 ----- ...tories_target_variance_item_group_wise.txt | 26 - .../__init__.py | 1 - .../territory_sales___variance_report.js | 22 - .../territory_sales___variance_report.py | 205 ------- .../territory_sales___variance_report.txt | 26 - .../total_target_variance_report/__init__.py | 1 - .../total_target_variance_report.js | 28 - .../total_target_variance_report.py | 223 -------- .../total_target_variance_report.sql | 1 - .../total_target_variance_report.txt | 27 - .../variance_report/__init__.py | 1 - .../variance_report/variance_report.js | 28 - .../variance_report/variance_report.py | 520 ------------------ .../variance_report/variance_report.sql | 1 - .../variance_report/variance_report.txt | 26 - support/search_criteria/__init__.py | 1 - .../search_criteria/amc_summary/__init__.py | 1 - .../amc_summary/amc_summary.js | 25 - .../amc_summary/amc_summary.py | 87 --- .../amc_summary/amc_summary.txt | 28 - .../customer_issues/__init__.py | 1 - .../customer_issues/customer_issues.js | 23 - .../customer_issues/customer_issues.txt | 26 - .../__init__.py | 1 - ...enance_orderwise_pending_amount_to_bill.js | 20 - ...nance_orderwise_pending_amount_to_bill.txt | 29 - .../maintenance_schedule_details/__init__.py | 1 - .../maintenance_schedule_details.js | 29 - .../maintenance_schedule_details.txt | 27 - .../warranty_amc_expiry_details/__init__.py | 1 - .../warranty_amc_expiry_details.js | 25 - .../warranty_amc_expiry_details.txt | 26 - .../warranty_amc_summary/__init__.py | 1 - .../warranty_amc_summary.js | 26 - .../warranty_amc_summary.py | 54 -- .../warranty_amc_summary.sql | 1 - .../warranty_amc_summary.txt | 25 - 254 files changed, 5 insertions(+), 8145 deletions(-) delete mode 100644 accounts/search_criteria/__init__.py delete mode 100644 accounts/search_criteria/accounts_payable/__init__.py delete mode 100644 accounts/search_criteria/accounts_payable/accounts_payable.js delete mode 100644 accounts/search_criteria/accounts_payable/accounts_payable.py delete mode 100644 accounts/search_criteria/accounts_payable/accounts_payable.sql delete mode 100644 accounts/search_criteria/accounts_payable/accounts_payable.txt delete mode 100644 accounts/search_criteria/accounts_receivable/__init__.py delete mode 100644 accounts/search_criteria/accounts_receivable/accounts_receivable.js delete mode 100644 accounts/search_criteria/accounts_receivable/accounts_receivable.py delete mode 100644 accounts/search_criteria/accounts_receivable/accounts_receivable.sql delete mode 100644 accounts/search_criteria/accounts_receivable/accounts_receivable.txt delete mode 100644 accounts/search_criteria/bank_clearance_report/__init__.py delete mode 100644 accounts/search_criteria/bank_clearance_report/bank_clearance_report.js delete mode 100644 accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt delete mode 100644 accounts/search_criteria/bank_reconciliation_statement/__init__.py delete mode 100644 accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.js delete mode 100644 accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.py delete mode 100644 accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.txt delete mode 100644 accounts/search_criteria/budget_variance_report/__init__.py delete mode 100644 accounts/search_criteria/budget_variance_report/budget_variance_report.js delete mode 100644 accounts/search_criteria/budget_variance_report/budget_variance_report.py delete mode 100644 accounts/search_criteria/budget_variance_report/budget_variance_report.sql delete mode 100644 accounts/search_criteria/budget_variance_report/budget_variance_report.txt delete mode 100644 accounts/search_criteria/business_associate_commission_report/__init__.py delete mode 100644 accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.js delete mode 100644 accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.py delete mode 100644 accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.txt delete mode 100644 accounts/search_criteria/collection_report/__init__.py delete mode 100644 accounts/search_criteria/collection_report/collection_report.js delete mode 100644 accounts/search_criteria/collection_report/collection_report.py delete mode 100644 accounts/search_criteria/collection_report/collection_report.sql delete mode 100644 accounts/search_criteria/collection_report/collection_report.txt delete mode 100644 accounts/search_criteria/cost_center_wise_expense/__init__.py delete mode 100644 accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.js delete mode 100644 accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.py delete mode 100644 accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.sql delete mode 100644 accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.txt delete mode 100644 accounts/search_criteria/creditors_ledger/__init__.py delete mode 100644 accounts/search_criteria/creditors_ledger/creditors_ledger.js delete mode 100644 accounts/search_criteria/creditors_ledger/creditors_ledger.py delete mode 100644 accounts/search_criteria/creditors_ledger/creditors_ledger.sql delete mode 100644 accounts/search_criteria/creditors_ledger/creditors_ledger.txt delete mode 100644 accounts/search_criteria/debtors_ledger/__init__.py delete mode 100644 accounts/search_criteria/debtors_ledger/debtors_ledger.js delete mode 100644 accounts/search_criteria/debtors_ledger/debtors_ledger.py delete mode 100644 accounts/search_criteria/debtors_ledger/debtors_ledger.sql delete mode 100644 accounts/search_criteria/debtors_ledger/debtors_ledger.txt delete mode 100644 accounts/search_criteria/general_ledger/__init__.py delete mode 100644 accounts/search_criteria/general_ledger/general_ledger.js delete mode 100644 accounts/search_criteria/general_ledger/general_ledger.py delete mode 100644 accounts/search_criteria/general_ledger/general_ledger.sql delete mode 100644 accounts/search_criteria/general_ledger/general_ledger.txt delete mode 100644 accounts/search_criteria/invoices_overdue/__init__.py delete mode 100644 accounts/search_criteria/invoices_overdue/invoices_overdue.txt delete mode 100644 accounts/search_criteria/itemwise_purchase_register/__init__.py delete mode 100644 accounts/search_criteria/itemwise_purchase_register/itemwise_purchase_register.js delete mode 100644 accounts/search_criteria/itemwise_purchase_register/itemwise_purchase_register.txt delete mode 100644 accounts/search_criteria/itemwise_sales_register/__init__.py delete mode 100755 accounts/search_criteria/itemwise_sales_register/itemwise_sales_register.js delete mode 100644 accounts/search_criteria/itemwise_sales_register/itemwise_sales_register.txt delete mode 100644 accounts/search_criteria/monthly_transaction_summary/__init__.py delete mode 100644 accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.js delete mode 100644 accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.py delete mode 100644 accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.sql delete mode 100644 accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.txt delete mode 100644 accounts/search_criteria/payment_receipt_report/__init__.py delete mode 100644 accounts/search_criteria/payment_receipt_report/payment_receipt_report.py delete mode 100644 accounts/search_criteria/payment_receipt_report/payment_receipt_report.sql delete mode 100644 accounts/search_criteria/payment_receipt_report/payment_receipt_report.txt delete mode 100644 accounts/search_criteria/purchase_register/__init__.py delete mode 100644 accounts/search_criteria/purchase_register/purchase_register.py delete mode 100644 accounts/search_criteria/purchase_register/purchase_register.txt delete mode 100644 accounts/search_criteria/sales_register/__init__.py delete mode 100644 accounts/search_criteria/sales_register/sales_register.js delete mode 100644 accounts/search_criteria/sales_register/sales_register.py delete mode 100644 accounts/search_criteria/sales_register/sales_register.txt delete mode 100644 accounts/search_criteria/trend_analyzer/__init__.py delete mode 100644 accounts/search_criteria/trend_analyzer/trend_analyzer.js delete mode 100644 accounts/search_criteria/trend_analyzer/trend_analyzer.py delete mode 100644 accounts/search_criteria/trend_analyzer/trend_analyzer.txt delete mode 100644 accounts/search_criteria/voucher_wise_tax_details/__init__.py delete mode 100644 accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.js delete mode 100644 accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.py delete mode 100644 accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.txt delete mode 100644 accounts/search_criteria/yearly_transaction_summary/__init__.py delete mode 100644 accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.js delete mode 100644 accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.py delete mode 100644 accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.sql delete mode 100644 accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.txt delete mode 100644 buying/search_criteria/__init__.py delete mode 100644 buying/search_criteria/itemwise_purchase_details/__init__.py delete mode 100644 buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.js delete mode 100644 buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.txt delete mode 100644 buying/search_criteria/pending_po_items_to_bill/__init__.py delete mode 100644 buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.js delete mode 100644 buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.txt delete mode 100644 buying/search_criteria/pending_po_items_to_receive/__init__.py delete mode 100644 buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.js delete mode 100644 buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.txt delete mode 100644 buying/search_criteria/purchase_in_transit/__init__.py delete mode 100644 buying/search_criteria/purchase_in_transit/purchase_in_transit.js delete mode 100644 buying/search_criteria/purchase_in_transit/purchase_in_transit.py delete mode 100644 buying/search_criteria/purchase_in_transit/purchase_in_transit.txt delete mode 100644 hr/search_criteria/__init__.py delete mode 100644 hr/search_criteria/employee_appraisals/__init__.py delete mode 100644 hr/search_criteria/employee_appraisals/employee_appraisals.txt delete mode 100644 hr/search_criteria/employee_information/__init__.py delete mode 100644 hr/search_criteria/employee_information/employee_information.py delete mode 100644 hr/search_criteria/employee_information/employee_information.txt delete mode 100644 hr/search_criteria/employees_birthday/__init__.py delete mode 100644 hr/search_criteria/employees_birthday/employees_birthday.txt delete mode 100644 hr/search_criteria/expense_claims/__init__.py delete mode 100644 hr/search_criteria/expense_claims/expense_claims.txt delete mode 100644 hr/search_criteria/monthly_attendance_details/__init__.py delete mode 100644 hr/search_criteria/monthly_attendance_details/monthly_attendance_details.js delete mode 100644 hr/search_criteria/monthly_attendance_details/monthly_attendance_details.py delete mode 100644 hr/search_criteria/monthly_attendance_details/monthly_attendance_details.sql delete mode 100644 hr/search_criteria/monthly_attendance_details/monthly_attendance_details.txt delete mode 100644 hr/search_criteria/monthly_salary_register/__init__.py delete mode 100644 hr/search_criteria/monthly_salary_register/monthly_salary_register.py delete mode 100644 hr/search_criteria/monthly_salary_register/monthly_salary_register.txt delete mode 100644 hr/search_criteria/new_or_left_employees_for_a_month/__init__.py delete mode 100644 hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.js delete mode 100644 hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.py delete mode 100644 hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.txt delete mode 100644 hr/search_criteria/salary_register/__init__.py delete mode 100644 hr/search_criteria/salary_register/salary_register.js delete mode 100644 hr/search_criteria/salary_register/salary_register.py delete mode 100644 hr/search_criteria/salary_register/salary_register.txt delete mode 100644 hr/search_criteria/salary_slips/__init__.py delete mode 100644 hr/search_criteria/salary_slips/salary_slips.txt delete mode 100644 hr/search_criteria/salary_structure_details/__init__.py delete mode 100644 hr/search_criteria/salary_structure_details/salary_structure_details.txt delete mode 100644 manufacturing/search_criteria/__init__.py delete mode 100644 manufacturing/search_criteria/consumption_against_production/__init__.py delete mode 100644 manufacturing/search_criteria/consumption_against_production/consumption_against_production.txt delete mode 100644 manufacturing/search_criteria/itemwise_production_report/__init__.py delete mode 100644 manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.js delete mode 100644 manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.txt delete mode 100644 manufacturing/search_criteria/production_orders_in_process/__init__.py delete mode 100644 manufacturing/search_criteria/production_orders_in_process/production_orders_in_process.txt rename buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.py => patches/june_2013/p05_remove_search_criteria_reports.py (85%) delete mode 100644 projects/search_criteria/__init__.py delete mode 100644 projects/search_criteria/projectwise_delivered_qty_and_costs/__init__.py delete mode 100644 projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.js delete mode 100644 projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.txt delete mode 100644 projects/search_criteria/projectwise_pending_qty_and_costs/__init__.py delete mode 100644 projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.js delete mode 100644 projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.txt delete mode 100644 projects/search_criteria/projectwise_purchase_details/__init__.py delete mode 100644 projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.js delete mode 100644 projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.py delete mode 100644 projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.txt delete mode 100644 projects/search_criteria/projectwise_sales_details/__init__.py delete mode 100644 projects/search_criteria/projectwise_sales_details/projectwise_sales_details.js delete mode 100644 projects/search_criteria/projectwise_sales_details/projectwise_sales_details.py delete mode 100644 projects/search_criteria/projectwise_sales_details/projectwise_sales_details.txt delete mode 100644 projects/search_criteria/projectwise_sales_orders/__init__.py delete mode 100644 projects/search_criteria/projectwise_sales_orders/projectwise_sales_orders.txt delete mode 100644 projects/search_criteria/timesheet_report/__init__.py delete mode 100644 projects/search_criteria/timesheet_report/timesheet_report.js delete mode 100644 projects/search_criteria/timesheet_report/timesheet_report.txt delete mode 100644 selling/search_criteria/__init__.py delete mode 100644 selling/search_criteria/delivered_items_to_be_install/__init__.py delete mode 100644 selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.js delete mode 100644 selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.txt delete mode 100644 selling/search_criteria/draft_sales_orders/__init__.py delete mode 100644 selling/search_criteria/draft_sales_orders/draft_sales_orders.js delete mode 100644 selling/search_criteria/draft_sales_orders/draft_sales_orders.txt delete mode 100644 selling/search_criteria/follow_up_report/__init__.py delete mode 100644 selling/search_criteria/follow_up_report/follow_up_report.js delete mode 100644 selling/search_criteria/follow_up_report/follow_up_report.py delete mode 100644 selling/search_criteria/follow_up_report/follow_up_report.txt delete mode 100644 selling/search_criteria/itemwise_delivery_details/__init__.py delete mode 100644 selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.js delete mode 100644 selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.py delete mode 100644 selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.txt delete mode 100644 selling/search_criteria/itemwise_sales_details/__init__.py delete mode 100644 selling/search_criteria/itemwise_sales_details/itemwise_sales_details.js delete mode 100644 selling/search_criteria/itemwise_sales_details/itemwise_sales_details.py delete mode 100644 selling/search_criteria/itemwise_sales_details/itemwise_sales_details.txt delete mode 100644 selling/search_criteria/lead_to_follow_up/__init__.py delete mode 100644 selling/search_criteria/lead_to_follow_up/lead_to_follow_up.js delete mode 100644 selling/search_criteria/lead_to_follow_up/lead_to_follow_up.txt delete mode 100644 selling/search_criteria/lead_to_follow_up/sales_order_overdue.js delete mode 100644 selling/search_criteria/opportunity_to_follow_up/__init__.py delete mode 100644 selling/search_criteria/opportunity_to_follow_up/opportunity_to_follow_up.txt delete mode 100644 selling/search_criteria/sales_orderwise_booking_&_delivery_summary/__init__.py delete mode 100644 selling/search_criteria/sales_orderwise_booking_&_delivery_summary/sales_orderwise_booking_&_delivery_summary.txt delete mode 100644 selling/search_criteria/sales_orderwise_pending_amount_to_bill/__init__.py delete mode 100644 selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.js delete mode 100644 selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.txt delete mode 100644 selling/search_criteria/sales_orderwise_pending_qty_to_deliver/__init__.py delete mode 100644 selling/search_criteria/sales_orderwise_pending_qty_to_deliver/sales_orderwise_pending_qty_to_deliver.txt delete mode 100644 selling/search_criteria/sales_persons_target_variance_item_group_wise/__init__.py delete mode 100644 selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.js delete mode 100644 selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.py delete mode 100644 selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.txt delete mode 100755 selling/search_criteria/sales_personwise_transaction_summary/__init__.py delete mode 100755 selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.js delete mode 100755 selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.py delete mode 100755 selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.txt delete mode 100644 selling/search_criteria/serial_no_amc_expiring_this_month/__init__.py delete mode 100644 selling/search_criteria/serial_no_amc_expiring_this_month/serial_no_amc_expiring_this_month.txt delete mode 100644 selling/search_criteria/serial_no_warranty_expiring_this_month/__init__.py delete mode 100644 selling/search_criteria/serial_no_warranty_expiring_this_month/serial_no_warranty_expiring_this_month.txt delete mode 100644 selling/search_criteria/target_variance_report/__init__.py delete mode 100644 selling/search_criteria/target_variance_report/target_variance_report.js delete mode 100644 selling/search_criteria/target_variance_report/target_variance_report.py delete mode 100644 selling/search_criteria/target_variance_report/target_variance_report.txt delete mode 100644 selling/search_criteria/territories_target_variance_item_group_wise/__init__.py delete mode 100644 selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.js delete mode 100644 selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.py delete mode 100644 selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.txt delete mode 100644 selling/search_criteria/territory_sales___variance_report/__init__.py delete mode 100644 selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.js delete mode 100644 selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.py delete mode 100644 selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.txt delete mode 100644 selling/search_criteria/total_target_variance_report/__init__.py delete mode 100644 selling/search_criteria/total_target_variance_report/total_target_variance_report.js delete mode 100644 selling/search_criteria/total_target_variance_report/total_target_variance_report.py delete mode 100644 selling/search_criteria/total_target_variance_report/total_target_variance_report.sql delete mode 100644 selling/search_criteria/total_target_variance_report/total_target_variance_report.txt delete mode 100644 selling/search_criteria/variance_report/__init__.py delete mode 100644 selling/search_criteria/variance_report/variance_report.js delete mode 100644 selling/search_criteria/variance_report/variance_report.py delete mode 100644 selling/search_criteria/variance_report/variance_report.sql delete mode 100644 selling/search_criteria/variance_report/variance_report.txt delete mode 100644 support/search_criteria/__init__.py delete mode 100644 support/search_criteria/amc_summary/__init__.py delete mode 100644 support/search_criteria/amc_summary/amc_summary.js delete mode 100644 support/search_criteria/amc_summary/amc_summary.py delete mode 100644 support/search_criteria/amc_summary/amc_summary.txt delete mode 100644 support/search_criteria/customer_issues/__init__.py delete mode 100644 support/search_criteria/customer_issues/customer_issues.js delete mode 100644 support/search_criteria/customer_issues/customer_issues.txt delete mode 100644 support/search_criteria/maintenance_orderwise_pending_amount_to_bill/__init__.py delete mode 100644 support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.js delete mode 100644 support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.txt delete mode 100644 support/search_criteria/maintenance_schedule_details/__init__.py delete mode 100644 support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.js delete mode 100644 support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.txt delete mode 100644 support/search_criteria/warranty_amc_expiry_details/__init__.py delete mode 100644 support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.js delete mode 100644 support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.txt delete mode 100644 support/search_criteria/warranty_amc_summary/__init__.py delete mode 100644 support/search_criteria/warranty_amc_summary/warranty_amc_summary.js delete mode 100644 support/search_criteria/warranty_amc_summary/warranty_amc_summary.py delete mode 100644 support/search_criteria/warranty_amc_summary/warranty_amc_summary.sql delete mode 100644 support/search_criteria/warranty_amc_summary/warranty_amc_summary.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 07c8b669d8..31660eb904 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -170,11 +170,6 @@ wn.module_page["Accounts"] = [ "label":wn._("Financial Analytics"), page: "financial-analytics" }, - { - "label":wn._("Trend Analyzer"), - route: "Report/Profile/Trend Analyzer", - doctype: "Sales Invoice" - }, { "label":wn._("Gross Profit"), route: "query-report/Gross Profit", diff --git a/accounts/search_criteria/__init__.py b/accounts/search_criteria/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/accounts_payable/__init__.py b/accounts/search_criteria/accounts_payable/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/accounts_payable/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/accounts_payable/accounts_payable.js b/accounts/search_criteria/accounts_payable/accounts_payable.js deleted file mode 100644 index 8d8f1abfd0..0000000000 --- a/accounts/search_criteria/accounts_payable/accounts_payable.js +++ /dev/null @@ -1,37 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'From Posting Date'].df.filter_hide = 1; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Account'].df.filter_hide = 0; - - this.add_filter({fieldname:'aging_based_on', label:'Aging Based On', fieldtype:'Select', options:NEWLINE+'Aging Date'+NEWLINE+'Due Date',ignore : 1, parent:'Purchase Invoice', report_default:'Aging Date'}); - this.add_filter({fieldname:'range_1', label:'Range 1', fieldtype:'Data', ignore : 1, parent:'GL Entry', report_default:30}); - this.add_filter({fieldname:'range_2', label:'Range 2', fieldtype:'Data', ignore : 1, parent:'GL Entry', report_default:45}); - this.add_filter({fieldname:'range_3', label:'Range 3', fieldtype:'Data', ignore : 1, parent:'GL Entry', report_default:60}); - this.add_filter({fieldname:'range_4', label:'Range 4', fieldtype:'Data', ignore : 1, parent:'GL Entry', report_default:90}); - - this.filter_fields_dict['GL Entry'+FILTER_SEP +'To Posting Date'].df['report_default']=dateutil.obj_to_str(new Date()); - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Company'].df['report_default']=sys_defaults.company; - - this.dt.set_no_limit(1); -} - -this.mytabs.items['Select Columns'].hide() \ No newline at end of file diff --git a/accounts/search_criteria/accounts_payable/accounts_payable.py b/accounts/search_criteria/accounts_payable/accounts_payable.py deleted file mode 100644 index 3a5b899db5..0000000000 --- a/accounts/search_criteria/accounts_payable/accounts_payable.py +++ /dev/null @@ -1,183 +0,0 @@ -# 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 . - -# Check mandatory filters -# ------------------------------------------------------------------ - -from __future__ import unicode_literals -if not filter_values.get('posting_date1'): - msgprint("Please select To Posting Date ") - raise Exception -else: - to_date = filter_values.get('posting_date1') - -if not filter_values['range_1'] or not filter_values['range_2'] \ - or not filter_values['range_3'] or not filter_values['range_4']: - msgprint("Please select aging ranges in no of days in 'More Filters' ") - raise Exception - -# validate Range -range_list = ['range_1','range_2','range_3','range_4'] -for r in range(len(range_list)-1): - if not cint(filter_values[range_list[r]]) < cint(filter_values[range_list[r + 1]]): - msgprint("Range %s should be less than Range %s." % (cstr(r+1),cstr(r+2))) - raise Exception - - -# Add columns -# ------------------------------------------------------------------ -data = [['Aging Date','Date','80px',''], - ['Account','Data','120px',''], - ['Against Voucher Type','Data','120px',''], - ['Against Voucher','Data','120px',''], - ['Voucher Type','Data','120px',''], - ['Voucher No','Data','120px',''], - ['Remarks','Data','160px',''], - ['Supplier Type', 'Data', '80px', ''], - ['Due Date', 'Data', '80px', ''], - ['Bill No','Data','80px',''], - ['Bill Date','Data','80px',''], - ['Opening Amt','Currency','120px',''], - ['Outstanding Amt','Currency','120px',''], - ['Age (Days)', 'Currency', '150px', ''], - ['0-'+cstr(filter_values['range_1']),'Currency','100px',''], - [cstr(cint(filter_values['range_1']) + 1)+ '-' +cstr(filter_values['range_2']),'Currency','100px',''], - [cstr(cint(filter_values['range_2']) + 1)+ '-' +cstr(filter_values['range_3']),'Currency','100px',''], - [cstr(cint(filter_values['range_3']) + 1)+ '-' +cstr(filter_values['range_4']),'Currency','100px',''], - [cstr(filter_values['range_4']) + '-Above','Currency','100px','']] - - -for d in data: - colnames.append(d[0]) - coltypes.append(d[1]) - colwidths.append(d[2]) - coloptions.append(d[3]) - col_idx[d[0]] = len(colnames)-1 - -# ageing based on -# ------------------------------------------------------------------ -aging_based_on = 'Aging Date' -if filter_values.has_key('aging_based_on') and filter_values['aging_based_on']: - aging_based_on = filter_values['aging_based_on'].split(NEWLINE)[-1] - -if len(res) > 2000 and from_export == 0: - msgprint("""This is a very large report and cannot be shown in the browser - as it is likely to make your browser very slow. - Please select Account or click on 'Export' to open in excel""", raise_exception=1) - -# get supplier type -supp_type_dict = {} -for each in sql("""select t2.name, t1.supplier_type from tabSupplier t1, tabAccount t2 - where t1.name = t2.account_name group by t2.name"""): - supp_type_dict[each[0]] = each[1] - -# get due_date, bill_no, bill_date from PV -pv_dict = {} -for t in sql("""select name, due_date, bill_no, bill_date - from `tabPurchase Invoice` group by name"""): - pv_dict[t[0]] = [cstr(t[1]), t[2], cstr(t[3])] - -# pv after to-date -pv_after_to_date = [d[0] for d in sql("""select distinct name from `tabPurchase Invoice` - where posting_date > %s and docstatus = 1""", (to_date,))] - - -from webnotes.utils import nowdate - -out = [] -total_booking_amt, total_outstanding_amt = 0,0 - -for r in res: - outstanding_amt, due_date, bill_no, bill_date, cond = 0, '','','', '' - booking_amt = r.pop(7) - - # supplier type - r.append(supp_type_dict.get(r[col_idx['Account']], '')) - - if r[col_idx['Voucher Type']] == 'Purchase Invoice': - r += pv_dict.get(r[col_idx['Voucher No']], ['', '', '']) - else: - r += ['', '', ''] - - # if entry against Purchase Invoice - if r[col_idx['Against Voucher']] and r[col_idx['Voucher Type']] == 'Purchase Invoice': - cond = " and ifnull(against_voucher, '') = '%s'" % r[col_idx['Against Voucher']] - - # if entry against JV & and not adjusted within period - elif r[col_idx['Against Voucher Type']] == 'Purchase Invoice' \ - and r[col_idx['Against Voucher']] in pv_after_to_date: - booking_amt = 0 - cond = """ and voucher_no = '%s' and ifnull(against_voucher, '') = '%s'""" \ - % (r[col_idx['Voucher No']], r[col_idx['Against Voucher']]) - - # if un-adjusted - elif not r[col_idx['Against Voucher']]: - booking_amt = 0 - cond = """ and ((voucher_no = '%s' and ifnull(against_voucher, '') = '') - or (ifnull(against_voucher, '') = '%s' and voucher_type = 'Journal Voucher'))""" \ - % (r[col_idx['Voucher No']], r[col_idx['Voucher No']]) - - if cond: - outstanding_amt = flt(sql("""select sum(ifnull(credit, 0))-sum(ifnull(debit, 0)) - from `tabGL Entry` where account = %s and ifnull(is_cancelled, 'No') = 'No' - and posting_date <= %s %s""" - % ('%s', '%s', cond), (r[col_idx['Account']], to_date,))[0][0] or 0) - - # add to total outstanding - total_outstanding_amt += flt(outstanding_amt) - - # add to total booking amount - if outstanding_amt and r[col_idx['Voucher Type']] == 'Purchase Invoice' \ - and r[col_idx['Against Voucher']]: - total_booking_amt += flt(booking_amt) - - r += [booking_amt, outstanding_amt] - - # split into date ranges - val_l1 = val_l2 = val_l3 = val_l4 = val_l5_above= 0 - if r[col_idx[aging_based_on]]: - if getdate(to_date) > getdate(nowdate()): - to_date = nowdate() - diff = (getdate(to_date) - getdate(r[col_idx[aging_based_on]])).days - if diff < cint(filter_values['range_1']): - val_l1 = outstanding_amt - if diff >= cint(filter_values['range_1']) and diff < cint(filter_values['range_2']): - val_l2 = outstanding_amt - if diff >= cint(filter_values['range_2']) and diff < cint(filter_values['range_3']): - val_l3 = outstanding_amt - if diff >= cint(filter_values['range_3']) and diff < cint(filter_values['range_4']): - val_l4 = outstanding_amt - if diff >= cint(filter_values['range_4']): - val_l5_above = outstanding_amt - - r += [diff, val_l1, val_l2, val_l3, val_l4, val_l5_above] - - # Only show that entry which has outstanding - if abs(flt(outstanding_amt)) > 0.001: - out.append(r) - -if len(out) > 300 and from_export == 0: - msgprint("This is a very large report and cannot be shown in the browser as it is likely to make your browser very slow.Please select Account or click on 'Export' to open in excel") - raise Exception - - -# Append Extra rows to RES -# ------------------------------------------------------------------ -t_row = ['' for i in range(len(colnames))] -t_row[col_idx['Voucher No']] = 'Total' -t_row[col_idx['Opening Amt']] = total_booking_amt -t_row[col_idx['Outstanding Amt']] = total_outstanding_amt -out.append(t_row) diff --git a/accounts/search_criteria/accounts_payable/accounts_payable.sql b/accounts/search_criteria/accounts_payable/accounts_payable.sql deleted file mode 100644 index 56ff4cf739..0000000000 --- a/accounts/search_criteria/accounts_payable/accounts_payable.sql +++ /dev/null @@ -1,16 +0,0 @@ -SELECT DISTINCT - `tabGL Entry`.`Aging_date`,`tabGL Entry`.`account`, `tabGL Entry`.`against_voucher_type`, - `tabGL Entry`.`against_voucher`,`tabGL Entry`.`voucher_type`,`tabGL Entry`.`voucher_no`, - `tabGL Entry`.`remarks`, `tabGL Entry`.`credit` -FROM - `tabGL Entry`,`tabAccount` -WHERE - `tabGL Entry`.`posting_date`<= '%(posting_date1)s' - AND `tabGL Entry`.`account` LIKE '%(account)s%%' - AND `tabGL Entry`.`company` LIKE '%(company)s%%' - AND ((ifnull(`tabGL Entry`.`voucher_type`,'') = 'Purchase Invoice' - AND `tabGL Entry`.`credit`>0) OR `tabGL Entry`.voucher_type = 'Journal Voucher') - AND `tabGL Entry`.`is_cancelled` = 'No' - AND `tabAccount`.master_type = 'Supplier' - AND `tabAccount`.name = `tabGL Entry`.account -ORDER BY `tabGL Entry`.`posting_date` diff --git a/accounts/search_criteria/accounts_payable/accounts_payable.txt b/accounts/search_criteria/accounts_payable/accounts_payable.txt deleted file mode 100644 index 7f9151c443..0000000000 --- a/accounts/search_criteria/accounts_payable/accounts_payable.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-05-14 18:05:41", - "modified_by": "Administrator", - "modified": "2012-10-17 10:51:41" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"GL Entry\\u0001Voucher Type\":[\"\"],\"GL Entry\\u0001Is Cancelled\":[\"\"],\"GL Entry\\u0001Is Opening\":[\"\"],\"GL Entry\\u0001Fiscal Year\":[\"\"],\"Purchase Invoice\\u0001Aging Based On\":[\"Aging Date\"],\"GL Entry\\u0001Range 1\":\"30\",\"GL Entry\\u0001Range 2\":\"45\",\"GL Entry\\u0001Range 3\":\"60\",\"GL Entry\\u0001Range 4\":\"90\"}", - "doctype": "Search Criteria", - "doc_type": "GL Entry", - "name": "__common__", - "sort_by": "`tabGL Entry`.`name`", - "page_len": 50, - "criteria_name": "Accounts Payable", - "columns": "GL Entry\u0001Posting Date,GL Entry\u0001Account,GL Entry\u0001Against Voucher,GL Entry\u0001Voucher No" - }, - { - "name": "accounts_payable", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/accounts_receivable/__init__.py b/accounts/search_criteria/accounts_receivable/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/accounts_receivable/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/accounts_receivable/accounts_receivable.js b/accounts/search_criteria/accounts_receivable/accounts_receivable.js deleted file mode 100644 index e8ed3e3ee0..0000000000 --- a/accounts/search_criteria/accounts_receivable/accounts_receivable.js +++ /dev/null @@ -1,35 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'From Posting Date'].df.filter_hide = 1; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Account'].df.filter_hide = 0; - - this.add_filter({fieldname:'aging_based_on', label:'Aging Based On', fieldtype:'Select', options:NEWLINE+'Aging Date'+NEWLINE+'Due Date',ignore : 1, parent:'Sales Invoice', report_default:'Aging Date'}); - this.add_filter({fieldname:'range_1', label:'Range 1', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - this.add_filter({fieldname:'range_2', label:'Range 2', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - this.add_filter({fieldname:'range_3', label:'Range 3', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - this.add_filter({fieldname:'range_4', label:'Range 4', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - - this.filter_fields_dict['GL Entry'+FILTER_SEP +'To Posting Date'].df['report_default']=dateutil.obj_to_str(new Date()); - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Company'].df['report_default']=sys_defaults.company; - - this.dt.set_no_limit(1); -} \ No newline at end of file diff --git a/accounts/search_criteria/accounts_receivable/accounts_receivable.py b/accounts/search_criteria/accounts_receivable/accounts_receivable.py deleted file mode 100644 index 34aafe5ffc..0000000000 --- a/accounts/search_criteria/accounts_receivable/accounts_receivable.py +++ /dev/null @@ -1,168 +0,0 @@ -# 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 . - - -# Check mandatory filters -#------------------------------ -from __future__ import unicode_literals -if not filter_values.get('posting_date1'): - msgprint("Please select To Posting Date", raise_exception=1) -else: - to_date = filter_values.get('posting_date1') - -if not filter_values['range_1'] or not filter_values['range_2'] or \ - not filter_values['range_3'] or not filter_values['range_4']: - msgprint("Please select aging ranges in no of days in 'More Filters' section") - raise Exception - -# validate Range -range_list = ['range_1','range_2','range_3','range_4'] -for r in range(len(range_list)-1): - if not cint(filter_values[range_list[r]]) < cint(filter_values[range_list[r + 1]]): - msgprint("Range %s should be less than Range %s." % (cstr(r+1),cstr(r+2))) - raise Exception - - -# Add columns -# ----------- -data = [['Aging Date','Date','80px',''], - ['Account','Data','120px',''], - ['Against Voucher Type','Data','120px',''], - ['Against Voucher','Data','120px',''], - ['Voucher Type','Data','120px',''], - ['Voucher No','Data','120px',''], - ['Remarks','Data','160px',''], - ['Territory','Data','120px',''], - ['Due Date', 'Date', '80px', ''], - ['Opening Amt','Currency','120px',''], - ['Outstanding Amt','Currency','120px',''], - ['Age (Days)', 'Data', '60px', ''], - ['0-'+cstr(filter_values['range_1']),'Currency','100px',''], - [cstr(cint(filter_values['range_1']) + 1)+ '-' +cstr(filter_values['range_2']),'Currency','100px',''], - [cstr(cint(filter_values['range_2']) + 1)+ '-' +cstr(filter_values['range_3']),'Currency','100px',''], - [cstr(cint(filter_values['range_3']) + 1)+ '-' +cstr(filter_values['range_4']),'Currency','100px',''], - [cstr(filter_values['range_4']) + '-Above','Currency','100px','']] - - -for d in data: - colnames.append(d[0]) - coltypes.append(d[1]) - colwidths.append(d[2]) - coloptions.append(d[3]) - col_idx[d[0]] = len(colnames)-1 - -# ageing based on -aging_based_on = filter_values.get('aging_based_on') and filter_values['aging_based_on'].split(NEWLINE)[-1] or 'Aging Date' - -if len(res) > 2000 and from_export == 0: - msgprint("This is a very large report and cannot be shown in the browser as it is likely to make your browser very slow.Please select Account or click on 'Export' to open in excel") - raise Exception - - -# get supplier type -territory_dict = {} -for each in sql("""select t2.name, t1.territory from `tabCustomer` t1, `tabAccount` t2 - where t1.name = t2.master_name group by t2.name"""): - territory_dict[each[0]] = each[1] - -# get due_date from sales invoice -si_dict = {} -for t in sql("""select name, due_date from `tabSales Invoice` group by name"""): - si_dict[t[0]] = t[1] - -# sales invoice after to-date -si_after_to_date = [d[0] for d in sql("""select distinct name from `tabSales Invoice` - where posting_date > %s and docstatus = 1""", (to_date,))] - - -from webnotes.utils import nowdate -out = [] -total_booking_amt, total_outstanding_amt = 0,0 -for r in res: - outstanding_amt = 0 - cond = due_date = '' - booking_amt = r.pop(7) - - # get customer territory - r.append(territory_dict.get(r[col_idx['Account']], '')) - - # if entry against Sales Invoice - if r[col_idx['Against Voucher']] and r[col_idx['Voucher Type']] == 'Sales Invoice': - # get due date - due_date = si_dict.get(r[col_idx['Voucher No']], '') - cond = """ and ifnull(against_voucher, '') = '%s'""" % r[col_idx['Against Voucher']] - - # if entry against JV & and not adjusted within period - elif r[col_idx['Against Voucher Type']] == 'Sales Invoice' \ - and r[col_idx['Against Voucher']] in si_after_to_date: - booking_amt = 0 - cond = """ and voucher_no = '%s' and ifnull(against_voucher, '') = '%s'""" \ - % (r[col_idx['Voucher No']], r[col_idx['Against Voucher']]) - # if entry against JV and unadjusted - elif not r[col_idx['Against Voucher']]: - booking_amt = 0 - cond = """ and ((voucher_no = '%s' and ifnull(against_voucher, '') = '') - or (ifnull(against_voucher, '') = '%s' and voucher_type = 'Journal Voucher'))""" \ - % (r[col_idx['Voucher No']], r[col_idx['Voucher No']]) - - if cond: - outstanding_amt = flt(sql("""select ifnull(sum(debit),0) - ifnull(sum(credit),0) - from `tabGL Entry` where account = %s and ifnull(is_cancelled, 'No') = 'No' - and posting_date <= %s %s""" - % ('%s', '%s', cond), (r[col_idx['Account']], to_date,))[0][0] or 0) - - # add to total outstanding - total_outstanding_amt += flt(outstanding_amt) - # add to total booking amount - if outstanding_amt and r[col_idx['Voucher Type']] == 'Sales Invoice' and r[col_idx['Against Voucher']]: - total_booking_amt += flt(booking_amt) - - r += [due_date, booking_amt, outstanding_amt] - - #Ageing Outstanding - val_l1 = val_l2 = val_l3 = val_l4 = val_l5_above = 0 - diff = 0 - if r[col_idx[aging_based_on]]: - if getdate(to_date) > getdate(nowdate()): - to_date = nowdate() - diff = (getdate(to_date) - getdate(r[col_idx[aging_based_on]])).days - if diff <= cint(filter_values['range_1']): - val_l1 = outstanding_amt - if diff > cint(filter_values['range_1']) and diff <= cint(filter_values['range_2']): - val_l2 = outstanding_amt - if diff > cint(filter_values['range_2']) and diff <= cint(filter_values['range_3']): - val_l3 = outstanding_amt - if diff > cint(filter_values['range_3']) and diff <= cint(filter_values['range_4']): - val_l4 = outstanding_amt - if diff > cint(filter_values['range_4']): - val_l5_above = outstanding_amt - r += [diff, val_l1, val_l2, val_l3, val_l4, val_l5_above] - - # Only show that entry which has outstanding - if abs(flt(outstanding_amt)) > 0.001: - out.append(r) - -if len(out) > 500 and from_export == 0: - msgprint("This is a very large report and cannot be shown in the browser as it is likely to make your browser very slow.Please select Account or click on 'Export' to open in excel") - raise Exception - -# Append Extra rows to res -if len(out) > 0: - t_row = ['' for i in range(len(colnames))] - t_row[col_idx['Voucher No']] = 'Total' - t_row[col_idx['Opening Amt']] = total_booking_amt - t_row[col_idx['Outstanding Amt']] = total_outstanding_amt - out.append(t_row) \ No newline at end of file diff --git a/accounts/search_criteria/accounts_receivable/accounts_receivable.sql b/accounts/search_criteria/accounts_receivable/accounts_receivable.sql deleted file mode 100644 index 6f1c459fdb..0000000000 --- a/accounts/search_criteria/accounts_receivable/accounts_receivable.sql +++ /dev/null @@ -1,16 +0,0 @@ -SELECT - `tabGL Entry`.`aging_date`,`tabGL Entry`.`account`, `tabGL Entry`.`against_voucher_type`, - `tabGL Entry`.`against_voucher`,`tabGL Entry`.`voucher_type`,`tabGL Entry`.`voucher_no`, - `tabGL Entry`.`remarks`, `tabGL Entry`.`debit` -FROM - `tabGL Entry`,`tabAccount` -WHERE - `tabGL Entry`.`posting_date`<= '%(posting_date1)s' - AND `tabGL Entry`.`account` LIKE '%(account)s%%' - AND `tabGL Entry`.`company` LIKE '%(company)s%%' - AND ((`tabGL Entry`.`voucher_type` = 'Sales Invoice' and `tabGL Entry`.`debit`>0) - OR `tabGL Entry`.`voucher_type` = 'Journal Voucher') - AND `tabGL Entry`.`is_cancelled` = 'No' - AND `tabAccount`.`master_type` = 'Customer' - AND `tabAccount`.`name` = `tabGL Entry`.`account` -ORDER BY `tabGL Entry`.`posting_date`, `tabGL Entry`.`account` diff --git a/accounts/search_criteria/accounts_receivable/accounts_receivable.txt b/accounts/search_criteria/accounts_receivable/accounts_receivable.txt deleted file mode 100644 index cfa496e1e5..0000000000 --- a/accounts/search_criteria/accounts_receivable/accounts_receivable.txt +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "ASC", - "filters": "{'GL Entry\u0001Is Cancelled':'No','GL Entry\u0001Fiscal Year':'','GL Entry\u0001Company':'','GL Entry\u0001Range 1':'30','GL Entry\u0001Range 2':'45','GL Entry\u0001Range 3':'60','GL Entry\u0001Range 4':'90'}", - "doctype": "Search Criteria", - "doc_type": "GL Entry", - "name": "__common__", - "sort_by": "`tabGL Entry`.`name`", - "criteria_name": "Accounts Receivable", - "columns": "GL Entry\u0001Posting Date,GL Entry\u0001Transaction Date,GL Entry\u0001Account,GL Entry\u0001Against Voucher,GL Entry\u0001Voucher No" - }, - { - "name": "accounts_receivable", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/bank_clearance_report/__init__.py b/accounts/search_criteria/bank_clearance_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/bank_clearance_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.js b/accounts/search_criteria/bank_clearance_report/bank_clearance_report.js deleted file mode 100644 index b5f9ffda03..0000000000 --- a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.js +++ /dev/null @@ -1,50 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'From Posting Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Voucher Type'].df.in_first_page = 0; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'From Clearance Date'].df.in_first_page = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Clearance Date'].df.in_first_page = 1; - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Account'].df.in_first_page = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Company'].df.in_first_page = 1; - - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'ID'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Owner'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Saved'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Submitted'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Cancelled'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Against Receivable'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'TDS Category'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Cost Center'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Against Payable'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Owner'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'TDS Applicable'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Is Opening'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Total Debit >='].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Total Debit <='].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Total Credit >='].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Total Credit <='].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'User Remark'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'From Voucher Date'].df.filter_hide = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Voucher Date'].df.filter_hide = 1; -} - -this.mytabs.items['More Filters'].hide() \ No newline at end of file diff --git a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt b/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt deleted file mode 100644 index 8aa94bf50a..0000000000 --- a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "creation": "2012-05-14 18:05:41", - "docstatus": 0, - "modified": "2013-04-30 14:49:06", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "columns": "Journal Voucher\u0001ID,Journal Voucher Detail\u0001Account,Journal Voucher Detail\u0001Debit,Journal Voucher Detail\u0001Credit,Journal Voucher\u0001Clearance Date,Journal Voucher\u0001Cheque No,Journal Voucher\u0001Cheque Date,Journal Voucher\u0001Voucher Date,Journal Voucher\u0001Posting Date,Journal Voucher Detail\u0001Against Payable,Journal Voucher Detail\u0001Against Receivable", - "criteria_name": "Bank Clearance report", - "custom_query": "", - "description": "Bank Clearance report", - "dis_filters": "fiscal_year", - "disabled": 0, - "doc_type": "Journal Voucher Detail", - "doctype": "Search Criteria", - "filters": "{'Journal Voucher\u0001Submitted':1,'Journal Voucher\u0001Voucher Type':'','Journal Voucher\u0001Is Opening':'','Journal Voucher\u0001Fiscal Year':'','Journal Voucher\u0001Company':'','Journal Voucher\u0001TDS Applicable':'','Journal Voucher\u0001TDS Category':''}", - "module": "Accounts", - "name": "__common__", - "page_len": 50, - "parent_doc_type": "Journal Voucher", - "report_script": null, - "sort_by": "ID", - "sort_order": "DESC", - "standard": "Yes" - }, - { - "doctype": "Search Criteria", - "name": "bank_clearance_report" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/bank_reconciliation_statement/__init__.py b/accounts/search_criteria/bank_reconciliation_statement/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/bank_reconciliation_statement/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.js b/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.js deleted file mode 100644 index f6bec4b97a..0000000000 --- a/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.js +++ /dev/null @@ -1,34 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Account'].df.filter_hide = 0; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Clearance Date'].df.filter_hide = 0; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Company'].df.filter_hide = 0; - - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Account'].df.in_first_page = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Clearance Date'].df.in_first_page = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Company'].df.in_first_page = 1; - - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Company'].df['report_default']=sys_defaults.company; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Clearance Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - - this.dt.set_no_limit(1); -} - -this.mytabs.items['More Filters'].hide(); \ No newline at end of file diff --git a/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.py b/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.py deleted file mode 100644 index 1ab94a4687..0000000000 --- a/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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 . - -#get company -from __future__ import unicode_literals - -import webnotes.defaults -company = filter_values.get('company') or webnotes.defaults.get_user_default('company') - -# To date -if not filter_values.get('clearance_date1'): - msgprint('Please enter To Clearance Date') - raise Exception -else: - to_date = filter_values['clearance_date1'] - - -#Fiscal year and year start date -#---------------------------------- -ysd, fiscal_year = sql("select year_start_date, name from `tabFiscal Year` where %s between year_start_date and date_add(year_start_date,interval 1 year)",to_date)[0] -# Account -if not filter_values.get('account'): - msgprint('Please select Account in filter section') - raise Exception -else: - acc_name = filter_values.get('account') - - -if len(res) > 300 and from_export == 0: - msgprint("This is a very large report and cannot be shown in the browser as it is likely to make your browser very slow.Please select Account or click on 'Export' to open in excel") - raise Exception - -acc = sql("select debit_or_credit, is_pl_account, lft, rgt from tabAccount where name = '%s'" % acc_name) - -from accounts.utils import get_balance_on -opening = get_balance_on(acc_name, to_date) - -total_debit, total_credit = 0,0 -out = [] - -for r in res: - total_debit = flt(total_debit) + flt(r[col_idx['Debit']]) - total_credit = flt(total_credit) + flt(r[col_idx['Credit']]) - out.append(r) - -if acc and acc[0][0] == 'Debit': - bank_bal = flt(opening)-flt(total_debit)+flt(total_credit) -else: - bank_bal = flt(opening)+flt(total_debit)-flt(total_credit) - -out.append(['','','','Balance as per Company Books: ', opening,'', '']) -out.append(['','','','Amounts not reflected in Bank: ', total_debit,total_credit,'']) -out.append(['','','','Balance as per Bank: ', bank_bal,'','']) diff --git a/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.txt b/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.txt deleted file mode 100644 index 4d5d5fcf70..0000000000 --- a/accounts/search_criteria/bank_reconciliation_statement/bank_reconciliation_statement.txt +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" - }, - { - "parent_doc_type": "Journal Voucher", - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Journal Voucher\u0001Submitted':1,'Journal Voucher\u0001Voucher Type':'','Journal Voucher\u0001Is Opening':'','Journal Voucher\u0001Company':'','Journal Voucher\u0001TDS Applicable':'','Journal Voucher\u0001TDS Category':''}", - "dis_filters": "clearance_date\nfiscal_year", - "doc_type": "Journal Voucher Detail", - "name": "__common__", - "add_cond": "(`tabJournal Voucher Detail`.credit >= 0 or `tabJournal Voucher Detail`.credit is null)\n(`tabJournal Voucher`.cheque_no is not null or `tabJournal Voucher`.cheque_no != '')\n(ifnull(`tabJournal Voucher`.clearance_date, '0000-00-00') >'%(clearance_date1)s' or `tabJournal Voucher`.clearance_date is null or `tabJournal Voucher`.clearance_date = '0000-00-00')\n(`tabJournal Voucher`.posting_date <= '%(clearance_date1)s')", - "doctype": "Search Criteria", - "sort_by": "`tabJournal Voucher`.`name`", - "page_len": 50, - "criteria_name": "Bank Reconciliation Statement", - "columns": "Journal Voucher\u0001ID,Journal Voucher\u0001Posting Date,Journal Voucher\u0001Cheque No,Journal Voucher\u0001Cheque Date,Journal Voucher\u0001Clearance Date,Journal Voucher Detail\u0001Account,Journal Voucher Detail\u0001Debit,Journal Voucher Detail\u0001Credit,Journal Voucher Detail\u0001Against Account" - }, - { - "name": "bank_reconciliation_statement", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/budget_variance_report/__init__.py b/accounts/search_criteria/budget_variance_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/budget_variance_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/budget_variance_report/budget_variance_report.js b/accounts/search_criteria/budget_variance_report/budget_variance_report.js deleted file mode 100644 index 5cc644cc8c..0000000000 --- a/accounts/search_criteria/budget_variance_report/budget_variance_report.js +++ /dev/null @@ -1,31 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Budget Detail'}); - this.add_filter({fieldname:'company', label:'Company', fieldtype:'Link', options:'Company', report_default:sys_defaults.company, ignore : 1, parent:'Budget Detail', in_first_page:1}); - this.filter_fields_dict['Budget Detail'+FILTER_SEP +'Fiscal Year'].df.in_first_page = 1; - this.filter_fields_dict['Budget Detail'+FILTER_SEP +'Period'].df.in_first_page = 1; - - this.filter_fields_dict['Budget Detail'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} - -report.aftertableprint = function(t) { - $yt(t,'*',1,{whiteSpace:'pre'}); -} - -this.mytabs.items['More Filters'].hide(); -this.mytabs.items['Select Columns'].hide(); diff --git a/accounts/search_criteria/budget_variance_report/budget_variance_report.py b/accounts/search_criteria/budget_variance_report/budget_variance_report.py deleted file mode 100644 index 301c19a230..0000000000 --- a/accounts/search_criteria/budget_variance_report/budget_variance_report.py +++ /dev/null @@ -1,184 +0,0 @@ -# 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 . - -# validate Filters -from __future__ import unicode_literals -flt_dict = {'fiscal_year': 'Fiscal Year', 'period': 'Period'} -for f in flt_dict: - if not filter_values.get(f): - msgprint("Please Select " + cstr(flt_dict[f])) - raise Exception - -# Get Values from fliters -fiscal_year = filter_values.get('fiscal_year') -period = filter_values.get('period') -under = "GL Entry" -based_on = "Cost Center" - -#add distributed id field -col = [] -col.append([based_on,'Date','150px','']) -col.append(['Budget Allocated','Currency','150px','']) -col.append(['Distribution Id','Date','150px','']) - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - - col_idx[c[0]] = len(colnames)-1 - -def make_child_lst(based_on,name): - rg = sql("select lft, rgt from `tab%s` where name = '%s'"%(based_on,name)) - ch_name = sql("select name from `tab%s` where lft between %d and %d"%(based_on,int(rg[0][0]),int(rg[0][1]))) - chl ='(' - flag = 1 - for c in ch_name: - if flag == 1: - chl += "'%s'"%c[0] - flag = 2 - else: - chl +=",'%s'"%c[0] - - chl +=")" - return chl - - - -for r in res: - - cond1 =" t1.fiscal_year ='%s' and t1.parent=t2.name and t1.parenttype = '%s' and t1.docstatus !=2" - - q = "select t1.name from `tabBudget Detail` t1, `tab%s` t2 where "+cond1+" and t2.name = '%s'" - ch = sql(q%(based_on,fiscal_year,based_on,r[0].strip())) - q1 = "select sum(t1.budget_allocated) from `tabBudget Detail` t1, `tab%s` t2, `tabAccount` t3 where " - cond2 = " t3.is_pl_account = 'Yes' and t3.debit_or_credit = 'Debit' and t3.name = t1.account and t1.docstatus != 2 and " - if ch: - qur = q1+cond2+cond1+" and t2.name = '%s'" - ret_amt = sql(qur%(based_on,fiscal_year,based_on,r[0].strip())) - - - #---------------------------------------------------------------- - else: - node_lst = make_child_lst(based_on,r[0].strip()) - qur = q1+cond1+' and '+cond2+" t2.name in %s" - - ret_amt = sql(qur%(based_on,fiscal_year,based_on,node_lst)) - - #---------------------------------------------------------------- - ret_dis_id = sql("select distribution_id from `tab%s` where name = '%s'"%(based_on,r[0].strip())) - - target_amt = ret_amt and flt(ret_amt[0][0]) or 0 - dis_id = ret_dis_id and ret_dis_id[0][0] or '' - - r.append(target_amt) - r.append(dis_id) - - - -# Set required field names -based_on_fn = 'cost_center' - -date_fn = 'posting_date' - -mon_list = [] - -data = {'start_date':0, 'end_date':1} - -def make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx): - count = 1 - if period == 'Quarterly' or period == 'Half Yearly' or period == 'Annual': mon_list.append([str(start_date)]) - for m in range(12): - # get last date - last_date = str(sql("select LAST_DAY('%s')" % start_date)[0][0]) - - # make mon_list for Monthly Period - if period == 'Monthly' : - mon_list.append([start_date, last_date]) - # add months as Column names - month_name = sql("select MONTHNAME('%s')" % start_date)[0][0] - append_colnames(str(month_name)[:3], colnames, coltypes, colwidths, coloptions, col_idx) - - # get start date - start_date = str(sql("select DATE_ADD('%s',INTERVAL 1 DAY)" % last_date)[0][0]) - - # make mon_list for Quaterly Period - if period == 'Quarterly' and count % 3 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column names - append_colnames('Q '+ str(count / 3), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Half Yearly Period - if period == 'Half Yearly' and count % 6 == 0 : - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('H'+str(count / 6), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Annual Period - if period == 'Annual' and count % 12 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('', colnames, coltypes, colwidths, coloptions, col_idx) - count = count +1 - -def append_colnames(name, colnames, coltypes, colwidths, coloptions, col_idx): - col = ['Target', 'Actual', 'Variance'] - for c in col: - n = str(name) and ' (' + str(name) +')' or '' - colnames.append(str(c) + n) - coltypes.append('Currency') - colwidths.append('150px') - coloptions.append('') - col_idx[str(c) + n ] = len(colnames) - 1 - - -# get start date -start_date = webnotes.conn.get_value('Fiscal Year', fiscal_year, 'year_start_date') -if not start_date: - msgprint("Please Define Year Start Date for Fiscal Year " + str(fiscal_year)) - raise Exception -start_date = start_date.strftime('%Y-%m-%d') - -# make month list and columns -make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx) - - -bc_obj = get_obj('Budget Control') -for r in res: - count = 0 - - for idx in range(3, len(colnames), 3): - cidx = 2 - - # ================= Calculate Target ========================================== - r.append(bc_obj.get_monthly_budget( r[cidx], fiscal_year, mon_list[count][data['start_date']], mon_list[count][data['end_date']], r[cidx-1])) - - #================== Actual Amount ============================================= - actual = 0 - - ch = make_child_lst(based_on,r[0].strip()) - - actual = sql("select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) from `tabGL Entry` t1, `tabAccount` t2 where ifnull(t2.is_pl_account, 'No') = 'Yes' and ifnull(t1.is_cancelled, 'No') = 'No' and t1.cost_center in %s and t2.debit_or_credit = 'Debit' and t1.posting_date between '%s' and '%s' and t1.account = t2.name"%(ch, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - - #---------------------------------------------------------- - actual = flt(actual[0][0]) - r.append(actual) - # ================ Variance =================================================== - r.append(r[idx] - r[idx + 1]) - count = count +1 diff --git a/accounts/search_criteria/budget_variance_report/budget_variance_report.sql b/accounts/search_criteria/budget_variance_report/budget_variance_report.sql deleted file mode 100644 index 575fbfbc19..0000000000 --- a/accounts/search_criteria/budget_variance_report/budget_variance_report.sql +++ /dev/null @@ -1,10 +0,0 @@ -SELECT - CONCAT(REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name -FROM - `tabCost Center` AS node,`tabCost Center` AS parent -WHERE - node.lft BETWEEN parent.lft AND parent.rgt - AND node.docstatus !=2 - AND node.company_name like '%(company)s%%' -GROUP BY node.name -ORDER BY node.lft diff --git a/accounts/search_criteria/budget_variance_report/budget_variance_report.txt b/accounts/search_criteria/budget_variance_report/budget_variance_report.txt deleted file mode 100644 index 2c079a0294..0000000000 --- a/accounts/search_criteria/budget_variance_report/budget_variance_report.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" - }, - { - "parent_doc_type": "Cost Center", - "module": "Accounts", - "doctype": "Search Criteria", - "sort_order": "DESC", - "filters": "{'Budget Detail\u0001Fiscal Year':''}", - "standard": "Yes", - "doc_type": "Budget Detail", - "name": "__common__", - "sort_by": "`tabCost Center`.`lft`", - "page_len": 50, - "criteria_name": "Budget Variance Report" - }, - { - "name": "budget_variance_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/business_associate_commission_report/__init__.py b/accounts/search_criteria/business_associate_commission_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/business_associate_commission_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.js b/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.js deleted file mode 100644 index 272327bc6d..0000000000 --- a/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.js +++ /dev/null @@ -1,26 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Sales Partner'].df.filter_hide = 0; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Sales Partner'].df.in_first_page = 1; - -} - - -this.mytabs.items['Select Columns'].hide(); -this.mytabs.items['More Filters'].hide(); \ No newline at end of file diff --git a/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.py b/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.py deleted file mode 100644 index 6aa7529ca3..0000000000 --- a/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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 -colwidths[col_idx['Business Associate']] = '200px' - -coltypes[col_idx['Average Commission Rate']] = 'Currency' -colwidths[col_idx['Average Commission Rate']] = '200px' - -coltypes[col_idx['Net Total']] = 'Currency' -colwidths[col_idx['Net Total']] = '150px' - -coltypes[col_idx['Total Commission']] = 'Currency' -colwidths[col_idx['Total Commission']] = '150px' \ No newline at end of file diff --git a/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.txt b/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.txt deleted file mode 100644 index ffc642290c..0000000000 --- a/accounts/search_criteria/business_associate_commission_report/business_associate_commission_report.txt +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "owner": "saumil@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" - }, - { - "add_col": "SUM(`tabSales Invoice`.`total_commission`) AS 'Total Commission'\nSUM(`tabSales Invoice`.`net_total`) AS 'Net Total'\n((SUM(`tabSales Invoice`.`total_commission`) / SUM(`tabSales Invoice`.`net_total`)) * 100) AS 'Average Commission Rate'", - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Invoice\u0001Submitted':1}", - "description": "Track total commission given to your Business Associate", - "doc_type": "Sales Invoice", - "name": "__common__", - "add_cond": "`tabSales Invoice`.`net_total` > 0\n`tabSales Invoice`.`total_commission` > 0", - "doctype": "Search Criteria", - "group_by": "`tabSales Invoice`.sales_partner", - "page_len": 50, - "criteria_name": "Business Associate Commission Report", - "columns": "Sales Invoice\u0001Business Associate" - }, - { - "name": "business_associate_commission_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/collection_report/__init__.py b/accounts/search_criteria/collection_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/collection_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/collection_report/collection_report.js b/accounts/search_criteria/collection_report/collection_report.js deleted file mode 100644 index dfa58a66d3..0000000000 --- a/accounts/search_criteria/collection_report/collection_report.js +++ /dev/null @@ -1,40 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Is Opening'].df.filter_hide = 0; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'From Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Account'].df.filter_hide = 0; - - this.add_filter({fieldname:'range_1', label:'Range 1', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - this.add_filter({fieldname:'range_2', label:'Range 2', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - this.add_filter({fieldname:'range_3', label:'Range 3', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - this.add_filter({fieldname:'range_4', label:'Range 4', fieldtype:'Data', ignore : 1, parent:'GL Entry'}); - - this.add_filter({fieldname:'aging_based_on', label:'Aging Based On', fieldtype:'Select', options:NEWLINE+'Transaction Date'+NEWLINE+'Aging Date',ignore : 1, parent:'Sales Invoice', 'report_default': 'Aging Date'}); - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'From Posting Date'].df.in_first_page = 1; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Posting Date'].df.in_first_page = 1; - this.filter_fields_dict['Journal Voucher Detail'+FILTER_SEP +'Account'].df.in_first_page = 1; - - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'From Posting Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'To Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['Journal Voucher'+FILTER_SEP +'Company'].df['report_default']=sys_defaults.company; -} -this.mytabs.items['Select Columns'].hide() \ No newline at end of file diff --git a/accounts/search_criteria/collection_report/collection_report.py b/accounts/search_criteria/collection_report/collection_report.py deleted file mode 100644 index bc40a69e93..0000000000 --- a/accounts/search_criteria/collection_report/collection_report.py +++ /dev/null @@ -1,107 +0,0 @@ -# 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 . - -#check mandatory -from __future__ import unicode_literals -if not filter_values.get('posting_date') or not filter_values.get('posting_date1'): - msgprint("Please select From Posting Date and To Posting Date ") - raise Exception -else: - from_date = filter_values.get('posting_date') - to_date = filter_values.get('posting_date1') - -if not filter_values['range_1'] or not filter_values['range_2'] or not filter_values['range_3'] or not filter_values['range_4']: - msgprint("Please select aging ranges in no of days in 'Set Filters' section") - raise Exception - -# ageing based on -aging_based_on = 'Aging Date' -if filter_values.get('aging_based_on'): - aging_based_on = filter_values['aging_based_on'].split(NEWLINE)[-1] - - -# Add columns -# ----------- -row_list = [['ID','Data','150px',''], - ['Account','Data','150px',''], - ['Credit','Data','150px',''], - ['Debit','Data','150px',''], - ['Against Receivable','Data','150px',''], - ['Is Advance','Data','150px',''], - ['Transaction Date','Date','150px',''], - ['Aging Date','Date','150px',''], - ['Company','Data','150px',''], - ['Cheque No','Data','100px',''], - ['Cheque Date','Date','150px',''], - ['Territory','Data','150px',''], - ['Remark','Data','250px',''], - ['Advance','Data','250px',''], - ['RV Transaction Date','Date','150px',''], - ['RV Aging Date','Date','150px',''], - ['Age (Days)','Data','50px',''], - ['0-'+cstr(filter_values['range_1']),'Currency','100px',''], - [cstr(cint(filter_values['range_1']) + 1)+ '-' +cstr(filter_values['range_2']),'Currency','100px',''], - [cstr(cint(filter_values['range_2']) + 1)+ '-' +cstr(filter_values['range_3']),'Currency','100px',''], - [cstr(cint(filter_values['range_3']) + 1)+ '-' +cstr(filter_values['range_4']),'Currency','100px',''], - [cstr(filter_values['range_4']) + '-Above','Currency','100px','']] - -for r in row_list: - colnames.append(r[0]) - coltypes.append(r[1]) - colwidths.append(r[2]) - coloptions.append(r[3]) - col_idx[r[0]] = len(colnames)-1 - -for r in res: - if r[col_idx['Against Receivable']]: - dt=sql("select date(modified), Aging_date from `tabSales Invoice` where name='%s'"%r[col_idx['Against Receivable']]) - r.append('') - r.append(dt and cstr(dt[0][0]) or '') - r.append(dt and cstr(dt[0][1]) or '') - else: - r.append(r[col_idx['Credit']]) - r.append('') - r.append('') - - - # Aging Credit Amount - val_l1 = val_l2 = val_l3 = val_l4 = val_l5_above = diff = 0 - - if r[col_idx['Against Receivable']]: - amt = flt(r[col_idx['Credit']]) or (-1)*flt(r[col_idx['Debit']]) - - if aging_based_on == 'Transaction Date' and r[col_idx['RV Transaction Date']]: - diff = (getdate(r[col_idx['Transaction Date']]) - getdate(r[col_idx['RV Transaction Date']])).days - elif aging_based_on == 'Aging Date' and r[col_idx['RV Aging Date']]: - diff = (getdate(r[col_idx['Aging Date']]) - getdate(r[col_idx['RV Aging Date']])).days - - if diff < cint(filter_values['range_1']): - val_l1 = amt - if diff >= cint(filter_values['range_1']) and diff < cint(filter_values['range_2']): - val_l2 = amt - if diff >= cint(filter_values['range_2']) and diff < cint(filter_values['range_3']): - val_l3 = amt - if diff >= cint(filter_values['range_3']) and diff < cint(filter_values['range_4']): - val_l4 = amt - if diff >= cint(filter_values['range_4']): - val_l5_above = amt - - r.append(diff) - r.append(val_l1) - r.append(val_l2) - r.append(val_l3) - r.append(val_l4) - r.append(val_l5_above) diff --git a/accounts/search_criteria/collection_report/collection_report.sql b/accounts/search_criteria/collection_report/collection_report.sql deleted file mode 100644 index 8743b886a0..0000000000 --- a/accounts/search_criteria/collection_report/collection_report.sql +++ /dev/null @@ -1,13 +0,0 @@ -SELECT `tabJournal Voucher`.`name`,`tabJournal Voucher Detail`.`account`,`tabJournal Voucher Detail`.`credit`,`tabJournal Voucher Detail`.`debit`,`tabJournal Voucher Detail`.`against_invoice`,`tabJournal Voucher Detail`.`is_advance`,`tabJournal Voucher`.`voucher_date`,`tabJournal Voucher`.`aging_date`,`tabJournal Voucher`.`company`,`tabJournal Voucher`.`cheque_no`,`tabJournal Voucher`.`cheque_date`,`tabCustomer`.`territory`, `tabJournal Voucher`.`remark` - FROM `tabJournal Voucher Detail`,`tabJournal Voucher`,`tabAccount`,`tabCustomer` - WHERE `tabJournal Voucher`.docstatus=1 - AND `tabJournal Voucher`.`posting_date`>='%(posting_date)s' - AND `tabJournal Voucher`.`posting_date`<='%(posting_date1)s' - AND `tabJournal Voucher`.`company` LIKE '%(company)s%%' - AND `tabJournal Voucher`.`is_opening` LIKE '%(is_opening)s%%' - AND `tabJournal Voucher Detail`.`account` LIKE '%(account)s%%' - AND `tabAccount`.master_type = 'Customer' - AND `tabAccount`.`account_name` = `tabCustomer`.`name` - AND `tabJournal Voucher Detail`.`account` = `tabAccount`.`name` - AND `tabJournal Voucher Detail`.`parent` = `tabJournal Voucher`.`name` - ORDER BY `tabJournal Voucher`.`name` \ No newline at end of file diff --git a/accounts/search_criteria/collection_report/collection_report.txt b/accounts/search_criteria/collection_report/collection_report.txt deleted file mode 100644 index 781e74c22c..0000000000 --- a/accounts/search_criteria/collection_report/collection_report.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" - }, - { - "parent_doc_type": "Journal Voucher", - "module": "Accounts", - "doctype": "Search Criteria", - "sort_order": "DESC", - "filters": "{'Journal Voucher\u0001Saved':1,'Journal Voucher\u0001Submitted':1,'Journal Voucher\u0001Voucher Type':'','Journal Voucher\u0001Is Opening':'','Journal Voucher\u0001Company':'','Journal Voucher\u0001TDS Applicable':'','Journal Voucher\u0001TDS Category':'','GL Entry\u0001Range 1':'30','GL Entry\u0001Range 2':'45','GL Entry\u0001Range 3':'60','GL Entry\u0001Range 4':'90'}", - "standard": "Yes", - "doc_type": "Journal Voucher Detail", - "name": "__common__", - "sort_by": "`tabJournal Voucher`.`name`", - "criteria_name": "Collection Report", - "columns": "Journal Voucher\u0001ID,Journal Voucher\u0001Posting Date,Journal Voucher\u0001Company,Journal Voucher\u0001Cheque No,Journal Voucher\u0001Cheque Date,Journal Voucher Detail\u0001Account,Journal Voucher Detail\u0001Credit,Journal Voucher Detail\u0001Against Receivable,Journal Voucher Detail\u0001Is Advance" - }, - { - "name": "collection_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/cost_center_wise_expense/__init__.py b/accounts/search_criteria/cost_center_wise_expense/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/cost_center_wise_expense/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.js b/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.js deleted file mode 100644 index d33a4e8d92..0000000000 --- a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.js +++ /dev/null @@ -1,39 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Cost Center'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'From Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Account'].df.filter_hide = 0; - - this.filter_fields_dict['GL Entry'+FILTER_SEP +'From Posting Date'].df.in_first_page = 1; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'To Posting Date'].df.in_first_page = 1; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Account'].df.in_first_page = 1; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Cost Center'].df.in_first_page = 1; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Company'].df.in_first_page = 1; - - - this.filter_fields_dict['GL Entry'+FILTER_SEP +'From Posting Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['GL Entry'+FILTER_SEP +'To Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['GL Entry'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - -} -this.mytabs.items['Select Columns'].hide(); -this.mytabs.items['More Filters'].hide(); \ No newline at end of file diff --git a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.py b/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.py deleted file mode 100644 index 1c7350128c..0000000000 --- a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.py +++ /dev/null @@ -1,30 +0,0 @@ -# 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 . - -# Add columns -# ----------- -from __future__ import unicode_literals -row_list = [['Cost Center','Data','160px'], - ['Account','Data','160px'], - ['Debit','Currency','120px'], - ['Credit','Currency','120px'], - ['Expense','Currency','120px']] - -for r in row_list: - colnames.append(r[0]) - coltypes.append(r[1]) - colwidths.append(r[2]) - col_idx[r[0]] = len(colnames)-1 \ No newline at end of file diff --git a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.sql b/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.sql deleted file mode 100644 index b5fbb7c85b..0000000000 --- a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.sql +++ /dev/null @@ -1,20 +0,0 @@ -SELECT - `tabGL Entry`.`cost_center`, - `tabAccount`.`parent_account`, - sum(ifnull(`tabGL Entry`.`debit`, 0)), - sum(ifnull(`tabGL Entry`.`credit`, 0)), - sum(ifnull(`tabGL Entry`.`debit`,0))-sum(ifnull(`tabGL Entry`.`credit`, 0)) - FROM - `tabGL Entry`,`tabAccount` - WHERE - `tabGL Entry`.`account`=`tabAccount`.`name` - AND ifnull(`tabGL Entry`.`is_cancelled`,'No')='No' - AND `tabAccount`.is_pl_account='Yes' - AND `tabAccount`.debit_or_credit='Debit' - AND `tabGL Entry`.`posting_date`>='%(posting_date)s' - AND `tabGL Entry`.`posting_date`<='%(posting_date1)s' - AND `tabGL Entry`.`company` LIKE '%(company)s%%' - AND `tabAccount`.`parent_account` LIKE '%(account)s%%' - AND `tabGL Entry`.`cost_center` LIKE '%(cost_center)s%%' - GROUP BY - `tabGL Entry`.`cost_center` , `tabAccount`.`parent_account` \ No newline at end of file diff --git a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.txt b/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.txt deleted file mode 100644 index 98e3b080e8..0000000000 --- a/accounts/search_criteria/cost_center_wise_expense/cost_center_wise_expense.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "swarnalata@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'GL Entry\u0001Voucher Type':'','GL Entry\u0001Is Cancelled':'','GL Entry\u0001Fiscal Year':'','GL Entry\u0001Company':''}", - "doctype": "Search Criteria", - "dis_filters": "fiscal_year", - "doc_type": "GL Entry", - "name": "__common__", - "sort_by": "`tabGL Entry`.`name`", - "page_len": 50, - "criteria_name": "Cost Center wise Expense" - }, - { - "name": "cost_center_wise_expense", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/creditors_ledger/__init__.py b/accounts/search_criteria/creditors_ledger/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/creditors_ledger/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/creditors_ledger/creditors_ledger.js b/accounts/search_criteria/creditors_ledger/creditors_ledger.js deleted file mode 100644 index b3f1a09571..0000000000 --- a/accounts/search_criteria/creditors_ledger/creditors_ledger.js +++ /dev/null @@ -1,40 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - //to hide all filters - this.hide_all_filters(); - field_list=['Voucher Type', 'Voucher No', 'From Posting Date','To Posting Date','Account','Company', 'Remarks', 'Is Cancelled', 'Is Opening'] - for(var i=0;i. - -#get company -from __future__ import unicode_literals - -import webnotes.defaults -company = filter_values.get('company') or webnotes.defaults.get_user_default('company') - -#get company letter head -l_head = sql("select letter_head from `tabCompany` where name='%s'" % company) -l_head = l_head and l_head[0][0] or '' - -# Posting date, fiscal year and year start date -#----------------------------------------------- -if not filter_values.get('posting_date') or not filter_values.get('posting_date1'): - msgprint("Please enter From Date and To Date") - raise Exception -else: - from_date = filter_values['posting_date'] - to_date = filter_values['posting_date1'] - -ysd, from_date_year = sql("select year_start_date, name from `tabFiscal Year` where %s between year_start_date and date_add(year_start_date,interval 1 year)",from_date)[0] - - -# define columns -#--------------- -col = [] -col.append(['Date','Date','80px','']) -col.append(['Detail','Text','475px','']) -col.append(['Debit','Currency','75px','']) -col.append(['Credit','Currency','75px','']) - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames) - - -total_debit, total_credit, total_opening, total_diff = 0,0,0,0 - -#total query -q = query.split('WHERE')[1].split('LIMIT') -if len(q) > 2: - query_where_clause = 'LIMIT'.join(q[:-1]) -else: - query_where_clause = q[0] - -tot = sql('select sum(`tabGL Entry`.debit),sum(`tabGL Entry`.credit) from `tabGL Entry`, tabAccount where %s' % query_where_clause) - -for t in tot: - total_debit += t and flt(t[0]) or 0 - total_credit += t and flt(t[1]) or 0 - -total_diff = total_debit - total_credit - -# opening -account = filter_values.get('account') -if account: - acc_det = sql("select debit_or_credit, is_pl_account, lft, rgt, group_or_ledger from tabAccount where name = '%s'" % account) - from accounts.utils import get_balance_on - opening_bal = get_balance_on(account, add_days(from_date, -1)) - - if acc_det[0][0] == 'Credit': - opening_bal = -1*opening_bal - - -out = [] -count = 0 -for r in res: - count +=1 - det = r[1].split('~~~') - if from_export == 1: - a = "Account: " + det[0] + NEWLINE + det[1] + NEWLINE + "Against: " + det[2] + NEWLINE + "Voucher No: " + det[4] - else: - a = "Account: " + det[0]+ "" + NEWLINE + "
    " +det[1]+ "
    Against: " + det[2] + "
    Voucher No: " + det[4] + "
    " - r[1] = a - out.append(r) - -if total_debit != 0 or total_credit != 0: - # Total debit/credit - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Total' - t_row[col_idx['Debit']-1] = total_debit - t_row[col_idx['Credit']-1] = total_credit - out.append(t_row) - - # opening - if account: - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Opening Balance on '+ from_date - t_row[col_idx['Debit']-1] = opening_bal - out.append(t_row) - - # diffrence (dr-cr) - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Total(Dr-Cr)' - t_row[col_idx['Debit']-1] = total_diff - out.append(t_row) - - # closing - if account: - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Closing Balance on ' + to_date - t_row[col_idx['Debit']-1] = flt(opening_bal) + flt(total_diff ) - out.append(t_row) - -# Print Format -myheader = """ - -
    """+l_head+"""
    -

    %(acc)s

    -
    Ledger Between %(fdt)s and %(tdt)s

    - - """ % {'acc':account, - 'fdt':from_date, - 'tdt':to_date} - -page_template = myheader+"
    %(table)s
    " \ No newline at end of file diff --git a/accounts/search_criteria/creditors_ledger/creditors_ledger.sql b/accounts/search_criteria/creditors_ledger/creditors_ledger.sql deleted file mode 100644 index 03784b9cb3..0000000000 --- a/accounts/search_criteria/creditors_ledger/creditors_ledger.sql +++ /dev/null @@ -1,15 +0,0 @@ -SELECT `tabGL Entry`.`posting_date`, CONCAT(`tabGL Entry`.`account`, "~~~", ifnull(`tabGL Entry`.`remarks`, ''), "~~~", ifnull(`tabGL Entry`.`against`,''), "~~~", ifnull(`tabGL Entry`.`voucher_type`, ''), "~~~", ifnull(`tabGL Entry`.`voucher_no`, '')), sum(`tabGL Entry`.`debit`), sum(`tabGL Entry`.`credit`) - FROM `tabGL Entry`, `tabAccount` - WHERE `tabGL Entry`.`is_cancelled` LIKE '%(is_cancelled)s%%' - AND `tabGL Entry`.`posting_date`>='%(posting_date)s' - AND `tabGL Entry`.`posting_date`<='%(posting_date1)s' - AND `tabGL Entry`.`company` LIKE '%(company)s%%' - AND `tabGL Entry`.`account` LIKE '%(account)s%%' - AND `tabGL Entry`.`remarks` LIKE '%(remarks)s%%' - AND `tabGL Entry`.`is_opening` LIKE '%(is_opening)s%%' - AND `tabGL Entry`.`voucher_no` LIKE '%(voucher_no)s%%' - AND `tabGL Entry`.`voucher_type` LIKE '%(voucher_type)s%%' - AND `tabGL Entry`.`account` = `tabAccount`.`name` - AND `tabAccount`.`master_type` = 'Supplier' - GROUP BY `tabGL Entry`.`voucher_no`,`tabGL Entry`.`account` - ORDER BY `tabGL Entry`.`posting_date` DESC \ No newline at end of file diff --git a/accounts/search_criteria/creditors_ledger/creditors_ledger.txt b/accounts/search_criteria/creditors_ledger/creditors_ledger.txt deleted file mode 100644 index 7f44ab4c66..0000000000 --- a/accounts/search_criteria/creditors_ledger/creditors_ledger.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "nabin@erpnext.com", - "docstatus": 0, - "creation": "2012-05-14 18:05:41", - "modified_by": "nabin@erpnext.com", - "modified": "2012-12-06 11:36:10" - }, - { - "custom_query": null, - "report_script": null, - "page_len": 50, - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"GL Entry\\u0001Voucher Type\":[],\"GL Entry\\u0001Is Cancelled\":[\"No\"],\"GL Entry\\u0001Is Opening\":[\"\"],\"GL Entry\\u0001Fiscal Year\":[\"\"]}", - "doc_type": "GL Entry", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabGL Entry`.`name`", - "criteria_name": "Creditors Ledger" - }, - { - "name": "creditors_ledger", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/debtors_ledger/__init__.py b/accounts/search_criteria/debtors_ledger/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/debtors_ledger/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/debtors_ledger/debtors_ledger.js b/accounts/search_criteria/debtors_ledger/debtors_ledger.js deleted file mode 100644 index b3f1a09571..0000000000 --- a/accounts/search_criteria/debtors_ledger/debtors_ledger.js +++ /dev/null @@ -1,40 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - //to hide all filters - this.hide_all_filters(); - field_list=['Voucher Type', 'Voucher No', 'From Posting Date','To Posting Date','Account','Company', 'Remarks', 'Is Cancelled', 'Is Opening'] - for(var i=0;i. - -#get company -from __future__ import unicode_literals - -import webnotes.defaults -company = filter_values.get('company') or webnotes.defaults.get_user_default('company') - -#get company letter head -l_head = sql("select letter_head from `tabCompany` where name='%s'" % company) -l_head = l_head and l_head[0][0] or '' - -# Posting date, fiscal year and year start date -#----------------------------------------------- -if not filter_values.get('posting_date') or not filter_values.get('posting_date1'): - msgprint("Please enter From Date and To Date") - raise Exception -else: - from_date = filter_values['posting_date'] - to_date = filter_values['posting_date1'] - -ysd, from_date_year = sql("select year_start_date, name from `tabFiscal Year` where %s between year_start_date and date_add(year_start_date,interval 1 year)",from_date)[0] - - -# define columns -#--------------- -col = [] -col.append(['Date','Date','80px','']) -col.append(['Detail','Text','475px','']) -col.append(['Debit','Currency','75px','']) -col.append(['Credit','Currency','75px','']) - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames) - - -total_debit, total_credit, total_opening, total_diff = 0,0,0,0 - -#total query -q = query.split('WHERE')[1].split('LIMIT') -if len(q) > 2: - query_where_clause = 'LIMIT'.join(q[:-1]) -else: - query_where_clause = q[0] - -tot = sql('select sum(`tabGL Entry`.debit),sum(`tabGL Entry`.credit) from `tabGL Entry`, tabAccount where %s' % query_where_clause) - -for t in tot: - total_debit += t and flt(t[0]) or 0 - total_credit += t and flt(t[1]) or 0 - -total_diff = total_debit - total_credit - -# opening -account = filter_values.get('account') -if account: - acc_det = sql("select debit_or_credit, is_pl_account, lft, rgt, group_or_ledger from tabAccount where name = '%s'" % account) - from accounts.utils import get_balance_on - opening_bal = get_balance_on(account, add_days(from_date, -1)) - if acc_det[0][0] == 'Credit': - opening_bal = -1*opening_bal - - -out = [] -count = 0 -for r in res: - count +=1 - det = r[1].split('~~~') - if from_export == 1: - a = "Account: " + det[0] + NEWLINE + det[1] + NEWLINE + "Against: " + det[2] + NEWLINE + "Voucher No: " + det[4] - else: - a = "Account: " + det[0]+ "" + NEWLINE + "
    " +det[1]+ "
    Against: " + det[2] + "
    Voucher No: " + det[4] + "
    " - r[1] = a - out.append(r) - -if total_debit != 0 or total_credit != 0: - # Total debit/credit - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Total' - t_row[col_idx['Debit']-1] = total_debit - t_row[col_idx['Credit']-1] = total_credit - out.append(t_row) - - # opening - if account: - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Opening Balance on '+ from_date - t_row[col_idx['Debit']-1] = opening_bal - out.append(t_row) - - # diffrence (dr-cr) - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Total(Dr-Cr)' - t_row[col_idx['Debit']-1] = total_diff - out.append(t_row) - - # closing - if account: - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Closing Balance on ' + to_date - t_row[col_idx['Debit']-1] = flt(opening_bal) + flt(total_diff ) - out.append(t_row) - -# Print Format -myheader = """ - -
    """+l_head+"""
    -

    %(acc)s

    -
    Ledger Between %(fdt)s and %(tdt)s

    - - """ % {'acc':account, - 'fdt':from_date, - 'tdt':to_date} - -page_template = myheader+"
    %(table)s
    " \ No newline at end of file diff --git a/accounts/search_criteria/debtors_ledger/debtors_ledger.sql b/accounts/search_criteria/debtors_ledger/debtors_ledger.sql deleted file mode 100644 index 59103fdadb..0000000000 --- a/accounts/search_criteria/debtors_ledger/debtors_ledger.sql +++ /dev/null @@ -1,15 +0,0 @@ -SELECT `tabGL Entry`.`posting_date`, CONCAT(`tabGL Entry`.`account`, "~~~", ifnull(`tabGL Entry`.`remarks`, ''), "~~~", ifnull(`tabGL Entry`.`against`,''), "~~~", ifnull(`tabGL Entry`.`voucher_type`, ''), "~~~", ifnull(`tabGL Entry`.`voucher_no`, '')), sum(`tabGL Entry`.`debit`), sum(`tabGL Entry`.`credit`) - FROM `tabGL Entry`, `tabAccount` - WHERE `tabGL Entry`.`is_cancelled` LIKE '%(is_cancelled)s%%' - AND `tabGL Entry`.`posting_date`>='%(posting_date)s' - AND `tabGL Entry`.`posting_date`<='%(posting_date1)s' - AND `tabGL Entry`.`company` LIKE '%(company)s%%' - AND `tabGL Entry`.`account` LIKE '%(account)s%%' - AND `tabGL Entry`.`remarks` LIKE '%(remarks)s%%' - AND `tabGL Entry`.`is_opening` LIKE '%(is_opening)s%%' - AND `tabGL Entry`.`voucher_no` LIKE '%(voucher_no)s%%' - AND `tabGL Entry`.`voucher_type` LIKE '%(voucher_type)s%%' - AND `tabGL Entry`.`account` = `tabAccount`.`name` - AND `tabAccount`.`master_type` = 'Customer' - GROUP BY `tabGL Entry`.`voucher_no`,`tabGL Entry`.`account` - ORDER BY `tabGL Entry`.`posting_date` DESC \ No newline at end of file diff --git a/accounts/search_criteria/debtors_ledger/debtors_ledger.txt b/accounts/search_criteria/debtors_ledger/debtors_ledger.txt deleted file mode 100644 index e04492f488..0000000000 --- a/accounts/search_criteria/debtors_ledger/debtors_ledger.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "nabin@erpnext.com", - "docstatus": 0, - "creation": "2012-05-14 18:05:42", - "modified_by": "nabin@erpnext.com", - "modified": "2012-12-06 11:37:16" - }, - { - "custom_query": null, - "report_script": null, - "page_len": 50, - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"GL Entry\\u0001Voucher Type\":[],\"GL Entry\\u0001Is Cancelled\":[\"No\"],\"GL Entry\\u0001Is Opening\":[],\"GL Entry\\u0001Fiscal Year\":[]}", - "doc_type": "GL Entry", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabGL Entry`.`name`", - "criteria_name": "Debtors Ledger" - }, - { - "name": "debtors_ledger", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/general_ledger/__init__.py b/accounts/search_criteria/general_ledger/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/general_ledger/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/general_ledger/general_ledger.js b/accounts/search_criteria/general_ledger/general_ledger.js deleted file mode 100644 index 3d6505fcdd..0000000000 --- a/accounts/search_criteria/general_ledger/general_ledger.js +++ /dev/null @@ -1,39 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.mytabs.tabs['Select Columns'].hide(); - //to hide all filters - this.hide_all_filters(); - field_list=['Voucher Type', 'Voucher No', 'From Posting Date','To Posting Date','Account','Company', 'Remarks', 'Is Cancelled', 'Is Opening'] - for(var i=0;i. - -#get company letter head -#--------------------------------------------------------------------- -from __future__ import unicode_literals -l_head = sql("select content from `tabLetter Head` where ifnull(is_default, 0) = 1 and ifnull(disabled, 0) = 0") -l_head = l_head and l_head[0][0] or '' - - -# Posting date, fiscal year and year start date -#--------------------------------------------------------------------- -if not filter_values.get('posting_date') or not filter_values.get('posting_date1'): - msgprint("Please enter From Date and To Date") - raise Exception -else: - from_date = filter_values['posting_date'] - to_date = filter_values['posting_date1'] - -# define columns -#--------------------------------------------------------------------- -col = [] -col.append(['Date','Date','80px','']) -col.append(['Detail','Text','475px','']) -col.append(['Debit','Currency','75px','']) -col.append(['Credit','Currency','75px','']) - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames) - - -#total query -#--------------------------------------------------------------------- -total_debit, total_credit, total_opening, total_diff = 0,0,0,0 -q = query.split('WHERE')[1].split('LIMIT') -if len(q) > 2: - query_where_clause = 'LIMIT'.join(q[:-1]) -else: - query_where_clause = q[0] - -tot = sql('select sum(debit),sum(credit) from `tabGL Entry` where %s' % query_where_clause) - -for t in tot: - total_debit += t and flt(t[0]) or 0 - total_credit += t and flt(t[1]) or 0 - -total_diff = total_debit - total_credit - -out = [] - - -# If account mentioned, show opening and closing -#--------------------------------------------------------------------- -account = filter_values.get('account') - -if account and (total_debit != 0 or total_credit != 0): - acc_det = sql("select debit_or_credit, is_pl_account, lft, rgt, group_or_ledger from tabAccount where name = '%s'" % account) - - from accounts.utils import get_balance_on - opening_bal = get_balance_on(account, add_days(from_date, -1)) - closing_bal = get_balance_on(account, to_date) - - if acc_det[0][0] == 'Credit': - closing_bal = -1*closing_bal - opening_bal = -1*opening_bal - - # add opening row - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Opening as on '+formatdate(from_date) - t_row[col_idx['Debit']-1] = opening_bal - out.append(t_row) - - -# table output -#--------------------------------------------------------------------- -count = 0 -for r in res: - count +=1 - det = r[1].split('~~~') - if from_export == 1: - a = "Account: " + det[0] + NEWLINE + det[1] + NEWLINE + "Against: " + det[2] + NEWLINE + "Voucher No: " + det[4] - else: - a = "Account: " + det[0]+ "" + NEWLINE + "
    " +det[1]+ "
    Against: " + det[2] + "
    Voucher No: " + det[4] + "
    " - r[1] = a - out.append(r) - - -# Total, Difference and closing balance -#--------------------------------------------------------------------- -if total_debit != 0 or total_credit != 0: - # Total debit/credit - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Total' - t_row[col_idx['Debit']-1] = total_debit - t_row[col_idx['Credit']-1] = total_credit - out.append(t_row) - - # diffrence (dr-cr) - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Total(Dr-Cr)' - t_row[col_idx['Debit']-1] = total_diff - out.append(t_row) - - # closing - if account: - t_row = ['' for i in range(len(colnames))] - t_row[1] = 'Closing Balance on ' + formatdate(to_date) - t_row[col_idx['Debit']-1] = flt(closing_bal) - out.append(t_row) - - -# Print Format -#--------------------------------------------------------------------- -myheader = """ - -
    """+l_head+"""
    -

    %(acc)s

    -
    Ledger Between %(fdt)s and %(tdt)s

    - - """ % {'acc':account, - 'fdt':from_date, - 'tdt':to_date} - -page_template = myheader+"
    %(table)s
    " diff --git a/accounts/search_criteria/general_ledger/general_ledger.sql b/accounts/search_criteria/general_ledger/general_ledger.sql deleted file mode 100644 index 51123cd990..0000000000 --- a/accounts/search_criteria/general_ledger/general_ledger.sql +++ /dev/null @@ -1,13 +0,0 @@ -SELECT `tabGL Entry`.`posting_date`, CONCAT(`tabGL Entry`.`account`, "~~~", ifnull(`tabGL Entry`.`remarks`, ''), "~~~", ifnull(`tabGL Entry`.`against`,''), "~~~", ifnull(`tabGL Entry`.`voucher_type`, ''), "~~~", ifnull(`tabGL Entry`.`voucher_no`, '')), sum(`tabGL Entry`.`debit`), sum(`tabGL Entry`.`credit`) - FROM `tabGL Entry` - WHERE `tabGL Entry`.`is_cancelled` LIKE '%(is_cancelled)s%%' - AND `tabGL Entry`.`posting_date`>='%(posting_date)s' - AND `tabGL Entry`.`posting_date`<='%(posting_date1)s' - AND `tabGL Entry`.`company` LIKE '%(company)s%%' - AND `tabGL Entry`.`account` LIKE '%(account)s%%' - AND `tabGL Entry`.`remarks` LIKE '%(remarks)s%%' - AND `tabGL Entry`.`is_opening` LIKE '%(is_opening)s%%' - AND `tabGL Entry`.`voucher_no` LIKE '%(voucher_no)s%%' - AND `tabGL Entry`.`voucher_type` LIKE '%(voucher_type)s%%' - GROUP BY `tabGL Entry`.`voucher_no`,`tabGL Entry`.`account` - ORDER BY `tabGL Entry`.`posting_date` DESC \ No newline at end of file diff --git a/accounts/search_criteria/general_ledger/general_ledger.txt b/accounts/search_criteria/general_ledger/general_ledger.txt deleted file mode 100644 index 939b8a3f49..0000000000 --- a/accounts/search_criteria/general_ledger/general_ledger.txt +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "Accounts", - "sort_order": "DESC", - "filters": "{'GL Entry\u0001From Posting Date\u0001lower':'','GL Entry\u0001To Posting Date\u0001upper':'','GL Entry\u0001Voucher Type':'','GL Entry\u0001Is Cancelled':'No','GL Entry\u0001Is Opening':'','GL Entry\u0001Fiscal Year':'','GL Entry\u0001Company':''}", - "standard": "Yes", - "doc_type": "GL Entry", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabGL Entry`.`name`", - "page_len": 50, - "criteria_name": "General Ledger" - }, - { - "name": "general_ledger", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/invoices_overdue/__init__.py b/accounts/search_criteria/invoices_overdue/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/invoices_overdue/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/invoices_overdue/invoices_overdue.txt b/accounts/search_criteria/invoices_overdue/invoices_overdue.txt deleted file mode 100644 index 514afacee6..0000000000 --- a/accounts/search_criteria/invoices_overdue/invoices_overdue.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Invoice\u0001Saved':1,'Sales Invoice\u0001Submitted':1,'Sales Invoice\u0001Is Opening':'','Sales Invoice\u0001Fiscal Year':''}", - "doc_type": "Sales Invoice", - "name": "__common__", - "add_cond": "`tabSales Invoice`.due_date. - -report.customize_filters = function() { - var me = this; - var set_filter_property = function(dt, field, property, value) { - if (me.filter_fields_dict[dt + FILTER_SEP + field]) - me.filter_fields_dict[dt + FILTER_SEP + field].df[property] = value; - } - - this.hide_all_filters(); - filter_list = ['Credit To', 'Is Opening', - 'From Posting Date', 'To Posting Date', "Company"] - - for(var i=0;i. - -report.customize_filters = function() { - var me = this; - var set_filter_property = function(dt, field, property, value) { - if (me.filter_fields_dict[dt + FILTER_SEP + field]) - me.filter_fields_dict[dt + FILTER_SEP + field].df[property] = value; - } - - this.hide_all_filters(); - filter_list_main = ['Debit To', 'From Posting Date', 'To Posting Date', "Company"] - for(var i=0;i. - - - -report.customize_filters = function() { - this.mytabs.items['Select Columns'].hide() - this.hide_all_filters(); - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'DocType'}); - this.add_filter({fieldname:'company', label:'Company', fieldtype:'Link', options:'Company', report_default:sys_defaults.company, ignore : 1, parent:'DocType'}); - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',ignore : 1, parent:'DocType'}); -} - -report.aftertableprint = function(t) { - $yt(t,'*',1,{NEWLINE:'
    '}); -} \ No newline at end of file diff --git a/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.py b/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.py deleted file mode 100644 index 7660fe9fd6..0000000000 --- a/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.py +++ /dev/null @@ -1,168 +0,0 @@ -# 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 -if filter_values.get('period'): - period_values = filter_values.get('period').split(NEWLINE) - -if not filter_values.get('fiscal_year'): - msgprint("Please Select Fiscal Year") - raise Exception -elif not filter_values.get('period'): - msgprint("Please Select Period") - raise Exception -elif len(period_values) > 2: - msgprint("You can view report only for one period. Please select only one value in period.") - raise Exception -else: - fiscal_year = filter_values.get('fiscal_year') - period = filter_values.get('period') - company = filter_values.get('company') - -# get fiscal year start date and start month -# --------------------------------------------------------- -year_start_date = sql("select year_start_date,MONTH(year_start_date) from `tabFiscal Year` where name = %s",fiscal_year) -start_date = year_start_date and year_start_date[0][0] or '' -start_month = year_start_date and year_start_date[0][1] or '' -month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] - -# Add columns based on period -# -------------------------------- -columns = [] -columns.append(['ID','Data','150px','']) -columns.append(['Description','Data','150px','']) -# ================ Annual ====================== -if period == 'Annual': - columns.append([fiscal_year,'Currency','150px','']) - -# =========== Half Yearly ====================== -elif period == 'Half Yearly': - columns.append([month_name[start_month-1]+' to '+month_name[start_month+4],'Currency','150px','']) # first half - if start_month == 1: # this is case when fiscal year starts with JAN - columns.append([month_name[start_month+5]+' to '+month_name[start_month+11],'Currency','150px','']) - else: #this is case when fiscal year starts with other than JAN - columns.append([month_name[start_month+5]+' to '+month_name[start_month-2],'Currency','150px','']) - columns.append(['Total','Currency','150px','']) - -# ================ Quarterly =================== -elif period == 'Quarterly': - length_1 = (len(month_name) - start_month + 1) / 3 #this gives the total no. of times we need to iterate for quarter - val = length_1 % 4 - q_no = 1 - for i in range(length_1): - value = 3*i + val - columns.append(['Q'+cstr(q_no)+' ('+month_name[value]+' to '+month_name[value+2]+')','Currency','150px','']) - q_no += 1 - length_2 = (start_month - 1) / 3 #this gives the total no. of times we need to iterate for quarter (this is required only if fiscal year starts from april) - for i in range(length_2): - columns.append(['Q'+cstr(q_no)+' ('+month_name[3*i]+' to '+month_name[3*i+2]+')','Currency','150px','']) - q_no += 1; - columns.append(['Total','Currency','150px','']) - -# =============== Monthly ====================== -elif period == 'Monthly': - for i in range(start_month-1,len(month_name)): - columns.append([month_name[i],'Currency','150px','']) - for i in range(start_month-1): - columns.append([month_name[i],'Currency','150px','']) - columns.append(['Total','Currency','150px','']) - -for c in columns: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 - -out = [] -if company: - condition = 'docstatus = 1 and fiscal_year = "'+fiscal_year+'" and company = "'+company+'"' -else: - condition = 'docstatus = 1 and fiscal_year = "'+fiscal_year+'"' - -for r in res: - det = '' - list_range = 0 - query = '' - # ================= Annual Report =============== - if period == 'Annual': - # Main Query - det = sql("SELECT count(*), SUM(net_total), MIN(net_total), MAX(net_total), AVG(net_total) from `tab%s` where %s" %(r[col_idx['ID']],condition)) - list_range = 1 - - # ============ Half Yearly Report =============== - elif period == 'Half Yearly': - # first half - query += 'COUNT(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN name ELSE NULL END),SUM(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),MIN(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),MAX(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),AVG(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),' - # second half - query += 'COUNT(CASE WHEN MONTH(transaction_date) NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN name ELSE NULL END),SUM(CASE WHEN MONTH(transaction_date) NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),MIN(CASE WHEN MONTH(transaction_date) NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),MAX(CASE WHEN MONTH(transaction_date) NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),AVG(CASE WHEN MONTH(transaction_date) NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),' - - # Main Query - det = sql("SELECT %s count(*), SUM(net_total), MIN(net_total), MAX(net_total), AVG(net_total) from `tab%s` where %s and transaction_date > CAST('%s' AS DATE)" %(query,r[col_idx['ID']],condition,start_date)) - list_range = 3 - - # =============== Quarterly Report ============== - elif period == 'Quarterly': - length_1 = (len(month_name) - start_month + 1) / 3; #this gives the total no. of times we need to iterate for quarter - val = length_1 % 4; - for i in range(length_1): - value = 3*i + val; - query += 'COUNT(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN name ELSE NULL END),SUM(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN net_total ELSE NULL END),MIN(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN net_total ELSE NULL END),MAX(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN net_total ELSE NULL END),AVG(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN net_total ELSE NULL END),' - length_2 = (start_month - 1) / 3; #this gives the total no. of times we need to iterate for quarter (this is required only if fiscal year starts from april) - for i in range(length_2): - query += 'COUNT(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN name ELSE NULL END),SUM(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN net_total ELSE NULL END),MIN(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN net_total ELSE NULL END),MAX(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN net_total ELSE NULL END),AVG(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN net_total ELSE NULL END),'; - # Main Query - det = sql("SELECT %s count(*), SUM(net_total), MIN(net_total), MAX(net_total), AVG(net_total) from `tab%s` where %s and transaction_date > CAST('%s' AS DATE)" %(query,r[col_idx['ID']],condition,start_date)) - list_range = 5 - - # ================ Monthly Report =============== - elif period == 'Monthly': - # for loop is required twice coz fiscal year starts from April (this will also work if fiscal year starts in January) - for i in range(start_month-1,len(month_name)): - query += 'COUNT(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN name ELSE NULL END), SUM(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END),MIN(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END), MAX(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END), AVG(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END),' - # the above query calculates total_no, total_amt, min_amt, max_amt, avg_amt of doctypes in monthwise - for i in range(start_month-1): - query += 'COUNT(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN name ELSE NULL END), SUM(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END),MIN(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END), MAX(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END), AVG(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END),' - - # Main Query - det = sql("SELECT %s count(*), SUM(net_total), MIN(net_total), MAX(net_total), AVG(net_total) from `tab%s` where %s and transaction_date > CAST('%s' AS DATE)" %(query,r[col_idx['ID']],condition,start_date)) - list_range = 13 - - # bifurcate all values and append them in list - total_no,total_amt,min_amt,max_amt,avg_amt = [],[],[],[],[] - - count = 0 - # append values to list - for i in range(list_range): - total_no.append(cstr(det and det[0][count] or 0)) - total_amt.append(cstr(det and det[0][count+1] or 0)) - min_amt.append(cstr(det and det[0][count+2] or 0)) - max_amt.append(cstr(det and det[0][count+3] or 0)) - avg_amt.append(cstr(det and det[0][count+4] or 0)) - count += 5 - - for col in range(len(colnames)-1): # this would make all first row blank. just for look - r.append('') - out.append(r) - - d = [['Total No',total_no],['Total Amount',total_amt],['Min Amount',min_amt],['Max Amount',max_amt],['Avg Amount',avg_amt]] - - for des in range(5): - t_row = ['' for i in range(len(colnames))] - t_row[col_idx['Description']] = d[des][0] - for v in range(list_range): - t_row[col_idx[colnames[v+2]]] = flt(d[des][1][v]) - out.append(t_row) \ No newline at end of file diff --git a/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.sql b/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.sql deleted file mode 100644 index b05de58bbb..0000000000 --- a/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT DISTINCT name FROM tabDocType WHERE document_type="Transaction" AND ifnull(istable,0) = 0 \ No newline at end of file diff --git a/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.txt b/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.txt deleted file mode 100644 index 1c6c60b0e8..0000000000 --- a/accounts/search_criteria/monthly_transaction_summary/monthly_transaction_summary.txt +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'DocType\u0001Period':'Monthly'}", - "doctype": "Search Criteria", - "doc_type": "Profile", - "name": "__common__", - "sort_by": "`tabProfile`.`name`", - "criteria_name": "Monthly Transaction Summary", - "columns": "Profile\u0001ID" - }, - { - "name": "monthly_transaction_summary", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/payment_receipt_report/__init__.py b/accounts/search_criteria/payment_receipt_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/payment_receipt_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/payment_receipt_report/payment_receipt_report.py b/accounts/search_criteria/payment_receipt_report/payment_receipt_report.py deleted file mode 100644 index 84cbbec245..0000000000 --- a/accounts/search_criteria/payment_receipt_report/payment_receipt_report.py +++ /dev/null @@ -1,41 +0,0 @@ -# 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 -if not filter_values.get('posting_date'): - msgprint("Enter From Posting Date.") - raise Exception - -if not filter_values.get('posting_date1'): - msgprint("Enter To Posting Date.") - raise Exception - -if not filter_values.get('company'): - msgprint("Select Company to proceed.") - raise Exception - - - -col_list = [['Account', 'Link', '150px', 'Account'] - ,['Total', 'Currency', '150px', ''] - ] - -for c in col_list: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames) - 1 diff --git a/accounts/search_criteria/payment_receipt_report/payment_receipt_report.sql b/accounts/search_criteria/payment_receipt_report/payment_receipt_report.sql deleted file mode 100644 index a4132b7f5c..0000000000 --- a/accounts/search_criteria/payment_receipt_report/payment_receipt_report.sql +++ /dev/null @@ -1 +0,0 @@ -select t1.account, sum(if(t2.debit_or_credit = 'Debit', ifnull(t1.debit,0) - ifnull(t1.credit,0), ifnull(t1.credit,0) - ifnull(t1.debit,0))) from `tabGL Entry` t1, `tabAccount` t2 where t1.account = t2.name and t2.account_type != 'Bank or Cash' and t1.name in (select t1.name from `tabGL Entry` t1, `tabAccount` t2 where t1.against = t2.name and t2.account_type = 'Bank or Cash' and posting_date >= '%(posting_date)s' and posting_date <= '%(posting_date1)s') group by t1.account \ No newline at end of file diff --git a/accounts/search_criteria/payment_receipt_report/payment_receipt_report.txt b/accounts/search_criteria/payment_receipt_report/payment_receipt_report.txt deleted file mode 100644 index 2e039e2254..0000000000 --- a/accounts/search_criteria/payment_receipt_report/payment_receipt_report.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "jai@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'GL Entry\u0001Voucher Type':'','GL Entry\u0001Is Cancelled':'','GL Entry\u0001Is Opening':'','GL Entry\u0001Fiscal Year':''}", - "doctype": "Search Criteria", - "doc_type": "GL Entry", - "name": "__common__", - "sort_by": "`tabGL Entry`.`name`", - "page_len": 50, - "criteria_name": "Payment Receipt Report", - "columns": "GL Entry\u0001ID,GL Entry\u0001Owner,GL Entry\u0001Posting Date,GL Entry\u0001Account,GL Entry\u0001Cost Center,GL Entry\u0001Against Voucher,GL Entry\u0001Voucher Type,GL Entry\u0001Voucher No,GL Entry\u0001Remarks,GL Entry\u0001Is Cancelled,GL Entry\u0001Is Opening,GL Entry\u0001Fiscal Year,GL Entry\u0001Company" - }, - { - "name": "payment_receipt_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/purchase_register/__init__.py b/accounts/search_criteria/purchase_register/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/purchase_register/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/purchase_register/purchase_register.py b/accounts/search_criteria/purchase_register/purchase_register.py deleted file mode 100644 index 82ab047e3b..0000000000 --- a/accounts/search_criteria/purchase_register/purchase_register.py +++ /dev/null @@ -1,97 +0,0 @@ -# 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 . - -# add expense head columns -from __future__ import unicode_literals -from webnotes.utils import flt - -expense_acc = [c[0] for c in sql("""select distinct expense_head - from `tabPurchase Invoice Item` - where parenttype='Purchase Invoice' - and docstatus=1 - order by expense_head asc""")] - -expense_acc.append('Net Total') - -for i in expense_acc: - colnames.append(i) - coltypes.append('Currency') - colwidths.append('100px') - coloptions.append('') - -# Add tax head columns -tax_acc = [c[0] for c in sql("""select distinct account_head - from `tabPurchase Taxes and Charges` - where parenttype = 'Purchase Invoice' - and add_deduct_tax = 'Add' - and category in ('Total', 'Valuation and Total') - and docstatus=1 - order by account_head asc""")] - -tax_acc.append('Total Tax') -tax_acc.append('Grand Total') - -for c in tax_acc: - if c: - colnames.append(c) - coltypes.append('Currency') - colwidths.append('100px') - coloptions.append('') - -# remove total columns from the list -expense_acc = expense_acc[:-1] -tax_acc = tax_acc[:-2] - -# add the values -for r in res: - #Get amounts for expense heads - exp_head_amount = sql("""select expense_head, sum(amount) - from `tabPurchase Invoice Item` - where parent = %s and parenttype='Purchase Invoice' - group by expense_head""", (r[col_idx['ID']])) - - #convert the result to dictionary for easy retrieval - exp_head_amount_dict = {} - for e in exp_head_amount: - exp_head_amount_dict[e[0]] = e[1] - - net_total = 0 - # get expense amount - for i in expense_acc: - val = exp_head_amount_dict.get(i, 0) - net_total += val - r.append(val) - r.append(net_total) - - #Get tax for account heads - acc_head_tax = sql("""select account_head, - sum(if(add_deduct_tax='Add', tax_amount, -tax_amount)) - from `tabPurchase Taxes and Charges` where parent = %s and parenttype = 'Purchase Invoice' - and category in ('Total', 'Valuation and Total') group by account_head""", r[col_idx['ID']]) - - #Convert the result to dictionary for easy retrieval - acc_head_tax_dict = {} - for a in acc_head_tax: - acc_head_tax_dict[a[0]] = flt(a[1]) - - # get tax amount - total_tax = 0 - for c in tax_acc: - val = acc_head_tax_dict.get(c, 0) - total_tax += val - r.append(val) - r.append(total_tax) - r.append(flt(total_tax)+ flt(net_total)) # grand total \ No newline at end of file diff --git a/accounts/search_criteria/purchase_register/purchase_register.txt b/accounts/search_criteria/purchase_register/purchase_register.txt deleted file mode 100644 index f4fdb36e23..0000000000 --- a/accounts/search_criteria/purchase_register/purchase_register.txt +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "doc_type": "Purchase Invoice", - "name": "__common__", - "add_cond": "`tabPurchase Invoice`.is_opening != 'Yes'\n`tabPurchase Invoice`.name not like 'OP/%'", - "module": "Accounts", - "standard": "Yes", - "filters": "{'Purchase Invoice\u0001Submitted':1}", - "doctype": "Search Criteria", - "criteria_name": "Purchase Register", - "columns": "Purchase Invoice\u0001ID,Purchase Invoice\u0001Voucher Date,Purchase Invoice\u0001Posting Date,Purchase Invoice\u0001Credit To,Purchase Invoice\u0001Expense Head" - }, - { - "name": "purchase_register", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/sales_register/__init__.py b/accounts/search_criteria/sales_register/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/sales_register/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/sales_register/sales_register.js b/accounts/search_criteria/sales_register/sales_register.js deleted file mode 100644 index 872e198a35..0000000000 --- a/accounts/search_criteria/sales_register/sales_register.js +++ /dev/null @@ -1,32 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'From Posting Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'To Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'ID'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Owner'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Saved'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Submitted'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Cancelled'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Grand Total >='].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Grand Total <='].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Sales Partner'].df.filter_hide = 1; - this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Is Opening Entry'].df.filter_hide = 1; -} diff --git a/accounts/search_criteria/sales_register/sales_register.py b/accounts/search_criteria/sales_register/sales_register.py deleted file mode 100644 index f9cbc5d02a..0000000000 --- a/accounts/search_criteria/sales_register/sales_register.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 . - -# add additional columns -from __future__ import unicode_literals -from webnotes.utils import flt - -cl = [c[0] for c in sql("""select distinct account_head - from `tabSales Taxes and Charges` - where parenttype='Sales Invoice' - and docstatus=1 - order by account_head asc""")] - -income_acc = [c[0] for c in sql("""select distinct income_account - from `tabSales Invoice Item` - where parenttype='Sales Invoice' - and docstatus=1 - order by income_account asc""")] - -income_acc.append('Net Total') - -for i in income_acc: - colnames.append(i) - coltypes.append('Currency') - colwidths.append('100px') - coloptions.append('') - -cl.append('Total Tax') -cl.append('Grand Total') -for c in cl: - colnames.append(c) - coltypes.append('Currency') - colwidths.append('100px') - coloptions.append('') - -income_acc = income_acc[:-1] -cl = cl[:-2] - - -# add the values -for r in res: - - #Get amounts for income account - income_acc_list = sql("""select income_account, sum(amount) - from `tabSales Invoice Item` - where parent = %s - and parenttype='Sales Invoice' - group by income_account""", (r[col_idx['ID']],)) - - #convert the result to dictionary for easy retrieval - income_acc_dict = {} - for ia in income_acc_list: - income_acc_dict[ia[0]] = flt(ia[1]) - - net_total = 0 - for i in income_acc: - val = income_acc_dict.get(i, 0) - net_total += val - r.append(val) - r.append(net_total) - - #Get tax for account heads - acc_head_tax = sql("""select account_head, sum(tax_amount) - from `tabSales Taxes and Charges` - where parent = '%s' - and parenttype = 'Sales Invoice' - group by account_head""" %(r[col_idx['ID']],)) - - #Convert the result to dictionary for easy retrieval - acc_head_tax_dict = {} - for a in acc_head_tax: - acc_head_tax_dict[a[0]] = flt(a[1]) - - total_tax = 0 - for c in cl: - val = acc_head_tax_dict.get(c, 0) - total_tax += val - r.append(val) - r.append(total_tax) - r.append(net_total+total_tax) \ No newline at end of file diff --git a/accounts/search_criteria/sales_register/sales_register.txt b/accounts/search_criteria/sales_register/sales_register.txt deleted file mode 100644 index b7b63f0cfc..0000000000 --- a/accounts/search_criteria/sales_register/sales_register.txt +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-11 17:36:48", - "modified_by": "Administrator", - "modified": "2012-04-18 17:41:46" - }, - { - "add_col": "`tabAccount`.`parent_account` AS 'Parent Account'\n`tabCustomer`.`territory` AS 'Territory'\n`tabCustomer`.`customer_details` AS 'Customer Details'", - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "add_tab": "`tabAccount`\n`tabCustomer`", - "dis_filters": "fiscal_year", - "doc_type": "Sales Invoice", - "name": "__common__", - "filters": "{'Sales Invoice\u0001Submitted':1,'Sales Invoice\u0001Is Opening':''}", - "add_cond": "ifnull(`tabSales Invoice`.`is_opening`, 'No') = 'No'\n`tabAccount`.name =`tabSales Invoice`.debit_to\n`tabCustomer`.`name` = `tabAccount`.`master_name`", - "doctype": "Search Criteria", - "sort_by": "`Parent Account`", - "page_len": 50, - "criteria_name": "Sales Register", - "columns": "Sales Invoice\u0001ID,Sales Invoice\u0001Posting Date,Sales Invoice\u0001Debit To" - }, - { - "name": "sales_register", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/trend_analyzer/__init__.py b/accounts/search_criteria/trend_analyzer/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/trend_analyzer/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/trend_analyzer/trend_analyzer.js b/accounts/search_criteria/trend_analyzer/trend_analyzer.js deleted file mode 100644 index 59d948342a..0000000000 --- a/accounts/search_criteria/trend_analyzer/trend_analyzer.js +++ /dev/null @@ -1,153 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.dt.set_no_limit(1); - - // hide transaction based on permissions - var all_transactions = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", - "Purchase Order", "Purchase Receipt", "Purchase Invoice"]; - var transaction_list = []; - $.each(all_transactions, function(i, dt) { - if(wn.boot.profile.can_read.indexOf(dt)!=-1) { - transaction_list.push(dt); - } - }); - - this.add_filter({fieldname:'transaction', label:'Transaction', fieldtype:'Select', options:transaction_list.join(NEWLINE),report_default:'Delivery Note',ignore : 1,parent:'Profile',in_first_page : 1,single_select : 1}); - - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Profile',in_first_page:1,single_select:1}); - - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Item'+NEWLINE+'Item Group'+NEWLINE+'Customer'+NEWLINE+'Customer Group'+NEWLINE+'Territory'+NEWLINE+'Supplier'+NEWLINE+'Supplier Type'+NEWLINE+'Project', ignore : 1, parent:'Profile', report_default:'Item', in_first_page : 1,single_select:1}); - - this.add_filter({fieldname:'group_by', label:'Group By', fieldtype:'Select', options:NEWLINE+'Item'+NEWLINE+'Customer'+NEWLINE+'Supplier', ignore : 1, parent:'Profile',single_select:1}); - - this.add_filter({fieldname:'order_type', label:'Order Type', fieldtype:'Select', options:NEWLINE+'Sales'+NEWLINE+'Maintenance',ignore : 1, parent:'Profile',single_select:1}); - - this.add_filter({fieldname:'company', label:'Company', fieldtype:'Link', options:'Company', report_default:sys_defaults.company, ignore : 1, parent:'Profile'}); - - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Profile', in_first_page:1}); - - - // Add Filters - this.add_filter({fieldname:'item', label:'Item', fieldtype:'Link', options:'Item', ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'item_group', label:'Item Group', fieldtype:'Link', options:'Item Group', ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'customer', label:'Customer', fieldtype:'Link', options:'Customer', ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'customer_group', label:'Customer Group', fieldtype:'Link', options:'Customer Group', ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'territory', label:'Territory', fieldtype:'Link', options:'Territory', ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'supplier', label:'Supplier', fieldtype:'Link', options:'Supplier', ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'supplier_type', label:'Supplier Type', fieldtype:'Link', options:'Supplier Type', ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'project', label:'Project', fieldtype:'Link', options:'Project', ignore : 1, parent:'Profile'}); -} - - -this.mytabs.tabs['Select Columns'].hide(); - -report.aftertableprint = function(t) { - $yt(t,'*',1,{whiteSpace:'pre'}); -} - -var validate_values = function(trans,based_on,order_type) { - if(!fiscal_year){ - msgprint("Please select Fiscal Year"); - return 0; - } - if((in_list(['Quotation','Sales Order','Delivery Note','Sales Invoice'],trans) && in_list(['Supplier','Supplier Type'],based_on)) || (in_list(['Purchase Order','Purchase Receipt','Purchase Invoice'],trans) && in_list(['Customer','Customer Group','Territory'],based_on))){ - msgprint("Sorry! You cannot fetch "+trans+" trend based on "+based_on); - return 0; - } - if(in_list(['Purchase Order','Purchase Receipt','Purchase Invoice'],trans) && order_type){ - msgprint("Please deselect Order Type for "+trans); - return 0; - } - return 1; -} - - -report.get_query = function() { - trans = this.get_filter('Profile', 'Transaction').get_value(); - order_type = this.get_filter('Profile', 'Order Type').get_value(); - based_on = this.get_filter('Profile', 'Based On').get_value(); - company = this.get_filter('Profile', 'Company').get_value(); - fiscal_year = this.get_filter('Profile', 'Fiscal Year').get_value(); - - if(validate_values(trans,based_on,order_type)){ - col = ''; - add_cond = ''; - add_col = ''; - add_tables = ''; - sp_cond = ''; - - trans_det = trans+' Item' - - if(order_type != '') add_code += ' AND t1.order_type = '+order_type; - - switch(based_on){ - case 'Item' : item = this.get_filter('Profile', 'Item').get_value(); - col = 'DISTINCT t2.item_code, t3.item_name'; - add_tables = ',tabItem t3'; - add_cond += ' AND t2.item_code = t3.name'; - if(item) add_cond += ' AND t2.item_code = "'+item+'"'; - break; - case 'Customer' : cust = this.get_filter('Profile', 'Customer').get_value(); - col = 'DISTINCT t1.customer, t3.territory'; - add_tables = ',tabCustomer t3'; - add_cond += ' AND t1.customer = t3.name'; - if(cust) add_cond += ' AND t1.customer = "'+cust+'"'; - break; - case 'Supplier' : supp = this.get_filter('Profile', 'Supplier').get_value(); - col = 'DISTINCT t1.supplier, t3.supplier_type'; - add_tables = ',tabSupplier t3'; - add_cond += ' AND t1.supplier = t3.name'; - if(supp) add_cond += ' AND t1.supplier = "'+supp+'"'; - break; - case 'Supplier Type' : supp_type = this.get_filter('Profile', 'Supplier Type').get_value(); - col = 'DISTINCT t3.supplier_type'; - add_tables = ',tabSupplier t3'; - add_cond += ' AND t1.supplier = t3.name'; - if(supp_type) add_cond += ' AND t1.supplier_type = "'+supp_type+'"'; - break; - case 'Project' : pro = this.get_filter('Profile', 'Project').get_value(); - if (inList(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], trans)) { - col = 'DISTINCT t2.project_name'; - if(pro) add_cond += ' AND t2.project_name = "'+pro+'"'; - } else { - col = 'DISTINCT t1.project_name'; - if(pro) add_cond += ' AND t1.project_name = "'+pro+'"'; - } - break; - case 'Item Group' : ig = this.get_filter('Profile', 'Item Group').get_value(); - if(ig) sp_cond += ' AND parent.name = "'+ig+'"'; - break; - case 'Customer Group' : cg = this.get_filter('Profile', 'Customer Group').get_value(); - if(cg) sp_cond += ' AND parent.name = "'+cg+'"'; - break; - case 'Territory' : ter = this.get_filter('Profile', 'Territory').get_value(); - if(ter) sp_cond += ' AND parent.name = "'+ter+'"'; - break; - - } - - - if(based_on == 'Item' || based_on == 'Customer' || based_on == 'Supplier' || based_on == 'Supplier Type' || based_on == 'Project') - var q ='SELECT '+col+' FROM `tab'+trans+'` t1, `tab'+trans_det+'` t2 '+add_tables+' WHERE t1.fiscal_year = "'+fiscal_year+'" and t1.company = "'+company+'" and t2.parent = t1.name '+add_cond; - else - var q = 'SELECT CONCAT(REPEAT(" ", COUNT(parent.name) - 1), node.name) AS "Name" FROM `tab'+based_on+'` node,`tab'+based_on+'` parent WHERE node.lft BETWEEN parent.lft and parent.rgt and node.docstatus !=2 '+sp_cond+' GROUP BY node.name ORDER BY node.lft'; - - return q; - } -} diff --git a/accounts/search_criteria/trend_analyzer/trend_analyzer.py b/accounts/search_criteria/trend_analyzer/trend_analyzer.py deleted file mode 100644 index 6bc4cf6f69..0000000000 --- a/accounts/search_criteria/trend_analyzer/trend_analyzer.py +++ /dev/null @@ -1,177 +0,0 @@ -# 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 . - -# ********************************************* INITIALIZATION ******************************************* -from __future__ import unicode_literals -out = [] - -# Filter Values -# ============================================= -based_on = filter_values.get('based_on') -group_by = filter_values.get('group_by') -trans = filter_values.get('transaction') -period = filter_values.get('period') -order_type = filter_values.get('order_type') -company = filter_values.get('company') -fiscal_year = filter_values.get('fiscal_year') -item = filter_values.get('item') -item_group = filter_values.get('item_group') -customer = filter_values.get('customer') -customer_group = filter_values.get('customer_group') -territory = filter_values.get('territory') -supplier = filter_values.get('supplier') -supplier_type = filter_values.get('supplier_type') -project = filter_values.get('project') - - -# ********************************************* SET DEFAULTS ************************************************** -# Details Table -# -------------- - -trans_det = trans+' Item' - -col_names, query_val = get_obj('Trend Analyzer Control').get_single_year_query_value(fiscal_year, period, trans, trans_det) -query_val += 'SUM(t2.qty), SUM(t2.amount)' - -col_names.append('Total (Qty)') -col_names.append('Total (Amt)') - - -# ********************************************* VALIDATIONS *************************************************** -if (based_on in ['Customer','Customer Group','Territory'] and group_by == 'Supplier') or (based_on in ['Supplier','Supplier Type'] and group_by == 'Customer'): - msgprint("Sorry! You cannot group Trend Analyzer based on %s by %s" % (based_on,group_by)) - raise Exception - -if based_on == group_by: - msgprint("Based On and Group By value cannot be same for Trend Analyzer") - raise Exception - - -# ********************************************** ADD COLUMNS ********************************************** -cols = [[based_on, 'Data', '300px', '']] -cr = 1 -if based_on == 'Item': - cols.append(['Item Name','Data','200px','']) - cr = 2 -elif based_on == 'Customer': - cols.append(['Territory','Link','150px','Territory']) - cr = 2 -elif based_on == 'Supplier': - cols.append(['Supplier Type','Link','150px','Supplier Type']) - cr = 2 -if group_by: - cr += 1 - -if group_by: - cols.append([group_by,'Data','150px','']) - -for c in col_names: - cols.append([c, ("Amt" in c) and 'Currency' or 'Float','150px','']) - -for c in cols: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 - - -# ******************************************* ADDITIONAL CONDITION ************************************************ -add_cond = ' t2.parent = t1.name AND t1.company = "%s" AND t1.fiscal_year = "%s" and t1.docstatus = 1' % (company, fiscal_year) -add_tab = ' `tab'+trans+'` t1, `tab'+trans_det+'` t2' -if order_type: add_cond += ' AND t1.order_type = "%s"' % order_type - - -# Item -if item or based_on == 'Item': - add_cond += ' AND t2.item_code = "%s"' % (based_on != 'Item' and item or '%(value)s') - -# Item Group -if item_group or based_on == 'Item Group': - add_tab += ' ,`tabItem` t3, `tabItem Group` t4 ' - add_cond += ' AND t3.name = t2.item_code AND t3.item_group = t4.name and (t4.name = "%s" or t4.name IN (SELECT t5.name FROM `tabItem Group` t5,`tabItem Group` t6 WHERE t5.lft BETWEEN t6.lft and t6.rgt and t5.docstatus !=2 and t6.name = "%s"))' % (based_on != 'Item Group' and item_group or '%(value)s', based_on != 'Item Group' and item_group or '%(value)s') - -# Customer -if customer or based_on == 'Customer': - add_cond += ' AND t1.customer = "%s"' % (based_on != 'Customer' and customer or '%(value)s') - -# Customer Group -if customer_group or based_on == 'Customer Group': - add_tab += ' ,`tabCustomer` t7, `tabCustomer Group` t8 ' - add_cond += ' AND t7.name = t1.customer AND t7.customer_group = t8.name and (t8.name = "%s" or t8.name IN (SELECT t9.name FROM `tabCustomer Group` t9,`tabCustomer Group` t10 WHERE t9.lft BETWEEN t10.lft and t10.rgt and t9.docstatus !=2 and ifnull(t9.is_group,"No") = "No" and t10.name = "%s"))' % (based_on != 'Customer Group' and customer_group or '%(value)s', based_on != 'Customer Group' and customer_group or '%(value)s') - -# Territory -if territory or based_on == 'Territory': - add_tab += ' ,`tabTerritory` t11 ' - add_cond += ' AND t1.territory = t11.name and (t11.name = "%s" or t11.name IN (SELECT t12.name FROM `tabTerritory` t12,`tabTerritory` t13 WHERE t12.lft BETWEEN t13.lft and t13.rgt and t12.docstatus !=2 and ifnull(t12.is_group,"No") = "No" and t13.name = "%s"))' % (based_on != 'Territory' and territory or '%(value)s', based_on != 'Territory' and territory or '%(value)s') - -# Supplier -if supplier or based_on == 'Supplier': - add_cond += ' AND t1.supplier = "%s"' % (based_on != 'Supplier' and supplier or '%(value)s') - -# Supplier Type -if supplier_type or based_on == 'Supplier Type': - add_tab += ' ,`tabSupplier` t14, `tabSupplier Type` t15 ' - add_cond += ' AND t14.name = t1.supplier AND t14.supplier_type = t15.name and t15.name = "%s"' % (based_on != 'Supplier Type' and supplier_type or '%(value)s') - -# Project -if project or based_on == 'Project': - if trans in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']: - add_cond += ' AND t2.project_name = "%s"' % (based_on != 'Project' and project or '%(value)s') - else: - add_cond += ' AND t1.project_name = "%s"' % (based_on != 'Project' and project or '%(value)s') - -# Column to be seleted for group by condition -# ============================================== -sel_col = '' -if group_by == 'Item': - sel_col = 't2.item_code' -elif group_by == 'Customer': - sel_col = 't1.customer' -elif group_by == 'Supplier': - sel_col = 't1.supplier' - - -# ********************************************** Result Set ************************************************ -for r in res: - main_det = sql("SELECT %s FROM %s WHERE %s" % (query_val, add_tab, add_cond % {'value':cstr(r[col_idx[based_on]]).strip()})) - if group_by: - for col in range(cr,cr+1): # this would make all first row blank. just for look - r.append('') - if main_det[0][len(colnames) - cr - 1]: - for d in range(len(colnames) - cr): - r.append(flt(main_det[0][d])) - out.append(r) - - if group_by: - flag = 1 - # check for root nodes - if based_on in ['Item Group','Customer Group','Territory']: - is_grp = sql("select is_group from `tab%s` where name = '%s'" % (based_on, cstr(r[col_idx[based_on]]).strip())) - is_grp = is_grp and cstr(is_grp[0][0]) or '' - if is_grp != 'No': - flag = 0 - - if flag == 1: - det = [x[0] for x in sql("SELECT DISTINCT %s FROM %s where %s" % (sel_col, add_tab, add_cond % {'value':cstr(r[col_idx[based_on]]).strip()}))] - - for des in range(len(det)): - t_row = ['' for i in range(len(colnames))] - t_row[col_idx[group_by]] = cstr(det[des]) - gr_det = sql("SELECT %s FROM %s WHERE %s = '%s' and %s" % (query_val, add_tab, sel_col, cstr(det[des]), add_cond % {'value':cstr(r[col_idx[based_on]]).strip()})) - for d in range(len(col_names)): - t_row[col_idx[col_names[d]]] = flt(gr_det[0][d]) - out.append(t_row) \ No newline at end of file diff --git a/accounts/search_criteria/trend_analyzer/trend_analyzer.txt b/accounts/search_criteria/trend_analyzer/trend_analyzer.txt deleted file mode 100644 index ab15003bd0..0000000000 --- a/accounts/search_criteria/trend_analyzer/trend_analyzer.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "saumil@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-23 12:46:00", - "modified_by": "Administrator", - "modified": "2012-05-04 12:49:43" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{}", - "doctype": "Search Criteria", - "doc_type": "Profile", - "name": "__common__", - "sort_by": "`tabProfile`.`name`", - "page_len": 50, - "criteria_name": "Trend Analyzer", - "columns": "Profile\u0001ID,Profile\u0001Owner" - }, - { - "name": "trend_analyzer", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/voucher_wise_tax_details/__init__.py b/accounts/search_criteria/voucher_wise_tax_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/voucher_wise_tax_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.js b/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.js deleted file mode 100644 index 09a4498237..0000000000 --- a/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.js +++ /dev/null @@ -1,63 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - //Add filter - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice', report_default:'Sales Invoice', ignore : 1,parent:'Sales Taxes and Charges', single_select :1, in_first_page:1}); - - this.add_filter({fieldname:'posting_date', label:'Date', fieldtype:'Date', options:'', ignore : 1,parent:'Sales Taxes and Charges', in_first_page:1}); - - this.add_filter({fieldname:'voucher_id', label:'Voucher Id', fieldtype:'Data', options:'', ignore : 1,parent:'Sales Taxes and Charges', in_first_page:1}); - - this.add_filter({fieldname:'tax_account', label:'Tax Account', fieldtype:'Link', options:'Account', ignore : 1,parent:'Sales Taxes and Charges', in_first_page:1}); -} - - -// hide sections -//-------------------------------------- -this.mytabs.items['More Filters'].hide(); -this.mytabs.items['Select Columns'].hide(); - -// Get query -//-------------------------------------- -report.get_query = function() { - based_on = this.get_filter('Sales Taxes and Charges', 'Based On').get_value(); - from_date = this.get_filter('Sales Taxes and Charges', 'From Date').get_value(); - to_date = this.get_filter('Sales Taxes and Charges', 'To Date').get_value(); - vid = this.get_filter('Sales Taxes and Charges', 'Voucher Id').get_value(); - acc = this.get_filter('Sales Taxes and Charges', 'Tax Account').get_value(); - - date_fld = 'transaction_date'; - if(based_on == 'Sales Invoice') { - based_on = 'Sales Invoice'; - date_fld = 'posting_date'; - } - - sp_cond = ''; - if (from_date) sp_cond += repl(' AND t1.%(dt)s >= "%(from_date)s"', {dt:date_fld, from_date:from_date}); - if (to_date) sp_cond += repl(' AND t1.%(dt)s <= "%(to_date)s"', {dt:date_fld, to_date:to_date}); - if (vid) sp_cond += repl(' AND t1.name LIKE "%%(voucher)s%"', {voucher:vid}); - if (acc) sp_cond += repl(' AND t2.account_head = "%(acc)s"', {acc:acc}); - - return repl('SELECT t1.`name`, t1.`%(dt)s`, t1.`customer_name`, t1.net_total, t2.account_head, t2.description, t2.rate, t2.tax_amount \ - FROM `tab%(parent)s` t1, `tabSales Taxes and Charges` t2 \ - WHERE t1.docstatus=1 AND t2.`parenttype` = "%(parent)s" \ - AND t2.`parent` = t1.`name` \ - %(cond)s ORDER BY t1.`name` DESC, t1.%(dt)s DESC', {parent:based_on, cond:sp_cond, dt:date_fld}); -} - diff --git a/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.py b/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.py deleted file mode 100644 index 0f99d0b418..0000000000 --- a/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.py +++ /dev/null @@ -1,39 +0,0 @@ -# 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 -if filter_values.get('based_on') == 'Sales Invoice': - based_on_dt = 'Sales Invoice' -else: - based_on_dt = filter_values.get('based_on') - -cols = [ - [filter_values.get('based_on'), 'Link','150px', based_on_dt], - ['Transaction Date', 'Date', '120px', ''], - ['Customer', 'Link','150px','Customer'], - ['Net Total', 'Currency', '80px', ''], - ['Tax Account', 'Link','150px','Account'], - ['Description', 'Text','120px',''], - ['Tax Rate', 'Currency', '80px', ''], - ['Tax Amount', 'Currency', '80px', ''] -] - -for c in cols: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 diff --git a/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.txt b/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.txt deleted file mode 100644 index 7e8adf3230..0000000000 --- a/accounts/search_criteria/voucher_wise_tax_details/voucher_wise_tax_details.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "parent_doc_type": "Delivery Note", - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Delivery Note\u0001Submitted':1,'Delivery Note\u0001Status':'','Delivery Note\u0001Fiscal Year':''}", - "doc_type": "Sales Taxes and Charges", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabSales Taxes and Charges`.`parent`", - "page_len": 50, - "criteria_name": "Voucher wise tax details", - "columns": "Sales Taxes and Charges\u0001Type,Sales Taxes and Charges\u0001Account Head,Sales Taxes and Charges\u0001Cost Center,Sales Taxes and Charges\u0001Description,Sales Taxes and Charges\u0001Rate,Sales Taxes and Charges\u0001Amount*,Sales Taxes and Charges\u0001Total*" - }, - { - "name": "voucher_wise_tax_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/accounts/search_criteria/yearly_transaction_summary/__init__.py b/accounts/search_criteria/yearly_transaction_summary/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/accounts/search_criteria/yearly_transaction_summary/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.js b/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.js deleted file mode 100644 index febd6a3b95..0000000000 --- a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.js +++ /dev/null @@ -1,28 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.mytabs.items['Select Columns'].hide() - this.hide_all_filters(); - this.add_filter({fieldname:'company', label:'Company', fieldtype:'Link', options:'Company', report_default:sys_defaults.company, ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'from_fiscal_year', label:'From Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'to_fiscal_year', label:'To Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Profile'}); - this.add_filter({fieldname:'date', label:'Date', fieldtype:'Date', options:'',ignore : 1, parent:'Profile'}); -} - -report.aftertableprint = function(t) { - $yt(t,'*',1,{NEWLINE:'
    '}); -} \ No newline at end of file diff --git a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.py b/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.py deleted file mode 100644 index 166d3133ad..0000000000 --- a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.py +++ /dev/null @@ -1,131 +0,0 @@ -# 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 -if not filter_values.get('from_fiscal_year'): - msgprint("Please Select From Fiscal Year") - raise Exception -elif not filter_values.get('to_fiscal_year'): - msgprint("Please Select To Fiscal Year") - raise Exception -else: - from_fiscal_year = filter_values.get('from_fiscal_year') - to_fiscal_year = filter_values.get('to_fiscal_year') - company = filter_values.get('company') - from_date = filter_values.get('date') - to_date = filter_values.get('date1') - if from_date != "" and to_date != "": - get_obj('MIS Control').dates(from_fiscal_year,from_date,to_date) # validate dates (i.e. dates should be between particular fiscal year) - -# Add columns based on from and to fiscal year--------- -columns = [] -columns.append(['ID','Data','150px','']) -columns.append(['Description','Data','150px','']) -columns.append([from_fiscal_year,'Data','150px','']) # append from fiscal year column - -# === get no. of fiscal years between from and to fiscal year and append columns accordingly ======== -start_year = from_fiscal_year.split('-')[1] # eg. from fiscal year 2008-2009 . this gives value 2009 -end_year = to_fiscal_year.split('-')[0] # eg. to fiscal year 2009-2010 . this gives value 2009 -diff = cint(end_year)-cint(start_year) -if diff > 0: - year = cint(start_year); - next_year = 0 - f_year = '' - for i in range(1,diff+1): - next_year = cint(year)+1 - f_year = cstr(year)+'-'+cstr(next_year) - columns.append([f_year,'Data','150px','']) -# ==================================================================================================== - -columns.append([to_fiscal_year,'Data','150px','']) # append to fiscal year column - -for c in columns: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 - -out = [] -# =========================== condition for result =================================================== -if company: - condition = 'docstatus = 1 and company = "'+company+'"' -else: - condition = 'docstatus = 1' - -# ==================================================================================================== - -for r in res: - det = '' - query = '' - list_range = 0 - if from_date != "" and to_date != "": - date_1 = cstr(get_obj('MIS Control').dates(from_fiscal_year,from_date,to_date)) - query += 'COUNT(CASE WHEN (fiscal_year = "'+from_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_1.split('~~~')[0]+'" AS DATE) AND CAST("'+date_1.split('~~~')[1]+'" AS DATE))) THEN name ELSE NULL END),SUM(CASE WHEN (fiscal_year = "'+from_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_1.split('~~~')[0]+'" AS DATE) AND CAST("'+date_1.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),MIN(CASE WHEN (fiscal_year = "'+from_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_1.split('~~~')[0]+'" AS DATE) AND CAST("'+date_1.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),MAX(CASE WHEN (fiscal_year = "'+from_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_1.split('~~~')[0]+'" AS DATE) AND CAST("'+date_1.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),AVG(CASE WHEN (fiscal_year = "'+from_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_1.split('~~~')[0]+'" AS DATE) AND CAST("'+date_1.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),' - else: - query += 'COUNT(CASE WHEN fiscal_year = "'+from_fiscal_year+'" THEN name ELSE NULL END),SUM(CASE WHEN fiscal_year = "'+from_fiscal_year+'" THEN net_total ELSE NULL END),MIN(CASE WHEN fiscal_year = "'+from_fiscal_year+'" THEN net_total ELSE NULL END),MAX(CASE WHEN fiscal_year = "'+from_fiscal_year+'" THEN net_total ELSE NULL END),AVG(CASE WHEN fiscal_year = "'+from_fiscal_year+'" THEN net_total ELSE NULL END),' - list_range += 1 - - if diff > 0: - year = cint(start_year); - next_year = 0 - f_year = '' - for i in range(1,diff+1): - next_year = cint(year)+1; - f_year = cstr(year)+'-'+cstr(next_year); - if from_date != "" and to_date != "": - date_2 = cstr(get_obj('MIS Control').dates(f_year,from_date,to_date)) - query += 'COUNT(CASE WHEN (fiscal_year = "'+f_year+'" and (transaction_date BETWEEN CAST("'+date_2.split('~~~')[0]+'" AS DATE) AND CAST("'+date_2.split('~~~')[1]+'" AS DATE))) THEN name ELSE NULL END),SUM(CASE WHEN (fiscal_year = "'+f_year+'" and (transaction_date BETWEEN CAST("'+date_2.split('~~~')[0]+'" AS DATE) AND CAST("'+date_2.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),MIN(CASE WHEN (fiscal_year = "'+f_year+'" and (transaction_date BETWEEN CAST("'+date_2.split('~~~')[0]+'" AS DATE) AND CAST("'+date_2.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),MAX(CASE WHEN (fiscal_year = "'+f_year+'" and (transaction_date BETWEEN CAST("'+date_2.split('~~~')[0]+'" AS DATE) AND CAST("'+date_2.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),AVG(CASE WHEN (fiscal_year = "'+f_year+'" and (transaction_date BETWEEN CAST("'+date_2.split('~~~')[0]+'" AS DATE) AND CAST("'+date_2.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),' - else: - query += 'COUNT(CASE WHEN fiscal_year = "'+f_year+'" THEN name ELSE NULL END),SUM(CASE WHEN fiscal_year = "'+f_year+'" THEN net_total ELSE NULL END),MIN(CASE WHEN fiscal_year = "'+f_year+'" THEN net_total ELSE NULL END),MAX(CASE WHEN fiscal_year = "'+f_year+'" THEN net_total ELSE NULL END),AVG(CASE WHEN fiscal_year = "'+f_year+'" THEN net_total ELSE NULL END),' - year += 1 - list_range += 1 - - if from_date != "" and to_date != "": - date_3 = cstr(get_obj('MIS Control').dates(to_fiscal_year,from_date,to_date)) - query += 'COUNT(CASE WHEN (fiscal_year = "'+to_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_3.split('~~~')[0]+'" AS DATE) AND CAST("'+date_3.split('~~~')[1]+'" AS DATE))) THEN name ELSE NULL END),SUM(CASE WHEN (fiscal_year = "'+to_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_3.split('~~~')[0]+'" AS DATE) AND CAST("'+date_3.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),MIN(CASE WHEN (fiscal_year = "'+to_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_3.split('~~~')[0]+'" AS DATE) AND CAST("'+date_3.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),MAX(CASE WHEN (fiscal_year = "'+to_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_3.split('~~~')[0]+'" AS DATE) AND CAST("'+date_3.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END),AVG(CASE WHEN (fiscal_year = "'+to_fiscal_year+'" and (transaction_date BETWEEN CAST("'+date_3.split('~~~')[0]+'" AS DATE) AND CAST("'+date_3.split('~~~')[1]+'" AS DATE))) THEN net_total ELSE NULL END)' - else: - query += 'COUNT(CASE WHEN fiscal_year = "'+to_fiscal_year+'" THEN name ELSE NULL END),SUM(CASE WHEN fiscal_year = "'+to_fiscal_year+'" THEN net_total ELSE NULL END),MIN(CASE WHEN fiscal_year = "'+to_fiscal_year+'" THEN net_total ELSE NULL END),MAX(CASE WHEN fiscal_year = "'+to_fiscal_year+'" THEN net_total ELSE NULL END),AVG(CASE WHEN fiscal_year = "'+to_fiscal_year+'" THEN net_total ELSE NULL END)' - list_range += 1 - - # Main Query - det = sql("SELECT %s from `tab%s` where %s" %(query,r[col_idx['ID']],condition)) - - # bifurcate all values and append them in list - total_no,total_amt,min_amt,max_amt,avg_amt = [],[],[],[],[] - - count = 0 - # append values to list - for i in range(list_range): - total_no.append(cstr(det and det[0][count] or 0)) - total_amt.append(cstr(det and det[0][count+1] or 0)) - min_amt.append(cstr(det and det[0][count+2] or 0)) - max_amt.append(cstr(det and det[0][count+3] or 0)) - avg_amt.append(cstr(det and det[0][count+4] or 0)) - count += 5 - - for col in range(len(colnames)-1): # this would make all first row blank. just for look - r.append('') - out.append(r) - - d = [['Total No',total_no],['Total Amount',total_amt],['Min Amount',min_amt],['Max Amount',max_amt],['Avg Amount',avg_amt]] - - for des in range(5): - t_row = ['' for i in range(len(colnames))] - t_row[col_idx['Description']] = d[des][0] - for v in range(list_range): - t_row[col_idx[colnames[v+2]]] = flt(d[des][1][v]) - out.append(t_row) \ No newline at end of file diff --git a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.sql b/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.sql deleted file mode 100644 index b05de58bbb..0000000000 --- a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT DISTINCT name FROM tabDocType WHERE document_type="Transaction" AND ifnull(istable,0) = 0 \ No newline at end of file diff --git a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.txt b/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.txt deleted file mode 100644 index 9c6ad4bcbc..0000000000 --- a/accounts/search_criteria/yearly_transaction_summary/yearly_transaction_summary.txt +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'DocType\u0001Fiscal Year':'2009-2010','DocType\u0001Company':'Alpha Company','DocType\u0001Period':'Monthly'}", - "doctype": "Search Criteria", - "doc_type": "Profile", - "name": "__common__", - "sort_by": "`tabProfile`.`name`", - "criteria_name": "Yearly Transaction Summary", - "columns": "Profile\u0001ID" - }, - { - "name": "yearly_transaction_summary", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js index 0e078fefc9..182b075808 100644 --- a/buying/page/buying_home/buying_home.js +++ b/buying/page/buying_home/buying_home.js @@ -91,11 +91,6 @@ wn.module_page["Buying"] = [ "label":wn._("Purchase Analytics"), page: "purchase-analytics" }, - { - "label":wn._("Trend Analyzer"), - route: "Report/Profile/Trend Analyzer", - doctype: "Purchase Order" - }, ] }, { diff --git a/buying/search_criteria/__init__.py b/buying/search_criteria/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/buying/search_criteria/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/buying/search_criteria/itemwise_purchase_details/__init__.py b/buying/search_criteria/itemwise_purchase_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/buying/search_criteria/itemwise_purchase_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.js b/buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.js deleted file mode 100644 index 139d69e197..0000000000 --- a/buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.js +++ /dev/null @@ -1,21 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'From Purchase Order Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'To Purchase Order Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - -} \ No newline at end of file diff --git a/buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.txt b/buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.txt deleted file mode 100644 index ac92b71e0e..0000000000 --- a/buying/search_criteria/itemwise_purchase_details/itemwise_purchase_details.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-11 13:16:55", - "modified_by": "Administrator", - "modified": "2012-04-13 11:15:06" - }, - { - "parent_doc_type": "Purchase Order", - "module": "Buying", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"Purchase Order\\u0001Submitted\":1,\"Purchase Order\\u0001Status\":[],\"Purchase Order\\u0001Fiscal Year\":[]}", - "doc_type": "Purchase Order Item", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabPurchase Order`.`transaction_date`", - "page_len": 50, - "criteria_name": "Itemwise Purchase Details", - "columns": "Purchase Order\u0001ID,Purchase Order\u0001Purchase Order Date,Purchase Order Item\u0001Item Code,Purchase Order Item\u0001Item Name,Purchase Order Item\u0001Quantity,Purchase Order Item\u0001Stock UOM,Purchase Order Item\u0001Rate ,Purchase Order Item\u0001Amount,Purchase Order\u0001Net Total*,Purchase Order\u0001Grand Total" - }, - { - "name": "itemwise_purchase_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/buying/search_criteria/pending_po_items_to_bill/__init__.py b/buying/search_criteria/pending_po_items_to_bill/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/buying/search_criteria/pending_po_items_to_bill/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.js b/buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.js deleted file mode 100644 index de2c03722d..0000000000 --- a/buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.js +++ /dev/null @@ -1,20 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} \ No newline at end of file diff --git a/buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.txt b/buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.txt deleted file mode 100644 index 9cb44a5b00..0000000000 --- a/buying/search_criteria/pending_po_items_to_bill/pending_po_items_to_bill.txt +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "owner": "dhanalekshmi@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "add_col": "(CASE WHEN (`tabPurchase Order Item`.qty- ifnull(`tabPurchase Order Item`.billed_qty, 0) > 0 ) THEN (`tabPurchase Order Item`.qty-ifnull(`tabPurchase Order Item`.billed_qty, 0) ) ELSE 0 END) AS \"Pending To Bill\"", - "parent_doc_type": "Purchase Order", - "module": "Buying", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Purchase Order\u0001Saved':1,'Purchase Order\u0001Submitted':1,'Purchase Order\u0001Status':'','Purchase Order\u0001Fiscal Year':''}", - "description": "Pending PO Items To Bill", - "doc_type": "Purchase Order Item", - "name": "__common__", - "add_cond": "(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0) > 0 or `tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.billed_qty, 0) > 0)\n`tabPurchase Order`.status != \"Stopped\"", - "doctype": "Search Criteria", - "sort_by": "`tabPurchase Order`.`name`", - "page_len": 50, - "criteria_name": "Pending PO Items To Bill", - "columns": "Purchase Order\u0001ID,Purchase Order\u0001Supplier,Purchase Order\u0001Supplier Name,Purchase Order\u0001Status,Purchase Order\u0001PO Date,Purchase Order\u0001Fiscal Year,Purchase Order Item\u0001Material Request No,Purchase Order Item\u0001Item Code,Purchase Order Item\u0001Item Name,Purchase Order Item\u0001Description,Purchase Order Item\u0001Quantity,Purchase Order Item\u0001UOM,Purchase Order Item\u0001Received Qty" - }, - { - "name": "pending_po_items_to_bill", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/buying/search_criteria/pending_po_items_to_receive/__init__.py b/buying/search_criteria/pending_po_items_to_receive/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/buying/search_criteria/pending_po_items_to_receive/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.js b/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.js deleted file mode 100644 index de2c03722d..0000000000 --- a/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.js +++ /dev/null @@ -1,20 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} \ No newline at end of file diff --git a/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.txt b/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.txt deleted file mode 100644 index 51cf16590d..0000000000 --- a/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.txt +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "owner": "dhanalekshmi@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "add_col": "`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0) AS \"Pending Quantity To Receive\"\n(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) * `tabPurchase Order Item`.purchase_rate AS \"Pending Amount To Receive\"", - "parent_doc_type": "Purchase Order", - "module": "Buying", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Purchase Order\u0001Saved':1,'Purchase Order\u0001Submitted':1,'Purchase Order\u0001Status':'','Purchase Order\u0001Fiscal Year':''}", - "description": "Pending PO Items To Receive", - "doc_type": "Purchase Order Item", - "name": "__common__", - "add_cond": "`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0) > 0 \n`tabPurchase Order`.status != \"Stopped\"", - "doctype": "Search Criteria", - "sort_by": "`tabPurchase Order`.`name`", - "page_len": 50, - "criteria_name": "Pending PO Items To Receive", - "columns": "Purchase Order\u0001ID,Purchase Order\u0001Supplier,Purchase Order\u0001Supplier Name,Purchase Order\u0001Status,Purchase Order\u0001PO Date,Purchase Order\u0001Fiscal Year,Purchase Order Item\u0001Material Request No,Purchase Order Item\u0001Item Code,Purchase Order Item\u0001Item Name,Purchase Order Item\u0001Description,Purchase Order Item\u0001Quantity,Purchase Order Item\u0001UOM,Purchase Order Item\u0001Received Qty" - }, - { - "name": "pending_po_items_to_receive", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/buying/search_criteria/purchase_in_transit/__init__.py b/buying/search_criteria/purchase_in_transit/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/buying/search_criteria/purchase_in_transit/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/buying/search_criteria/purchase_in_transit/purchase_in_transit.js b/buying/search_criteria/purchase_in_transit/purchase_in_transit.js deleted file mode 100644 index a08b921106..0000000000 --- a/buying/search_criteria/purchase_in_transit/purchase_in_transit.js +++ /dev/null @@ -1,48 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - this.hide_all_filters(); - - - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'From Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'Credit To'].df.filter_hide = 0; - - - this.add_filter({fieldname:'pr_posting_date', label:'PR Posting Date', fieldtype:'Date', ignore : 1, parent:'Purchase Receipt'}); - - - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'Credit To'].df.in_first_page = 0; - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'From Posting Date'].df.in_first_page = 1; - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'To Posting Date'].df.in_first_page = 1; - - - this.filter_fields_dict['Purchase Receipt'+FILTER_SEP +'From PR Posting Date'].df.ignore = 1; - this.filter_fields_dict['Purchase Receipt'+FILTER_SEP +'To PR Posting Date'].df.ignore = 1; - - - this.filter_fields_dict['Purchase Receipt'+FILTER_SEP +'From PR Posting Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Purchase Receipt'+FILTER_SEP +'To PR Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'From Posting Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'To Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['Purchase Invoice'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - -} - -this.mytabs.items['Select Columns'].hide(); \ No newline at end of file diff --git a/buying/search_criteria/purchase_in_transit/purchase_in_transit.py b/buying/search_criteria/purchase_in_transit/purchase_in_transit.py deleted file mode 100644 index c8739bbf7e..0000000000 --- a/buying/search_criteria/purchase_in_transit/purchase_in_transit.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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 . - -#check mendatory -from __future__ import unicode_literals -if not filter_values.get('posting_date') or not filter_values.get('posting_date1'): - msgprint("Please select From Posting Date and To Posting Date in 'Set Filters' section") - raise Exception -else: - from_date = filter_values.get('posting_date') - to_date = filter_values.get('posting_date1') \ No newline at end of file diff --git a/buying/search_criteria/purchase_in_transit/purchase_in_transit.txt b/buying/search_criteria/purchase_in_transit/purchase_in_transit.txt deleted file mode 100644 index 9fa5ccf311..0000000000 --- a/buying/search_criteria/purchase_in_transit/purchase_in_transit.txt +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-11 13:16:55", - "modified_by": "Administrator", - "modified": "2012-04-13 12:06:15" - }, - { - "add_col": "`tabPurchase Receipt`.`posting_date` AS 'PR Posting Date'", - "parent_doc_type": "Purchase Invoice", - "module": "Buying", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"Purchase Invoice\\u0001Submitted\":1,\"Purchase Invoice\\u0001Is Opening\":[\"\"],\"Purchase Invoice\\u0001Fiscal Year\":[\"\"]}", - "dis_filters": "`tabPurchase Receipt`.`pr_posting_date`", - "description": "List of PR whose posting date is after PV posting date", - "doc_type": "Purchase Invoice Item", - "name": "__common__", - "add_tab": "`tabPurchase Receipt`", - "add_cond": "`tabPurchase Invoice Item`.`purchase_receipt` = `tabPurchase Receipt`.`name`\n`tabPurchase Receipt`.`posting_date` >= '%(pr_posting_date)s'\n`tabPurchase Receipt`.`posting_date` <= '%(pr_posting_date1)s'\n`tabPurchase Receipt`.`posting_date` > `tabPurchase Invoice`.`posting_date`", - "doctype": "Search Criteria", - "sort_by": "`tabPurchase Invoice`.`name`", - "page_len": 50, - "criteria_name": "Purchase in Transit", - "columns": "Purchase Invoice\u0001ID,Purchase Invoice\u0001Posting Date,Purchase Invoice\u0001Credit To,Purchase Invoice Item\u0001Qty,Purchase Invoice Item\u0001Amount,Purchase Invoice Item\u0001Pur Order,Purchase Invoice Item\u0001Pur Receipt" - }, - { - "name": "purchase_in_transit", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/__init__.py b/hr/search_criteria/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/employee_appraisals/__init__.py b/hr/search_criteria/employee_appraisals/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/employee_appraisals/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/employee_appraisals/employee_appraisals.txt b/hr/search_criteria/employee_appraisals/employee_appraisals.txt deleted file mode 100644 index ce12a0e97f..0000000000 --- a/hr/search_criteria/employee_appraisals/employee_appraisals.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "ashwini@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Appraisal\u0001Saved':1,'Appraisal\u0001Submitted':1,'Appraisal\u0001Status':'','Appraisal\u0001Fiscal Year':''}", - "doctype": "Search Criteria", - "doc_type": "Appraisal", - "name": "__common__", - "sort_by": "`tabAppraisal`.`name`", - "page_len": 50, - "criteria_name": "Employee Appraisals", - "columns": "Appraisal\u0001ID,Appraisal\u0001Status,Appraisal\u0001Employee,Appraisal\u0001Employee Name,Appraisal\u0001Start Date,Appraisal\u0001End Date,Appraisal\u0001Approver,Appraisal\u0001Total Score" - }, - { - "name": "employee_appraisals", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/employee_information/__init__.py b/hr/search_criteria/employee_information/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/employee_information/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/employee_information/employee_information.py b/hr/search_criteria/employee_information/employee_information.py deleted file mode 100644 index 6e0887270f..0000000000 --- a/hr/search_criteria/employee_information/employee_information.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 -for c in range(0,len(colnames)): - l = (len(colnames[c])*9) - if l < 150 : col_width = '150px' - else: col_width = '%spx'%(l) - - colwidths[c] = col_width \ No newline at end of file diff --git a/hr/search_criteria/employee_information/employee_information.txt b/hr/search_criteria/employee_information/employee_information.txt deleted file mode 100644 index d05461380f..0000000000 --- a/hr/search_criteria/employee_information/employee_information.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Employee\u0001Saved':1,'Employee\u0001Submitted':1,'Employee\u0001Gender':'','Employee\u0001Month of Birth':'','Employee\u0001Status':''}", - "doctype": "Search Criteria", - "doc_type": "Employee", - "name": "__common__", - "sort_by": "`tabEmployee`.`name`", - "page_len": 50, - "criteria_name": "Employee Information", - "columns": "Employee\u0001ID,Employee\u0001Employee Name,Employee\u0001Employee Number,Employee\u0001Date of Joining,Employee\u0001Gender,Employee\u0001Date of Birth,Employee\u0001Employment Type,Employee\u0001Scheduled Confirmation Date,Employee\u0001Contract End Date,Employee\u0001Status,Employee\u0001Branch,Employee\u0001Department,Employee\u0001Designation,Employee\u0001Grade,Employee\u0001Reports to,Employee\u0001Email (By company),Employee\u0001Bank Name,Employee\u0001Relieving Date" - }, - { - "name": "employee_information", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/employees_birthday/__init__.py b/hr/search_criteria/employees_birthday/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/employees_birthday/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/employees_birthday/employees_birthday.txt b/hr/search_criteria/employees_birthday/employees_birthday.txt deleted file mode 100644 index a96b8698b9..0000000000 --- a/hr/search_criteria/employees_birthday/employees_birthday.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Employee\u0001Saved':1,'Employee\u0001Gender':'','Employee\u0001Month of Birth':'May','Employee\u0001Status':''}", - "doctype": "Search Criteria", - "doc_type": "Employee", - "name": "__common__", - "sort_by": "`tabEmployee`.`name`", - "page_len": 50, - "criteria_name": "Employee's Birthday", - "columns": "Employee\u0001ID,Employee\u0001Employee Name,Employee\u0001Department,Employee\u0001Gender,Employee\u0001Date of Birth,Employee\u0001Month of Birth" - }, - { - "name": "employees_birthday", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/expense_claims/__init__.py b/hr/search_criteria/expense_claims/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/expense_claims/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/expense_claims/expense_claims.txt b/hr/search_criteria/expense_claims/expense_claims.txt deleted file mode 100644 index f1324d2e1c..0000000000 --- a/hr/search_criteria/expense_claims/expense_claims.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "ashwini@webnotestech.com", - "docstatus": 0, - "creation": "2012-03-30 13:33:32", - "modified_by": "Administrator", - "modified": "2012-03-30 13:33:32" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Expense Claim\u0001Saved':1,'Expense Claim\u0001Submitted':1,'Expense Claim\u0001Approval Status':'','Expense Claim\u0001Fiscal Year':''}", - "doctype": "Search Criteria", - "doc_type": "Expense Claim", - "name": "__common__", - "sort_by": "`tabExpense Claim`.`name`", - "page_len": 50, - "criteria_name": "Expense Claims", - "columns": "Expense Claim\u0001ID,Expense Claim\u0001Approval Status,Expense Claim\u0001From Employee,Expense Claim\u0001Employee Name,Expense Claim\u0001Approver,Expense Claim\u0001Posting Date,Expense Claim\u0001Total Claimed Amount,Expense Claim\u0001Total Sanctioned Amount" - }, - { - "name": "expense_claims", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/monthly_attendance_details/__init__.py b/hr/search_criteria/monthly_attendance_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/monthly_attendance_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.js b/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.js deleted file mode 100644 index e922a2f433..0000000000 --- a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.js +++ /dev/null @@ -1,45 +0,0 @@ -// 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 . - -var get_month = function(){ - - var dict = {0:'Jan', 1:'Feb',2:'Mar',3:'Apr',4:'May',5:'June',6:'July',7:'Aug',8:'Sept',9:'Oct',10:'Nov',11:'Dec'} - var d = new Date(); - return dict[d.getMonth()] - -} - -report.customize_filters = function() { - this.hide_all_filters(); - this.add_filter({fieldname:'month', label:'Month', fieldtype:'Select', options:'Jan'+NEWLINE+'Feb'+NEWLINE+'Mar'+NEWLINE+'Apr'+NEWLINE+'May'+NEWLINE+'June'+NEWLINE+'July'+NEWLINE+'Aug'+NEWLINE+'Sept'+NEWLINE+'Oct'+NEWLINE+'Nov'+NEWLINE+'Dec',ignore : 1,parent:'Attendance', single_select:1}); - - this.filter_fields_dict['Attendance'+FILTER_SEP +'Employee'].df.filter_hide = 0; - this.filter_fields_dict['Attendance'+FILTER_SEP +'Month'].df.filter_hide = 0; - this.filter_fields_dict['Attendance'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 0; - this.filter_fields_dict['Attendance'+FILTER_SEP +'Company'].df.filter_hide = 0; - - this.filter_fields_dict['Attendance'+FILTER_SEP +'Employee'].df.in_first_page = 1; - this.filter_fields_dict['Attendance'+FILTER_SEP +'Month'].df.in_first_page = 1; - this.filter_fields_dict['Attendance'+FILTER_SEP +'Fiscal Year'].df.in_first_page = 1; - this.filter_fields_dict['Attendance'+FILTER_SEP +'Company'].df.in_first_page = 1; - - this.filter_fields_dict['Attendance'+FILTER_SEP +'Month'].df['report_default'] = get_month(); - this.filter_fields_dict['Attendance'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Attendance'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; - this.get_filter('Attendance', 'Fiscal Year').set_as_single(); -} -this.mytabs.items['More Filters'].hide(); -this.mytabs.items['Select Columns'].hide(); \ No newline at end of file diff --git a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.py b/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.py deleted file mode 100644 index dff95df169..0000000000 --- a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 . - -#add column employee, employee name -#-------------------------------------------------------------------------------------- -from __future__ import unicode_literals -col =[['Employee','Link','155px','Employee'],['Employee Name','Data','150px','']] - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - - col_idx[c[0]] = len(colnames)-1 - - -#get feb months last day -#-------------------------------------------------------------------------------------- -fy = filter_values.get('fiscal_year') -month = filter_values.get('month') -mdict = {'Jan':'01', 'Feb':'02','Mar':'03','Apr':'04','May':'05','June':'06','July':'07','Aug':'08','Sept':'09','Oct':'10','Nov':'11','Dec':'12'} - -import webnotes.utils -from dateutil.relativedelta import relativedelta - -ysd = sql("select year_start_date from `tabFiscal Year` where name = '%s' and docstatus !=2"%fy)[0][0] - -last_date = webnotes.utils.get_last_day(ysd + relativedelta(months = (cint(ysd.strftime('%m'))>cint(mdict[month]) and (12-cint(ysd.strftime('%m'))+cint(mdict[month])) or (cint(mdict[month]) - cint(ysd.strftime('%m')))))) -feb = last_date.strftime('%d') - - - -#get last day and add columns -#-------------------------------------------------------------------------------------- -dict = {'Jan': 31,'Feb':cint(feb), 'Mar':31,'Apr':30,'May':31,'June':30,'July':31,'Aug':31,'Sept':30,'Oct':31,'Nov':30,'Dec':31} - -for i in range(0,dict[month]): - colnames.append(i+1) - coltypes.append('Data') - colwidths.append('25px') - - col_idx[c[0]] = len(colnames)-1 - -#add total present, absent days -#-------------------------------------------------------------------------------------- -tot_col =[['Total Present Days','Data','120px'],['Total Absent Days','Data','120px']] - -for c in tot_col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - - col_idx[c[0]] = len(colnames)-1 - -#get data -#-------------------------------------------------------------------------------------- - - -year = last_date.strftime('%Y') -out = [] -for r in res: - p_cnt = a_cnt = 0 - - for i in range(0,dict[month]): - new_date = str(year)+'-'+mdict[month]+'-'+((i>=9) and str(i+1) or ('0'+str(i+1))) - - chk = sql("select status from `tabAttendance` where employee='%s' and att_date = '%s' and docstatus=1"%(r[0],new_date)) - chk = chk and chk[0][0][0] or '-' - if chk=='P': - p_cnt +=1 - elif chk=='A': - a_cnt +=1 - r.append(chk) - - r.append(p_cnt) - r.append(a_cnt) - - if p_cnt or a_cnt: - out.append(r) diff --git a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.sql b/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.sql deleted file mode 100644 index 762a94f7fb..0000000000 --- a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT DISTINCT `tabAttendance`.employee, `tabEmployee`.employee_name FROM `tabAttendance`, `tabEmployee` WHERE `tabEmployee`.name = `tabAttendance`.employee and `tabAttendance`.fiscal_year like '%(fiscal_year)s%%' AND `tabAttendance`.company like '%(company)s%%' AND `tabAttendance`.employee like '%(employee)s%%' \ No newline at end of file diff --git a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.txt b/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.txt deleted file mode 100644 index abbacae1e9..0000000000 --- a/hr/search_criteria/monthly_attendance_details/monthly_attendance_details.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Attendance\u0001Status':'','Attendance\u0001Fiscal Year':''}", - "doctype": "Search Criteria", - "doc_type": "Attendance", - "name": "__common__", - "sort_by": "`tabAttendance`.`employee`", - "page_len": 50, - "criteria_name": "Monthly Attendance Details", - "columns": "Attendance\u0001Employee" - }, - { - "name": "monthly_attendance_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/monthly_salary_register/__init__.py b/hr/search_criteria/monthly_salary_register/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/monthly_salary_register/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/monthly_salary_register/monthly_salary_register.py b/hr/search_criteria/monthly_salary_register/monthly_salary_register.py deleted file mode 100644 index 2291b6682d..0000000000 --- a/hr/search_criteria/monthly_salary_register/monthly_salary_register.py +++ /dev/null @@ -1,99 +0,0 @@ -from __future__ import unicode_literals -colwidths[col_idx['Employee Name']]="120px" -colwidths[col_idx['Leave Without Pay']] = '120px' - -sum_net = total_earning = total_deduction = total_lwp = total_arr = 0 -total = ['Total Net Pay','',''] - - -earn_type_lst = sql("select name from `tabEarning Type`") - -ded_type_lst = sql("select name from `tabDeduction Type`") -li=[] -for lst in earn_type_lst: - - li.append(lst[0]) - - - -li.append('Total Earning') -for lst in ded_type_lst: - - li.append(lst[0]) - - -li.append('Total Deduction') -li.append('Net Pay') - - - -for d in li: - colnames.append(d) - colwidths.append("150px") - coltypes.append("Currency") - coloptions.append("") - col_idx[d] = len(colnames)-1 - for r in res: - r.append("0") - -for r in res: - - total_lwp += flt(r[col_idx['Leave Without Pay']]) - total_arr += flt(r[col_idx['Arrear Amount']]) - - for d1 in li: - d2 = '%s'%d1 - - earn_ret=webnotes.conn.convert_to_lists(sql("select e_type,e_amount from `tabSalary Slip Earning` where parent = '%s'"%r[col_idx['ID']])) - ded_ret=webnotes.conn.convert_to_lists(sql("select d_type,d_amount from `tabSalary Slip Deduction` where parent = '%s'"%r[col_idx['ID']])) - - - for e in earn_ret: - e0 = '%s'%e[0] - r[col_idx[e0]]=flt(e[1]) or 0.00 - - - for d in ded_ret: - d0 = '%s'%d[0] - r[col_idx[d0]]=flt(d[1]) or 0.00 - - - tot_earn_ded_net_ret = sql("select gross_pay, total_deduction,net_pay from `tabSalary Slip` where name = '%s'"%r[col_idx['ID']]) - if d2 == 'Total Earning': - r[col_idx[d2]] = flt(tot_earn_ded_net_ret[0][0]) or 0 - total_earning += flt(tot_earn_ded_net_ret[0][0]) or 0 - elif d2 == 'Total Deduction': - r[col_idx[d2]] = flt(tot_earn_ded_net_ret[0][1]) or 0 - total_deduction += flt(tot_earn_ded_net_ret[0][1]) or 0 - elif d2 == 'Net Pay': - r[col_idx[d2]] = flt(tot_earn_ded_net_ret[0][2]) or 0 - sum_net += flt(tot_earn_ded_net_ret[0][2]) or 0 - - -total.append(total_lwp) -total.append(total_arr) - -for lst in earn_type_lst: - - total_ear = 0 - for r in res: - - lst0 = '%s'%lst[0] - total_ear += flt(r[col_idx[lst0]]) - - total.append(total_ear) - -total.append(total_earning) -for lst in ded_type_lst: - total_ded = 0 - for r in res: - lst0 = '%s'%lst[0] - total_ded += flt(r[col_idx[lst0]]) - - total.append(total_ded) - - -total.append(total_deduction) -total.append(sum_net) - -res.append(total) diff --git a/hr/search_criteria/monthly_salary_register/monthly_salary_register.txt b/hr/search_criteria/monthly_salary_register/monthly_salary_register.txt deleted file mode 100644 index 2e1db8c7cf..0000000000 --- a/hr/search_criteria/monthly_salary_register/monthly_salary_register.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-03-30 14:50:44", - "modified_by": "Administrator", - "modified": "2012-04-05 17:23:13" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"Salary Slip\\u0001Submitted\":1,\"Salary Slip\\u0001Company\":[\"\"],\"Salary Slip\\u0001Month\":[\"\"]}", - "doctype": "Search Criteria", - "doc_type": "Salary Slip", - "name": "__common__", - "sort_by": "`tabSalary Slip`.`name`", - "page_len": 50, - "criteria_name": "Monthly Salary Register", - "columns": "Salary Slip\u0001ID,Salary Slip\u0001Employee,Salary Slip\u0001Employee Name,Salary Slip\u0001Leave Without Pay,Salary Slip\u0001Arrear Amount" - }, - { - "name": "monthly_salary_register", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/new_or_left_employees_for_a_month/__init__.py b/hr/search_criteria/new_or_left_employees_for_a_month/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/new_or_left_employees_for_a_month/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.js b/hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.js deleted file mode 100644 index e6dc2675e7..0000000000 --- a/hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.js +++ /dev/null @@ -1,66 +0,0 @@ -// 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 . - -report.customize_filters = function() { - var d = new Date(); - - var month=["January","February","March","April","May","June","July","August","September","October","November","December"] - - this.add_filter({fieldname:'month', label:'Month',fieldtype:'Select', options:"January"+NEWLINE+"February"+NEWLINE+"March"+NEWLINE+"April"+NEWLINE+"May"+NEWLINE+"June"+NEWLINE+"July"+NEWLINE+"August"+NEWLINE+"September"+NEWLINE+"October"+NEWLINE+"November"+NEWLINE+"December",report_default : month[d.getMonth()],ignore : 1, parent:'Employee'}); - - this.filter_fields_dict['Employee'+FILTER_SEP +'Month'].df.in_first_page = 1; - this.filter_fields_dict['Employee'+FILTER_SEP +'Status'].df.in_first_page = 1; - this.filter_fields_dict['Employee'+FILTER_SEP +'Status'].df.report_default = 'Active'; - - this.add_filter({fieldname:'year', label:'Year',fieldtype:'Select', options:"2000"+NEWLINE+"2001"+NEWLINE+"2002"+NEWLINE+"2003"+NEWLINE+"2004"+NEWLINE+"2005"+NEWLINE+"2006"+NEWLINE+"2007"+NEWLINE+"2008"+NEWLINE+"2009"+NEWLINE+"2010"+NEWLINE+"2011",report_default : d.getFullYear(),ignore : 1, parent:'Employee'}); - - this.filter_fields_dict['Employee'+FILTER_SEP +'Year'].df.in_first_page = 1; -} - -report.get_query = function() { - - emp_month = this.filter_fields_dict['Employee'+FILTER_SEP+'Month'].get_value(); - emp_year = this.filter_fields_dict['Employee'+FILTER_SEP+'Year'].get_value(); - emp_status = this.filter_fields_dict['Employee'+FILTER_SEP+'Status'].get_value(); - - // month and year mandatory - if ((emp_month == '') || (emp_year == '')) { - alert("Please enter Month and Year"); - return; - } - - month={"January":"1", "February":"2", "March":"3", "April":"4","May":"5", "June":"6", "July":"7","August":"8", "September":"9", "October":"10", "November":"11", "December":"12"} - - mnt = '' - for(m=0; m. - -from __future__ import unicode_literals -status = filter_values.get('status') -month = filter_values.get('month') - - -if status == 'Active' and not status == 'Left': - col = [['Employee', 'Link', 'Employee'], ['Employee Name', 'Data', ''], ['Employee Number', 'Data', ''], ['Employment Type','Link','Employment Type'],['Scheduled Confirmation Date','Data',''],['Final Confirmation Date','Data',''],['Contract End Date','Data',''],['Branch','Link','Branch'],['Department','Link','Department'],['Designation','Link','Designation'],['Reports to','Link','Employee'],['Grade','Link','Grade']] - -elif status == 'Left' and not status == 'Active': - col = [['Employee', 'Link', 'Employee'], ['Employee Name', 'Data', ''], ['Employee Number', 'Data', ''], ['Resignation Letter Date','Data',''],['Relieving Date','Data',''],['Notice - Number of Days','Data',''],['Reason for Leaving','Data',''],['Leave Encashed?','Data',''],['Encashment Date','Data',''],['Reason for Resignation','Data','']] - -else: - col = [['Employee', 'Link', 'Employee'], ['Employee Name', 'Data', ''], ['Employee Number', 'Data', ''], ['Employment Type','Link','Employment Type'],['Scheduled Confirmation Date','Data',''],['Final Confirmation Date','Data',''],['Contract End Date','Data',''],['Branch','Link','Branch'],['Department','Link','Department'],['Designation','Link','Designation'],['Reports to','Link','Employee'],['Grade','Link','Grade'],['Resignation Letter Date','Data',''],['Relieving Date','Data',''],['Notice - Number of Days','Data',''],['Reason for Leaving','Data',''],['Leave Encashed?','Data',''],['Encashment Date','Data',''],['Reason for Resignation','Data','']] - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append('150px') - coloptions.append(c[2]) - - col_idx[c[0]] = len(colnames)-1 - - -for c in range(0,len(colnames)): - l = (len(colnames[c])*9) - if l < 150 : col_width = '150px' - else: col_width = '%spx'%(l) - - colwidths[c] = col_width - -for r in res: - - if status == 'Active': - ret = sql("select employment_type,scheduled_confirmation_date,final_confirmation_date,contract_end_date,branch,department,designation,reports_to,grade from `tabEmployee` where name = %s",r[0]) - - elif status == 'Left': - ret = sql("select resignation_letter_date,relieving_date,notice_number_of_days,reason_for_leaving,leave_encashed,encashment_date,reason_for_resignation from `tabEmployee` where name =%s",r[0]) - - else: - ret = sql("select employment_type,scheduled_confirmation_date,final_confirmation_date,contract_end_date,branch,department,designation,reports_to,grade,resignation_letter_date,relieving_date,notice_number_of_days,reason_for_leaving,leave_encashed,encashment_date,reason_for_resignation from `tabEmployee` where name = %s",r[0]) - - ret = ret and ret[0] or [] - for t in ret: - r.append(cstr(t)) diff --git a/hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.txt b/hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.txt deleted file mode 100644 index 86d09b5372..0000000000 --- a/hr/search_criteria/new_or_left_employees_for_a_month/new_or_left_employees_for_a_month.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Employee\u0001Saved':1,'Employee\u0001Submitted':1,'Employee\u0001Gender':'','Employee\u0001Month of Birth':'','Employee\u0001Status':''}", - "doctype": "Search Criteria", - "doc_type": "Employee", - "name": "__common__", - "sort_by": "`tabEmployee`.`name`", - "page_len": 50, - "criteria_name": "New or left employees for a month", - "columns": "Employee\u0001ID" - }, - { - "name": "new_or_left_employees_for_a_month", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/salary_register/__init__.py b/hr/search_criteria/salary_register/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/salary_register/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/salary_register/salary_register.js b/hr/search_criteria/salary_register/salary_register.js deleted file mode 100644 index c451b272f7..0000000000 --- a/hr/search_criteria/salary_register/salary_register.js +++ /dev/null @@ -1,25 +0,0 @@ -// 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 . - -report.customize_filters = function() { - fld_lst = ['ID','Employee'] - - for(var i = 0; i. - -from __future__ import unicode_literals -sal_slips_ids = '' -for r in res: - if not sal_slips_ids == '': sal_slips_ids +="," - sal_slips_ids+="'%s'"%r[col_idx['ID']] - -earn_heads, ded_heads = [], [] -if res: - earn_heads =[i[0] for i in sql("select distinct e_type from `tabSalary Slip Earning` where parent in (%s)"%sal_slips_ids)] - ded_heads =[i[0] for i in sql("select distinct d_type from `tabSalary Slip Deduction` where parent in (%s)"%sal_slips_ids)] - -col=[] -for e in earn_heads: - l = (len(cstr(e))*9) - if l < 150 : - col_width = '150px' - else: - col_width = '%spx'%(l) - col.append([e,'Currency',col_width,'']) - -col.append(['Arrear Amount','Currency','150px','']) -col.append(['Encashment Amount','Currency','170px','']) -col.append(['Gross Pay','Currency','150px','']) -for d in ded_heads: - l = (len(cstr(d))*9) - if l < 150 : col_width = '150px' - else: col_width = '%spx'%(l) - col.append([d,'Currency',col_width,'']) - -col.append(['Total Deduction','Currency','150px','']) -col.append(['Net Pay','Currency','150px','']) - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames) - -grand_tot = 0 -for r in res: - for i in range(6,len(colnames)): - if colnames[i] not in ('Arrear Amount','Encashment Amount','Net Pay','Gross Pay','Total Deduction'): - amt = sql("select e_modified_amount from `tabSalary Slip Earning` where e_type = '%s' and parent = '%s'"%(colnames[i],r[0])) - if not amt: - amt = sql("select d_modified_amount from `tabSalary Slip Deduction` where d_type = '%s' and parent = '%s'"%(colnames[i],r[0])) - amt = amt and amt[0][0] or 0 - r.append(flt(amt)) - - else: - fld_nm = cstr(colnames[i]).lower().replace(' ','_') - tot = sql("select %s from `tabSalary Slip` where name ='%s'"%(fld_nm,r[0])) - tot = tot and flt(tot[0][0]) or 0 - if colnames[i] == 'Net Pay': - grand_tot += tot - r.append(tot) - -gt_row = ['' for i in range(len(colnames))] -gt_row[col_idx['Employee Name']] = 'Grand Totals' -gt_row[col_idx['Net Pay']-1] = grand_tot -res.append(gt_row) \ No newline at end of file diff --git a/hr/search_criteria/salary_register/salary_register.txt b/hr/search_criteria/salary_register/salary_register.txt deleted file mode 100644 index aab7f2518e..0000000000 --- a/hr/search_criteria/salary_register/salary_register.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Salary Slip\u0001Submitted':1,'Salary Slip\u0001Month':''}", - "doctype": "Search Criteria", - "doc_type": "Salary Slip", - "name": "__common__", - "sort_by": "`tabSalary Slip`.`name`", - "page_len": 50, - "criteria_name": "Salary Register", - "columns": "Salary Slip\u0001ID,Salary Slip\u0001Employee,Salary Slip\u0001Employee Name,Salary Slip\u0001Year,Salary Slip\u0001Month,Salary Slip\u0001Total days in month,Salary Slip\u0001Payment days" - }, - { - "name": "salary_register", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/salary_slips/__init__.py b/hr/search_criteria/salary_slips/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/salary_slips/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/salary_slips/salary_slips.txt b/hr/search_criteria/salary_slips/salary_slips.txt deleted file mode 100644 index e1bcccf259..0000000000 --- a/hr/search_criteria/salary_slips/salary_slips.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Salary Slip\u0001Saved':1,'Salary Slip\u0001Submitted':1}", - "doctype": "Search Criteria", - "doc_type": "Salary Slip", - "name": "__common__", - "sort_by": "`tabSalary Slip`.`name`", - "page_len": 50, - "criteria_name": "Salary Slips", - "columns": "Salary Slip\u0001ID,Salary Slip\u0001Fiscal Year,Salary Slip\u0001Month,Salary Slip\u0001Employee,Salary Slip\u0001Employee Name,Salary Slip\u0001Department,Salary Slip\u0001Designation,Salary Slip\u0001Branch,Salary Slip\u0001Grade,Salary Slip\u0001PF No.,Salary Slip\u0001ESIC No.,Salary Slip\u0001Leave Without Pay,Salary Slip\u0001Bank Name,Salary Slip\u0001Bank Account No.,Salary Slip\u0001Payment days,Salary Slip\u0001Arrear Amount,Salary Slip\u0001Encashment Amount,Salary Slip\u0001Gross Pay,Salary Slip\u0001Total Deduction,Salary Slip\u0001Net Pay" - }, - { - "name": "salary_slips", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/hr/search_criteria/salary_structure_details/__init__.py b/hr/search_criteria/salary_structure_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/hr/search_criteria/salary_structure_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/hr/search_criteria/salary_structure_details/salary_structure_details.txt b/hr/search_criteria/salary_structure_details/salary_structure_details.txt deleted file mode 100644 index c1ed01bc84..0000000000 --- a/hr/search_criteria/salary_structure_details/salary_structure_details.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "module": "HR", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Salary Structure\u0001Branch':'','Salary Structure\u0001Designation':'','Salary Structure\u0001Department':'','Salary Structure\u0001Grade':'','Salary Structure\u0001Is Active':''}", - "doctype": "Search Criteria", - "doc_type": "Salary Structure", - "name": "__common__", - "sort_by": "`tabSalary Structure`.`name`", - "page_len": 50, - "criteria_name": "Salary Structure Details", - "columns": "Salary Structure\u0001ID,Salary Structure\u0001Employee,Salary Structure\u0001From Date,Salary Structure\u0001To Date,Salary Structure\u0001Fiscal Year,Salary Structure\u0001Branch,Salary Structure\u0001Designation,Salary Structure\u0001Department,Salary Structure\u0001Grade,Salary Structure\u0001Is Active,Salary Structure\u0001Total Earning,Salary Structure\u0001Total Deduction,Salary Structure\u0001CTC,Salary Structure\u0001Total" - }, - { - "name": "salary_structure_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/manufacturing/search_criteria/__init__.py b/manufacturing/search_criteria/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/manufacturing/search_criteria/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/manufacturing/search_criteria/consumption_against_production/__init__.py b/manufacturing/search_criteria/consumption_against_production/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/manufacturing/search_criteria/consumption_against_production/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/manufacturing/search_criteria/consumption_against_production/consumption_against_production.txt b/manufacturing/search_criteria/consumption_against_production/consumption_against_production.txt deleted file mode 100644 index 70620ff193..0000000000 --- a/manufacturing/search_criteria/consumption_against_production/consumption_against_production.txt +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "owner": "jai@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-12-10 18:30:00" - }, - { - "add_col": "`tabProduction Order`.consider_sa_items", - "parent_doc_type": "Stock Entry", - "module": "Manufacturing", - "standard": "Yes", - "sort_order": "DESC", - "add_tab": "`tabProduction Order`", - "doc_type": "Stock Entry Detail", - "name": "__common__", - "filters": "{'Stock Entry\u0001Submitted':1,'Stock Entry\u0001Purpose':'Production Order','Stock Entry\u0001Process':''}", - "add_cond": "`tabProduction Order`.name = `tabStock Entry`.production_order", - "doctype": "Search Criteria", - "sort_by": "`tabStock Entry`.`name`", - "page_len": 50, - "criteria_name": "Consumption Against Production", - "columns": "Stock Entry\u0001ID,Stock Entry\u0001Production Order,Stock Entry\u0001Process,Stock Entry\u0001Posting Date,Stock Entry\u0001Company,Stock Entry Detail\u0001Source Warehouse,Stock Entry Detail\u0001Target Warehouse,Stock Entry Detail\u0001FG Item,Stock Entry Detail\u0001Item Code,Stock Entry Detail\u0001Description,Stock Entry Detail\u0001Reqd Qty,Stock Entry Detail\u0001Transfer Quantity" - }, - { - "name": "consumption_against_production", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/manufacturing/search_criteria/itemwise_production_report/__init__.py b/manufacturing/search_criteria/itemwise_production_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/manufacturing/search_criteria/itemwise_production_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.js b/manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.js deleted file mode 100644 index 4998fad3bd..0000000000 --- a/manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.js +++ /dev/null @@ -1,32 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - //to hide all filters - this.hide_all_filters(); - - // to unhide required filters - this.filter_fields_dict['Stock Entry'+FILTER_SEP +'ID'].df.filter_hide = 0; - this.filter_fields_dict['Stock Entry'+FILTER_SEP +'Production Order'].df.filter_hide = 0; - - this.filter_fields_dict['Stock Entry'+FILTER_SEP +'From Posting Date'].df.filter_hide = 0; - this.filter_fields_dict['Stock Entry'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0; - - this.filter_fields_dict['Stock Entry Detail'+FILTER_SEP +'Target Warehouse'].df.filter_hide = 0; - - this.filter_fields_dict['Stock Entry Detail'+FILTER_SEP +'Item Code'].df.filter_hide = 0; -} \ No newline at end of file diff --git a/manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.txt b/manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.txt deleted file mode 100644 index 7814fecd5b..0000000000 --- a/manufacturing/search_criteria/itemwise_production_report/itemwise_production_report.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "jai@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-12-10 18:30:00" - }, - { - "parent_doc_type": "Stock Entry", - "module": "Manufacturing", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Stock Entry\u0001Saved':1,'Stock Entry\u0001Submitted':1,'Stock Entry\u0001Purpose':'Production Order','Stock Entry\u0001Process':'Backflush','Stock Entry Detail\u0001FG Item':1}", - "doc_type": "Stock Entry Detail", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabStock Entry`.`name`", - "page_len": 50, - "criteria_name": "Itemwise Production Report", - "columns": "Stock Entry\u0001ID,Stock Entry\u0001Posting Date,Stock Entry\u0001Production Order,Stock Entry\u0001Process,Stock Entry\u0001Company,Stock Entry Detail\u0001Target Warehouse,Stock Entry Detail\u0001Item Code,Stock Entry Detail\u0001Description,Stock Entry Detail\u0001Stock UOM,Stock Entry Detail\u0001Transfer Quantity" - }, - { - "name": "itemwise_production_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/manufacturing/search_criteria/production_orders_in_process/__init__.py b/manufacturing/search_criteria/production_orders_in_process/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/manufacturing/search_criteria/production_orders_in_process/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/manufacturing/search_criteria/production_orders_in_process/production_orders_in_process.txt b/manufacturing/search_criteria/production_orders_in_process/production_orders_in_process.txt deleted file mode 100644 index 3a8c8c99d4..0000000000 --- a/manufacturing/search_criteria/production_orders_in_process/production_orders_in_process.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "jai@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-12-10 18:30:00" - }, - { - "module": "Manufacturing", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Production Order\u0001Submitted':1,'Production Order\u0001Origin':'','Production Order\u0001Status':'','Production Order\u0001Consider SA Items':'','Production Order\u0001Fiscal Year':''}", - "doc_type": "Production Order", - "name": "__common__", - "add_cond": "`tabProduction Order`.qty > `tabProduction Order`.produced_qty", - "doctype": "Search Criteria", - "sort_by": "`tabProduction Order`.`name`", - "page_len": 50, - "criteria_name": "Production Orders In Process", - "columns": "Production Order\u0001ID,Production Order\u0001Origin,Production Order\u0001Status,Production Order\u0001Posting Date,Production Order\u0001Production Item,Production Order\u0001BOM No,Production Order\u0001Description,Production Order\u0001Stock UOM,Production Order\u0001Qty,Production Order\u0001Produced Qty,Production Order\u0001FG Warehouse,Production Order\u0001WIP Warehouse,Production Order\u0001Consider SA Items,Production Order\u0001Fiscal Year,Production Order\u0001Company" - }, - { - "name": "production_orders_in_process", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.py b/patches/june_2013/p05_remove_search_criteria_reports.py similarity index 85% rename from buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.py rename to patches/june_2013/p05_remove_search_criteria_reports.py index e7ada4ff97..78963dbac1 100644 --- a/buying/search_criteria/pending_po_items_to_receive/pending_po_items_to_receive.py +++ b/patches/june_2013/p05_remove_search_criteria_reports.py @@ -15,5 +15,7 @@ # along with this program. If not, see . from __future__ import unicode_literals -colwidths[col_idx['Pending Quantity To Receive']] = '200px' -colwidths[col_idx['Pending Amount To Receive']] = '200px' +import webnotes + +def execute(): + webnotes.conn.sql("""delete from `tabSearch Criteria` where ifnull(standard, 'No') = 'Yes'""") \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 6b88955a10..0353c74b1e 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -255,4 +255,5 @@ patch_list = [ "patches.june_2013.p01_update_bom_exploded_items", "execute:webnotes.delete_doc('DocType', 'System Console')", "patches.june_2013.p04_fix_event_for_lead_oppty_project", + "patches.june_2013.p05_remove_search_criteria_reports", ] \ No newline at end of file diff --git a/projects/search_criteria/__init__.py b/projects/search_criteria/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/projects/search_criteria/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/projects/search_criteria/projectwise_delivered_qty_and_costs/__init__.py b/projects/search_criteria/projectwise_delivered_qty_and_costs/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/projects/search_criteria/projectwise_delivered_qty_and_costs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.js b/projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.js deleted file mode 100644 index c8b42dc2c3..0000000000 --- a/projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.js +++ /dev/null @@ -1,32 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - //this.add_filter({fieldname:'item_code', label:'Item Code', fieldtype:'Link', options:'Item',ignore : 1,parent:'Delivery Note Item'}); - - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Project Name'].df.filter_hide = 0; - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 0; - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Customer'].df.filter_hide = 0; - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Customer Name'].df.filter_hide = 0; - - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Project Name'].df.in_first_page = 1; - - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} diff --git a/projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.txt b/projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.txt deleted file mode 100644 index b2ecb6c1ce..0000000000 --- a/projects/search_criteria/projectwise_delivered_qty_and_costs/projectwise_delivered_qty_and_costs.txt +++ /dev/null @@ -1,28 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "parent_doc_type": "Delivery Note", - "module": "Projects", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Delivery Note\u0001Submitted':1,'Delivery Note\u0001Status':'Submitted','Delivery Note\u0001Fiscal Year':''}", - "doc_type": "Delivery Note Item", - "name": "__common__", - "add_cond": "IFNULL(`tabDelivery Note`.`project_name`,\"\")!=\"\"", - "doctype": "Search Criteria", - "sort_by": "`tabDelivery Note`.`name`", - "page_len": 50, - "criteria_name": "Projectwise Delivered Qty and Costs", - "columns": "Delivery Note\u0001ID,Delivery Note\u0001Project Name,Delivery Note\u0001Customer,Delivery Note\u0001Customer Name,Delivery Note Item\u0001Item Code,Delivery Note Item\u0001Item Name,Delivery Note Item\u0001Quantity,Delivery Note\u0001Posting Date,Delivery Note\u0001% Billed,Delivery Note Item\u0001Amount*" - }, - { - "name": "projectwise_delivered_qty_and_costs", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/projects/search_criteria/projectwise_pending_qty_and_costs/__init__.py b/projects/search_criteria/projectwise_pending_qty_and_costs/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/projects/search_criteria/projectwise_pending_qty_and_costs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.js b/projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.js deleted file mode 100644 index ce56077bd7..0000000000 --- a/projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.js +++ /dev/null @@ -1,31 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - //this.add_filter({fieldname:'item_code', label:'Item Code', fieldtype:'Link', options:'Item',ignore : 1,parent:'Sales Order Item'}); - - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Project Name'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Customer'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Customer Name'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Project Name'].df.in_first_page = 1; - - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} diff --git a/projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.txt b/projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.txt deleted file mode 100644 index 37b62944e8..0000000000 --- a/projects/search_criteria/projectwise_pending_qty_and_costs/projectwise_pending_qty_and_costs.txt +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "owner": "ashwini@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "add_col": "SUM(`tabSales Order Item`.`qty` - `tabSales Order Item`.`delivered_qty`) AS 'Pending Qty'\nSUM((`tabSales Order Item`.`qty` - `tabSales Order Item`.`delivered_qty`)* `tabSales Order Item`.basic_rate) AS 'Pending Amount'", - "parent_doc_type": "Sales Order", - "module": "Projects", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Order\u0001Submitted':1,'Sales Order\u0001Status':'Submitted','Sales Order\u0001Fiscal Year':''}", - "doc_type": "Sales Order Item", - "name": "__common__", - "add_cond": "`tabSales Order Item`.`qty` > `tabSales Order Item`.`delivered_qty`\nIFNULL(`tabSales Order`.`project_name`,\"\")!=\"\"\n`tabSales Order`.order_type = 'Sales'", - "doctype": "Search Criteria", - "sort_by": "`tabSales Order`.`name`", - "page_len": 50, - "criteria_name": "Projectwise Pending Qty and Costs", - "columns": "Sales Order\u0001ID,Sales Order\u0001Project Name,Sales Order\u0001Customer,Sales Order\u0001Customer Name,Sales Order Item\u0001Item Code,Sales Order Item\u0001Item Name,Sales Order\u0001% Delivered,Sales Order\u0001% Billed,Sales Order\u0001Sales Order Date,Sales Order\u0001Expected Delivery Date" - }, - { - "name": "projectwise_pending_qty_and_costs", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/projects/search_criteria/projectwise_purchase_details/__init__.py b/projects/search_criteria/projectwise_purchase_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/projects/search_criteria/projectwise_purchase_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.js b/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.js deleted file mode 100644 index bd4be54e78..0000000000 --- a/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.js +++ /dev/null @@ -1,108 +0,0 @@ -// 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 . - -report.customize_filters = function() { - //hide all filters - //------------------------------------------------ - this.hide_all_filters(); - - //add filters - //------------------------------------------------ - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Purchase Order'+NEWLINE+'Purchase Invoice'+NEWLINE+'Purchase Receipt',report_default:'Purchase Order',ignore : 1,parent:'Purchase Order', single_select:1}); - - this.add_filter({fieldname:'purchase_order', label:'Purchase Order', fieldtype:'Link', options:'Purchase Order', ignore : 1, parent:'Purchase Order'}); - this.add_filter({fieldname:'purchase_receipt', label:'Purchase Receipt',fieldtype:'Link', options:'Purchase Receipt',ignore : 1, parent:'Purchase Order'}); - this.add_filter({fieldname:'purchase_invoice', label:'Purchase Invoice',fieldtype:'Link', options:'Purchase Invoice',ignore : 1, parent:'Purchase Order'}); - - //unhide filters - //------------------------------------------------ - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Project Name'].df.filter_hide = 0; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 0; - - //move filter field in first page - //------------------------------------------------ - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Based On'].df.in_first_page = 1; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Project Name'].df.in_first_page = 1; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Purchase Order'].df.in_first_page = 1; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Purchase Invoice'].df.in_first_page = 1; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Purchase Receipt'].df.in_first_page = 1; - - // default values - //------------------------------------------------ - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Purchase Order'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} - -//hide select columns field -//------------------------------------------------ -this.mytabs.items['Select Columns'].hide(); - - -report.get_query = function() { - - //get filter values - based_on = this.filter_fields_dict['Purchase Order'+FILTER_SEP+'Based On'].get_value(); - purchase_order = this.filter_fields_dict['Purchase Order'+FILTER_SEP+'Purchase Order'].get_value(); - purchase_invoice = this.filter_fields_dict['Purchase Order'+FILTER_SEP+'Purchase Invoice'].get_value(); - purchase_receipt = this.filter_fields_dict['Purchase Order'+FILTER_SEP+'Purchase Receipt'].get_value(); - project_name = this.filter_fields_dict['Purchase Order'+FILTER_SEP+'Project Name'].get_value(); - company = this.filter_fields_dict['Purchase Order'+FILTER_SEP+'Company'].get_value(); - fy = this.filter_fields_dict['Purchase Order'+FILTER_SEP+'Fiscal Year'].get_value(); - - // make query based on transaction - //------------------------------------------------------------------------------------------- - - var cond = ''; - //for purchase order - if(based_on == 'Purchase Order'){ - - if(purchase_order) cond += ' AND `tabPurchase Order`.name = "'+purchase_order+'"'; - if(project_name) cond += ' AND `tabPurchase Order`.project_name = "'+project_name+'"'; - if(company) cond += ' AND `tabPurchase Order`.company = "'+company+'"'; - if(fy !='') cond += ' AND `tabPurchase Order`.fiscal_year = "'+fy+'"'; - - var q = 'SELECT DISTINCT `tabPurchase Order`.name, `tabPurchase Order`.status, `tabPurchase Order`.project_name, `tabPurchase Order`.supplier,`tabPurchase Order`.supplier_name,`tabPurchase Order`.per_received, `tabPurchase Order`.per_billed, `tabPurchase Order`.grand_total FROM `tabPurchase Order` WHERE IFNULL(`tabPurchase Order`.project_name,"") != ""'+cond+' AND `tabPurchase Order`.docstatus != 2'; - return q; - } - - //for purchase receipt - else if(based_on == 'Purchase Receipt'){ - if(purchase_order) cond += ' t2.purchase_order = "'+purchase_order+'" AND '; - if(purchase_receipt) cond += ' t1.name = "'+purchase_receipt+'" AND '; - if(project_name) cond += ' t1.project_name = "'+project_name+'" AND '; - if(company) cond += ' t1.company = "'+company+'" AND '; - if(fy !='') cond += ' t1.fiscal_year = "'+fy+'" AND '; - - - var q = 'SELECT DISTINCT t1.name, t1.status, t1.project_name, t1.supplier, t1.supplier_name,t1.grand_total FROM `tabPurchase Receipt` t1, `tabPurchase Receipt Item` t2 WHERE '+cond +'IFNULL(t1.project_name,"") !="" AND t1.docstatus != 2 AND t1.name = t2.parent'; - - return q; - } - //for purchase invoice - else if(based_on == 'Purchase Invoice'){ - if(purchase_order) cond += ' t2.purchase_order = "'+purchase_order+'" AND '; - if(purchase_receipt) cond += ' t2.purchase_receipt = "'+purchase_receipt+'" AND'; - if(purchase_invoice) cond += ' t1.name = "'+purchase_invoice+'" AND'; - if(project_name) cond += ' t1.project_name = "'+project_name+'" AND '; - if(company) cond += ' t1.company = "'+company+'" AND '; - if(fy !='') cond += ' t1.fiscal_year = "'+fy+'" AND '; - - var q = 'SELECT DISTINCT t1.name , t1.credit_to , t1.project_name, t1.supplier, t1.supplier_name , t1.grand_total FROM `tabPurchase Invoice` t1, `tabPurchase Invoice Item` t2 WHERE '+cond +'IFNULL(t1.project_name,"") !="" AND t1.docstatus != 2 AND t1.name = t2.parent'; - - return q; - } -} \ No newline at end of file diff --git a/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.py b/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.py deleted file mode 100644 index ed82cebc79..0000000000 --- a/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.py +++ /dev/null @@ -1,40 +0,0 @@ -# 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 -based_on = filter_values.get('based_on') -# make default columns -#for r in res: -col = [] -if based_on == 'Purchase Order': - col = [['Purchase Order ID','Link','Purchase Order'],['Status','Data',''],['Project Name','Link','Project'],['Supplier','Link','Supplier'],['Supplier Name','Data',''],['% Received','Data',''],['% Billed','Data',''],['Grand Total','Currency','']] - -elif based_on == 'Purchase Invoice': - col = [['Purchase Receipt ID','Link','Purchase Invoice'],['Status','Data',''],['Project Name','Link','Project'],['Supplier','Link','Supplier'],['Supplier Name','Data',''],['Grand Total','Currency','']] - -elif based_on == 'Purchase Receipt': - col = [['Purchase Invoice ID','Link','Purchase Receipt'],['Credit To','Data',''],['Project Name','Link','Project'],['Supplier','Link','Supplier'],['Supplier Name','Data',''],['Grand Total','Currency','']] - - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - coloptions.append(c[2]) - l = (len(c[0])*9) - if l < 150 : col_width = '150px' - else: col_width = '%spx'%(l) - colwidths.append(col_width) - col_idx[c[0]] = len(colnames)-1 \ No newline at end of file diff --git a/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.txt b/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.txt deleted file mode 100644 index cb88f584e0..0000000000 --- a/projects/search_criteria/projectwise_purchase_details/projectwise_purchase_details.txt +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "owner": "ashwini@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "doc_type": "Purchase Order", - "name": "__common__", - "module": "Projects", - "doctype": "Search Criteria", - "sort_order": "DESC", - "filters": "{'Purchase Order\u0001Status':'','Purchase Order\u0001Fiscal Year':''}", - "page_len": 50, - "criteria_name": "Projectwise Purchase Details", - "standard": "Yes" - }, - { - "name": "projectwise_purchase_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/projects/search_criteria/projectwise_sales_details/__init__.py b/projects/search_criteria/projectwise_sales_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/projects/search_criteria/projectwise_sales_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.js b/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.js deleted file mode 100644 index 4e92c3c463..0000000000 --- a/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.js +++ /dev/null @@ -1,112 +0,0 @@ -// 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 . - -report.customize_filters = function() { - //hide all filters - //------------------------------------------------ - this.hide_all_filters(); - - //add filters - //------------------------------------------------ - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice',report_default:'Sales Order',ignore : 1,parent:'Sales Order', single_select:1}); - - this.add_filter({fieldname:'sales_order', label:'Sales Order', fieldtype:'Link', options:'Sales Order', ignore : 1, parent:'Sales Order'}); - this.add_filter({fieldname:'delivery_note', label:'Delivery Note', fieldtype:'Link', options:'Delivery Note',ignore : 1, parent:'Sales Order'}); - this.add_filter({fieldname:'sales_invoice', label:'Sales Invoice',fieldtype:'Link', options:'Sales Invoice',ignore : 1, parent:'Sales Order'}); - - //unhide filters - //------------------------------------------------ - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Project Name'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Customer'].df.filter_hide = 0; - - //move filter field in first page - //------------------------------------------------ - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Based On'].df.in_first_page = 1; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Project Name'].df.in_first_page = 1; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Sales Order'].df.in_first_page = 1; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Delivery Note'].df.in_first_page = 1; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Sales Invoice'].df.in_first_page = 1; - - // default values - //------------------------------------------------ - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; - -} - -//hide select columns field -//------------------------------------------------ -//this.mytabs.items['Select Columns'].hide(); - - -report.get_query = function() { - - //get filter values - based_on = this.filter_fields_dict['Sales Order'+FILTER_SEP+'Based On'].get_value(); - sales_order = this.filter_fields_dict['Sales Order'+FILTER_SEP+'Sales Order'].get_value(); - delivery_note = this.filter_fields_dict['Sales Order'+FILTER_SEP+'Delivery Note'].get_value(); - sales_invoice = this.filter_fields_dict['Sales Order'+FILTER_SEP+'Sales Invoice'].get_value(); - project_name = this.filter_fields_dict['Sales Order'+FILTER_SEP+'Project Name'].get_value(); - company = this.filter_fields_dict['Sales Order'+FILTER_SEP+'Company'].get_value(); - fy = this.filter_fields_dict['Sales Order'+FILTER_SEP+'Fiscal Year'].get_value(); - - // make query based on transaction - //------------------------------------------------------------------------------------------- - - var cond = ''; - //for sales order - if(based_on == 'Sales Order'){ - - if(sales_order) cond += ' AND `tabSales Order`.name = "'+sales_order+'"'; - if(project_name) cond += ' AND `tabSales Order`.project_name = "'+project_name+'"'; - if(company) cond += ' AND `tabSales Order`.company = "'+company+'"'; - if(fy) cond += ' AND `tabSales Order`.fiscal_year = "'+fy+'"'; - - var q = 'SELECT DISTINCT `tabSales Order`.name, `tabSales Order`.order_type, `tabSales Order`.status, `tabSales Order`.project_name, `tabSales Order`.customer,`tabSales Order`.customer_name,`tabSales Order`.per_delivered, `tabSales Order`.per_billed, `tabSales Order`.grand_total FROM `tabSales Order` WHERE IFNULL(`tabSales Order`.project_name,"") != ""'+cond+' AND `tabSales Order`.docstatus != 2'; - return q; - } - - //for delivery note - else if(based_on == 'Delivery Note'){ - if(sales_order) cond += ' t1.name = t2.parent AND t2.prevdoc_docname = "'+sales_order+'" AND '; - if(delivery_note) cond += ' t1.name = "'+delivery_note+'" AND '; - if(project_name) cond += ' t1.project_name = "'+project_name+'" AND '; - if(company) cond += ' t1.company = "'+company+'" AND '; - if(fy) cond += ' t1.fiscal_year = "'+fy+'" AND '; - - var q = 'SELECT DISTINCT t1.name, t1.status, t1.project_name, t1.customer, t1.customer_name, t1.per_billed, t1.per_installed, t1.grand_total FROM `tabDelivery Note` t1, `tabDelivery Note Item` t2 WHERE '+cond+' IFNULL(t1.project_name,"") !="" AND t1.docstatus != 2'; - - return q; - } - - //for sales invoice - else if(based_on == 'Sales Invoice'){ - if(sales_order) cond += ' t2.sales_order = "'+sales_order+'" AND '; - if(delivery_note) cond += ' t2.delivery_note = "'+delivery_note+'" AND '; - if(sales_invoice) cond += ' t1.name = "'+sales_invoice+'" AND '; - if(project_name) cond += ' t1.project_name = "'+project_name+'" AND '; - if(company) cond += ' t1.company = "'+company+'" AND '; - if(fy) cond += ' t1.fiscal_year = "'+fy+'" AND '; - - - var q = 'SELECT DISTINCT t1.name , t1.debit_to , t1.project_name , t1.customer , t1.customer_name , t1.grand_total FROM `tabSales Invoice` t1, `tabSales Invoice Item` t2 WHERE '+cond +'IFNULL(t1.project_name,"") !="" AND t1.docstatus != 2 AND t1.name = t2.parent'; - - return q; - } - -} \ No newline at end of file diff --git a/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.py b/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.py deleted file mode 100644 index 50ade660a3..0000000000 --- a/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.py +++ /dev/null @@ -1,37 +0,0 @@ -# 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 -based_on = filter_values.get('based_on') - -cols=[] - -if based_on == 'Sales Order': - cols = [['Sales Order No','Link','150px','Sales Order'], ['Order Type','Data','100px',''], ['Status','Data','100px',''], ['Project Name','Link','150px','Project'], ['Customer','Link','150px','Customer'], ['Customer Name','Data','200px',''], ['% Delivered','Currency','100px',''], ['% Billed','Currency','100px',''], ['Grand Total','Currency','150px','']] - -elif based_on == 'Delivery Note': - cols = [['Delivery Note No','Link','150px','Delivery Note'], ['Status','Data','100px',''], ['Project Name','Link','200px','Project'], ['Customer','Link','150px','Customer'], ['Customer Name','Data','200px',''], ['% Installed','Currency','100px',''], ['% Billed','Currency','100px',''], ['Grand Total','Currency','150px','']] - -elif based_on == 'Sales Invoice': - cols = [['Sales Invoice No','Link','150px','Sales Invoice'], ['Debit To','Data','150px',''], ['Project Name','Link','200px','Project'], ['Customer','Link','150px','Customer'], ['Customer Name','Data','200px',''], ['Grand Total','Currency','150px','']] - - -for c in cols: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 \ No newline at end of file diff --git a/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.txt b/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.txt deleted file mode 100644 index 11a50a6e0b..0000000000 --- a/projects/search_criteria/projectwise_sales_details/projectwise_sales_details.txt +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "doc_type": "Sales Order", - "name": "__common__", - "module": "Projects", - "doctype": "Search Criteria", - "sort_order": "DESC", - "filters": "{'Sales Order\u0001Saved':1,'Sales Order\u0001Submitted':1,'Sales Order\u0001Status':''}", - "page_len": 50, - "criteria_name": "Projectwise Sales Details", - "standard": "Yes" - }, - { - "name": "projectwise_sales_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/projects/search_criteria/projectwise_sales_orders/__init__.py b/projects/search_criteria/projectwise_sales_orders/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/projects/search_criteria/projectwise_sales_orders/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/projects/search_criteria/projectwise_sales_orders/projectwise_sales_orders.txt b/projects/search_criteria/projectwise_sales_orders/projectwise_sales_orders.txt deleted file mode 100644 index 29919e6d12..0000000000 --- a/projects/search_criteria/projectwise_sales_orders/projectwise_sales_orders.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "module": "Projects", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Order\u0001Saved':1,'Sales Order\u0001Submitted':1,'Sales Order\u0001Status':'','Sales Order\u0001Fiscal Year':''}", - "doc_type": "Sales Order", - "name": "__common__", - "add_cond": "ifnull(`tabSales Order`.project_name ,'') != ''", - "doctype": "Search Criteria", - "sort_by": "`tabSales Order`.`name`", - "page_len": 50, - "criteria_name": "Projectwise Sales Orders", - "columns": "Sales Order\u0001ID,Sales Order\u0001Status,Sales Order\u0001Project Name,Sales Order\u0001Customer,Sales Order\u0001Sales Order Date,Sales Order\u0001Expected Delivery Date,Sales Order\u0001Quotation No,Sales Order\u0001% Delivered,Sales Order\u0001% Billed,Sales Order\u0001Grand Total*" - }, - { - "name": "projectwise_sales_orders", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/projects/search_criteria/timesheet_report/__init__.py b/projects/search_criteria/timesheet_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/projects/search_criteria/timesheet_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/projects/search_criteria/timesheet_report/timesheet_report.js b/projects/search_criteria/timesheet_report/timesheet_report.js deleted file mode 100644 index 12d9680146..0000000000 --- a/projects/search_criteria/timesheet_report/timesheet_report.js +++ /dev/null @@ -1,23 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Timesheet Detail'+FILTER_SEP +'Project Name'].df.in_first_page = 1; - this.filter_fields_dict['Timesheet Detail'+FILTER_SEP +'Task Id'].df.in_first_page = 1; -this.filter_fields_dict['Timesheet'+FILTER_SEP +'Timesheet by'].df.filter_hide = 1; -} - -//this.mytabs.items['Select Columns'].hide() \ No newline at end of file diff --git a/projects/search_criteria/timesheet_report/timesheet_report.txt b/projects/search_criteria/timesheet_report/timesheet_report.txt deleted file mode 100644 index 14bbe084d3..0000000000 --- a/projects/search_criteria/timesheet_report/timesheet_report.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "ashwini@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "parent_doc_type": "Timesheet", - "module": "Projects", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Timesheet\u0001Saved':1,'Timesheet\u0001Submitted':1}", - "doc_type": "Timesheet Detail", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabTimesheet`.`name`", - "page_len": 50, - "criteria_name": "Timesheet Report", - "columns": "Timesheet\u0001ID,Timesheet\u0001Timesheet Date,Timesheet\u0001Timesheet by,Timesheet Detail\u0001Project Name,Timesheet Detail\u0001Task Id,Timesheet Detail\u0001Task Name,Timesheet Detail\u0001Actual Start Time,Timesheet Detail\u0001Actual End Time,Timesheet Detail\u0001Total Hours (Actual)" - }, - { - "name": "timesheet_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 91649e39c8..73cc7185c8 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -137,11 +137,6 @@ wn.module_page["Selling"] = [ "label":wn._("Sales Analytics"), page: "sales-analytics" }, - { - "label":wn._("Trend Analyzer"), - route: "Report/Profile/Trend Analyzer", - doctype: "Sales Order" - }, ] }, { diff --git a/selling/search_criteria/__init__.py b/selling/search_criteria/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/delivered_items_to_be_install/__init__.py b/selling/search_criteria/delivered_items_to_be_install/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/delivered_items_to_be_install/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.js b/selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.js deleted file mode 100644 index df8fc21fb4..0000000000 --- a/selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.js +++ /dev/null @@ -1,20 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} \ No newline at end of file diff --git a/selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.txt b/selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.txt deleted file mode 100644 index ab23a299ff..0000000000 --- a/selling/search_criteria/delivered_items_to_be_install/delivered_items_to_be_install.txt +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "add_col": "(`tabDelivery Note Item`.`qty`- ifnull(`tabDelivery Note Item`.`installed_qty`, 0)) AS 'Pending to Install'", - "parent_doc_type": "Delivery Note", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Delivery Note\u0001Saved':1,'Delivery Note\u0001Submitted':1,'Delivery Note\u0001Status':'','Delivery Note\u0001Fiscal Year':''}", - "description": "Delivered Items to be Install", - "doc_type": "Delivery Note Item", - "name": "__common__", - "add_cond": "`tabDelivery Note Item`.`qty` > ifnull(`tabDelivery Note Item`.`installed_qty`, 0)", - "doctype": "Search Criteria", - "sort_by": "`tabDelivery Note`.`name`", - "page_len": 50, - "criteria_name": "Delivered Items to be Install", - "columns": "Delivery Note\u0001ID,Delivery Note\u0001Status,Delivery Note\u0001Customer,Delivery Note\u0001Customer Name,Delivery Note\u0001Contact Person,Delivery Note\u0001Voucher Date,Delivery Note Item\u0001Item Code,Delivery Note Item\u0001Item Name,Delivery Note Item\u0001Quantity,Delivery Note Item\u0001Installed Qty,Delivery Note\u0001% Installed" - }, - { - "name": "delivered_items_to_be_install", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/draft_sales_orders/__init__.py b/selling/search_criteria/draft_sales_orders/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/draft_sales_orders/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/draft_sales_orders/draft_sales_orders.js b/selling/search_criteria/draft_sales_orders/draft_sales_orders.js deleted file mode 100644 index 7f8aed8f41..0000000000 --- a/selling/search_criteria/draft_sales_orders/draft_sales_orders.js +++ /dev/null @@ -1,22 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Sales Order'+FILTER_SEP +'From Sales Order Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'To Sales Order Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - -} \ No newline at end of file diff --git a/selling/search_criteria/draft_sales_orders/draft_sales_orders.txt b/selling/search_criteria/draft_sales_orders/draft_sales_orders.txt deleted file mode 100644 index af60e71713..0000000000 --- a/selling/search_criteria/draft_sales_orders/draft_sales_orders.txt +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "creation": "2012-05-14 18:20:52", - "docstatus": 0, - "modified": "2013-01-30 15:19:07", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "columns": "Sales Order\u0001ID,Sales Order\u0001Status,Sales Order\u0001Sales Order Date,Sales Order\u0001Customer,Sales Order\u0001P.O. No,Sales Order\u0001Currency,Sales Order\u0001Grand Total (Export)", - "criteria_name": "Draft Sales Orders", - "custom_query": "", - "description": "List of Open Sales orders filtered by period, customer and other details", - "doc_type": "Sales Order", - "doctype": "Search Criteria", - "filters": "{\"Sales Order\\u0001Saved\":1,\"Sales Order\\u0001Submitted\":1,\"Sales Order\\u0001To Sales Order Date\\u0001upper\":\"2013-01-30\",\"Sales Order\\u0001Company\":\"Alpha\",\"Sales Order\\u0001Fiscal Year\":[\"\"],\"Sales Order\\u0001Status\":[\"Draft\"]}", - "module": "Selling", - "name": "__common__", - "page_len": 50, - "report_script": null, - "sort_by": "`tabSales Order`.`name`", - "sort_order": "DESC", - "standard": "Yes" - }, - { - "doctype": "Search Criteria", - "name": "draft_sales_orders" - } -] \ No newline at end of file diff --git a/selling/search_criteria/follow_up_report/__init__.py b/selling/search_criteria/follow_up_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/follow_up_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/follow_up_report/follow_up_report.js b/selling/search_criteria/follow_up_report/follow_up_report.js deleted file mode 100644 index 2126d0b9bb..0000000000 --- a/selling/search_criteria/follow_up_report/follow_up_report.js +++ /dev/null @@ -1,63 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.mytabs.items['Select Columns'].hide() - this.mytabs.items['More Filters'].hide() - - this.hide_all_filters(); - this.add_filter({fieldname:'follow_up_on', label:'Communication on', fieldtype:'Select', options:''+NEWLINE+'Lead'+NEWLINE+'Opportunity'+NEWLINE+'Quotation',ignore : 1,parent:'Communication Log', in_first_page : 1, single_select :1}); - this.add_filter({fieldname:'lead_name', label:'Lead', fieldtype:'Link', options:'Lead', report_default:'', ignore : 1, parent:'Communication Log', in_first_page : 1}); - this.add_filter({fieldname:'enq_name', label:'Opportunity', fieldtype:'Link', options:'Opportunity', report_default:'', ignore : 1, parent:'Communication Log', in_first_page : 1}); - this.add_filter({fieldname:'qtn_name', label:'Quotation', fieldtype:'Link', options:'Quotation', report_default:'', ignore : 1, parent:'Communication Log', in_first_page : 1}); - - this.get_filter('Communication Log', 'Communication type').set_as_single(); - this.set_filter_properties('Communication Log', 'Communication by', {filter_hide:0, in_first_page : 1}); - this.set_filter_properties('Communication Log', 'Communication type', {filter_hide:0, in_first_page : 1}); - this.set_filter_properties('Communication Log', 'From Date', {filter_hide:0, in_first_page : 1}); - this.set_filter_properties('Communication Log', 'To Date', {filter_hide:0, in_first_page : 1}); - - this.orig_sort_list = [['Date','`tabCommunication Log`.`date`'],['Document Type','`tabCommunication Log`.`parenttype`'],['Document','`tabCommunication Log`.`parent`'],['Follow Up By','`tabCommunication Log`.`follow_up_by`'],['Follow Up Type','`tabCommunication Log`.`follow_up_type`']]; -} - - -report.get_query = function() { - var lead_id = this.filter_fields_dict['Communication Log'+FILTER_SEP+'Lead'].get_value(); - var enq_id = this.filter_fields_dict['Communication Log'+FILTER_SEP+'Opportunity'].get_value(); - var quo_id = this.filter_fields_dict['Communication Log'+FILTER_SEP+'Quotation'].get_value(); - - var follow_up_on = this.filter_fields_dict['Communication Log'+FILTER_SEP+'Communication on'].get_value(); - var follow_up_by = this.filter_fields_dict['Communication Log'+FILTER_SEP+'Communication by'].get_value(); - - var on_type = this.filter_fields_dict['Communication Log'+FILTER_SEP+'Communication type'].get_value(); - var from_date = this.filter_fields_dict['Communication Log'+FILTER_SEP+'From Date'].get_value(); - var to_date = this.filter_fields_dict['Communication Log'+FILTER_SEP+'To Date'].get_value(); - - var cond = 'parenttype IN ("Lead","Opportunity","Quotation")'; - if(follow_up_on) cond = 'parenttype = "'+follow_up_on+'"'; - - if((follow_up_on == 'Lead' && lead_id) || (lead_id && !follow_up_on)) cond +=' AND parent = "'+lead_id+'"'; - if((follow_up_on == 'Opportunity' && enq_id) || (enq_id && !follow_up_on)) cond +=' AND parent = "'+enq_id+'"'; - if((follow_up_on == 'Quotation' && quo_id) || (quo_id && !follow_up_on)) cond +=' AND parent = "'+quo_id+'"'; - - if(on_type) cond += ' AND follow_up_type ="'+on_type+'"'; - if(from_date) cond += ' AND date >="'+from_date+'"'; - if(to_date) cond += ' AND date <="'+to_date+'"'; - if(follow_up_by) cond += ' AND follow_up_by = "'+follow_up_by+'"'; - - var q ='SELECT distinct `tabCommunication Log`.`parenttype`, `tabCommunication Log`.`parent`, `tabCommunication Log`.`date`, `tabCommunication Log`.`notes`, `tabCommunication Log`.`follow_up_type`, `tabCommunication Log`.`follow_up_by` FROM `tabCommunication Log` WHERE '+cond+' ORDER BY '+sel_val(this.dt.sort_sel)+' '+this.dt.sort_order; - return q; -} diff --git a/selling/search_criteria/follow_up_report/follow_up_report.py b/selling/search_criteria/follow_up_report/follow_up_report.py deleted file mode 100644 index 40066274d2..0000000000 --- a/selling/search_criteria/follow_up_report/follow_up_report.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 -follow_up_on = filter_values.get('follow_up_on') - -cols = [['Document Type', 'Data', '150px', ''] - ,['Document', 'Link', '150px', follow_up_on] - ,['Follow Up Date', 'Date', '150px', ''] - ,['Description','Data','300px',''] - ,['Follow Up Type','Data','150px',''] - ,['Follow Up By','Link','150px','Sales Person'] - ] - -for c in cols: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 diff --git a/selling/search_criteria/follow_up_report/follow_up_report.txt b/selling/search_criteria/follow_up_report/follow_up_report.txt deleted file mode 100644 index b789bd92a2..0000000000 --- a/selling/search_criteria/follow_up_report/follow_up_report.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "ashwini@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "parent_doc_type": "Lead", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Lead\u0001Status':'','Lead\u0001Source':'','Lead\u0001Lead Type':'','Lead\u0001Rating':'','Lead\u0001Market Segment':'','Lead\u0001Fiscal Year':''}", - "doc_type": "Communication Log", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabLead`.`name`", - "page_len": 50, - "criteria_name": "Follow-up Report", - "columns": "Lead\u0001ID,Communication Log\u0001Date,Communication Log\u0001Notes,Communication Log\u0001Communication Log type" - }, - { - "name": "follow-up_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/itemwise_delivery_details/__init__.py b/selling/search_criteria/itemwise_delivery_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/itemwise_delivery_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.js b/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.js deleted file mode 100644 index b0566765c0..0000000000 --- a/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.js +++ /dev/null @@ -1,21 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'From Posting Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Delivery Note'+FILTER_SEP +'To Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - -} diff --git a/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.py b/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.py deleted file mode 100644 index 0529d1b92d..0000000000 --- a/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 -out=[] -qty,amt,bill_amt=0,0,0 - -for r in res: - qty += flt(r[col_idx['Quantity']]) - amt += flt(r[col_idx['Amount*']]) - bill_amt += flt(r[col_idx['Billed Amt']]) - out.append(r) - - -#Add the totals row -l_row = ['' for i in range(len(colnames))] -l_row[col_idx['Item Name']] = 'TOTALS' -l_row[col_idx['Quantity']] = qty -l_row[col_idx['Amount*']] = amt -l_row[col_idx['Billed Amt']] = bill_amt -out.append(l_row) diff --git a/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.txt b/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.txt deleted file mode 100644 index 9ba342e2ae..0000000000 --- a/selling/search_criteria/itemwise_delivery_details/itemwise_delivery_details.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-23 12:46:00", - "modified_by": "Administrator", - "modified": "2012-05-03 18:13:49" - }, - { - "parent_doc_type": "Delivery Note", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"Delivery Note\\u0001Submitted\":1,\"Delivery Note\\u0001Status\":[\"\"],\"Delivery Note\\u0001Fiscal Year\":[\"\"]}", - "doc_type": "Delivery Note Item", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabDelivery Note`.`name`", - "page_len": 50, - "criteria_name": "Itemwise Delivery Details", - "columns": "Delivery Note\u0001ID,Delivery Note Item\u0001Item Code,Delivery Note Item\u0001Item Name,Delivery Note Item\u0001Quantity,Delivery Note Item\u0001Rate*,Delivery Note Item\u0001Amount*,Delivery Note Item\u0001Billed Amt" - }, - { - "name": "itemwise_delivery_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/itemwise_sales_details/__init__.py b/selling/search_criteria/itemwise_sales_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/itemwise_sales_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.js b/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.js deleted file mode 100644 index 7ff214a495..0000000000 --- a/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.js +++ /dev/null @@ -1,27 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Sales Order'+FILTER_SEP +'ID'].df.in_first_page = 1; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'From Sales Order Date'].df['report_default'] = sys_defaults.year_start_date; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'To Sales Order Date'].df['report_default'] = dateutil.obj_to_str(new Date()); - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Sales Order Item'+FILTER_SEP +'Item Code'].df.in_first_page = 1; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Status'].df.filter_hide = 1; -} - -//this.mytabs.items['Select Columns'].hide(); -this.mytabs.items['More Filters'].hide(); \ No newline at end of file diff --git a/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.py b/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.py deleted file mode 100644 index 1b7a19524f..0000000000 --- a/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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 -out=[] -qty,amt,del_qty,bill_amt=0,0,0,0 - -for r in res: - qty += flt(r[col_idx['Quantity']]) - amt += flt(r[col_idx['Amount*']]) - del_qty += flt(r[col_idx['Delivered Qty']]) - bill_amt += flt(r[col_idx['Billed Amt']]) - out.append(r) - - -#Add the totals row -l_row = ['' for i in range(len(colnames))] -l_row[col_idx['Item Name']] = 'TOTALS' -l_row[col_idx['Quantity']] = qty -l_row[col_idx['Amount*']] = amt -l_row[col_idx['Delivered Qty']] = del_qty -l_row[col_idx['Billed Amt']] = bill_amt -out.append(l_row) diff --git a/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.txt b/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.txt deleted file mode 100644 index 7737040809..0000000000 --- a/selling/search_criteria/itemwise_sales_details/itemwise_sales_details.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-23 12:46:00", - "modified_by": "Administrator", - "modified": "2012-05-03 18:18:31" - }, - { - "parent_doc_type": "Sales Order", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{\"Sales Order\\u0001Submitted\":1,\"Sales Order\\u0001Fiscal Year\":[\"\"],\"Sales Order\\u0001Status\":[\"\"]}", - "doc_type": "Sales Order Item", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabSales Order`.`name`", - "page_len": 50, - "criteria_name": "Itemwise Sales Details", - "columns": "Sales Order\u0001ID,Sales Order\u0001Customer,Sales Order Item\u0001Item Code,Sales Order Item\u0001Item Name,Sales Order Item\u0001Quantity,Sales Order Item\u0001Basic Rate*,Sales Order Item\u0001Amount*,Sales Order Item\u0001Delivered Qty,Sales Order Item\u0001Billed Amt" - }, - { - "name": "itemwise_sales_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/lead_to_follow_up/__init__.py b/selling/search_criteria/lead_to_follow_up/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/lead_to_follow_up/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/lead_to_follow_up/lead_to_follow_up.js b/selling/search_criteria/lead_to_follow_up/lead_to_follow_up.js deleted file mode 100644 index 94f2ef5baf..0000000000 --- a/selling/search_criteria/lead_to_follow_up/lead_to_follow_up.js +++ /dev/null @@ -1,21 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Lead'+FILTER_SEP +'Status'].df.filter_hide = 1; - this.filter_fields_dict['Lead'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} - \ No newline at end of file diff --git a/selling/search_criteria/lead_to_follow_up/lead_to_follow_up.txt b/selling/search_criteria/lead_to_follow_up/lead_to_follow_up.txt deleted file mode 100644 index 2ddd5e9753..0000000000 --- a/selling/search_criteria/lead_to_follow_up/lead_to_follow_up.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Lead\u0001Status':'','Lead\u0001Source':'','Lead\u0001Expected Month':'','Lead\u0001Industry':'','Lead\u0001Market Segment':'','Lead\u0001Rating':''}", - "doc_type": "Lead", - "name": "__common__", - "add_cond": "`tabLead`.status!='Converted' \n`tabLead`.status!='Lead Lost' \n`tabLead`.status!='Not Interested'", - "doctype": "Search Criteria", - "sort_by": "`tabLead`.`name`", - "page_len": 50, - "criteria_name": "Lead-To follow up", - "columns": "Lead\u0001ID,Lead\u0001Status,Lead\u0001Lead Name,Lead\u0001Lead Date,Lead\u0001Lead Owner,Lead\u0001Next Contact Date" - }, - { - "name": "lead-to_follow_up", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/lead_to_follow_up/sales_order_overdue.js b/selling/search_criteria/lead_to_follow_up/sales_order_overdue.js deleted file mode 100644 index 799cd22948..0000000000 --- a/selling/search_criteria/lead_to_follow_up/sales_order_overdue.js +++ /dev/null @@ -1,27 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Customer'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'ID'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Quotation No'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Sales Partner'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'From Sales Order Date'].df.filter_hide = 0; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'To Sales Order Date'].df.filter_hide = 0; -} \ No newline at end of file diff --git a/selling/search_criteria/opportunity_to_follow_up/__init__.py b/selling/search_criteria/opportunity_to_follow_up/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/opportunity_to_follow_up/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/opportunity_to_follow_up/opportunity_to_follow_up.txt b/selling/search_criteria/opportunity_to_follow_up/opportunity_to_follow_up.txt deleted file mode 100644 index b524e6af73..0000000000 --- a/selling/search_criteria/opportunity_to_follow_up/opportunity_to_follow_up.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-03-30 14:50:44", - "modified_by": "Administrator", - "modified": "2012-03-30 14:50:44" - }, - { - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Opportunity\u0001Fiscal Year':''}", - "doc_type": "Opportunity", - "name": "__common__", - "add_cond": "`tabOpportunity`.name=`tabQuotation`.enq_no\n`tabOpportunity`.docstatus=1\n`tabQuotation`.status='Submitted'", - "doctype": "Search Criteria", - "sort_by": "`tabOpportunity`.`name`", - "page_len": 50, - "criteria_name": "Opportunity-To follow up", - "columns": "Opportunity\u0001ID,Opportunity\u0001Owner,Opportunity\u0001Company,Opportunity\u0001Fiscal Year" - }, - { - "name": "opportunity-to_follow_up", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/sales_orderwise_booking_&_delivery_summary/__init__.py b/selling/search_criteria/sales_orderwise_booking_&_delivery_summary/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/sales_orderwise_booking_&_delivery_summary/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/sales_orderwise_booking_&_delivery_summary/sales_orderwise_booking_&_delivery_summary.txt b/selling/search_criteria/sales_orderwise_booking_&_delivery_summary/sales_orderwise_booking_&_delivery_summary.txt deleted file mode 100644 index 9050204f68..0000000000 --- a/selling/search_criteria/sales_orderwise_booking_&_delivery_summary/sales_orderwise_booking_&_delivery_summary.txt +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "add_col": "SUM(CASE WHEN `tabSales Order`.`status` = 'Stopped' THEN (`tabSales Order Item`.`delivered_qty` * `tabSales Order Item`.`basic_rate`) ELSE (`tabSales Order Item`.`qty` * `tabSales Order Item`.`basic_rate`) END) AS 'Booking Total'\nSUM(`tabSales Order Item`.`delivered_qty` * `tabSales Order Item`.`basic_rate`) AS 'Delivered Amount'", - "parent_doc_type": "Sales Order", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Order\u0001Submitted':1,'Sales Order\u0001Status':'','Sales Order\u0001Fiscal Year':''}", - "description": "Sales Orderwise Booking & Delivery Summary", - "doc_type": "Sales Order Item", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabSales Order`.`name`", - "group_by": "`tabSales Order`.`name`", - "page_len": 50, - "criteria_name": "Sales Orderwise Booking & Delivery Summary", - "columns": "Sales Order\u0001ID,Sales Order\u0001Status,Sales Order\u0001% Billed,Sales Order\u0001Sales Order Date,Sales Order\u0001Customer,Sales Order\u0001Customer Name,Sales Order\u0001Territory" - }, - { - "name": "sales_orderwise_booking_&_delivery_summary", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/sales_orderwise_pending_amount_to_bill/__init__.py b/selling/search_criteria/sales_orderwise_pending_amount_to_bill/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/sales_orderwise_pending_amount_to_bill/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.js b/selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.js deleted file mode 100644 index 1b3837a4ed..0000000000 --- a/selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.js +++ /dev/null @@ -1,20 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} \ No newline at end of file diff --git a/selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.txt b/selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.txt deleted file mode 100644 index 1a8d19bd3d..0000000000 --- a/selling/search_criteria/sales_orderwise_pending_amount_to_bill/sales_orderwise_pending_amount_to_bill.txt +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "owner": "dhanalekshmi@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "add_col": "SUM((`tabSales Order Item`.`qty` - ifnull(`tabSales Order Item`.`billed_qty`, 0)) * `tabSales Order Item`.`basic_rate`) AS \"Pending Amount\"", - "parent_doc_type": "Sales Order", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Order\u0001Saved':1,'Sales Order\u0001Submitted':1,'Sales Order\u0001Company Name':'','Sales Order\u0001Fiscal Year':''}", - "description": "Sales Orderwise Pending Amount To Bill", - "doc_type": "Sales Order Item", - "name": "__common__", - "add_cond": "`tabSales Order`.status != \"Stopped\"\n`tabSales Order`.order_type!=\"Maintenance\"", - "doctype": "Search Criteria", - "sort_by": "`tabSales Order`.`name`", - "group_by": "`tabSales Order`.`name`", - "page_len": 50, - "criteria_name": "Sales Orderwise Pending Amount To Bill", - "columns": "Sales Order\u0001ID,Sales Order\u0001Customer,Sales Order\u0001Customer Address,Sales Order\u0001Status,Sales Order\u0001% Delivered,Sales Order\u0001% Billed,Sales Order\u0001Company Name,Sales Order\u0001Sales Order Date,Sales Order\u0001Net Total,Sales Order\u0001Zone,Sales Order\u0001Territory,Sales Order\u0001Email Id" - }, - { - "name": "sales_orderwise_pending_amount_to_bill", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/sales_orderwise_pending_qty_to_deliver/__init__.py b/selling/search_criteria/sales_orderwise_pending_qty_to_deliver/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/sales_orderwise_pending_qty_to_deliver/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/sales_orderwise_pending_qty_to_deliver/sales_orderwise_pending_qty_to_deliver.txt b/selling/search_criteria/sales_orderwise_pending_qty_to_deliver/sales_orderwise_pending_qty_to_deliver.txt deleted file mode 100644 index d9b15819e0..0000000000 --- a/selling/search_criteria/sales_orderwise_pending_qty_to_deliver/sales_orderwise_pending_qty_to_deliver.txt +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "owner": "dhanalekshmi@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "add_col": "SUM(`tabSales Order Item`.`qty` - ifnull(`tabSales Order Item`.`delivered_qty`, 0)) AS \"Pending Qty\"", - "parent_doc_type": "Sales Order", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Order\u0001Saved':1,'Sales Order\u0001Submitted':1,'Sales Order\u0001Status':'','Sales Order\u0001Fiscal Year':''}", - "description": "Sales Orderwise Pending Qty To Deliver", - "doc_type": "Sales Order Item", - "name": "__common__", - "add_cond": "`tabSales Order`.status != \"Stopped\"\n`tabSales Order Item`.`qty` > ifnull(`tabSales Order Item`.`delivered_qty`, 0)", - "doctype": "Search Criteria", - "sort_by": "`tabSales Order`.`name`", - "group_by": "`tabSales Order`.`name`", - "page_len": 50, - "criteria_name": "Sales Orderwise Pending Qty To Deliver", - "columns": "Sales Order\u0001ID,Sales Order\u0001Customer,Sales Order\u0001Customer Name,Sales Order\u0001Customer Address,Sales Order\u0001Status,Sales Order\u0001% Delivered,Sales Order\u0001% Billed,Sales Order\u0001Sales Order Date,Sales Order\u0001Territory,Sales Order\u0001Email Id" - }, - { - "name": "sales_orderwise_pending_qty_to_deliver", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/sales_persons_target_variance_item_group_wise/__init__.py b/selling/search_criteria/sales_persons_target_variance_item_group_wise/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/sales_persons_target_variance_item_group_wise/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.js b/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.js deleted file mode 100644 index 8e46f42322..0000000000 --- a/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.js +++ /dev/null @@ -1,57 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - this.hide_all_filters(); - - this.add_filter({fieldname:'sales_person', label:'Sales Person', fieldtype:'Link', options:'Sales Person',ignore : 1,parent:'Target Detail'}); - - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Target Detail'}); - - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Target Detail'}); - - this.add_filter({fieldname:'under', label:'Under',fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice',report_default:'Sales Order',ignore : 1, parent:'Target Detail'}); - - this.add_filter({fieldname : 'target_on', label:'Target On', fieldtype:'Select', options:'Quantity'+NEWLINE+'Amount',report_default:'Quantity',ignore : 1,parent:'Target Detail'}); - this.filter_fields_dict['Target Detail'+FILTER_SEP +'Sales Person'].df.in_first_page = 1; -} -this.mytabs.items['Select Columns'].hide(); -report.get_query = function() { - - sales_person = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Sales Person'].get_value(); - target_on = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Target On'].get_value(); - under = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Under'].get_value(); - if(under == 'Sales Invoice') under = 'Sales Invoice'; - - if(target_on == 'Quantity'){ - q1 = 't1.target_qty AS "Target Quantity"'; - q2 = '0 AS "Target Quantity"'; - cond1 = 'ifnull(t1.target_qty,"")!=""'; - cond2 = 'ifnull(t6.target_qty,"")!=""'; - } - else{ - q1 = 't1.target_amount AS "Target Amount"'; - q2 = '0 AS "Target Amount"'; - cond1 = 'ifnull(t1.target_amount,"")!=""'; - cond2 = 'ifnull(t6.target_amount,"")!=""'; - } - - var q ='SELECT t1.item_group AS "Item Group", '+q1+', t2.distribution_id AS "Distribution Id" FROM `tabTarget Detail` t1, `tabSales Person` t2 WHERE t1.parenttype = "Sales Person" AND t1.parent = "'+sales_person+'" AND t1.parent=t2.name AND ifnull(t1.item_group,"") != "" AND '+cond1+' UNION SELECT t3.item_group AS "Item Group", '+q2+',"" AS "Distribution Id" FROM `tab'+under+' Item` t3,`tabSales Team` t4,`tab'+under+'` t5 where t3.item_group NOT IN (SELECT t6.item_group AS "Item Group" FROM `tabTarget Detail` t6, `tabSales Person` t7 WHERE t6.parenttype = "Sales Person" AND t6.parent = "'+sales_person+'" AND t6.parent=t7.name AND '+cond2+') AND t4.sales_person = "'+sales_person+'" AND t3.parent = t5.name AND t4.parent = t5.name AND t5.docstatus = 1'; - - return q; - -} diff --git a/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.py b/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.py deleted file mode 100644 index 3c822a6aac..0000000000 --- a/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.py +++ /dev/null @@ -1,145 +0,0 @@ -# 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 . -# validate Filters -from __future__ import unicode_literals -flt_dict = {'fiscal_year': 'Fiscal Year', 'period': 'Period', 'under' : 'Under', 'sales_person':'Sales Person', 'target_on':'Target On'} -for f in flt_dict: - if not filter_values.get(f): - msgprint("Please Select " + cstr(flt_dict[f])) - raise Exception - -# Get Values from fliters -fiscal_year = filter_values.get('fiscal_year')[0] -period = filter_values.get('period') -under = filter_values.get('under') -sales_person = filter_values.get('sales_person') -target_on = filter_values.get('target_on') - - -# set colnames -for d in ['Item Group', 'Total Target Allocated', 'Distribution Id']: - colnames.append(d) - coltypes.append('Data') - colwidths.append('150px') - coloptions.append('') - col_idx[d] = len(colnames) - 1 - - - -# Set required field names -based_on_fn = 'sales_person' - -date_fn = (under == 'Sales Order' ) and 'transaction_date' or 'posting_date' - -mon_list = [] - -data = {'start_date':0, 'end_date':1} - -def make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx): - count = 1 - if period == 'Quarterly' or period == 'Half Yearly' or period == 'Annual': mon_list.append([str(start_date)]) - for m in range(12): - # get last date - last_date = str(sql("select LAST_DAY('%s')" % start_date)[0][0]) - - # make mon_list for Monthly Period - if period == 'Monthly' : - mon_list.append([start_date, last_date]) - # add months as Column names - month_name = sql("select MONTHNAME('%s')" % start_date)[0][0] - append_colnames(str(month_name)[:3], colnames, coltypes, colwidths, coloptions, col_idx) - - # get start date - start_date = str(sql("select DATE_ADD('%s',INTERVAL 1 DAY)" % last_date)[0][0]) - - # make mon_list for Quaterly Period - if period == 'Quarterly' and count % 3 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column names - append_colnames('Q '+ str(count / 3), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Half Yearly Period - if period == 'Half Yearly' and count % 6 == 0 : - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('H'+str(count / 6), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Annual Period - if period == 'Annual' and count % 12 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('', colnames, coltypes, colwidths, coloptions, col_idx) - count = count +1 - -def append_colnames(name, colnames, coltypes, colwidths, coloptions, col_idx): - col = ['Target', 'Actual', 'Variance'] - for c in col: - n = str(name) and ' (' + str(name) +')' or '' - colnames.append(str(c) + n ) - coltypes.append('Currency') - colwidths.append('150px') - coloptions.append('') - col_idx[str(c) + n ] = len(colnames) - 1 - - - - -# get start date -start_date = webnotes.conn.get_value('Fiscal Year', fiscal_year, 'year_start_date') -if not start_date: - msgprint("Please Define Year Start Date for Fiscal Year " + str(fiscal_year)) - raise Exception -start_date = start_date.strftime('%Y-%m-%d') - -# make month list and columns -make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx) - - -bc_obj = get_obj('Budget Control') -for r in res: - - count = 0 - - for idx in range(3, len(colnames), 3): - - cidx = 2 - # ================= Calculate Target ========================================== - r.append(bc_obj.get_monthly_budget(r[cidx], fiscal_year, mon_list[count][data['start_date']], mon_list[count][data['end_date']], r[cidx-1])) - - #================== Actual Amount ============================================= - actual = 0 - - - #---------------------------------------------------------- - if target_on == "Quantity": - - actual = sql("select sum(ifnull(t2.qty,0) * ifnull(t3.allocated_percentage,0) / 100) from `tab%s` t1, `tab%s Item` t2, `tabSales Team` t3 where t2.parent = t1.name and t3.parent = t1.name and t3.%s = '%s' and t2.item_group = '%s' and t1.docstatus = 1 and t1.%s between '%s' and '%s' "%(under, under, based_on_fn, sales_person, r[0].strip(), date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - - #---------------------------------------------------------- - if target_on == "Amount": - - actual = sql("select sum(ifnull(t2.amount,0) * ifnull(t3.allocated_percentage,0) / 100) from `tab%s` t1, `tab%s Item` t2, `tabSales Team` t3 where t2.parent = t1.name and t3.parent = t1.name and t3.%s = '%s' and t2.item_group = '%s' and t1.docstatus = 1 and t1.%s between '%s' and '%s' "%(under, under, based_on_fn, sales_person, r[0].strip(), date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - #---------------------------------------------------------- - - actual = actual and flt(actual[0][0]) or 0 - r.append(actual) - # ================ Variance =================================================== - - r.append(r[idx] - r[idx + 1]) - - count = count +1 diff --git a/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.txt b/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.txt deleted file mode 100644 index 55351829ff..0000000000 --- a/selling/search_criteria/sales_persons_target_variance_item_group_wise/sales_persons_target_variance_item_group_wise.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "parent_doc_type": "Sales Person", - "module": "Selling", - "doctype": "Search Criteria", - "sort_order": "DESC", - "filters": "{'Sales Person\u0001Country':'','Sales Person\u0001State':'','Target Detail\u0001Fiscal Year':''}", - "standard": "Yes", - "doc_type": "Target Detail", - "name": "__common__", - "sort_by": "`tabTarget Detail`.`target_amount`", - "page_len": 50, - "criteria_name": "Sales Persons Target Variance (Item Group wise)" - }, - { - "name": "sales_persons_target_variance_item_group_wise", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/sales_personwise_transaction_summary/__init__.py b/selling/search_criteria/sales_personwise_transaction_summary/__init__.py deleted file mode 100755 index baffc48825..0000000000 --- a/selling/search_criteria/sales_personwise_transaction_summary/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.js b/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.js deleted file mode 100755 index 79dd9d5faf..0000000000 --- a/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.js +++ /dev/null @@ -1,53 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - - //Add filter - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice', report_default:'Sales Order', ignore : 1,parent:'Sales Person', single_select :1, in_first_page:1}); - this.add_filter({fieldname:'transaction_date', label:'Date', fieldtype:'Date', options:'', ignore : 1,parent:'Sales Person', in_first_page:1}); - this.add_filter({fieldname:'voucher_id', label:'Voucher Id', fieldtype:'Data', options:'', ignore : 1,parent:'Sales Person', in_first_page:1}); - this.add_filter({fieldname:'territory', label:'Territory', fieldtype:'Link', options:'Territory', ignore : 1,parent:'Sales Person', in_first_page:1}); - this.add_filter({fieldname:'sales_person', label:'Sales Person', fieldtype:'Link', options:'Sales Person', ignore : 1,parent:'Sales Person', in_first_page:1}); -} - -// hide sections -this.mytabs.items['More Filters'].hide(); -this.mytabs.items['Select Columns'].hide(); - -// Get query -report.get_query = function() { - based_on = this.get_filter('Sales Person', 'Based On').get_value(); - from_date = this.get_filter('Sales Person', 'From Date').get_value(); - to_date = this.get_filter('Sales Person', 'To Date').get_value(); - vid = this.get_filter('Sales Person', 'Voucher Id').get_value(); - terr = this.get_filter('Sales Person', 'Territory').get_value(); - sp = this.get_filter('Sales Person', 'Sales Person').get_value(); - - date_fld = 'transaction_date'; - if(based_on == 'Sales Invoice' || based_on == "Delivery Note") date_fld = 'posting_date'; - - sp_cond = ''; - if (from_date) sp_cond += ' AND t1.' + date_fld + '>= "' + from_date + '"'; - if (to_date) sp_cond += ' AND t1.' + date_fld + '<= "' + to_date + '"'; - if (vid) sp_cond += ' AND t1.name LIKE "%' + vid + '%"'; - if (terr) sp_cond += ' AND t1.territory = "' + terr + '"'; - if (sp) sp_cond += ' AND t2.sales_person = "' + sp + '"'; - - return 'SELECT t1.`name`, t1.`customer_name`, t1.`territory`, t1.`' + date_fld + '`, t1.`net_total`, t1.`grand_total`, t2.`sales_person`, t2.`allocated_percentage`, t2.`allocated_amount` FROM `tab' + based_on + '` t1, `tabSales Team` t2 WHERE t1.docstatus=1 AND t2.`parenttype` = "' + based_on + '" AND t2.`parent` = t1.`name`' + sp_cond + ' ORDER BY t1.`name` DESC'; -} - diff --git a/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.py b/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.py deleted file mode 100755 index 632e545905..0000000000 --- a/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.py +++ /dev/null @@ -1,30 +0,0 @@ -# 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 -if filter_values.get('based_on') == 'Sales Invoice': - based_on_dt = 'Sales Invoice' -else: - based_on_dt = filter_values.get('based_on') - -cols = [[filter_values.get('based_on'), 'Link','150px', based_on_dt], ['Customer', 'Link','150px','Customer'], ['Territory', 'Link','120px','Territory'], ['Transaction Date', 'Date', '120px', ''], ['Net Total', 'Currency', '80px', ''], ['Grand Total', 'Currency', '80px', ''], ['Sales Person', 'Link', '150px', 'Sales Person'], ['% Contribution', 'Currency', '120px', ''], ['Contribution Amt', 'Currency', '120px', '']] - -for c in cols: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 diff --git a/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.txt b/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.txt deleted file mode 100755 index 5f299815d7..0000000000 --- a/selling/search_criteria/sales_personwise_transaction_summary/sales_personwise_transaction_summary.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Person\u0001Saved':1,'Sales Person\u0001Submitted':1,'Sales Person\u0001Country':'','Sales Person\u0001State':''}", - "doctype": "Search Criteria", - "doc_type": "Sales Person", - "name": "__common__", - "sort_by": "`tabSales Person`.`name`", - "page_len": 50, - "criteria_name": "Sales Personwise Transaction Summary", - "columns": "Sales Person\u0001ID" - }, - { - "name": "sales_personwise_transaction_summary", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/serial_no_amc_expiring_this_month/__init__.py b/selling/search_criteria/serial_no_amc_expiring_this_month/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/serial_no_amc_expiring_this_month/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/serial_no_amc_expiring_this_month/serial_no_amc_expiring_this_month.txt b/selling/search_criteria/serial_no_amc_expiring_this_month/serial_no_amc_expiring_this_month.txt deleted file mode 100644 index 463d077cce..0000000000 --- a/selling/search_criteria/serial_no_amc_expiring_this_month/serial_no_amc_expiring_this_month.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Serial No\u0001Saved':1,'Serial No\u0001Submitted':1,'Serial No\u0001Status':'','Serial No\u0001Warranty Status':''}", - "doc_type": "Serial No", - "name": "__common__", - "add_cond": "`tab Serial No`.month(amc_expiry_date)=month(now()) \n`tab Serial No`.year(amc_expiry_date)=year(now())", - "doctype": "Search Criteria", - "sort_by": "`tabSerial No`.`name`", - "page_len": 50, - "criteria_name": "Serial No-AMC expiring this month", - "columns": "Serial No\u0001ID,Serial No\u0001Status,Serial No\u0001AMC Expiry Date,Serial No\u0001Customer Name" - }, - { - "name": "serial_no-amc_expiring_this_month", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/serial_no_warranty_expiring_this_month/__init__.py b/selling/search_criteria/serial_no_warranty_expiring_this_month/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/serial_no_warranty_expiring_this_month/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/serial_no_warranty_expiring_this_month/serial_no_warranty_expiring_this_month.txt b/selling/search_criteria/serial_no_warranty_expiring_this_month/serial_no_warranty_expiring_this_month.txt deleted file mode 100644 index 05a9f0fce9..0000000000 --- a/selling/search_criteria/serial_no_warranty_expiring_this_month/serial_no_warranty_expiring_this_month.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:52", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:52" - }, - { - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Serial No\u0001Saved':1,'Serial No\u0001Submitted':1,'Serial No\u0001Status':'Issued','Serial No\u0001Maintenance Status':''}", - "doc_type": "Serial No", - "name": "__common__", - "add_cond": "`tab Serial No`.docstatus!=2\n`tab Serial No`.maintenance_status='Under Warranty'\n`tab Serial No`.status!='Scrapped'\n`tab Serial No`.status!='Not in Use'\n`tab Serial No`.month(ifnull(warranty_expiry_date,0)) = month(now()) \n`tab Serial No`.yearmonth(ifnull(warranty_expiry_date,0)) = year(now())", - "doctype": "Search Criteria", - "sort_by": "`tabSerial No`.`name`", - "page_len": 50, - "criteria_name": "Serial No-Warranty expiring this month", - "columns": "Serial No\u0001ID,Serial No\u0001Status,Serial No\u0001Warranty Expiry Date,Serial No\u0001Customer Name" - }, - { - "name": "serial_no-warranty_expiring_this_month", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/target_variance_report/__init__.py b/selling/search_criteria/target_variance_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/target_variance_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/target_variance_report/target_variance_report.js b/selling/search_criteria/target_variance_report/target_variance_report.js deleted file mode 100644 index cc8e03ed4a..0000000000 --- a/selling/search_criteria/target_variance_report/target_variance_report.js +++ /dev/null @@ -1,43 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Cost Center'+NEWLINE+'Sales Person'+NEWLINE+'Sales Partner',report_default:'Cost Center',ignore : 1,parent:'Target Detail'}); - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'company', label:'Company', fieldtype:'Link', options:'Company',report_default:sys_defaults.company, ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'group_by', label:'Group By', fieldtype:'Select', options:NEWLINE+'Item Group',ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'under', label:'Under',fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice',report_default:'Sales Order',ignore : 1, parent:'Target Detail'}); - -} - -report.get_query = function() { - group_by = ''; - group_by = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Group By'].get_value(); - based_on = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Based On'].get_value(); - sel_fields = '`tabTarget Detail`.parent AS "' + cstr(based_on)+'"'; - cond = ' and ifnull(`tabTarget Detail`.item_group, "") = ""' - if (group_by == 'Item Group'){ - sel_fields = cstr(sel_fields) + ', `tabTarget Detail`.item_group'; - cond = ' and ifnull(`tabTarget Detail`.item_group,"") != ""' - } - sel_fields = cstr(sel_fields) + ', `tabTarget Detail`.target_amount, `tabTarget Detail`.distribution_id'; - var q = 'SELECT '+ cstr(sel_fields) +' FROM `tabTarget Detail` WHERE `tabTarget Detail`.parenttype = "' + cstr(based_on) + '"'+ cstr(cond); - return q -} - -this.mytabs.items['Select Columns'].hide(); \ No newline at end of file diff --git a/selling/search_criteria/target_variance_report/target_variance_report.py b/selling/search_criteria/target_variance_report/target_variance_report.py deleted file mode 100644 index 0c1411257a..0000000000 --- a/selling/search_criteria/target_variance_report/target_variance_report.py +++ /dev/null @@ -1,127 +0,0 @@ -# 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 . - -# validate Filters -from __future__ import unicode_literals -flt_dict = {'fiscal_year': 'Fiscal Year', 'period': 'Period', 'company':'Company', 'under' : 'Under', 'based_on' : 'Based On'} -for f in flt_dict: - if not filter_values.get(f): - msgprint("Please Select " + cstr(flt_dict[f])) - raise Exception - -# Get Values from fliters -fiscal_year = filter_values.get('fiscal_year') -period = filter_values.get('period') -under = filter_values.get('under') -based_on = filter_values.get('based_on') -group_by = filter_values.get('group_by') - -# Set required field names -based_on_fn = (based_on == 'Cost Center') and 'cost_center' or (based_on == 'Sales Partner') and 'sales_partner' or (based_on == 'Sales Person') and 'sales_person' -date_fn = (under == 'Sales Order' ) and 'transaction_date' or 'posting_date' - -mon_list = [] -data = {'start_date':0, 'end_date':1} - -def make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx): - count = 1 - if period == 'Quarterly' or period == 'Half Yearly' or period == 'Annual': mon_list.append([str(start_date)]) - for m in range(12): - # get last date - last_date = str(sql("select LAST_DAY('%s')" % start_date)[0][0]) - - # make mon_list for Monthly Period - if period == 'Monthly' : - mon_list.append([start_date, last_date]) - # add months as Column names - month_name = sql("select MONTHNAME('%s')" % start_date)[0][0] - append_colnames(str(month_name)[:3], colnames, coltypes, colwidths, coloptions, col_idx) - - # get start date - start_date = str(sql("select DATE_ADD('%s',INTERVAL 1 DAY)" % last_date)[0][0]) - - # make mon_list for Quaterly Period - if period == 'Quarterly' and count % 3 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column names - append_colnames('Q '+ str(count / 3), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Half Yearly Period - if period == 'Half Yearly' and count % 6 == 0 : - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('H'+str(count / 6), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Annual Period - if period == 'Annual' and count % 12 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('', colnames, coltypes, colwidths, coloptions, col_idx) - count = count +1 - -def append_colnames(name, colnames, coltypes, colwidths, coloptions, col_idx): - col = ['Budget', 'Actual', 'Variance'] - for c in col: - colnames.append(str(c) + ' (' + str(name) +')' ) - coltypes.append('Currency') - colwidths.append('150px') - coloptions.append('') - col_idx[str(c) + ' (' + str(name) +')' ] = len(colnames) - 1 - -# make default columns -coltypes[col_idx[based_on]] = 'Link' -coloptions[col_idx[based_on]]= based_on - -# get start date -start_date = webnotes.conn.get_value('Fiscal Year', fiscal_year, 'year_start_date') -if not start_date: - msgprint("Please Define Year Start Date for Fiscal Year " + str(fiscal_year)) - raise Exception -start_date = start_date.strftime('%Y-%m-%d') - -# make month list and columns -make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx) - -bc_obj = get_obj('Budget Control') -for r in res: - count = 0 - for idx in range((group_by == 'Item Group') and 4 or 3, len(colnames), 3): - cidx = (group_by == 'Item Group') and 3 or 2 - # ================= Calculate Target ========================================== - r.append(bc_obj.get_monthly_budget( r[cidx], fiscal_year, mon_list[count][data['start_date']], mon_list[count][data['end_date']], r[cidx-1])) - - #================== Actual Amount ============================================= - actual = 0 - if based_on == 'Cost Center' or based_on == 'Sales Partner': - if group_by =='Item Group': - actual = sql("select sum(ifnull(t2.amount,0)) from `tab%s` t1, `tab%s Detail` t2, `tabItem` t3 where t2.parenttype = '%s' and t2.parent = t1.name and t1.%s = '%s' and t3.name = t2.item_code and t3.item_group = '%s' and t1.docstatus = 1 and t1.%s between '%s' and '%s'" % (under, (under == 'Sales Invoice') and 'RV' or under, under, based_on_fn, r[0], r[1], date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - actual = flt(actual[0][0]) - else: - actual = sql("select sum(ifnull(net_total,0)) from `tab%s` where %s = '%s' and docstatus = 1 and %s between '%s' and '%s' " % (under, based_on_fn, r[0], date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - actual = flt(actual[0][0]) - elif based_on == 'Sales Person': - if group_by =='Item Group': - actual = sql("select sum(ifnull(t2.amount,0) * ifnull(t3.allocated_percentage,0) / 100) from `tab%s` t1, `tab%s Detail` t2, `tabSales Team` t3, `tabItem` t4 where t2.parent = t1.name and t3.parent = t1.name and t3.%s = '%s' and t4.name = t2.item_code and t4.item_group = '%s' and t1.docstatus != 2 and t1.docstatus = 1 and t1.%s between '%s' and '%s' "%(under, (under == 'Sales Invoice') and 'RV' or under, based_on_fn, r[0], r[1], date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - actual = flt(actual[0][0]) - else: - actual = sql("select sum(ifnull(t2.allocated_amount,0)) from `tab%s` t1, `tabSales Team` t2 where t2.%s = '%s' and t2.parenttype='%s' and t1.docstatus != 2 and t2.parent = t1.name and t1.%s between '%s' and '%s'"%(under, based_on_fn, r[0], under, date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - actual = flt(actual[0][0]) - r.append(actual) - # ================ Variance =================================================== - r.append(r[idx] - r[idx + 1]) - count = count +1 \ No newline at end of file diff --git a/selling/search_criteria/target_variance_report/target_variance_report.txt b/selling/search_criteria/target_variance_report/target_variance_report.txt deleted file mode 100644 index c89236632b..0000000000 --- a/selling/search_criteria/target_variance_report/target_variance_report.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "harshada@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "parent_doc_type": "Sales Person", - "module": "Selling", - "standard": "Yes", - "disabled": 0, - "sort_order": "DESC", - "filters": "{'Sales Person\u0001Saved':1,'Sales Person\u0001Submitted':1,'Sales Person\u0001Country':'','Sales Person\u0001State':'','Target Detail\u0001Fiscal Year':''}", - "doc_type": "Target Detail", - "name": "__common__", - "doctype": "Search Criteria", - "page_len": 50, - "criteria_name": "Target Variance Report", - "columns": "Sales Person\u0001ID,Sales Person\u0001Owner,Sales Person\u0001Sales Person,Sales Person\u0001Country,Sales Person\u0001State,Target Detail\u0001Item Group,Target Detail\u0001Fiscal Year,Target Detail\u0001Target Amount" - }, - { - "name": "target_variance_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/territories_target_variance_item_group_wise/__init__.py b/selling/search_criteria/territories_target_variance_item_group_wise/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/territories_target_variance_item_group_wise/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.js b/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.js deleted file mode 100644 index d7dcf70391..0000000000 --- a/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.js +++ /dev/null @@ -1,61 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - this.hide_all_filters(); - - this.add_filter({fieldname:'territory', label:'Territory', fieldtype:'Link', options:'Territory',ignore : 1,parent:'Target Detail'}); - - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Target Detail'}); - - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Target Detail'}); - - this.add_filter({fieldname:'under', label:'Under',fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice',report_default:'Sales Order',ignore : 1, parent:'Target Detail'}); - - this.add_filter({fieldname : 'target_on', label:'Target On', fieldtype:'Select', options:'Quantity'+NEWLINE+'Amount',report_default:'Quantity',ignore : 1,parent:'Target Detail'}); - this.filter_fields_dict['Target Detail'+FILTER_SEP +'Territory'].df.in_first_page = 1; -} -this.mytabs.items['Select Columns'].hide(); -report.get_query = function() { - - territory = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Territory'].get_value(); - target_on = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Target On'].get_value(); - under = this.filter_fields_dict['Target Detail'+FILTER_SEP+'Under'].get_value(); - if(under == 'Sales Invoice') under = 'Sales Invoice'; - - if(target_on == 'Quantity'){ - q1 = 't1.target_qty AS "Target Quantity"'; - q2 = '0 AS "Target Quantity"'; - cond1 = 'ifnull(t1.target_qty,"")!=""'; - cond2 = 'ifnull(t5.target_qty,"")!=""'; - } - else{ - q1 = 't1.target_amount AS "Target Amount"'; - q2 = '0 AS "Target Amount"'; - cond1 = 'ifnull(t1.target_amount,"")!=""'; - cond2 = 'ifnull(t5.target_amount,"")!=""'; - } - if(under =='Sales Invoice') - tab = 'RV'; - else - tab = under; - - var q ='SELECT distinct t1.item_group AS "Item Group", '+q1+', t2.distribution_id AS "Distribution Id" FROM `tabTarget Detail` t1, `tabTerritory` t2 WHERE t1.parenttype = "Territory" AND t1.parent = "'+territory+'" AND t1.parent=t2.name AND ifnull(t1.item_group,"")!="" AND '+cond1+' UNION SELECT distinct t3.item_group AS "Item Group", '+q2+',"" AS "Distribution Id" FROM `tab'+tab+' Detail` t3,`tab'+under+'` t4 where t3.item_group NOT IN (SELECT t5.item_group FROM `tabTarget Detail` t5, `tabTerritory` t6 WHERE t5.parenttype = "Territory" AND t5.parent = "'+territory+'" AND t5.parent=t6.name AND '+cond2+') AND t4.territory = "'+territory+'" AND t3.parent = t4.name AND t4.docstatus = 1'; - - return q; - -} \ No newline at end of file diff --git a/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.py b/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.py deleted file mode 100644 index 17acb478a5..0000000000 --- a/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.py +++ /dev/null @@ -1,144 +0,0 @@ -# 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 . - -# validate Filters -from __future__ import unicode_literals -flt_dict = {'fiscal_year': 'Fiscal Year', 'period': 'Period', 'under' : 'Under', 'territory':'Territory', 'target_on':'Target On'} -for f in flt_dict: - if not filter_values.get(f): - msgprint("Please Select " + cstr(flt_dict[f])) - raise Exception - -# Get Values from fliters -fiscal_year = filter_values.get('fiscal_year') -period = filter_values.get('period') -under = filter_values.get('under') -if under == 'Sales Invoice': under = 'Sales Invoice' -territory = filter_values.get('territory') -target_on = filter_values.get('target_on') - - -# Set required field names -based_on_fn = 'territory' - -date_fn = (under == 'Sales Order' ) and 'transaction_date' or 'posting_date' - -mon_list = [] - -data = {'start_date':0, 'end_date':1} - -def make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx): - count = 1 - if period == 'Quarterly' or period == 'Half Yearly' or period == 'Annual': mon_list.append([str(start_date)]) - for m in range(12): - # get last date - last_date = str(sql("select LAST_DAY('%s')" % start_date)[0][0]) - - # make mon_list for Monthly Period - if period == 'Monthly' : - mon_list.append([start_date, last_date]) - # add months as Column names - month_name = sql("select MONTHNAME('%s')" % start_date)[0][0] - append_colnames(str(month_name)[:3], colnames, coltypes, colwidths, coloptions, col_idx) - - # get start date - start_date = str(sql("select DATE_ADD('%s',INTERVAL 1 DAY)" % last_date)[0][0]) - - # make mon_list for Quaterly Period - if period == 'Quarterly' and count % 3 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column names - append_colnames('Q '+ str(count / 3), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Half Yearly Period - if period == 'Half Yearly' and count % 6 == 0 : - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('H'+str(count / 6), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Annual Period - if period == 'Annual' and count % 12 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('', colnames, coltypes, colwidths, coloptions, col_idx) - count = count +1 - -def append_colnames(name, colnames, coltypes, colwidths, coloptions, col_idx): - col = ['Target', 'Actual', 'Variance'] - for c in col: - n = str(name) and ' (' + str(name) +')' or '' - colnames.append(str(c) + n ) - coltypes.append('Currency') - colwidths.append('150px') - coloptions.append('') - col_idx[str(c) + n ] = len(colnames) - 1 - - - -# make default columns -#coltypes[col_idx['Item Group']] = 'Link' -#coloptions[col_idx['Item Group']]= 'Sales ' - -# get start date -start_date = webnotes.conn.get_value('Fiscal Year', fiscal_year, 'year_start_date') -if not start_date: - msgprint("Please Define Year Start Date for Fiscal Year " + str(fiscal_year)) - raise Exception -start_date = start_date.strftime('%Y-%m-%d') - -# make month list and columns -make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx) - - - -bc_obj = get_obj('Budget Control') -for r in res: - - count = 0 - - for idx in range(3, len(colnames), 3): - - cidx = 2 - # ================= Calculate Target ========================================== - r.append(bc_obj.get_monthly_budget(r[cidx], fiscal_year, mon_list[count][data['start_date']], mon_list[count][data['end_date']], r[cidx-1])) - - #================== Actual Amount ============================================= - actual = 0 - - - - #---------------------------------------------------------- - if target_on == "Quantity": - - actual = sql("select sum(ifnull(t2.qty,0)) from `tab%s` t1, `tab%s Detail` t2 where t2.parenttype = '%s' and t2.parent = t1.name and t1.%s = '%s' and t1.docstatus = 1 and t2.item_group = '%s' and t1.%s between '%s' and '%s'" % (under, (under == 'Sales Invoice') and 'RV' or under, under, based_on_fn, territory, r[0],date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - - - #---------------------------------------------------------- - if target_on == "Amount": - - actual = sql("select sum(ifnull(t2.amount,0)) from `tab%s` t1, `tab%s Detail` t2 where t2.parenttype = '%s' and t2.parent = t1.name and t1.%s = '%s' and t1.docstatus = 1 and t2.item_group = '%s' and t1.%s between '%s' and '%s'" % (under, (under == 'Sales Invoice') and 'RV' or under, under, based_on_fn, territory, r[0],date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - - #---------------------------------------------------------- - - actual = actual and flt(actual[0][0]) or 0 - r.append(actual) - # ================ Variance =================================================== - - r.append(r[idx] - r[idx + 1]) - - count = count +1 \ No newline at end of file diff --git a/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.txt b/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.txt deleted file mode 100644 index a64b47e915..0000000000 --- a/selling/search_criteria/territories_target_variance_item_group_wise/territories_target_variance_item_group_wise.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "parent_doc_type": "Sales Person", - "module": "Selling", - "doctype": "Search Criteria", - "sort_order": "DESC", - "filters": "{'Sales Person\u0001Country':'','Sales Person\u0001State':'','Target Detail\u0001Fiscal Year':''}", - "standard": "Yes", - "doc_type": "Target Detail", - "name": "__common__", - "sort_by": "`tabTarget Detail`.`target_amount`", - "page_len": 50, - "criteria_name": "Territories Target Variance (Item Group wise)" - }, - { - "name": "territories_target_variance_item_group_wise", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/territory_sales___variance_report/__init__.py b/selling/search_criteria/territory_sales___variance_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/territory_sales___variance_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.js b/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.js deleted file mode 100644 index aabd4e179e..0000000000 --- a/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.js +++ /dev/null @@ -1,22 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice',report_default:'Sales Order',ignore : 1, parent:'Territory'}); - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Territory'}); - this.add_filter({fieldname:'company', label:'Company', fieldtype:'Link', options:'Company',report_default:sys_defaults.company, ignore : 1, parent:'Territory'}); - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Territory'}); -} \ No newline at end of file diff --git a/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.py b/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.py deleted file mode 100644 index e9e40aa614..0000000000 --- a/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.py +++ /dev/null @@ -1,205 +0,0 @@ -# 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 -if filter_values.get('period'): - period_values = filter_values['period'] - if len(period_values.split(NEWLINE))>1: - msgprint("You can view report only for one period. Please select only one value in period.") - raise Exception - else: - period = period_values.split(NEWLINE)[0] - -if filter_values.get('based_on'): - based_on = filter_values['based_on'] - if len(based_on.split(NEWLINE)) > 1: - msgprint("You can view report based on only one criteria. Please select only one value in Based On.") - raise Exception - else: - based_on = based_on.split(NEWLINE)[0] - -if not filter_values.get('fiscal_year'): - msgprint("Please Select Fiscal Year") - raise Exception -elif not filter_values.get('period'): - msgprint("Please Select Period") - raise Exception -elif not filter_values.get('based_on'): - msgprint("Please Select the Criteria on which you want your report to be based") - raise Exception - -fiscal_year = filter_values.get('fiscal_year') - -# get fiscal year start date and start month -# --------------------------------------------------------- -year_start_date = sql("select year_start_date,MONTH(year_start_date) from `tabFiscal Year` where name = %s",fiscal_year) -start_date = year_start_date and year_start_date[0][0] or '' -start_month = year_start_date and year_start_date[0][1] or '' -month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] - -# Add columns based on period -# -------------------------------- -columns = [] -# ================ Annual ====================== -if period == 'Annual': - columns.append(['Target','Currency','120px','']) - columns.append(['Actual','Currency','120px','']) - -# =========== Half Yearly ====================== -elif period == 'Half Yearly': - columns.append(['Target (H1)','Currency','120px','']) # first half - columns.append(['Actual (H1)','Currency','120px','']) # first half - if start_month == 1: # this is case when fiscal year starts with JAN - columns.append(['Target (H2)','Currency','120px','']) - columns.append(['Actual (H2)','Currency','120px','']) - else: #this is case when fiscal year starts with other than JAN - columns.append(['Target (H2)','Currency','120px','']) - columns.append(['Actual (H2)','Currency','120px','']) - -# ================ Quarterly =================== -elif period == 'Quarterly': - length_1 = (len(month_name) - start_month + 1) / 3 #this gives the total no. of times we need to iterate for quarter - val = length_1 % 4 - q_no = 1 - for i in range(length_1): - value = 3*i + val - columns.append(['Target (Q'+cstr(q_no)+')','Currency','120px','']) - columns.append(['Actual (Q'+cstr(q_no)+')','Currency','120px','']) - q_no += 1 - length_2 = (start_month - 1) / 3 #this gives the total no. of times we need to iterate for quarter (this is required only if fiscal year starts from april) - for i in range(length_2): - columns.append(['Target (Q'+cstr(q_no)+')','Currency','120px','']) - columns.append(['Actual (Q'+cstr(q_no)+')','Currency','120px','']) - q_no += 1; - - -# =============== Monthly ====================== -elif period == 'Monthly': - for i in range(start_month-1,len(month_name)): - columns.append(['Target ('+month_name[i]+')','Currency','120px','']) - columns.append(['Actual ('+month_name[i]+')','Currency','120px','']) - - for i in range(start_month-1): - columns.append(['Target('+month_name[i]+')','Currency','120px','']) - columns.append(['Actual ('+month_name[i]+')','Currency','120px','']) - - - -for c in columns: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 - - -condition = ' docstatus = 1 and fiscal_year = "'+fiscal_year+'"' - - -for r in res: - query = '' - - # ================= Annual Report =============== - if period == 'Annual': - - target = sql("select sum(target_amount) from `tabTarget Detail` where parent = %s and parenttype= 'Territory' and fiscal_year = %s ",(r[col_idx['ID']],fiscal_year)) - target = target and flt(target[0][0]) or 0 - r.append(target) - - - so = sql("select sum(net_total) from `tab%s` where territory = '%s' and %s" % (based_on, r[col_idx['ID']],condition)) - so = so and flt(so[0][0]) or 0 - r.append(so) - - # ================= Half Yearly Report =============== - elif period == 'Half Yearly': - target = sql("select sum(target_amount) from `tabTarget Detail` where parent = %s and parenttype= 'Territory' and fiscal_year = %s",(r[col_idx['ID']],fiscal_year)) - target = target and flt(flt(target[0][0])/2) or 0 - r.append(target) - - query += ' MONTH(transaction_date) BETWEEN '+cstr(start_month)+' and '+cstr(start_month+5) - so = sql("select sum(net_total) from `tab%s` where territory = '%s' and %s and %s" % (based_on, r[col_idx['ID']],condition,query)) - so = so and flt(so[0][0]) or 0 - r.append(so) - - r.append(target) - - query ='' - query += 'MONTH(transaction_date) NOT BETWEEN '+cstr(start_month)+' and '+cstr(start_month+5) - so = sql("select sum(net_total) from `tab%s` where territory = '%s' and %s and %s" % (based_on, r[col_idx['ID']],condition,query)) - so = so and flt(so[0][0]) or 0 - r.append(so) - query = '' - - # =============== Quarterly Report ============== - elif period == 'Quarterly': - query = '' - length_1 = (len(month_name) - start_month + 1) / 3; #this gives the total no. of times we need to iterate for quarter - val = length_1 % 4; - for i in range(length_1): - value = 3*i + val; - query +='SUM(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN net_total ELSE NULL END),' - length_2 = (start_month - 1) / 3; #this gives the total no. of times we need to iterate for quarter (this is required only if fiscal year starts from april) - for i in range(length_2): - query += 'SUM(CASE WHEN MONTH(transaction_date) BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN net_total ELSE NULL END)'; - - target = sql("select sum(target_amount) from `tabTarget Detail` where parent = %s and parenttype= 'Territory' and fiscal_year = %s",(r[col_idx['ID']],fiscal_year)) - target = target and flt(flt(target[0][0])/4) or 0 - - - so = sql("SELECT %s from `tab%s` where territory ='%s' and %s " %(query,based_on,r[col_idx['ID']],condition)) - i = 0 - length_l = 0 - for c in columns: - if length_l == 0: - r.append(target) - length_l += 1 - else: - so_total = so and flt(so[0][i]) or 0 - r.append(so_total) - i +=1 - length_l = 0 - - # ================ Monthly Report =============== - elif period == 'Monthly': - query ='' - target = sql("select sum(target_amount) from `tabTarget Detail` where parent = %s and parenttype= 'Territory' and fiscal_year = %s",(r[col_idx['ID']],fiscal_year)) - #msgprint(target) - target = target and flt(flt(target[0][0])/12) or 0 - - - # for loop is required twice coz fiscal year starts from April (this will also work if fiscal year starts in January) - for i in range(start_month-1,len(month_name)): - query += 'SUM(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END),' - - for i in range(start_month-1): - if i != (start_month-2): - query += 'SUM(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END),' - else: - query += 'SUM(CASE WHEN MONTH(transaction_date) = '+cstr(i+1)+' THEN net_total ELSE NULL END)'; - so = sql("SELECT %s from `tab%s` where territory ='%s' and %s " %(query,based_on,r[col_idx['ID']],condition)) - - i = 0 - length_l = 0 - for c in columns: - if length_l == 0: - r.append(target) - length_l += 1 - else: - so_total = so and flt(so[0][i]) or 0 - r.append(so_total) - i +=1 - length_l = 0 \ No newline at end of file diff --git a/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.txt b/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.txt deleted file mode 100644 index 011b957818..0000000000 --- a/selling/search_criteria/territory_sales___variance_report/territory_sales___variance_report.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Territory\u0001State':'','Territory\u0001Based On':'Sales Order','Territory\u0001Fiscal Year':'2009-2010','Territory\u0001Company':'Alpha Company','Territory\u0001Period':'Quarterly'}", - "doctype": "Search Criteria", - "doc_type": "Territory", - "name": "__common__", - "sort_by": "ID", - "page_len": 50, - "criteria_name": "Territory Sales - Variance Report", - "columns": "Territory\u0001ID" - }, - { - "name": "territory_sales_-_variance_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/total_target_variance_report/__init__.py b/selling/search_criteria/total_target_variance_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/total_target_variance_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/total_target_variance_report/total_target_variance_report.js b/selling/search_criteria/total_target_variance_report/total_target_variance_report.js deleted file mode 100644 index 59a9b89871..0000000000 --- a/selling/search_criteria/total_target_variance_report/total_target_variance_report.js +++ /dev/null @@ -1,28 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Territory'+NEWLINE+'Sales Person',report_default:'Territory',ignore : 1,parent:'Target Detail', single_select :1}); - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Target Detail', single_select :1}); - this.add_filter({fieldname:'under', label:'Under',fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice',report_default:'Sales Order',ignore : 1, parent:'Target Detail', single_select :1}); - this.add_filter({fieldname : 'target_on', label:'Target On', fieldtype:'Select', options:'Quantity'+NEWLINE+'Amount',report_default:'Quantity',ignore : 1,parent:'Target Detail', single_select :1}); -} -report.aftertableprint = function(t) { - $yt(t,'*',1,{whiteSpace:'pre'}); -} -this.mytabs.items['Select Columns'].hide(); \ No newline at end of file diff --git a/selling/search_criteria/total_target_variance_report/total_target_variance_report.py b/selling/search_criteria/total_target_variance_report/total_target_variance_report.py deleted file mode 100644 index 954f9f2fc3..0000000000 --- a/selling/search_criteria/total_target_variance_report/total_target_variance_report.py +++ /dev/null @@ -1,223 +0,0 @@ -# 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 . - -# validate Filters -from __future__ import unicode_literals -flt_dict = {'fiscal_year': 'Fiscal Year', 'period': 'Period', 'under' : 'Under', 'based_on' : 'Based On','target_on':'Target On'} -for f in flt_dict: - if not filter_values.get(f): - msgprint("Please Select " + cstr(flt_dict[f])) - raise Exception - -# Get Values from fliters -fiscal_year = filter_values.get('fiscal_year') -period = filter_values.get('period') -under = filter_values.get('under') -if under == 'Sales Invoice': under = 'Sales Invoice' -based_on = filter_values.get('based_on') -target_on = filter_values.get('target_on') - -#add distributed id field -col = [] -col.append([based_on,'Date','150px','']) -if target_on == 'Quantity': - col.append(['Target Quantity','Currency','150px','']) -else: - col.append(['Target Amount','Currency','150px','']) -col.append(['Distribution Id','Date','150px','']) - -for c in col: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - - col_idx[c[0]] = len(colnames)-1 - -def make_child_lst(based_on,name): - rg = sql("select lft, rgt from `tab%s` where name = '%s'"%(based_on,name)) - ch_name = sql("select name from `tab%s` where lft between %d and %d"%(based_on,int(rg[0][0]),int(rg[0][1]))) - chl ='(' - flag = 1 - for c in ch_name: - if flag == 1: - chl += "'%s'"%c[0] - flag = 2 - else: - chl +=",'%s'"%c[0] - - chl +=")" - return chl - - -def get_target(target_on,based_on,fiscal_year,r): - - if target_on == 'Quantity': - q1 = "select t1.target_qty " - q2 = "select sum(t1.target_qty)" - if target_on == 'Amount': - q1 = "select t1.target_amount " - q2 = "select sum(t1.target_amount)" - - cond1 =" t1.fiscal_year ='%s' and t1.parent=t2.name and t1.parenttype = '%s' and t1.docstatus !=2" - #---------------------------------------------------------------- - q = "select t1.name from `tabTarget Detail` t1, `tab%s` t2 where "+cond1+" and t2.name = '%s'" - ch = sql(q%(based_on,fiscal_year,based_on,r)) - - return {'q1':q1,'q2':q2,'cond1':cond1,'ch':ch} - -for r in res: - - tt = get_target(target_on,based_on,fiscal_year,r[0].strip()) - - if tt['ch']: - - cond2 = " ifnull(t1.item_group,'')='' and" - qur = tt['q1']+"from `tabTarget Detail` t1, `tab%s` t2 where "+cond2+tt['cond1']+" and t2.name = '%s'" - ret_amt = sql(qur%(based_on,fiscal_year,based_on,r[0].strip())) - - #---------------------------------------------------------------- - if not ret_amt: - qur = tt['q2']+"from `tabTarget Detail` t1, `tab%s` t2 where "+tt['cond1']+" and t2.name = '%s'" - ret_amt = sql(qur%(based_on,fiscal_year,based_on,r[0].strip())) - - #---------------------------------------------------------------- - else: - node_lst = make_child_lst(based_on,r[0].strip()) - qur = tt['q2']+"from `tabTarget Detail` t1, `tab%s` t2 where "+tt['cond1']+" and t2.name in %s" - ret_amt = sql(qur%(based_on,fiscal_year,based_on,node_lst)) - - #---------------------------------------------------------------- - ret_dis_id = sql("select distribution_id from `tab%s` where name = '%s'"%(based_on,r[0].strip())) - - target_amt = ret_amt and flt(ret_amt[0][0]) or 0 - dis_id = ret_dis_id and ret_dis_id[0][0] or '' - - r.append(target_amt) - r.append(dis_id) - - -# Set required field names -based_on_fn = (based_on == 'Territory') and 'territory' or 'sales_person' - -date_fn = (under == 'Sales Order' ) and 'transaction_date' or 'posting_date' - -mon_list = [] - -data = {'start_date':0, 'end_date':1} - -def make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx): - count = 1 - if period == 'Quarterly' or period == 'Half Yearly' or period == 'Annual': mon_list.append([str(start_date)]) - for m in range(12): - # get last date - last_date = str(sql("select LAST_DAY('%s')" % start_date)[0][0]) - - # make mon_list for Monthly Period - if period == 'Monthly' : - mon_list.append([start_date, last_date]) - # add months as Column names - month_name = sql("select MONTHNAME('%s')" % start_date)[0][0] - append_colnames(str(month_name)[:3], colnames, coltypes, colwidths, coloptions, col_idx) - - # get start date - start_date = str(sql("select DATE_ADD('%s',INTERVAL 1 DAY)" % last_date)[0][0]) - - # make mon_list for Quaterly Period - if period == 'Quarterly' and count % 3 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column names - append_colnames('Q '+ str(count / 3), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Half Yearly Period - if period == 'Half Yearly' and count % 6 == 0 : - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('H'+str(count / 6), colnames, coltypes, colwidths, coloptions, col_idx) - if count != 12: mon_list.append([start_date]) - - # make mon_list for Annual Period - if period == 'Annual' and count % 12 == 0: - mon_list[len(mon_list) - 1 ].append(last_date) - # add Column Names - append_colnames('', colnames, coltypes, colwidths, coloptions, col_idx) - count = count +1 - -def append_colnames(name, colnames, coltypes, colwidths, coloptions, col_idx): - col = ['Target', 'Actual', 'Variance'] - for c in col: - n = str(name) and ' (' + str(name) +')' or '' - colnames.append(str(c) + n) - coltypes.append('Currency') - colwidths.append('150px') - coloptions.append('') - col_idx[str(c) + n ] = len(colnames) - 1 - - - -# make default columns -#coltypes[col_idx[based_on]] = 'Link' -#coloptions[col_idx[based_on]]= based_on - -# get start date -start_date = webnotes.conn.get_value('Fiscal Year', fiscal_year, 'year_start_date') -if not start_date: - msgprint("Please Define Year Start Date for Fiscal Year " + str(fiscal_year)) - raise Exception -start_date = start_date.strftime('%Y-%m-%d') - -# make month list and columns -make_month_list(append_colnames, start_date, mon_list, period, colnames, coltypes, colwidths, coloptions, col_idx) - - -bc_obj = get_obj('Budget Control') -for r in res: - count = 0 - - for idx in range(3, len(colnames), 3): - cidx = 2 - - # ================= Calculate Target ========================================== - r.append(bc_obj.get_monthly_budget( r[cidx], fiscal_year, mon_list[count][data['start_date']], mon_list[count][data['end_date']], r[cidx-1])) - - #================== Actual Amount ============================================= - actual = 0 - - ch = make_child_lst(based_on,r[0].strip()) - - #---------------------------------------------------------- - if target_on == "Quantity": - if based_on == "Territory": - actual = sql("select sum(ifnull(t2.qty,0)) from `tab%s` t1, `tab%s Detail` t2 where t2.parenttype = '%s' and t2.parent = t1.name and t1.%s in %s and t1.docstatus = 1 and t1.%s between '%s' and '%s'" % (under, (under == 'Sales Invoice') and 'RV' or under, under, based_on_fn, ch, date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - - elif based_on == 'Sales Person': - actual = sql("select sum(ifnull(t2.qty,0) * ifnull(t3.allocated_percentage,0) / 100) from `tab%s` t1, `tab%s Detail` t2, `tabSales Team` t3 where t2.parent = t1.name and t3.parent = t1.name and t3.%s in %s and t1.docstatus != 2 and t1.docstatus = 1 and t1.%s between '%s' and '%s' "%(under, (under == 'Sales Invoice') and 'RV' or under, based_on_fn, ch, date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - - #---------------------------------------------------------- - if target_on == "Amount": - if based_on == 'Territory': - - actual = sql("select sum(ifnull(net_total,0)) from `tab%s` where %s in %s and docstatus = 1 and %s between '%s' and '%s' " % (under, based_on_fn, ch, date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - - elif based_on == 'Sales Person': - actual = sql("select sum(ifnull(t2.allocated_amount,0)) from `tab%s` t1, `tabSales Team` t2 where t2.%s in %s and t2.parenttype='%s' and t1.docstatus != 2 and t2.parent = t1.name and t1.%s between '%s' and '%s'"%(under, based_on_fn, ch, under, date_fn, mon_list[count][data['start_date']], mon_list[count][data['end_date']])) - #---------------------------------------------------------- - actual = flt(actual[0][0]) - r.append(actual) - # ================ Variance =================================================== - r.append(r[idx] - r[idx + 1]) - count = count +1 \ No newline at end of file diff --git a/selling/search_criteria/total_target_variance_report/total_target_variance_report.sql b/selling/search_criteria/total_target_variance_report/total_target_variance_report.sql deleted file mode 100644 index ebd7ae845f..0000000000 --- a/selling/search_criteria/total_target_variance_report/total_target_variance_report.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT CONCAT(REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name FROM `tab%(based_on)s` AS node,`tab%(based_on)s` AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt AND node.docstatus !=2 GROUP BY node.name ORDER BY node.lft \ No newline at end of file diff --git a/selling/search_criteria/total_target_variance_report/total_target_variance_report.txt b/selling/search_criteria/total_target_variance_report/total_target_variance_report.txt deleted file mode 100644 index cf1484b479..0000000000 --- a/selling/search_criteria/total_target_variance_report/total_target_variance_report.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "parent_doc_type": "Sales Person", - "module": "Selling", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Person\u0001Saved':1,'Sales Person\u0001Submitted':1,'Sales Person\u0001Country':'','Sales Person\u0001State':'','Target Detail\u0001Fiscal Year':''}", - "doc_type": "Target Detail", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabSales Person`.`name`", - "page_len": 50, - "criteria_name": "Total Target Variance Report", - "columns": "Sales Person\u0001ID,Sales Person\u0001Owner,Sales Person\u0001Sales Person,Sales Person\u0001Country,Sales Person\u0001State,Sales Person\u0001lft,Sales Person\u0001rgt,Target Detail\u0001Item Group,Target Detail\u0001Fiscal Year,Target Detail\u0001Target Amount" - }, - { - "name": "total_target_variance_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/selling/search_criteria/variance_report/__init__.py b/selling/search_criteria/variance_report/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/selling/search_criteria/variance_report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/selling/search_criteria/variance_report/variance_report.js b/selling/search_criteria/variance_report/variance_report.js deleted file mode 100644 index b5fe3fb8fd..0000000000 --- a/selling/search_criteria/variance_report/variance_report.js +++ /dev/null @@ -1,28 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Territory'+NEWLINE+'Sales Person'+NEWLINE+'Sales Partner',report_default:'Territory',ignore : 1,parent:'Target Detail'}); - this.add_filter({fieldname:'fiscal_year', label:'Fiscal Year', fieldtype:'Link', options:'Fiscal Year', report_default:sys_defaults.fiscal_year, ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'company', label:'Company', fieldtype:'Link', options:'Company',report_default:sys_defaults.company, ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'period', label:'Period', fieldtype:'Select', options:'Monthly'+NEWLINE+'Quarterly'+NEWLINE+'Half Yearly'+NEWLINE+'Annual',report_default:'Quarterly',ignore : 1, parent:'Target Detail'}); -// this.add_filter({fieldname:'item_group', label:'Item Group', fieldtype:'Link', options:'Item Group', ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'group_by', label:'Group By', fieldtype:'Select', options:NEWLINE+'Item Group',ignore : 1, parent:'Target Detail'}); - this.add_filter({fieldname:'under', label:'Under',fieldtype:'Select', options:'Sales Order'+NEWLINE+'Delivery Note'+NEWLINE+'Sales Invoice',report_default:'Sales Order',ignore : 1, parent:'Target Detail'}); -} - -//this.mytabs.items['Select Columns'].hide() diff --git a/selling/search_criteria/variance_report/variance_report.py b/selling/search_criteria/variance_report/variance_report.py deleted file mode 100644 index 7b6856c157..0000000000 --- a/selling/search_criteria/variance_report/variance_report.py +++ /dev/null @@ -1,520 +0,0 @@ -# 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 . - -# Add columns -# ----------- -from __future__ import unicode_literals -row_list = [['ID','Data','150px','']] - -for r in row_list: - colnames.append(r[0]) - coltypes.append(r[1]) - colwidths.append(r[2]) - coloptions.append(r[3]) - col_idx[r[0]] = len(colnames)-1 - -if not filter_values.get('fiscal_year'): - msgprint("Please Select Fiscal Year") - raise Exception -elif not filter_values.get('period'): - msgprint("Please Select Period") - raise Exception -elif not filter_values.get('based_on'): - msgprint("Please Select the Criteria on which you want your report to be based") - raise Exception -elif not filter_values.get('group_by') and filter_values.get('item_group'): - msgprint("Item Group cannot be selected if Group By is not Item Group") - raise Exception - -fiscal_year = filter_values.get('fiscal_year') -period = filter_values.get('period') -based_on = filter_values.get('based_on') -group_by = filter_values.get('group_by') -item_group = filter_values.get('item_group') -msgprint(item_group) -company = filter_values.get('company') -under = filter_values.get('under') - -#if filter_values.get('item_group'): -# itm_grp = filter_values.get('item_group') - -if based_on == 'Territory': - based = 'territory' -elif based_on == 'Sales Person': - based = 'sales_person' -elif based_on == 'Sales Partner': - based = 'sales_partner' - - -if under == 'Sales Invoice': - under_detail = 'RV' - dt = 'posting_date' -else: - under_detail = under - dt = "transaction_date" - -# get fiscal year start date and start month -year_start_date = sql("select year_start_date,MONTH(year_start_date) from `tabFiscal Year` where name = %s",fiscal_year) -start_date = year_start_date and year_start_date[0][0] or '' -start_month = year_start_date and year_start_date[0][1] or '' -month_name = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] - -# Add columns based on period -# -------------------------------- -columns = [] -if group_by == 'Item Group': - columns.append(['Item Group','Data','120px','']) -# ================ Annual ====================== -if period == 'Annual': - columns.append(['Target','Currency','120px','']) - columns.append(['Actual','Currency','120px','']) - -# =========== Half Yearly ====================== -elif period == 'Half Yearly': - columns.append(['Target (H1)','Currency','120px','']) # first half - columns.append(['Actual (H1)','Currency','120px','']) # first half - columns.append(['Target (H2)','Currency','120px','']) - columns.append(['Actual (H2)','Currency','120px','']) - -# ================ Quarterly =================== -elif period == 'Quarterly': - length_1 = (len(month_name) - start_month + 1) / 3 #this gives the total no. of times we need to iterate for quarter - val = length_1 % 4 - q_no = 1 - for i in range(length_1): - value = 3*i + val - columns.append(['Target (Q'+cstr(q_no)+')','Currency','120px','']) - columns.append(['Actual (Q'+cstr(q_no)+')','Currency','120px','']) - q_no += 1 - length_2 = (start_month - 1) / 3 #this gives the total no. of times we need to iterate for quarter (this is required only if fiscal year starts from april) - for i in range(length_2): - columns.append(['Target (Q'+cstr(q_no)+')','Currency','120px','']) - columns.append(['Actual (Q'+cstr(q_no)+')','Currency','120px','']) - q_no += 1; - -# =============== Monthly ====================== -elif period == 'Monthly': - for i in range(start_month-1,len(month_name)): - columns.append(['Target ('+month_name[i]+')','Currency','120px','']) - columns.append(['Actual ('+month_name[i]+')','Currency','120px','']) - - for i in range(start_month-1): - columns.append(['Target('+month_name[i]+')','Currency','120px','']) - columns.append(['Actual ('+month_name[i]+')','Currency','120px','']) - -for c in columns: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 - -out = [] -if company: - condition = ' fiscal_year = "'+fiscal_year+'" and company = "'+company+'"' -else: - condition = ' fiscal_year = "'+fiscal_year+'"' - -#=================== function for fetching allocated percentage in Distribution id according to period============= -def get_budget_distribution(period,dist_id,fiscal_year): - query = '' - id1 = 1 - if period == 'Half Yearly': - id2 = 6 - for i in range(2): - query += 'SUM(CASE WHEN t2.idx BETWEEN '+str(id1)+' AND '+str(id2)+' THEN t2.percentage_allocation ELSE NULL END)' - id1 += 6 - id2 += 6 - if i < 1 : - query += ',' - - elif period == 'Quarterly': - id2 = 3 - for i in range(4): - query += 'SUM(CASE WHEN t2.idx BETWEEN '+str(id1)+' AND '+str(id2)+' THEN t2.percentage_allocation ELSE NULL END)' - id1 += 3 - id2 += 3 - if i < 3 : - query += ',' - - elif period == 'Monthly': - for i in range(12): - query += 'SUM(CASE WHEN t2.idx ='+str(id1)+' THEN t2.percentage_allocation ELSE NULL END)' - id1 += 1 - if i < 11 : - query += ',' - -# msgprint(query) - - # Main Query - dist = sql("select %s from `tabBudget Distribution` t1, `tabBudget Distribution Detail` t2 where t1.name = '%s' and t2.parent = t1.name and t1.fiscal_year = '%s'"%(query,dist_id,fiscal_year)) - dist = dist and dist[0] or 0 -# msgprint(dist) - bug = [] - for i in dist: - i = i and float(i) or 0 - bug.append(i) -# msgprint(bug) - return bug - - -#============ function for appending target amt and actual amt in a proper order ======================= -def appending_func(ran,tl,lst,actual,flt): - - c = 2 - for i in range(ran): - #==== for each itemgroup their actual amt is appended/inserted between target amt - if tl == 0: - lst.insert(c,actual and flt(actual[0][i]) or 0) - #======== here actual amt is appended/inserted b/w target amt for a particular territory/sales person/sales partner only if target is not zero - elif tl == 1: -# msgprint(lst) - lst.insert(c,actual and flt(actual[0][i]) or 0) - c += 2 - return lst - -def get_target(tar_det,group_by,period,fiscal_year,rng,r,get_budget_distribution,flt): - - grp,lst = [],[] - list_range,tl = 0,0 - if group_by == 'Item Group': - for i in tar_det: - if i[0] != '': - igrp = [i[0]] - if i[2]: - dist_id = i[2] - dist = get_budget_distribution(period,dist_id,fiscal_year) - for d in dist: - t = flt(flt(flt(i[1]) * flt(d))/ 100) - igrp.append(t) - else: - t = i and flt(i[1]/rng) or 0 - for i in range(rng): - igrp.append(t) - - grp.append(igrp) - list_range +=1 - lst = [1,grp,list_range] - - #============== Total target(on basis of whole target ) ============ - else: - for i in tar_det: - if i[0] == '': - if i[2]: - dist_id = i[2] - dist = get_budget_distribution(period,dist_id,fiscal_year) - for d in dist: - t = flt((flt(i[1]) * flt(d))/ 100) - r.append(t) - else: - tot_target = i and flt(i[1]/rng) or 0 - for i in range(rng): - r.append(tot_target) - tl = 1 - lst = [0,r,tl] - return lst -#============ report display function ===================== -for r in res: - query = '' - grp=[] - list_range, count, ap, tot_target, tl = 0,0,0,0,0 - - #============= ANNUAL REPORT =================== - if period == 'Annual': - tar_det = sql("select item_group, target_amount, distribution_id from `tabTarget Detail` where parent = %s and parenttype = %s and fiscal_year = %s",(r[col_idx['ID']],based_on,fiscal_year)) -# msgprint(tar_det) - - #================ Target based on individual item group ============== - if group_by == 'Item Group': - for i in tar_det: - if i[0] != '': - grp_target = i and flt(i[1]) or 0 - igrp = [i[0],grp_target] - grp.append(igrp) -# msgprint(grp) - list_range +=1 - count = 3 - - #============== Total target(will be displayed only if target is specified by the user) ============ - else: - for i in tar_det: - # ======= here target is considered and not sum of target of item groups - if i[0] == '': - tot_target = tar_det and flt(i[1]) or 0 -# msgprint(tot_target) - - #================== Actual Amount ============================================= - if based_on == 'Territory' or based_on == 'Sales Partner': - - if group_by =='Item Group': - - for i in grp: - item_group = i[0] - actual = sql("select sum(t2.amount) from `tab%s` t1, `tab%s Detail` t2, `tabItem` t3 where t2.parent = t1.name and t1.%s = '%s' and t3.name = t2.item_code and t3.item_group = '%s' and t1.docstatus = 1 and t1.docstatus != 2 and %s"%(under,under_detail,based,r[col_idx['ID']],item_group,condition)) - msgprint(actual) - actual = actual and flt(actual[0][0]) or 0 - i.append(actual) - - else: - actual = sql("select sum(net_total) from `tab%s` where %s = '%s' and docstatus = 1 and %s" % (under, based, r[col_idx['ID']],condition)) - actual = actual and flt(actual[0][0]) or 0 - - elif based_on == 'Sales Person': - if group_by =='Item Group': - for i in grp: - item_group = i[0] - actual = sql("select sum(t2.amount) from `tab%s` t1, `tab%s Detail` t2, `tabSales Team` t3, `tabItem` t4 where t2.parent = t1.name and t3.parent = t1.name and t3.%s = '%s' and t4.name = t2.item_code and t4.item_group = '%s' and t1.docstatus != 2 and t1.docstatus = 1 and %s"%(under,under_detail,based,r[col_idx['ID']],item_group,condition)) - actual = actual and flt(actual[0][0]) or 0 -# msgprint(actual) - i.append(actual) - - else: - actual = sql("select sum(t1.net_total) from `tab%s` t1, `tabSales Team` t2 where t2.%s = '%s' and t2.parenttype='%s' and t1.docstatus != 2 and t2.parent = t1.name and %s"%(under,based,r[col_idx['ID']],under,condition)) - actual = actual and flt(actual[0][0]) or 0 -# msgprint(actual) - - # ================= Half Yearly Report =============== - elif period == 'Half Yearly': - tl = 0 - grp_target = [] - - tar_det = sql("select item_group, target_amount, distribution_id from `tabTarget Detail` where parent = %s and parenttype = %s and fiscal_year = %s",(r[col_idx['ID']],based_on,fiscal_year)) -# msgprint(tar_det) - - tar = get_target(tar_det,group_by,period,fiscal_year,2,r,get_budget_distribution,flt) - if tar[0] == 1: - grp = tar[1] - list_range = tar[2] - count = 5 - else: - r = tar[1] - tl = tar[2] - - #============= Actual Amount====================== - if group_by == 'Item Group': - # first half - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN t2.amount ELSE NULL END),' - # second half - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN t2.amount ELSE NULL END)'; - - elif based_on != 'Sales Person': - # first half - query += 'SUM(CASE WHEN MONTH('+dt+') BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END),' - # second half - query += 'SUM(CASE WHEN MONTH('+dt+') NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN net_total ELSE NULL END)'; - - else: - # first half - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN t1.net_total ELSE NULL END),' - # second half - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') NOT BETWEEN '+cstr(start_month)+' AND '+cstr(start_month+5)+' THEN t1.net_total ELSE NULL END)'; - - #=========== Main Query =============== - if based_on == 'Territory' or based_on == 'Sales Partner': - - if group_by =='Item Group': - for i in grp: - item_group = i[0] - actual = sql("select %s from `tab%s` t1, `tab%s Detail` t2, `tabItem` t3 where t2.parent = t1.name and t1.%s = '%s' and t3.name = t2.item_code and t3.item_group = '%s' and t1.docstatus = 1 and t1.docstatus != 2 and %s"%(query,under,under_detail,based,r[col_idx['ID']],item_group,condition)) -# msgprint(actual) - i = appending_func(2,tl,i,actual,flt) - - else: - actual = sql("select %s from `tab%s` where %s = '%s' and docstatus = 1 and %s" % (query,under, based, r[col_idx['ID']],condition)) -# msgprint(actual) - - elif based_on == 'Sales Person': - if group_by =='Item Group': - for i in grp: - item_group = i[0] - actual = sql("select %s from `tab%s` t1, `tab%s Detail` t2, `tabSales Team` t3, `tabItem` t4 where t2.parent = t1.name and t3.parent = t1.name and t3.%s = '%s' and t4.name = t2.item_code and t4.item_group = '%s' and t1.docstatus != 2 and t1.docstatus = 1 and %s"%(query,under,under_detail,based,r[col_idx['ID']],item_group,condition)) -# msgprint(actual) - i = appending_func(2,tl,i,actual,flt) - else: - actual = sql("select %s from `tab%s` t1, `tabSales Team` t2 where t2.%s = '%s' and t2.parenttype='%s' and t1.docstatus != 2 and t2.parent = t1.name and %s"%(query,under,based,r[col_idx['ID']],under,condition)) -# msgprint(actual) - - if tl == 1: - r = appending_func(2,tl,r,actual,flt) -# msgprint(r) - - #============== Quarterly Report ========================= - elif period == 'Quarterly': - tl = 0 - grp_target = [] - tar_det = sql("select item_group, target_amount, distribution_id from `tabTarget Detail` where parent = %s and parenttype = %s and fiscal_year = %s",(r[col_idx['ID']],based_on,fiscal_year)) - - tar = get_target(tar_det,group_by,period,fiscal_year,4,r,get_budget_distribution,flt) - if tar[0] == 1: - grp = tar[1] - list_range = tar[2] - count = 9 - else: - r = tar[1] - tl = tar[2] - - #======= Actual Amt ================== - length_1 = (len(month_name) - start_month + 1) / 3; #this gives the total no. of times we need to iterate for quarter - val = length_1 % 4; - for i in range(length_1): - value = 3*i + val; - - if group_by == 'Item Group': - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN t2.amount ELSE NULL END),' - - elif based_on != 'Sales Person': - query += 'SUM(CASE WHEN MONTH('+dt+') BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN net_total ELSE NULL END),' - - else: - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') BETWEEN '+cstr(value+1)+' AND '+cstr(value+3)+' THEN t1.net_total ELSE NULL END),' - - length_2 = (start_month - 1) / 3; #this gives the total no. of times we need to iterate for quarter (this is required only if fiscal year starts from april) - for i in range(length_2): - if group_by == 'Item Group': - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN t2.amount ELSE NULL END)'; - - elif based_on != 'Sales Person': - query += 'SUM(CASE WHEN MONTH('+dt+') BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN net_total ELSE NULL END)'; - - else: - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') BETWEEN '+cstr(3*i+1)+' AND '+cstr(3*i+3)+' THEN t1.net_total ELSE NULL END)'; - - #=========== Main Query =============== - if based_on == 'Territory' or based_on == 'Sales Partner': - - if group_by =='Item Group': - for i in grp: - item_group = i[0] - actual = sql("select %s from `tab%s` t1, `tab%s Detail` t2, `tabItem` t3 where t2.parent = t1.name and t1.%s = '%s' and t3.name = t2.item_code and t3.item_group = '%s' and t1.docstatus = 1 and t1.docstatus != 2 and %s"%(query,under,under_detail,based,r[col_idx['ID']],item_group,condition)) -# msgprint(actual) - #================common function - i = appending_func(4,tl,i,actual,flt) - - else: - actual = sql("select %s from `tab%s` where %s = '%s' and docstatus = 1 and %s" % (query,under, based, r[col_idx['ID']],condition)) -# msgprint(actual) - - elif based_on == 'Sales Person': - if group_by =='Item Group': - for i in grp: - item_group = i[0] - actual = sql("select %s from `tab%s` t1, `tab%s Detail` t2, `tabSales Team` t3, `tabItem` t4 where t2.parent = t1.name and t3.parent = t1.name and t3.%s = '%s' and t4.name = t2.item_code and t4.item_group = '%s' and t1.docstatus != 2 and t1.docstatus = 1 and %s"%(query,under,under_detail,based,r[col_idx['ID']],item_group,condition)) -# msgprint(actual) - i = appending_func(4,tl,i,actual,flt) - else: - actual = sql("select %s from `tab%s` t1, `tabSales Team` t2 where t2.%s = '%s' and t2.parenttype='%s' and t1.docstatus != 2 and t2.parent = t1.name and %s"%(query,under,based,r[col_idx['ID']],under,condition)) -# msgprint(actual) - - if tl == 1: - r = appending_func(4,tl,r,actual,flt) -# msgprint(r) - - #================ Monthly Report =========================== - elif period == 'Monthly': - tl = 0 - grp_target = [] - tar_det = sql("select item_group, target_amount, distribution_id from `tabTarget Detail` where parent = %s and parenttype = %s and fiscal_year = %s",(r[col_idx['ID']],based_on,fiscal_year)) - - tar = get_target(tar_det,group_by,period,fiscal_year,12,r,get_budget_distribution,flt) - if tar[0] == 1: - grp = tar[1] - list_range = tar[2] - count = 25 - else: - r = tar[1] - tl = tar[2] - - #======= Actual Amt ================== - # for loop is required twice coz fiscal year starts from April (this will also work if fiscal year starts in January) - for i in range(start_month-1,len(month_name)): - if group_by == 'Item Group': - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') = '+cstr(i+1)+' THEN t2.amount ELSE NULL END),' - - elif based_on != 'Sales Person': - query += 'SUM(CASE WHEN MONTH('+dt+') = '+cstr(i+1)+' THEN net_total ELSE NULL END),' - - else: - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') = '+cstr(i+1)+' THEN t1.net_total ELSE NULL END),' - - for i in range(start_month-1): - if i != (start_month-1): - if group_by == 'Item Group': - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') = '+cstr(i+1)+' THEN t2.amount ELSE NULL END)' - - elif based_on != 'Sales Person': - query += 'SUM(CASE WHEN MONTH('+dt+') = '+cstr(i+1)+' THEN net_total ELSE NULL END)' - - else: - query += 'SUM(CASE WHEN MONTH(t1.'+dt+') = '+cstr(i+1)+' THEN t1.net_total ELSE NULL END)' - - if i < (start_month -2): - query += ',' - - #=========== Main Query =============== - if based_on == 'Territory' or based_on == 'Sales Partner': - - if group_by =='Item Group': - for i in grp: - item_group = i[0] - actual = sql("select %s from `tab%s` t1, `tab%s Detail` t2, `tabItem` t3 where t2.parent = t1.name and t1.%s = '%s' and t3.name = t2.item_code and t3.item_group = '%s' and t1.docstatus = 1 and t1.docstatus != 2 and %s"%(query,under,under_detail,based,r[col_idx['ID']],item_group,condition)) -# msgprint(actual) - #===============common function===================== - i = appending_func(12,tl,i,actual,flt) - - else: - actual = sql("select %s from `tab%s` where %s = '%s' and docstatus = 1 and %s" % (query,under, based, r[col_idx['ID']],condition)) -# msgprint(actual) - - elif based_on == 'Sales Person': - if group_by =='Item Group': - for i in grp: - item_group = i[0] - actual = sql("select %s from `tab%s` t1, `tab%s Detail` t2, `tabSales Team` t3, `tabItem` t4 where t2.parent = t1.name and t3.parent = t1.name and t3.%s = '%s' and t4.name = t2.item_code and t4.item_group = '%s' and t1.docstatus != 2 and t1.docstatus = 1 and %s"%(query,under,under_detail,based,r[col_idx['ID']],item_group,condition)) -# msgprint(actual) - i = appending_func(12,tl,i,actual,flt) - else: - actual = sql("select %s from `tab%s` t1, `tabSales Team` t2 where t2.%s = '%s' and t2.parenttype='%s' and t1.docstatus != 2 and t2.parent = t1.name and %s"%(query,under,based,r[col_idx['ID']],under,condition)) -# msgprint(actual) - - if tl == 1: - r = appending_func(12,tl,r,actual,flt) -# msgprint(r) - -#-------------DISPLAY OF TARGET vs ACTUAL ON BASIS OF TOTAL TARGET / ITEM GROUP - - if group_by == 'Item Group': - for col in range(len(colnames)-1): # this would make all first row blank. just for look - r.append('') - - for des in range(list_range): - if ap == 0: - out.append(r) - ap = 1 - t_row = ['' for i in range(len(colnames))] - - for v in range(count): - t_row[col_idx[colnames[v+1]]] = grp[des][v] -# msgprint(t_row) - out.append(t_row) - - elif tot_target != 0 and period =='Annual': - r.append(tot_target) - r.append(actual) - out.append(r) - tot_target = 0 - - elif tl == 1: - out.append(r) diff --git a/selling/search_criteria/variance_report/variance_report.sql b/selling/search_criteria/variance_report/variance_report.sql deleted file mode 100644 index 89becb7f06..0000000000 --- a/selling/search_criteria/variance_report/variance_report.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT DISTINCT t1.`name` FROM `tab%(based_on)s` t1, `tabTarget Detail` t2 WHERE t2.parent = t1.name and (t2.target_amount != 0 or t2.target_amount is not null) \ No newline at end of file diff --git a/selling/search_criteria/variance_report/variance_report.txt b/selling/search_criteria/variance_report/variance_report.txt deleted file mode 100644 index e1411917a5..0000000000 --- a/selling/search_criteria/variance_report/variance_report.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "parent_doc_type": "Territory", - "module": "Selling", - "doctype": "Search Criteria", - "sort_order": "DESC", - "filters": "{'Territory\u0001State':'','Target Detail\u0001Fiscal Year':'2009-2010','Target Detail\u0001Based On':'Territory','Target Detail\u0001Company':'Alpha Company','Target Detail\u0001Period':'Quarterly','Target Detail\u0001Under':'Sales Order'}", - "standard": "Yes", - "doc_type": "Target Detail", - "name": "__common__", - "sort_by": "ID", - "page_len": 50, - "criteria_name": "Variance Report" - }, - { - "name": "variance_report", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/support/search_criteria/__init__.py b/support/search_criteria/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/support/search_criteria/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/support/search_criteria/amc_summary/__init__.py b/support/search_criteria/amc_summary/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/support/search_criteria/amc_summary/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/support/search_criteria/amc_summary/amc_summary.js b/support/search_criteria/amc_summary/amc_summary.js deleted file mode 100644 index 0ce3619c69..0000000000 --- a/support/search_criteria/amc_summary/amc_summary.js +++ /dev/null @@ -1,25 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.mytabs.items['Select Columns'].hide(); - this.mytabs.items['More Filters'].hide() - this.filter_fields_dict['Serial No'+FILTER_SEP +'Territory'].df.filter_hide = 0; - this.filter_fields_dict['Serial No'+FILTER_SEP +'Item Group'].df.filter_hide = 0; - this.filter_fields_dict['Serial No'+FILTER_SEP +'Territory'].df.in_first_page = 1; - this.filter_fields_dict['Serial No'+FILTER_SEP +'Item Group'].df.in_first_page = 1; -} \ No newline at end of file diff --git a/support/search_criteria/amc_summary/amc_summary.py b/support/search_criteria/amc_summary/amc_summary.py deleted file mode 100644 index 926da017a3..0000000000 --- a/support/search_criteria/amc_summary/amc_summary.py +++ /dev/null @@ -1,87 +0,0 @@ -# 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 . - -# ADD NEW COLUMNS -from __future__ import unicode_literals -row_list = [['Item Group','Data','150px',''], - ['Out of AMC','Int','150px',''], - ['Under AMC','Int','150px',''], - ['Out of Warranty','Int','150px',''], - ['Under Warranty','Int','150px',''], - ['Total','Int','150px',''] - ] - -for r in row_list: - colnames.append(r[0]) - coltypes.append(r[1]) - colwidths.append(r[2]) - coloptions.append(r[3]) - col_idx[r[0]] = len(colnames)-1 - - -#ADD VALUES TO THE COLUMN -out=[] -oa,ua,ow,uw,sum=0,0,0,0,0 -nowdate = nowdate() -for r in res: - cc = r[col_idx['Territory']] - item_groups = sql("select distinct item_group from `tabSerial No` where territory = '%s' and item_group like '%%%s'" %(cc,filter_values.get('item_group'))) - - for col in range(len(colnames)-1): # this would make all first row blank. just for look - r.append('') - out.append(r) - - # Add Totals for each Territory - # ----------------------------- - det = sql("select COUNT(CASE WHEN amc_expiry_date > '%s' THEN name ELSE NULL END), COUNT(CASE WHEN amc_expiry_date <= '%s' THEN name ELSE NULL END), COUNT(CASE WHEN warranty_expiry_date > '%s' THEN name ELSE NULL END), COUNT(CASE WHEN warranty_expiry_date <= '%s' THEN name ELSE NULL END) from `tabSerial No` where territory = '%s' and item_group like '%%%s'" %(nowdate,nowdate,nowdate,nowdate,cc,filter_values.get('item_group'))) - r[col_idx['Item Group']] = '' - - r[col_idx['Out of AMC']] = cstr(det[0][0]) - r[col_idx['Under AMC']] = cstr(det[0][1]) - r[col_idx['Out of Warranty']] = cstr(det[0][2]) - r[col_idx['Under Warranty']] = cstr(det[0][3]) - tot = cint(det[0][0]) + cint(det[0][1]) + cint(det[0][2]) + cint(det[0][3]) - r[col_idx['Total']] = cstr(tot) - - - oa += cint(det[0][0]) - ua += cint(det[0][1]) - ow += cint(det[0][2]) - uw += cint(det[0][3]) - sum += tot - - - # Add Brand Details belonging to Territory - # ---------------------------------------- - for br in item_groups: - br_det = sql("select COUNT(CASE WHEN amc_expiry_date > '%s' THEN name ELSE NULL END), COUNT(CASE WHEN amc_expiry_date <= '%s' THEN name ELSE NULL END), COUNT(CASE WHEN warranty_expiry_date > '%s' THEN name ELSE NULL END), COUNT(CASE WHEN warranty_expiry_date <= '%s' THEN name ELSE NULL END) from `tabSerial No` where territory = '%s' and item_group = '%s'"%(nowdate,nowdate,nowdate,nowdate,cc,br[0])) - t_row = ['' for i in range(len(colnames))] - t_row[col_idx['Item Group']] = br[0] - - t_row[col_idx['Out of AMC']] = cint(br_det[0][0]) - t_row[col_idx['Under AMC']] = cint(br_det[0][1]) - t_row[col_idx['Out of Warranty']] = cint(br_det[0][2]) - t_row[col_idx['Under Warranty']] = cint(br_det[0][3]) - tot = cint(br_det[0][0]) + cint(br_det[0][1]) + cint(br_det[0][2])+ cint(br_det[0][3]) - t_row[col_idx['Total']] = tot - out.append(t_row) - - -#ADD NEW ROW -# ---------- -newrow=['','TOTAL',oa,ua,ow,uw,sum] -out.append(newrow) -res=out \ No newline at end of file diff --git a/support/search_criteria/amc_summary/amc_summary.txt b/support/search_criteria/amc_summary/amc_summary.txt deleted file mode 100644 index a83e0d345e..0000000000 --- a/support/search_criteria/amc_summary/amc_summary.txt +++ /dev/null @@ -1,28 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:50", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" - }, - { - "module": "Support", - "standard": "Yes", - "sort_order": "ASC", - "filters": "{'Serial No\u0001Saved':1,'Serial No\u0001Status':''}", - "doc_type": "Serial No", - "name": "__common__", - "add_cond": "`tabSerial No`.`territory` is not null\n`tabSerial No`.`territory` != ''\n`tabSerial No`.`status` not in ('In Store', 'Scrapped')", - "doctype": "Search Criteria", - "sort_by": "`tabSerial No`.`cost_center`", - "group_by": "`tabSerial No`.`cost_center`", - "page_len": 50, - "criteria_name": "AMC Summary", - "columns": "Serial No\u0001Territory" - }, - { - "name": "amc_summary", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/support/search_criteria/customer_issues/__init__.py b/support/search_criteria/customer_issues/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/support/search_criteria/customer_issues/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/support/search_criteria/customer_issues/customer_issues.js b/support/search_criteria/customer_issues/customer_issues.js deleted file mode 100644 index 8eaf6f62b0..0000000000 --- a/support/search_criteria/customer_issues/customer_issues.js +++ /dev/null @@ -1,23 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Customer Issue'+FILTER_SEP +'Status'].df.in_first_page = 1; - this.filter_fields_dict['Customer Issue'+FILTER_SEP +'Allocated To'].df.in_first_page = 1; - this.filter_fields_dict['Customer Issue'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; -} - -this.mytabs.items['Select Columns'].hide() \ No newline at end of file diff --git a/support/search_criteria/customer_issues/customer_issues.txt b/support/search_criteria/customer_issues/customer_issues.txt deleted file mode 100644 index caa0775431..0000000000 --- a/support/search_criteria/customer_issues/customer_issues.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "module": "Support", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Customer Issue\u0001Saved':1,'Customer Issue\u0001Submitted':1,'Customer Issue\u0001Status':'','Customer Issue\u0001Fiscal Year':''}", - "doctype": "Search Criteria", - "doc_type": "Customer Issue", - "name": "__common__", - "sort_by": "`tabCustomer Issue`.`name`", - "page_len": 50, - "criteria_name": "Customer Issues", - "columns": "Customer Issue\u0001ID,Customer Issue\u0001Status,Customer Issue\u0001Complaint Date,Customer Issue\u0001Complaint,Customer Issue\u0001Item Code,Customer Issue\u0001Item Name,Customer Issue\u0001Allocated To,Customer Issue\u0001Allocated On,Customer Issue\u0001Resolved By,Customer Issue\u0001Resolution Date,Customer Issue\u0001Customer Name,Customer Issue\u0001Fiscal Year" - }, - { - "name": "customer_issues", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/__init__.py b/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.js b/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.js deleted file mode 100644 index 1b3837a4ed..0000000000 --- a/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.js +++ /dev/null @@ -1,20 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; - this.filter_fields_dict['Sales Order'+FILTER_SEP +'Fiscal Year'].df['report_default'] = sys_defaults.fiscal_year; -} \ No newline at end of file diff --git a/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.txt b/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.txt deleted file mode 100644 index ad9fe08dd0..0000000000 --- a/support/search_criteria/maintenance_orderwise_pending_amount_to_bill/maintenance_orderwise_pending_amount_to_bill.txt +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "owner": "ashwini@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "add_col": "SUM((`tabSales Order Item`.`qty` - `tabSales Order Item`.`billed_qty`) * `tabSales Order Item`.`basic_rate`) AS 'Pending Amount'", - "parent_doc_type": "Sales Order", - "module": "Support", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Sales Order\u0001Saved':1,'Sales Order\u0001Submitted':1,'Sales Order\u0001Status':'','Sales Order\u0001Fiscal Year':''}", - "doc_type": "Sales Order Item", - "name": "__common__", - "add_cond": "`tabSales Order`.status != 'Stopped'\n`tabSales Order`.order_type='Maintenance'", - "doctype": "Search Criteria", - "sort_by": "`tabSales Order`.`name`", - "page_len": 50, - "criteria_name": "Maintenance Orderwise Pending Amount To Bill", - "columns": "Sales Order\u0001ID,Sales Order\u0001Customer,Sales Order\u0001Customer Name,Sales Order\u0001Customer Address,Sales Order\u0001Status,Sales Order\u0001% Billed,Sales Order\u0001Sales Order Date" - }, - { - "name": "maintenance_orderwise_pending_amount_to_bill", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/support/search_criteria/maintenance_schedule_details/__init__.py b/support/search_criteria/maintenance_schedule_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/support/search_criteria/maintenance_schedule_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.js b/support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.js deleted file mode 100644 index c4800eac04..0000000000 --- a/support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.js +++ /dev/null @@ -1,29 +0,0 @@ -// 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 . - -report.customize_filters = function() { - - this.filter_fields_dict['Maintenance Schedule Detail'+FILTER_SEP +'From Scheduled Date'].df.in_first_page = 1; - this.filter_fields_dict['Maintenance Schedule Detail'+FILTER_SEP +'To Scheduled Date'].df.in_first_page = 1; - this.filter_fields_dict['Maintenance Schedule Detail'+FILTER_SEP +'Incharge Name'].df.in_first_page = 1; - this.filter_fields_dict['Maintenance Schedule'+FILTER_SEP +'Customer'].df.in_first_page = 1; - this.filter_fields_dict['Maintenance Schedule'+FILTER_SEP +'Customer Name'].df.in_first_page = 1; - this.filter_fields_dict['Maintenance Schedule'+FILTER_SEP +'Sales Order No'].df.in_first_page = 1; - //this.filter_fields_dict['Maintenance Schedule'+FILTER_SEP +'Status'].df.in_first_page = 0; - this.filter_fields_dict['Maintenance Schedule'+FILTER_SEP +'Company'].df['report_default'] = sys_defaults.company; -} - -this.mytabs.items['Select Columns'].hide() \ No newline at end of file diff --git a/support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.txt b/support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.txt deleted file mode 100644 index f8a588255a..0000000000 --- a/support/search_criteria/maintenance_schedule_details/maintenance_schedule_details.txt +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "owner": "nabin@webnotestech.com", - "docstatus": 0, - "creation": "2012-04-03 12:49:51", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:51" - }, - { - "parent_doc_type": "Maintenance Schedule", - "module": "Support", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Maintenance Schedule\u0001Submitted':1,'Maintenance Schedule\u0001Status':''}", - "doc_type": "Maintenance Schedule Detail", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "`tabMaintenance Schedule Detail`.`item_code`", - "page_len": 50, - "criteria_name": "Maintenance Schedule Details", - "columns": "Maintenance Schedule Detail\u0001Item Code,Maintenance Schedule Detail\u0001Item Name,Maintenance Schedule Detail\u0001Serial No,Maintenance Schedule Detail\u0001Incharge Name,Maintenance Schedule Detail\u0001Scheduled Date,Maintenance Schedule\u0001Customer Name,Maintenance Schedule\u0001Address,Maintenance Schedule\u0001Sales Order No" - }, - { - "name": "maintenance_schedule_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/support/search_criteria/warranty_amc_expiry_details/__init__.py b/support/search_criteria/warranty_amc_expiry_details/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/support/search_criteria/warranty_amc_expiry_details/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.js b/support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.js deleted file mode 100644 index ad5ce260ab..0000000000 --- a/support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.js +++ /dev/null @@ -1,25 +0,0 @@ -// 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 . - -//this.mytabs.items['Select Columns'].hide(); - -report.customize_filters = function() { - this.filter_fields_dict['Serial No'+FILTER_SEP +'Status'].df.in_first_page = 0; - this.filter_fields_dict['Serial No'+FILTER_SEP +'Customer'].df.in_first_page = 1; - this.filter_fields_dict['Serial No'+FILTER_SEP +'Customer Name'].df.in_first_page = 1; - this.filter_fields_dict['Serial No'+FILTER_SEP +'Maintenance Status'].df.in_first_page = 1; - -} \ No newline at end of file diff --git a/support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.txt b/support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.txt deleted file mode 100644 index bad5c9f10a..0000000000 --- a/support/search_criteria/warranty_amc_expiry_details/warranty_amc_expiry_details.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "module": "Support", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Serial No\u0001Saved':1,'Serial No\u0001Status':'','Serial No\u0001Maintenance Status':''}", - "doctype": "Search Criteria", - "doc_type": "Serial No", - "name": "__common__", - "sort_by": "`tabSerial No`.`name`", - "page_len": 50, - "criteria_name": "Warranty/AMC Expiry Details", - "columns": "Serial No\u0001ID,Serial No\u0001Item Code,Serial No\u0001Description,Serial No\u0001Status,Serial No\u0001Item Group,Serial No\u0001Purchase Receipt No,Serial No\u0001Delivery Note No,Serial No\u0001Customer,Serial No\u0001Customer Name,Serial No\u0001AMC Expiry Date,Serial No\u0001Warranty Expiry Date" - }, - { - "name": "warranty-amc_expiry_details", - "doctype": "Search Criteria" - } -] \ No newline at end of file diff --git a/support/search_criteria/warranty_amc_summary/__init__.py b/support/search_criteria/warranty_amc_summary/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/support/search_criteria/warranty_amc_summary/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.js b/support/search_criteria/warranty_amc_summary/warranty_amc_summary.js deleted file mode 100644 index 4ad5a767b9..0000000000 --- a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.js +++ /dev/null @@ -1,26 +0,0 @@ -// 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 . - -report.customize_filters = function() { - this.hide_all_filters(); - this.mytabs.items['Select Columns'].hide(); - this.mytabs.items['More Filters'].hide(); - this.add_filter({fieldname:'based_on', label:'Based On', fieldtype:'Select', options:'Territory'+NEWLINE+'Item Group',ignore:1,parent:'Serial No',in_first_page:1, report_default:'Item Group'}); -} - -report.aftertableprint = function(t) { - $yt(t,'*',1,{whiteSpace:'pre'}); -} \ No newline at end of file diff --git a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.py b/support/search_criteria/warranty_amc_summary/warranty_amc_summary.py deleted file mode 100644 index 8c102c3831..0000000000 --- a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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 -opt = filter_values.get('based_on') -opt_dict = {'Territory':'territory', 'Item Group':'item_group'} - -# ADD NEW COLUMNS -row_list = [[opt,'Data','150px',''], - ['Out of AMC','Int','150px',''], - ['Under AMC','Int','150px',''], - ['Out of Warranty','Int','150px',''], - ['Under Warranty','Int','150px',''], - ['Total','Int','150px','']] - -for r in row_list: - colnames.append(r[0]) - coltypes.append(r[1]) - colwidths.append(r[2]) - coloptions.append(r[3]) - col_idx[r[0]] = len(colnames)-1 - - -#ADD VALUES TO THE COLUMN -out=[] -oa,ua,ow,uw,sum=0,0,0,0,0 -nowdate = nowdate() -for r in res: - lft_rgt = sql("select lft, rgt from `tab%s` where name = '%s'" % (opt,r[col_idx[opt]].strip())) - - - det = sql("select COUNT(CASE WHEN t1.amc_expiry_date < '%s' THEN t1.name ELSE NULL END), COUNT(CASE WHEN t1.amc_expiry_date >= '%s' THEN t1.name ELSE NULL END), COUNT(CASE WHEN t1.warranty_expiry_date < '%s' THEN t1.name ELSE NULL END), COUNT(CASE WHEN t1.warranty_expiry_date >= '%s' THEN t1.name ELSE NULL END) from `tabSerial No` t1, `tab%s` t2 where t1.%s = t2.name and t2.lft>= '%s' and t2. rgt <= '%s' and t1.status = 'Delivered' and ifnull(item_group,'')!='' and ifnull(territory,'')!=''" %(nowdate,nowdate,nowdate,nowdate,opt, opt_dict[opt], lft_rgt[0][0], lft_rgt[0][1])) - - r.append(cint(det[0][0])) - r.append(cint(det[0][1])) - r.append(cint(det[0][2])) - r.append(cint(det[0][3])) - tot = cint(det[0][0]) + cint(det[0][1]) + cint(det[0][2]) + cint(det[0][3]) - r.append(tot) - out.append(r) diff --git a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.sql b/support/search_criteria/warranty_amc_summary/warranty_amc_summary.sql deleted file mode 100644 index ebd7ae845f..0000000000 --- a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT CONCAT(REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name FROM `tab%(based_on)s` AS node,`tab%(based_on)s` AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt AND node.docstatus !=2 GROUP BY node.name ORDER BY node.lft \ No newline at end of file diff --git a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.txt b/support/search_criteria/warranty_amc_summary/warranty_amc_summary.txt deleted file mode 100644 index 098b5d6dac..0000000000 --- a/support/search_criteria/warranty_amc_summary/warranty_amc_summary.txt +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "owner": "Administrator", - "docstatus": 0, - "creation": "2012-04-03 12:49:53", - "modified_by": "Administrator", - "modified": "2012-04-03 12:49:53" - }, - { - "module": "Support", - "sort_order": "DESC", - "filters": "{'Serial No\u0001Saved':1,'Serial No\u0001Status':''}", - "standard": "Yes", - "doc_type": "Serial No", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "name", - "page_len": 50, - "criteria_name": "Warranty/AMC Summary" - }, - { - "name": "warranty-amc_summary", - "doctype": "Search Criteria" - } -] \ No newline at end of file From 3e8eca35a259021e982c1003ddac50fa6d47d016 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Jun 2013 17:18:24 +0530 Subject: [PATCH 264/295] [report][fix] stock balance --- accounts/page/general_ledger/general_ledger.js | 2 +- startup/report_data_map.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index ffab56831a..e612bd8a41 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -326,7 +326,7 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ if(voucher_dict.totals.debit || voucher_dict.totals.credit) { voucher_dict.row.debit = voucher_dict.totals.debit; voucher_dict.row.credit = voucher_dict.totals.credit; - voucher_dict.row.id = "entry" + voucher + voucher_dict.row.id = "entry_grouped_by_" + voucher out = out.concat(voucher_dict.row); } }); diff --git a/startup/report_data_map.py b/startup/report_data_map.py index 068e470045..c3a5dd2cce 100644 --- a/startup/report_data_map.py +++ b/startup/report_data_map.py @@ -72,7 +72,7 @@ data_map = { }, "Item Group": { "columns": ["name", "parent_item_group"], - "conditions": ["docstatus < 2"], + # "conditions": ["docstatus < 2"], "order_by": "lft" }, "Brand": { From a19014a8469938ea38169fba9280aadb998e6760 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Mon, 24 Jun 2013 19:05:16 +0530 Subject: [PATCH 265/295] Moved Sales BOM & Sales BOM Item to Selling module, completed 'Available Stock for packing Items' report --- .../doctype/sales_bom/__init__.py | 0 .../sales_bom/locale/_messages_doc.json | 0 .../doctype/sales_bom/locale/ar-doc.json | 0 .../doctype/sales_bom/locale/de-doc.json | 0 .../doctype/sales_bom/locale/es-doc.json | 0 .../doctype/sales_bom/locale/fr-doc.json | 0 .../doctype/sales_bom/locale/hi-doc.json | 0 .../doctype/sales_bom/locale/hr-doc.json | 0 .../doctype/sales_bom/locale/nl-doc.json | 0 .../doctype/sales_bom/locale/pt-BR-doc.json | 0 .../doctype/sales_bom/locale/pt-doc.json | 0 .../doctype/sales_bom/locale/sr-doc.json | 0 .../doctype/sales_bom/locale/ta-doc.json | 0 .../doctype/sales_bom/locale/th-doc.json | 0 .../doctype/sales_bom/sales_bom.js | 0 .../doctype/sales_bom/sales_bom.py | 0 .../doctype/sales_bom/sales_bom.txt | 0 .../doctype/sales_bom/test_sales_bom.py | 0 .../doctype/sales_bom_item/__init__.py | 0 .../sales_bom_item/locale/_messages_doc.json | 0 .../doctype/sales_bom_item/locale/ar-doc.json | 0 .../doctype/sales_bom_item/locale/de-doc.json | 0 .../doctype/sales_bom_item/locale/es-doc.json | 0 .../doctype/sales_bom_item/locale/fr-doc.json | 0 .../doctype/sales_bom_item/locale/hi-doc.json | 0 .../doctype/sales_bom_item/locale/hr-doc.json | 0 .../doctype/sales_bom_item/locale/nl-doc.json | 0 .../sales_bom_item/locale/pt-BR-doc.json | 0 .../doctype/sales_bom_item/locale/pt-doc.json | 0 .../doctype/sales_bom_item/locale/sr-doc.json | 0 .../doctype/sales_bom_item/locale/ta-doc.json | 0 .../doctype/sales_bom_item/locale/th-doc.json | 0 .../doctype/sales_bom_item/sales_bom_item.py | 0 .../doctype/sales_bom_item/sales_bom_item.txt | 0 selling/locale/_messages_js.json | 1 + selling/locale/_messages_py.json | 7 ++ selling/locale/ar-py.json | 7 ++ selling/locale/de-py.json | 7 ++ selling/locale/es-py.json | 7 ++ selling/locale/fr-py.json | 7 ++ selling/locale/hi-py.json | 7 ++ selling/locale/hr-py.json | 7 ++ selling/locale/nl-py.json | 7 ++ selling/locale/pt-BR-py.json | 7 ++ selling/locale/pt-py.json | 7 ++ selling/locale/sr-py.json | 7 ++ selling/locale/ta-py.json | 7 ++ selling/locale/th-py.json | 7 ++ selling/page/selling_home/selling_home.js | 11 ++- .../__init__.py | 0 .../available_stock_for_packing_items.py | 89 +++++++++++++++++++ .../available_stock_for_packing_items.txt | 21 +++++ 52 files changed, 209 insertions(+), 4 deletions(-) rename {stock => selling}/doctype/sales_bom/__init__.py (100%) rename {stock => selling}/doctype/sales_bom/locale/_messages_doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/ar-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/de-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/es-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/fr-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/hi-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/hr-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/nl-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/pt-BR-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/pt-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/sr-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/ta-doc.json (100%) rename {stock => selling}/doctype/sales_bom/locale/th-doc.json (100%) rename {stock => selling}/doctype/sales_bom/sales_bom.js (100%) rename {stock => selling}/doctype/sales_bom/sales_bom.py (100%) rename {stock => selling}/doctype/sales_bom/sales_bom.txt (100%) rename {stock => selling}/doctype/sales_bom/test_sales_bom.py (100%) rename {stock => selling}/doctype/sales_bom_item/__init__.py (100%) rename {stock => selling}/doctype/sales_bom_item/locale/_messages_doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/ar-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/de-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/es-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/fr-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/hi-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/hr-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/nl-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/pt-BR-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/pt-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/sr-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/ta-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/locale/th-doc.json (100%) rename {stock => selling}/doctype/sales_bom_item/sales_bom_item.py (100%) rename {stock => selling}/doctype/sales_bom_item/sales_bom_item.txt (100%) create mode 100644 selling/locale/_messages_js.json create mode 100644 selling/locale/_messages_py.json create mode 100644 selling/locale/ar-py.json create mode 100644 selling/locale/de-py.json create mode 100644 selling/locale/es-py.json create mode 100644 selling/locale/fr-py.json create mode 100644 selling/locale/hi-py.json create mode 100644 selling/locale/hr-py.json create mode 100644 selling/locale/nl-py.json create mode 100644 selling/locale/pt-BR-py.json create mode 100644 selling/locale/pt-py.json create mode 100644 selling/locale/sr-py.json create mode 100644 selling/locale/ta-py.json create mode 100644 selling/locale/th-py.json create mode 100644 selling/report/available_stock_for_packing_items/__init__.py create mode 100644 selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py create mode 100644 selling/report/available_stock_for_packing_items/available_stock_for_packing_items.txt diff --git a/stock/doctype/sales_bom/__init__.py b/selling/doctype/sales_bom/__init__.py similarity index 100% rename from stock/doctype/sales_bom/__init__.py rename to selling/doctype/sales_bom/__init__.py diff --git a/stock/doctype/sales_bom/locale/_messages_doc.json b/selling/doctype/sales_bom/locale/_messages_doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/_messages_doc.json rename to selling/doctype/sales_bom/locale/_messages_doc.json diff --git a/stock/doctype/sales_bom/locale/ar-doc.json b/selling/doctype/sales_bom/locale/ar-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/ar-doc.json rename to selling/doctype/sales_bom/locale/ar-doc.json diff --git a/stock/doctype/sales_bom/locale/de-doc.json b/selling/doctype/sales_bom/locale/de-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/de-doc.json rename to selling/doctype/sales_bom/locale/de-doc.json diff --git a/stock/doctype/sales_bom/locale/es-doc.json b/selling/doctype/sales_bom/locale/es-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/es-doc.json rename to selling/doctype/sales_bom/locale/es-doc.json diff --git a/stock/doctype/sales_bom/locale/fr-doc.json b/selling/doctype/sales_bom/locale/fr-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/fr-doc.json rename to selling/doctype/sales_bom/locale/fr-doc.json diff --git a/stock/doctype/sales_bom/locale/hi-doc.json b/selling/doctype/sales_bom/locale/hi-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/hi-doc.json rename to selling/doctype/sales_bom/locale/hi-doc.json diff --git a/stock/doctype/sales_bom/locale/hr-doc.json b/selling/doctype/sales_bom/locale/hr-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/hr-doc.json rename to selling/doctype/sales_bom/locale/hr-doc.json diff --git a/stock/doctype/sales_bom/locale/nl-doc.json b/selling/doctype/sales_bom/locale/nl-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/nl-doc.json rename to selling/doctype/sales_bom/locale/nl-doc.json diff --git a/stock/doctype/sales_bom/locale/pt-BR-doc.json b/selling/doctype/sales_bom/locale/pt-BR-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/pt-BR-doc.json rename to selling/doctype/sales_bom/locale/pt-BR-doc.json diff --git a/stock/doctype/sales_bom/locale/pt-doc.json b/selling/doctype/sales_bom/locale/pt-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/pt-doc.json rename to selling/doctype/sales_bom/locale/pt-doc.json diff --git a/stock/doctype/sales_bom/locale/sr-doc.json b/selling/doctype/sales_bom/locale/sr-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/sr-doc.json rename to selling/doctype/sales_bom/locale/sr-doc.json diff --git a/stock/doctype/sales_bom/locale/ta-doc.json b/selling/doctype/sales_bom/locale/ta-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/ta-doc.json rename to selling/doctype/sales_bom/locale/ta-doc.json diff --git a/stock/doctype/sales_bom/locale/th-doc.json b/selling/doctype/sales_bom/locale/th-doc.json similarity index 100% rename from stock/doctype/sales_bom/locale/th-doc.json rename to selling/doctype/sales_bom/locale/th-doc.json diff --git a/stock/doctype/sales_bom/sales_bom.js b/selling/doctype/sales_bom/sales_bom.js similarity index 100% rename from stock/doctype/sales_bom/sales_bom.js rename to selling/doctype/sales_bom/sales_bom.js diff --git a/stock/doctype/sales_bom/sales_bom.py b/selling/doctype/sales_bom/sales_bom.py similarity index 100% rename from stock/doctype/sales_bom/sales_bom.py rename to selling/doctype/sales_bom/sales_bom.py diff --git a/stock/doctype/sales_bom/sales_bom.txt b/selling/doctype/sales_bom/sales_bom.txt similarity index 100% rename from stock/doctype/sales_bom/sales_bom.txt rename to selling/doctype/sales_bom/sales_bom.txt diff --git a/stock/doctype/sales_bom/test_sales_bom.py b/selling/doctype/sales_bom/test_sales_bom.py similarity index 100% rename from stock/doctype/sales_bom/test_sales_bom.py rename to selling/doctype/sales_bom/test_sales_bom.py diff --git a/stock/doctype/sales_bom_item/__init__.py b/selling/doctype/sales_bom_item/__init__.py similarity index 100% rename from stock/doctype/sales_bom_item/__init__.py rename to selling/doctype/sales_bom_item/__init__.py diff --git a/stock/doctype/sales_bom_item/locale/_messages_doc.json b/selling/doctype/sales_bom_item/locale/_messages_doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/_messages_doc.json rename to selling/doctype/sales_bom_item/locale/_messages_doc.json diff --git a/stock/doctype/sales_bom_item/locale/ar-doc.json b/selling/doctype/sales_bom_item/locale/ar-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/ar-doc.json rename to selling/doctype/sales_bom_item/locale/ar-doc.json diff --git a/stock/doctype/sales_bom_item/locale/de-doc.json b/selling/doctype/sales_bom_item/locale/de-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/de-doc.json rename to selling/doctype/sales_bom_item/locale/de-doc.json diff --git a/stock/doctype/sales_bom_item/locale/es-doc.json b/selling/doctype/sales_bom_item/locale/es-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/es-doc.json rename to selling/doctype/sales_bom_item/locale/es-doc.json diff --git a/stock/doctype/sales_bom_item/locale/fr-doc.json b/selling/doctype/sales_bom_item/locale/fr-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/fr-doc.json rename to selling/doctype/sales_bom_item/locale/fr-doc.json diff --git a/stock/doctype/sales_bom_item/locale/hi-doc.json b/selling/doctype/sales_bom_item/locale/hi-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/hi-doc.json rename to selling/doctype/sales_bom_item/locale/hi-doc.json diff --git a/stock/doctype/sales_bom_item/locale/hr-doc.json b/selling/doctype/sales_bom_item/locale/hr-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/hr-doc.json rename to selling/doctype/sales_bom_item/locale/hr-doc.json diff --git a/stock/doctype/sales_bom_item/locale/nl-doc.json b/selling/doctype/sales_bom_item/locale/nl-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/nl-doc.json rename to selling/doctype/sales_bom_item/locale/nl-doc.json diff --git a/stock/doctype/sales_bom_item/locale/pt-BR-doc.json b/selling/doctype/sales_bom_item/locale/pt-BR-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/pt-BR-doc.json rename to selling/doctype/sales_bom_item/locale/pt-BR-doc.json diff --git a/stock/doctype/sales_bom_item/locale/pt-doc.json b/selling/doctype/sales_bom_item/locale/pt-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/pt-doc.json rename to selling/doctype/sales_bom_item/locale/pt-doc.json diff --git a/stock/doctype/sales_bom_item/locale/sr-doc.json b/selling/doctype/sales_bom_item/locale/sr-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/sr-doc.json rename to selling/doctype/sales_bom_item/locale/sr-doc.json diff --git a/stock/doctype/sales_bom_item/locale/ta-doc.json b/selling/doctype/sales_bom_item/locale/ta-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/ta-doc.json rename to selling/doctype/sales_bom_item/locale/ta-doc.json diff --git a/stock/doctype/sales_bom_item/locale/th-doc.json b/selling/doctype/sales_bom_item/locale/th-doc.json similarity index 100% rename from stock/doctype/sales_bom_item/locale/th-doc.json rename to selling/doctype/sales_bom_item/locale/th-doc.json diff --git a/stock/doctype/sales_bom_item/sales_bom_item.py b/selling/doctype/sales_bom_item/sales_bom_item.py similarity index 100% rename from stock/doctype/sales_bom_item/sales_bom_item.py rename to selling/doctype/sales_bom_item/sales_bom_item.py diff --git a/stock/doctype/sales_bom_item/sales_bom_item.txt b/selling/doctype/sales_bom_item/sales_bom_item.txt similarity index 100% rename from stock/doctype/sales_bom_item/sales_bom_item.txt rename to selling/doctype/sales_bom_item/sales_bom_item.txt diff --git a/selling/locale/_messages_js.json b/selling/locale/_messages_js.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/selling/locale/_messages_js.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/selling/locale/_messages_py.json b/selling/locale/_messages_py.json new file mode 100644 index 0000000000..f5b4a010f5 --- /dev/null +++ b/selling/locale/_messages_py.json @@ -0,0 +1,7 @@ +[ + "is not a Stock Item", + "Item", + "Please check", + "reached its end of life on", + "is a cancelled Item" +] \ No newline at end of file diff --git a/selling/locale/ar-py.json b/selling/locale/ar-py.json new file mode 100644 index 0000000000..b8dd151cf9 --- /dev/null +++ b/selling/locale/ar-py.json @@ -0,0 +1,7 @@ +{ + "Item": "\u0628\u0646\u062f", + "Please check": "\u064a\u0631\u062c\u0649 \u0645\u0631\u0627\u062c\u0639\u0629", + "is a cancelled Item": "\u0647\u0648 \u0628\u0646\u062f \u0625\u0644\u063a\u0627\u0621", + "is not a Stock Item": "\u0644\u064a\u0633 \u0627\u0644\u0625\u063a\u0644\u0627\u0642 \u0644\u0644\u0633\u0647\u0645", + "reached its end of life on": "\u0648\u0635\u0644 \u0625\u0644\u0649 \u0646\u0647\u0627\u064a\u062a\u0647 \u0645\u0646 \u0627\u0644\u062d\u064a\u0627\u0629 \u0639\u0644\u0649" +} \ No newline at end of file diff --git a/selling/locale/de-py.json b/selling/locale/de-py.json new file mode 100644 index 0000000000..68b58d7a4b --- /dev/null +++ b/selling/locale/de-py.json @@ -0,0 +1,7 @@ +{ + "Item": "Artikel", + "Please check": "Bitte \u00fcberpr\u00fcfen Sie", + "is a cancelled Item": "ist ein gestempeltes", + "is not a Stock Item": "ist kein Lagerartikel", + "reached its end of life on": "erreichte Ende des Lebens auf" +} \ No newline at end of file diff --git a/selling/locale/es-py.json b/selling/locale/es-py.json new file mode 100644 index 0000000000..7d145d1e4c --- /dev/null +++ b/selling/locale/es-py.json @@ -0,0 +1,7 @@ +{ + "Item": "Art\u00edculo", + "Please check": "Por favor, compruebe", + "is a cancelled Item": "Es un Tema cancelado", + "is not a Stock Item": "no es un elemento de serie", + "reached its end of life on": "llegado al final de su vida en la" +} \ No newline at end of file diff --git a/selling/locale/fr-py.json b/selling/locale/fr-py.json new file mode 100644 index 0000000000..c466df4c7f --- /dev/null +++ b/selling/locale/fr-py.json @@ -0,0 +1,7 @@ +{ + "Item": "Article", + "Please check": "S'il vous pla\u00eet v\u00e9rifier", + "is a cancelled Item": "est un \u00e9l\u00e9ment annul\u00e9e", + "is not a Stock Item": "n'est pas un \u00e9l\u00e9ment de Stock", + "reached its end of life on": "atteint la fin de sa vie sur" +} \ No newline at end of file diff --git a/selling/locale/hi-py.json b/selling/locale/hi-py.json new file mode 100644 index 0000000000..58d4e65411 --- /dev/null +++ b/selling/locale/hi-py.json @@ -0,0 +1,7 @@ +{ + "Item": "\u092e\u0926", + "Please check": "\u0915\u0943\u092a\u092f\u093e \u091c\u093e\u0901\u091a \u0915\u0930\u0947\u0902", + "is a cancelled Item": "\u0930\u0926\u094d\u0926 \u0906\u0907\u091f\u092e", + "is not a Stock Item": "\u0938\u094d\u091f\u0949\u0915 \u0906\u0907\u091f\u092e \u0928\u0939\u0940\u0902 \u0939\u0948", + "reached its end of life on": "\u092a\u0930 \u0905\u092a\u0928\u0947 \u091c\u0940\u0935\u0928 \u0915\u0947 \u0905\u0902\u0924 \u0924\u0915 \u092a\u0939\u0941\u0901\u091a" +} \ No newline at end of file diff --git a/selling/locale/hr-py.json b/selling/locale/hr-py.json new file mode 100644 index 0000000000..24f60bb1c4 --- /dev/null +++ b/selling/locale/hr-py.json @@ -0,0 +1,7 @@ +{ + "Item": "Stavka", + "Please check": "Molimo provjerite", + "is a cancelled Item": "je otkazan artikla", + "is not a Stock Item": "nije katalo\u0161ki artikla", + "reached its end of life on": "dosegla svoj kraj \u017eivota na" +} \ No newline at end of file diff --git a/selling/locale/nl-py.json b/selling/locale/nl-py.json new file mode 100644 index 0000000000..7964d37f6e --- /dev/null +++ b/selling/locale/nl-py.json @@ -0,0 +1,7 @@ +{ + "Item": "Item", + "Please check": "Controleer", + "is a cancelled Item": "is een geannuleerde artikel", + "is not a Stock Item": "is niet een Stock Item", + "reached its end of life on": "het einde van zijn leven op" +} \ No newline at end of file diff --git a/selling/locale/pt-BR-py.json b/selling/locale/pt-BR-py.json new file mode 100644 index 0000000000..0bf350cad0 --- /dev/null +++ b/selling/locale/pt-BR-py.json @@ -0,0 +1,7 @@ +{ + "Item": "Item", + "Please check": "Por favor, verifique", + "is a cancelled Item": "\u00e9 um Item cancelado", + "is not a Stock Item": "n\u00e3o \u00e9 um Item de Estoque", + "reached its end of life on": "chegou ao fim de vida em" +} \ No newline at end of file diff --git a/selling/locale/pt-py.json b/selling/locale/pt-py.json new file mode 100644 index 0000000000..cfcbcec8df --- /dev/null +++ b/selling/locale/pt-py.json @@ -0,0 +1,7 @@ +{ + "Item": "Item", + "Please check": "Por favor, verifique", + "is a cancelled Item": "\u00e9 um item cancelado", + "is not a Stock Item": "n\u00e3o \u00e9 um item de estoque", + "reached its end of life on": "chegou ao fim da vida na" +} \ No newline at end of file diff --git a/selling/locale/sr-py.json b/selling/locale/sr-py.json new file mode 100644 index 0000000000..ed1672fc6c --- /dev/null +++ b/selling/locale/sr-py.json @@ -0,0 +1,7 @@ +{ + "Item": "\u0421\u0442\u0430\u0432\u043a\u0430", + "Please check": "\u041c\u043e\u043b\u0438\u043c\u043e \u0432\u0430\u0441 \u0434\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u0435", + "is a cancelled Item": "\u0458\u0435 \u043e\u0442\u043a\u0430\u0437\u0430\u043d\u0430 \u0448\u0438\u0444\u0440\u0430", + "is not a Stock Item": "\u043d\u0438\u0458\u0435 \u0431\u0435\u0440\u0437\u0430 \u0448\u0438\u0444\u0440\u0430", + "reached its end of life on": "\u0434\u043e\u0441\u0442\u0438\u0433\u0430\u043e \u0441\u0432\u043e\u0458 \u043a\u0440\u0430\u0458 \u0436\u0438\u0432\u043e\u0442\u0430 \u043d\u0430" +} \ No newline at end of file diff --git a/selling/locale/ta-py.json b/selling/locale/ta-py.json new file mode 100644 index 0000000000..355cc0e7ce --- /dev/null +++ b/selling/locale/ta-py.json @@ -0,0 +1,7 @@ +{ + "Item": "\u0b89\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0b9f\u0bbf", + "Please check": "\u0b9a\u0bb0\u0bbf\u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95\u0bb5\u0bc1\u0bae\u0bcd", + "is a cancelled Item": "\u0b92\u0bb0\u0bc1 \u0bb0\u0ba4\u0bcd\u0ba4\u0bc1 \u0b89\u0bb0\u0bc1\u0baa\u0bcd\u0baa\u0b9f\u0bbf \u0b89\u0bb3\u0bcd\u0bb3\u0ba4\u0bc1", + "is not a Stock Item": "\u0b92\u0bb0\u0bc1 \u0baa\u0b99\u0bcd\u0b95\u0bc1 \u0baa\u0bc6\u0bbe\u0bb0\u0bc1\u0bb3\u0bcd \u0b85\u0bb2\u0bcd\u0bb2", + "reached its end of life on": "\u0bb5\u0bbe\u0bb4\u0bcd\u0b95\u0bcd\u0b95\u0bc8 \u0b85\u0ba4\u0ba9\u0bcd \u0b87\u0bb1\u0bc1\u0ba4\u0bbf\u0baf\u0bbf\u0bb2\u0bcd \u0b85\u0b9f\u0bc8\u0ba8\u0bcd\u0ba4\u0ba4\u0bc1" +} \ No newline at end of file diff --git a/selling/locale/th-py.json b/selling/locale/th-py.json new file mode 100644 index 0000000000..5f31a59373 --- /dev/null +++ b/selling/locale/th-py.json @@ -0,0 +1,7 @@ +{ + "Item": "\u0e0a\u0e34\u0e49\u0e19", + "Please check": "\u0e01\u0e23\u0e38\u0e13\u0e32\u0e15\u0e23\u0e27\u0e08\u0e2a\u0e2d\u0e1a", + "is a cancelled Item": "\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e22\u0e01\u0e40\u0e25\u0e34\u0e01\u0e40\u0e1b\u0e47\u0e19", + "is not a Stock Item": "\u0e44\u0e21\u0e48\u0e44\u0e14\u0e49\u0e40\u0e1b\u0e47\u0e19\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e2a\u0e15\u0e47\u0e2d\u0e01", + "reached its end of life on": "\u0e16\u0e36\u0e07\u0e08\u0e38\u0e14\u0e2a\u0e34\u0e49\u0e19\u0e2a\u0e38\u0e14\u0e02\u0e2d\u0e07\u0e0a\u0e35\u0e27\u0e34\u0e15\u0e40\u0e21\u0e37\u0e48\u0e2d" +} \ No newline at end of file diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 7a0848e931..4157bbb809 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -167,13 +167,11 @@ wn.module_page["Selling"] = [ }, { "label":wn._("Territory Target Variance (Item Group-Wise)"), - route: "query-report/Territory Target Variance Item Group-Wise", - doctype: "Sales Order" + route: "query-report/Territory Target Variance Item Group-Wise" }, { "label":wn._("Sales Person Target Variance (Item Group-Wise)"), - route: "query-report/Sales Person Target Variance Item Group-Wise", - doctype: "Sales Order" + route: "query-report/Sales Person Target Variance Item Group-Wise" }, { "label":wn._("Customers Not Buying Since Long Time"), @@ -190,6 +188,11 @@ wn.module_page["Selling"] = [ route: "query-report/Sales Order Trends", doctype: "Sales Order" }, + { + "label":wn._("Available Stock for Packing Items"), + route: "query-report/Available Stock for Packing Items", + + }, ] } ] diff --git a/selling/report/available_stock_for_packing_items/__init__.py b/selling/report/available_stock_for_packing_items/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py b/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py new file mode 100644 index 0000000000..d766c4487d --- /dev/null +++ b/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py @@ -0,0 +1,89 @@ +# 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 cint + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + item_warehouse_quantity_map = get_item_warehouse_quantity_map() + + data = [] + for item, warehouse in item_warehouse_quantity_map.items(): + item_details = get_item_details(item)[0] + total = 0 + total_qty = 0 + for wh, item_qty in warehouse.items(): + total += 1 + row = [item, item_details.item_name, item_details.description, \ + item_details.stock_uom, wh] + for quantity in item_qty.items(): + max_qty = [] + max_qty.append(quantity[1]) + max_qty = min(max_qty) + total_qty += cint(max_qty.qty) + row += [max_qty.qty] + data.append(row) + if (total == len(warehouse)): + row = ["", "", "Total", "", "", total_qty] + data.append(row) + + return columns, data + +def get_columns(): + columns = ["Item Code:Link/Item:100", "Item Name::100", "Description::120", \ + "UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"] + + return columns + +def get_sales_bom(): + return webnotes.conn.sql("""select name from `tabSales BOM`""", as_dict=1) + +def get_sales_bom_items(item): + return webnotes.conn.sql("""select parent, item_code, qty from `tabSales BOM Item` + where parent=%s""", (item), as_dict=1) + +def get_item_details(item): + return webnotes.conn.sql("""select name, item_name, description, stock_uom + from `tabItem` where name=%s""", (item), as_dict=1) + +def get_item_warehouse_quantity(item): + return webnotes.conn.sql("""select item_code, warehouse, actual_qty from `tabBin` + where item_code=%s""", (item), as_dict=1) + +def get_item_warehouse_quantity_map(): + iwq_map = {} + + sales_bom = get_sales_bom() + + for item in sales_bom: + child_item = get_sales_bom_items(item.name) + for child in child_item: + item_warehouse_quantity = get_item_warehouse_quantity(child.item_code) + for iwq in item_warehouse_quantity: + iwq_map.setdefault(item.name, {}).setdefault(iwq.warehouse, {}).\ + setdefault(child.item_code, webnotes._dict({ + "qty" : 0 + })) + + q_dict = iwq_map[item.name][iwq.warehouse][child.item_code] + + q_dict.qty = cint(iwq.actual_qty / child.qty) + + return iwq_map \ No newline at end of file diff --git a/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.txt b/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.txt new file mode 100644 index 0000000000..5cf413393f --- /dev/null +++ b/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-06-21 13:40:05", + "docstatus": 0, + "modified": "2013-06-21 15:06:40", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales BOM", + "report_name": "Available Stock for Packing Items", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Available Stock for Packing Items" + } +] \ No newline at end of file From ec1f69ddb96955b7d10b55e2523472eca67ea910 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 25 Jun 2013 12:17:43 +0530 Subject: [PATCH 266/295] [reports][fix] removed old reports --- patches/august_2012/remove_cash_flow_statement.py | 4 ---- patches/december_2012/deprecate_tds.py | 1 - .../reload_debtors_creditors_ledger.py | 5 ----- patches/february_2013/p03_material_request.py | 2 -- .../remove_sales_order_pending_items.py | 3 --- .../deprecate_stock_search_criteria.py | 9 --------- .../january_2013/remove_support_search_criteria.py | 4 ---- patches/july_2012/sync_trial_balance.py | 5 ----- patches/june_2012/reports_list_permission.py | 1 - .../may_2012/reload_customer_address_contact.py | 5 ----- patches/may_2012/reload_reports.py | 7 ------- patches/may_2012/reload_so_pending_items.py | 9 --------- patches/patch_list.py | 14 -------------- 13 files changed, 69 deletions(-) delete mode 100644 patches/august_2012/remove_cash_flow_statement.py delete mode 100644 patches/december_2012/reload_debtors_creditors_ledger.py delete mode 100644 patches/february_2013/remove_sales_order_pending_items.py delete mode 100644 patches/january_2013/deprecate_stock_search_criteria.py delete mode 100644 patches/january_2013/remove_support_search_criteria.py delete mode 100644 patches/july_2012/sync_trial_balance.py delete mode 100644 patches/may_2012/reload_customer_address_contact.py delete mode 100644 patches/may_2012/reload_reports.py delete mode 100644 patches/may_2012/reload_so_pending_items.py diff --git a/patches/august_2012/remove_cash_flow_statement.py b/patches/august_2012/remove_cash_flow_statement.py deleted file mode 100644 index 739a4f2709..0000000000 --- a/patches/august_2012/remove_cash_flow_statement.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import unicode_literals -def execute(): - import webnotes - webnotes.conn.sql("delete from `tabSearch Criteria` where name = 'cash_flow_statement'") \ No newline at end of file diff --git a/patches/december_2012/deprecate_tds.py b/patches/december_2012/deprecate_tds.py index e351fd9f75..f3daeb0db7 100644 --- a/patches/december_2012/deprecate_tds.py +++ b/patches/december_2012/deprecate_tds.py @@ -15,7 +15,6 @@ def execute(): webnotes.conn.sql("drop table if exists `tab%s`" % dt) webnotes.conn.begin() - delete_doc("Search Criteria", "tds_return") # Add tds entry in tax table for purchase invoice pi_list = webnotes.conn.sql("""select name from `tabPurchase Invoice` diff --git a/patches/december_2012/reload_debtors_creditors_ledger.py b/patches/december_2012/reload_debtors_creditors_ledger.py deleted file mode 100644 index 7f88a6f47d..0000000000 --- a/patches/december_2012/reload_debtors_creditors_ledger.py +++ /dev/null @@ -1,5 +0,0 @@ -def execute(): - import webnotes - from webnotes.modules import reload_doc - reload_doc("accounts", "search_criteria", "debtors_ledger") - reload_doc("accounts", "search_criteria", "creditors_ledger") \ No newline at end of file diff --git a/patches/february_2013/p03_material_request.py b/patches/february_2013/p03_material_request.py index f0373bdffb..d85710d2a8 100644 --- a/patches/february_2013/p03_material_request.py +++ b/patches/february_2013/p03_material_request.py @@ -8,8 +8,6 @@ def execute(): webnotes.rename_doc("DocType", "Purchase Request Item", "Material Request Item", force=True) if not "tabMaterial Request" in tables: webnotes.rename_doc("DocType", "Purchase Request", "Material Request", force=True) - webnotes.reload_doc("buying", "search_criteria", "pending_po_items_to_bill") - webnotes.reload_doc("buying", "search_criteria", "pending_po_items_to_receive") webnotes.reload_doc("stock", "doctype", "material_request") webnotes.reload_doc("stock", "doctype", "material_request_item") diff --git a/patches/february_2013/remove_sales_order_pending_items.py b/patches/february_2013/remove_sales_order_pending_items.py deleted file mode 100644 index bde6a7b240..0000000000 --- a/patches/february_2013/remove_sales_order_pending_items.py +++ /dev/null @@ -1,3 +0,0 @@ -def execute(): - import webnotes - webnotes.delete_doc("Search Criteria", "sales_order_pending_items") \ No newline at end of file diff --git a/patches/january_2013/deprecate_stock_search_criteria.py b/patches/january_2013/deprecate_stock_search_criteria.py deleted file mode 100644 index d51aadb5cf..0000000000 --- a/patches/january_2013/deprecate_stock_search_criteria.py +++ /dev/null @@ -1,9 +0,0 @@ -import webnotes - -def execute(): - for sc in ["itemwise_price_list", "itemwise_receipt_details", - "shortage_to_purchase_request", "stock_aging_report", - "stock_ledger", "stock_level", "stock_report", - "custom_test2", "custom_test3", "custom_test4", - "test_so2", "test_so3"]: - webnotes.delete_doc("Search Criteria", sc) \ No newline at end of file diff --git a/patches/january_2013/remove_support_search_criteria.py b/patches/january_2013/remove_support_search_criteria.py deleted file mode 100644 index 0443afeeda..0000000000 --- a/patches/january_2013/remove_support_search_criteria.py +++ /dev/null @@ -1,4 +0,0 @@ -import webnotes -def execute(): - for sc in ["warranty-amc_expiry_details", "warranty-amc_summary"]: - webnotes.delete_doc("Search Criteria", sc) \ No newline at end of file diff --git a/patches/july_2012/sync_trial_balance.py b/patches/july_2012/sync_trial_balance.py deleted file mode 100644 index 3755ed442b..0000000000 --- a/patches/july_2012/sync_trial_balance.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals -def execute(): - import webnotes - from webnotes.modules import reload_doc - reload_doc('accounts', 'search_criteria', 'trial_balance') \ No newline at end of file diff --git a/patches/june_2012/reports_list_permission.py b/patches/june_2012/reports_list_permission.py index e34eb5ae46..e88e8fbc60 100644 --- a/patches/june_2012/reports_list_permission.py +++ b/patches/june_2012/reports_list_permission.py @@ -8,7 +8,6 @@ def execute(): webnotes.conn.commit() - webnotes.reload_doc('core', 'doctype', 'search_criteria') webnotes.reload_doc('core', 'doctype', 'report') webnotes.conn.begin() \ No newline at end of file diff --git a/patches/may_2012/reload_customer_address_contact.py b/patches/may_2012/reload_customer_address_contact.py deleted file mode 100644 index 4aec19d02d..0000000000 --- a/patches/may_2012/reload_customer_address_contact.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals -def execute(): - import webnotes - import webnotes.modules - webnotes.modules.reload_doc('selling', 'search_criteria', 'customer_address_contact') \ No newline at end of file diff --git a/patches/may_2012/reload_reports.py b/patches/may_2012/reload_reports.py deleted file mode 100644 index 1cee3a0061..0000000000 --- a/patches/may_2012/reload_reports.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -def execute(): - import webnotes - from webnotes.modules import reload_doc - reload_doc('selling', 'search_criteria', 'itemwise_sales_details') - reload_doc('selling', 'search_criteria', 'itemwise_delivery_details') - diff --git a/patches/may_2012/reload_so_pending_items.py b/patches/may_2012/reload_so_pending_items.py deleted file mode 100644 index 999004e1fe..0000000000 --- a/patches/may_2012/reload_so_pending_items.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -def execute(): - import webnotes - from webnotes.model import delete_doc - delete_doc("Search Criteria", "sales_order_pending_items1") - - webnotes.conn.sql("update `tabSearch Criteria` set module = 'Selling' where module = 'CRM'") - from webnotes.modules import reload_doc - reload_doc('selling', 'search_criteria', 'sales_order_pending_items') \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 0353c74b1e..efd4d51cce 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -38,19 +38,15 @@ patch_list = [ "patches.may_2012.cleanup_property_setter", "patches.may_2012.rename_prev_doctype", "patches.may_2012.cleanup_notification_control", - "patches.may_2012.renamedt_in_custom_search_criteria", "patches.may_2012.stock_reco_patch", - "patches.may_2012.reload_reports", "patches.may_2012.page_role_series_fix", "patches.may_2012.reload_sales_invoice_pf", "patches.may_2012.std_pf_readonly", - "patches.may_2012.reload_so_pending_items", "patches.may_2012.customize_form_cleanup", "patches.may_2012.cs_server_readonly", "patches.may_2012.clear_session_cache", "patches.may_2012.same_purchase_rate_patch", "patches.may_2012.create_report_manager_role", - "patches.may_2012.reload_customer_address_contact", "patches.may_2012.profile_perm_patch", "patches.may_2012.remove_euro_currency", "patches.may_2012.remove_communication_log", @@ -81,7 +77,6 @@ patch_list = [ "patches.august_2012.task_allocated_to_assigned", "patches.august_2012.change_profile_permission", "patches.august_2012.repost_billed_amt", - "patches.august_2012.remove_cash_flow_statement", "patches.september_2012.stock_report_permissions_for_accounts", "patches.september_2012.communication_delete_permission", "patches.september_2012.all_permissions_patch", @@ -123,7 +118,6 @@ patch_list = [ "patches.december_2012.expense_leave_reload", "patches.december_2012.repost_ordered_qty", "patches.december_2012.repost_projected_qty", - "patches.december_2012.reload_debtors_creditors_ledger", "patches.december_2012.website_cache_refactor", "patches.december_2012.production_cleanup", "patches.december_2012.fix_default_print_format", @@ -138,8 +132,6 @@ patch_list = [ "patches.december_2012.remove_project_mapper", "patches.december_2012.update_print_width", "patches.january_2013.remove_bad_permissions", - "patches.january_2013.deprecate_stock_search_criteria", - "patches.january_2013.remove_support_search_criteria", "patches.january_2013.holiday_list_patch", "patches.january_2013.stock_reconciliation_patch", "patches.january_2013.report_permission", @@ -163,7 +155,6 @@ patch_list = [ "patches.february_2013.remove_gl_mapper", "patches.february_2013.reload_bom_replace_tool_permission", "patches.february_2013.payment_reconciliation_reset_values", - "patches.february_2013.remove_sales_order_pending_items", "patches.february_2013.account_negative_balance", "patches.february_2013.remove_account_utils_folder", "patches.february_2013.update_company_in_leave_application", @@ -187,14 +178,11 @@ patch_list = [ "execute:webnotes.delete_doc('DocType', 'Service Quotation Detail')", "patches.february_2013.p06_material_request_mappers", "execute:webnotes.delete_doc('Page', 'Query Report')", - "execute:webnotes.delete_doc('Search Criteria', 'employeewise_balance_leave_report')", - "execute:webnotes.delete_doc('Search Criteria', 'employee_leave_balance_report')", "patches.february_2013.repost_reserved_qty", "execute:webnotes.reload_doc('core', 'doctype', 'report') # 2013-02-25", "execute:webnotes.conn.sql(\"update `tabReport` set report_type=if(ifnull(query, '')='', 'Report Builder', 'Query Report') where is_standard='No'\")", "execute:webnotes.conn.sql(\"update `tabReport` set report_name=name where ifnull(report_name,'')='' and is_standard='No'\")", "patches.february_2013.p08_todo_query_report", - "execute:webnotes.delete_doc('Search Criteria', 'gross_profit') # 2013-02-26", 'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Classic") # 2013-02-26', 'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Modern") # 2013-02-26', 'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Spartan") # 2013-02-26', @@ -208,7 +196,6 @@ patch_list = [ "execute:webnotes.delete_doc('DocType', 'Attendance Control Panel')", "patches.march_2013.p02_get_global_default", "patches.march_2013.p03_rename_blog_to_blog_post", - "execute:webnotes.reload_doc('hr', 'search_criteria', 'monthly_attendance_details')", "patches.march_2013.p04_pos_update_stock_check", "patches.march_2013.p05_payment_reconciliation", "patches.march_2013.p06_remove_sales_purchase_return_tool", @@ -234,7 +221,6 @@ patch_list = [ 'execute:webnotes.reload_doc("selling", "Print Format", "Quotation Modern") # 2013-04-02', 'execute:webnotes.reload_doc("selling", "Print Format", "Quotation Spartan") # 2013-04-02', "patches.april_2013.p04_reverse_modules_list", - "execute:webnotes.delete_doc('Search Criteria', 'time_log_summary')", "patches.april_2013.p04_update_role_in_pages", "patches.april_2013.p05_update_file_data", "patches.april_2013.p06_update_file_size", From 909e8b120f62507ec65afd14592248cf6cc4c619 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 25 Jun 2013 14:08:18 +0530 Subject: [PATCH 267/295] [fix][report] general ledger --- accounts/page/general_ledger/general_ledger.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index e612bd8a41..d30772acfd 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -232,7 +232,6 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ grouped_ledgers[item.account].totals.debit += item.debit; grouped_ledgers[item.account].totals.credit += item.credit; - grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] .totals.debit += item.debit; grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] @@ -248,8 +247,8 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ grouped_ledgers[item.account].entries.push(item); if(grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no].row){ - grouped_ledgers[item.account]. - entries_group_by_voucher[item.voucher_no].row = item; + grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] + .row = jQuery.extend({}, item); } } } @@ -321,7 +320,7 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ $.each(Object.keys(grouped_ledgers).sort(), function(i, account) { if(grouped_ledgers[account].entries.length) { $.each(Object.keys(grouped_ledgers[account].entries_group_by_voucher).sort(), - function(j, voucher) { + function(j, voucher) { voucher_dict = grouped_ledgers[account].entries_group_by_voucher[voucher]; if(voucher_dict.totals.debit || voucher_dict.totals.credit) { voucher_dict.row.debit = voucher_dict.totals.debit; From cca41a172cf397747f5300cf49e3ad3ca6331002 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 25 Jun 2013 14:14:32 +0530 Subject: [PATCH 268/295] [fix][report] general ledger --- accounts/page/general_ledger/general_ledger.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index d30772acfd..269ed5e3f2 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -319,10 +319,11 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ var out = [] $.each(Object.keys(grouped_ledgers).sort(), function(i, account) { if(grouped_ledgers[account].entries.length) { - $.each(Object.keys(grouped_ledgers[account].entries_group_by_voucher).sort(), + $.each(Object.keys(grouped_ledgers[account].entries_group_by_voucher), function(j, voucher) { voucher_dict = grouped_ledgers[account].entries_group_by_voucher[voucher]; - if(voucher_dict.totals.debit || voucher_dict.totals.credit) { + if(voucher_dict && + (voucher_dict.totals.debit || voucher_dict.totals.credit)) { voucher_dict.row.debit = voucher_dict.totals.debit; voucher_dict.row.credit = voucher_dict.totals.credit; voucher_dict.row.id = "entry_grouped_by_" + voucher From 50899d2bd4c53826356d76cb24c6ed13ebc0a28c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 25 Jun 2013 15:17:38 +0530 Subject: [PATCH 269/295] [fix][patch] bom exploded items --- patches/june_2013/p01_update_bom_exploded_items.py | 2 +- 1 file changed, 1 insertion(+), 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 eff0931e4d..f53eb5bc68 100644 --- a/patches/june_2013/p01_update_bom_exploded_items.py +++ b/patches/june_2013/p01_update_bom_exploded_items.py @@ -23,7 +23,7 @@ def execute(): if bom[0] not in updated_bom: try: bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1) - updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) + updated_bom += bom_obj.update_cost_and_exploded_items(bom[0]) webnotes.conn.commit() except: pass \ No newline at end of file From e05e6a114eab33efb5e1acb03e1ba2662b803bd9 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 25 Jun 2013 18:00:30 +0530 Subject: [PATCH 270/295] [Fix]SMS Center Cell Number --- selling/doctype/sms_center/sms_center.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/selling/doctype/sms_center/sms_center.py b/selling/doctype/sms_center/sms_center.py index c5db7383f7..7d50e71232 100644 --- a/selling/doctype/sms_center/sms_center.py +++ b/selling/doctype/sms_center/sms_center.py @@ -24,7 +24,7 @@ from webnotes.model.code import get_obj from webnotes import msgprint sql = webnotes.conn.sql - + # ---------- class DocType: @@ -45,17 +45,16 @@ class DocType: elif self.doc.send_to == 'All Lead (Open)': rec = sql("select lead_name, mobile_no from tabLead where ifnull(mobile_no,'')!='' and docstatus != 2 and status = 'Open'") elif self.doc.send_to == 'All Employee (Active)': - where_clause = self.doc.department and " and t1.department = '%s'" % self.doc.department or "" - where_clause += self.doc.branch and " and t1.branch = '%s'" % self.doc.branch or "" - rec = sql("select t1.employee_name, t2.cell_number from `tabEmployee` t1, `tabEmployee Profile` t2 where t2.employee = t1.name and t1.status = 'Active' and t1.docstatus != 2 and ifnull(t2.cell_number,'')!='' %s" % where_clause) + where_clause = self.doc.department and " and department = '%s'" % self.doc.department or "" + where_clause += self.doc.branch and " and branch = '%s'" % self.doc.branch or "" + rec = sql("select employee_name, cell_number from `tabEmployee` where status = 'Active' and docstatus < 2 and ifnull(cell_number,'')!='' %s" % where_clause) elif self.doc.send_to == 'All Sales Person': rec = sql("select sales_person_name, mobile_no from `tabSales Person` where docstatus != 2 and ifnull(mobile_no,'')!=''") - rec_list = '' for d in rec: rec_list += d[0] + ' - ' + d[1] + '\n' self.doc.receiver_list = rec_list - + webnotes.errprint(rec_list) def get_receiver_nos(self): receiver_nos = [] for d in self.doc.receiver_list.split('\n'): From 4c819edf24207c522b3289155f6be92afc54b909 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Wed, 26 Jun 2013 11:28:53 +0530 Subject: [PATCH 271/295] [Report] Added rounded_total & outstanding_amount to Sales & Purchase Register --- .../purchase_register/purchase_register.py | 23 +++++++++++-------- .../report/sales_register/sales_register.py | 16 +++++++------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 548b561344..d6233a41e0 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -38,11 +38,12 @@ def execute(filters=None): data = [] for inv in invoice_list: # invoice details - purchase_order = ", ".join(invoice_po_pr_map.get(inv.name, {}).get("purchase_order", [])) - purchase_receipt = ", ".join(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", [])) + purchase_order = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_order", []))) + purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", []))) + row = [inv.name, inv.posting_date, inv.supplier, inv.credit_to, account_map.get(inv.credit_to), inv.project_name, inv.bill_no, inv.bill_date, - inv.remarks, purchase_order, purchase_receipt] + inv.remarks, ", ".join(purchase_order), ", ".join(purchase_receipt)] # map expense values for expense_acc in expense_accounts: @@ -55,8 +56,9 @@ def execute(filters=None): for tax_acc in tax_accounts: row.append(invoice_tax_map.get(inv.name, {}).get(tax_acc)) - # total tax, grand total - row += [inv.total_tax, inv.grand_total] + # total tax, grand total, outstanding amount & rounded total + row += [inv.other_charges_total, inv.grand_total, flt(inv.grand_total, 2), \ + inv.outstanding_amount] data.append(row) return columns, data @@ -85,7 +87,8 @@ def get_columns(invoice_list): columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ - ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + \ + ["Rounded Total:Currency:120"] + ["Outstanding Amount:Currency:120"] return columns, expense_accounts, tax_accounts @@ -102,9 +105,11 @@ def get_conditions(filters): def get_invoices(filters): conditions = get_conditions(filters) - return webnotes.conn.sql("""select name, posting_date, credit_to, project_name, supplier, - bill_no, bill_date, remarks, net_total, total_tax, grand_total - from `tabPurchase Invoice` where docstatus = 1 %s + return webnotes.conn.sql("""select pi.name, pi.posting_date, pi.credit_to, + pii.project_name, pi.supplier, pi.bill_no, pi.bill_date, pi.remarks, pi.net_total, + pi.total_tax, pi.grand_total, pi.outstanding_amount + from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii + where pii.parent = pi.name and pi.docstatus = 1 %s order by posting_date desc, name desc""" % conditions, filters, as_dict=1) diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 99057f9a2e..3eebc33e9c 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -39,12 +39,12 @@ def execute(filters=None): data = [] for inv in invoice_list: # invoice details - sales_order = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("sales_order", [])) - delivery_note = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("delivery_note", [])) + sales_order = list(set(invoice_so_dn_map.get(inv.name, {}).get("sales_order", []))) + delivery_note = list(set(invoice_so_dn_map.get(inv.name, {}).get("delivery_note", []))) row = [inv.name, inv.posting_date, inv.customer, inv.debit_to, account_map.get(inv.debit_to), customer_map.get(inv.customer), inv.project_name, - inv.remarks, sales_order, delivery_note] + inv.remarks, ", ".join(sales_order), ", ".join(delivery_note)] # map income values for income_acc in income_accounts: @@ -57,8 +57,8 @@ def execute(filters=None): for tax_acc in tax_accounts: row.append(invoice_tax_map.get(inv.name, {}).get(tax_acc)) - # total tax, grand total - row += [inv.other_charges_total, inv.grand_total] + # total tax, grand total, outstanding amount & rounded total + row += [inv.other_charges_total, inv.grand_total, inv.rounded_total, inv.outstanding_amount] data.append(row) @@ -88,7 +88,8 @@ def get_columns(invoice_list): columns = columns + [(account + ":Currency:120") for account in income_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ - ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + \ + ["Rounded Total:Currency:120"] + ["Outstanding Amount:Currency:120"] return columns, income_accounts, tax_accounts @@ -106,7 +107,8 @@ def get_conditions(filters): def get_invoices(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select name, posting_date, debit_to, project_name, customer, - remarks, net_total, other_charges_total, grand_total from `tabSales Invoice` + remarks, net_total, other_charges_total, grand_total, rounded_total, + outstanding_amount from `tabSales Invoice` where docstatus = 1 %s order by posting_date desc, name desc""" % conditions, filters, as_dict=1) From b802050cd4935ff1722234556595737223ce2afc Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 26 Jun 2013 12:05:37 +0530 Subject: [PATCH 272/295] [grid report] round currency values --- public/js/stock_analytics.js | 2 ++ stock/page/stock_balance/stock_balance.js | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/js/stock_analytics.js b/public/js/stock_analytics.js index c3ed1cb232..91384f61ed 100644 --- a/public/js/stock_analytics.js +++ b/public/js/stock_analytics.js @@ -162,6 +162,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({ } else { break; } + + me.round_item_values(item); } } }, diff --git a/stock/page/stock_balance/stock_balance.js b/stock/page/stock_balance/stock_balance.js index 163b74f629..f4deb4fcd4 100644 --- a/stock/page/stock_balance/stock_balance.js +++ b/stock/page/stock_balance/stock_balance.js @@ -119,7 +119,7 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({ var qty_diff = sl.qty; var value_diff = me.get_value_diff(wh, sl, is_fifo); - + if(sl_posting_date < from_date) { item.opening_qty += qty_diff; item.opening_value += value_diff; @@ -146,6 +146,8 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({ } else { break; } + + me.round_item_values(item); } } From e26acffcfe593d6a4c688536d40aa83903e69fe1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 26 Jun 2013 13:47:11 +0530 Subject: [PATCH 273/295] [report] available qty for packing items --- selling/doctype/sales_bom/sales_bom.txt | 10 +- .../doctype/sales_bom_item/sales_bom_item.txt | 6 +- .../available_stock_for_packing_items.py | 95 ++++++++++--------- 3 files changed, 58 insertions(+), 53 deletions(-) diff --git a/selling/doctype/sales_bom/sales_bom.txt b/selling/doctype/sales_bom/sales_bom.txt index 031f255919..bf8fa479c2 100644 --- a/selling/doctype/sales_bom/sales_bom.txt +++ b/selling/doctype/sales_bom/sales_bom.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-10 16:34:29", + "creation": "2013-06-20 11:53:21", "docstatus": 0, - "modified": "2013-01-22 14:57:23", + "modified": "2013-06-25 16:43:18", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,7 +11,7 @@ "doctype": "DocType", "document_type": "Master", "is_submittable": 0, - "module": "Stock", + "module": "Selling", "name": "__common__" }, { @@ -23,7 +23,6 @@ "permlevel": 0 }, { - "amend": 0, "doctype": "DocPerm", "name": "__common__", "parent": "Sales BOM", @@ -74,6 +73,7 @@ "reqd": 1 }, { + "amend": 1, "cancel": 1, "create": 1, "doctype": "DocPerm", @@ -81,6 +81,7 @@ "write": 1 }, { + "amend": 0, "cancel": 0, "create": 0, "doctype": "DocPerm", @@ -88,6 +89,7 @@ "write": 0 }, { + "amend": 0, "cancel": 1, "create": 1, "doctype": "DocPerm", diff --git a/selling/doctype/sales_bom_item/sales_bom_item.txt b/selling/doctype/sales_bom_item/sales_bom_item.txt index 98285af104..f7906b72d8 100644 --- a/selling/doctype/sales_bom_item/sales_bom_item.txt +++ b/selling/doctype/sales_bom_item/sales_bom_item.txt @@ -1,15 +1,15 @@ [ { - "creation": "2013-02-22 01:28:03", + "creation": "2013-05-23 16:55:51", "docstatus": 0, - "modified": "2013-03-07 07:03:30", + "modified": "2013-06-26 13:45:41", "modified_by": "Administrator", "owner": "Administrator" }, { "doctype": "DocType", "istable": 1, - "module": "Stock", + "module": "Selling", "name": "__common__" }, { diff --git a/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py b/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py index d766c4487d..171c3bb8d9 100644 --- a/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py +++ b/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py @@ -16,33 +16,33 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint +from webnotes.utils import flt def execute(filters=None): if not filters: filters = {} columns = get_columns() - item_warehouse_quantity_map = get_item_warehouse_quantity_map() + iwq_map = get_item_warehouse_quantity_map() + item_map = get_item_details() - data = [] - for item, warehouse in item_warehouse_quantity_map.items(): - item_details = get_item_details(item)[0] + data = [] + for sbom, warehouse in iwq_map.items(): total = 0 total_qty = 0 + for wh, item_qty in warehouse.items(): total += 1 - row = [item, item_details.item_name, item_details.description, \ - item_details.stock_uom, wh] - for quantity in item_qty.items(): - max_qty = [] - max_qty.append(quantity[1]) - max_qty = min(max_qty) - total_qty += cint(max_qty.qty) - row += [max_qty.qty] - data.append(row) - if (total == len(warehouse)): - row = ["", "", "Total", "", "", total_qty] + row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description, + item_map.get(sbom).stock_uom, wh] + available_qty = min(item_qty.values()) + total_qty += flt(available_qty) + row += [available_qty] + + if available_qty: data.append(row) + if (total == len(warehouse)): + row = ["", "", "Total", "", "", total_qty] + data.append(row) return columns, data @@ -52,38 +52,41 @@ def get_columns(): return columns -def get_sales_bom(): - return webnotes.conn.sql("""select name from `tabSales BOM`""", as_dict=1) +def get_sales_bom_items(): + sbom_item_map = {} + for sbom in webnotes.conn.sql("""select parent, item_code, qty from `tabSales BOM Item` + where docstatus < 2""", as_dict=1): + sbom_item_map.setdefault(sbom.parent, {}).setdefault(sbom.item_code, sbom.qty) + + return sbom_item_map -def get_sales_bom_items(item): - return webnotes.conn.sql("""select parent, item_code, qty from `tabSales BOM Item` - where parent=%s""", (item), as_dict=1) +def get_item_details(): + item_map = {} + for item in webnotes.conn.sql("""select name, item_name, description, stock_uom + from `tabItem`""", as_dict=1): + item_map.setdefault(item.name, item) + + return item_map -def get_item_details(item): - return webnotes.conn.sql("""select name, item_name, description, stock_uom - from `tabItem` where name=%s""", (item), as_dict=1) - -def get_item_warehouse_quantity(item): - return webnotes.conn.sql("""select item_code, warehouse, actual_qty from `tabBin` - where item_code=%s""", (item), as_dict=1) +def get_item_warehouse_quantity(): + iwq_map = {} + bin = webnotes.conn.sql("""select item_code, warehouse, actual_qty from `tabBin` + where actual_qty > 0""") + for item, wh, qty in bin: + iwq_map.setdefault(item, {}).setdefault(wh, qty) + + return iwq_map def get_item_warehouse_quantity_map(): - iwq_map = {} + sbom_map = {} + iwq_map = get_item_warehouse_quantity() + sbom_item_map = get_sales_bom_items() + + for sbom, sbom_items in sbom_item_map.items(): + for item, child_qty in sbom_items.items(): + for wh, qty in iwq_map.get(item, {}).items(): + avail_qty = flt(qty) / flt(child_qty) + sbom_map.setdefault(sbom, {}).setdefault(wh, {}) \ + .setdefault(item, avail_qty) - sales_bom = get_sales_bom() - - for item in sales_bom: - child_item = get_sales_bom_items(item.name) - for child in child_item: - item_warehouse_quantity = get_item_warehouse_quantity(child.item_code) - for iwq in item_warehouse_quantity: - iwq_map.setdefault(item.name, {}).setdefault(iwq.warehouse, {}).\ - setdefault(child.item_code, webnotes._dict({ - "qty" : 0 - })) - - q_dict = iwq_map[item.name][iwq.warehouse][child.item_code] - - q_dict.qty = cint(iwq.actual_qty / child.qty) - - return iwq_map \ No newline at end of file + return sbom_map \ No newline at end of file From 89bd216a0c411588d273669d8b18731e30459221 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Wed, 26 Jun 2013 18:28:54 +0530 Subject: [PATCH 274/295] [Fix] SMS Center --- .../purchase_taxes_and_charges_master.js | 2 +- .../purchase_taxes_and_charges_master.py | 14 +++++++------- .../sales_invoice_item/sales_invoice_item.txt | 7 ++++--- .../supplier_quotation/supplier_quotation.js | 1 - selling/doctype/sms_center/sms_center.py | 7 ++++--- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js index 53588e0629..f04f895baf 100644 --- a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js +++ b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js @@ -147,7 +147,7 @@ cur_frm.cscript.account_head = function(doc, cdt, cdn) { d.account_head = ''; } else if(d.account_head && d.charge_type) { - arg = "{'charge_type' : '" + d.charge_type +"', 'account_head' : '" + d.account_head + "'}"; + arg = "{'charge_type' : '" + d.charge_type + "', 'account_head' : '" + d.account_head + "'}"; get_server_fields('get_rate', arg, 'purchase_tax_details', doc, cdt, cdn, 1); } refresh_field('account_head',d.name,'purchase_tax_details'); diff --git a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py index 1250db5c28..995415e29a 100644 --- a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py +++ b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py @@ -26,11 +26,11 @@ sql = webnotes.conn.sql class DocType: - def __init__(self, doc, doclist=[]): - self.doc = doc - self.doclist = doclist + def __init__(self, doc, doclist=[]): + self.doc = doc + self.doclist = doclist - # Get Tax Rate if account type is Tax - # =================================================================== - def get_rate(self, arg): - return get_obj('Purchase Common').get_rate(arg, self) \ No newline at end of file + # Get Tax Rate if account type is Tax + # =================================================================== + def get_rate(self, arg): + return get_obj('Purchase Common').get_rate(arg, self) \ No newline at end of file diff --git a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt index 89c86f8219..82ad3b1527 100644 --- a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt +++ b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-19 11:00:07", + "creation": "2013-06-04 11:02:19", "docstatus": 0, - "modified": "2013-05-22 12:06:15", + "modified": "2013-06-26 14:33:21", "modified_by": "Administrator", "owner": "Administrator" }, @@ -243,7 +243,8 @@ "oldfieldname": "serial_no", "oldfieldtype": "Small Text", "print_hide": 0, - "read_only": 0 + "read_only": 0, + "reqd": 0 }, { "doctype": "DocField", diff --git a/buying/doctype/supplier_quotation/supplier_quotation.js b/buying/doctype/supplier_quotation/supplier_quotation.js index 1e4f6cbfb7..bac6e9f4ef 100644 --- a/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/buying/doctype/supplier_quotation/supplier_quotation.js @@ -87,7 +87,6 @@ cur_frm.cscript.supplier = function(doc, dt, dn) { get_server_fields('get_default_supplier_address', JSON.stringify({ supplier: doc.supplier }), '', doc, dt, dn, 1, function() { cur_frm.refresh(); }); - cur_frm.cscript.toggle_contact_section(doc); } } diff --git a/selling/doctype/sms_center/sms_center.py b/selling/doctype/sms_center/sms_center.py index 7d50e71232..8b404e6a4d 100644 --- a/selling/doctype/sms_center/sms_center.py +++ b/selling/doctype/sms_center/sms_center.py @@ -35,11 +35,12 @@ class DocType: def create_receiver_list(self): rec, where_clause = '', '' if self.doc.send_to == 'All Customer Contact': - where_clause = self.doc.customer and " and customer = '%s'" % self.doc.customer or " and ifnull(is_customer, 0) = 1" + where_clause = self.doc.customer and " and customer = '%s'" % self.doc.customer or " and ifnull(customer, '') != ''" if self.doc.send_to == 'All Supplier Contact': - where_clause = self.doc.supplier and " and ifnull(is_supplier, 0) = 1 and supplier = '%s'" % self.doc.supplier or " and ifnull(is_supplier, 0) = 1" + where_clause = self.doc.supplier and " and ifnull(is_supplier, 0) = 1 and supplier = '%s'" % self.doc.supplier or " and ifnull(supplier, '') != ''" if self.doc.send_to == 'All Sales Partner Contact': - where_clause = self.doc.sales_partner and " and ifnull(is_sales_partner, 0) = 1 and sales_aprtner = '%s'" % self.doc.sales_partner or " and ifnull(is_sales_partner, 0) = 1" + where_clause = self.doc.sales_partner and " and ifnull(is_sales_partner, 0) = 1 and sales_partner = '%s'" % self.doc.sales_partner or " and ifnull(sales_partner, '') != ''" + if self.doc.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']: rec = sql("select CONCAT(ifnull(first_name,''),'',ifnull(last_name,'')), mobile_no from `tabContact` where ifnull(mobile_no,'')!='' and docstatus != 2 %s" % where_clause) elif self.doc.send_to == 'All Lead (Open)': From 4924a9f7648cb3515fdbe282a77926527a715e9b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 27 Jun 2013 11:01:14 +0530 Subject: [PATCH 275/295] [fix] payment reconciliation tool --- .../payment_to_invoice_matching_tool.js | 1 + 1 file changed, 1 insertion(+) diff --git a/accounts/doctype/payment_to_invoice_matching_tool/payment_to_invoice_matching_tool.js b/accounts/doctype/payment_to_invoice_matching_tool/payment_to_invoice_matching_tool.js index 8b35bc1028..ac7d9d2d14 100644 --- a/accounts/doctype/payment_to_invoice_matching_tool/payment_to_invoice_matching_tool.js +++ b/accounts/doctype/payment_to_invoice_matching_tool/payment_to_invoice_matching_tool.js @@ -50,6 +50,7 @@ cur_frm.fields_dict.voucher_no.get_query = function(doc) { where against_voucher_type = '%(dt)s' \ and against_voucher = gle.voucher_no \ and voucher_no != gle.voucher_no \ + and account = gle.account \ and ifnull(is_cancelled, 'No') = 'No') != \ abs(ifnull(gle.debit, 0) - ifnull(gle.credit, 0)) \ ORDER BY gle.posting_date DESC, gle.voucher_no DESC LIMIT 50", From f47617c6e606e10eaa78b1168819c60f93a1d2bd Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Thu, 27 Jun 2013 11:01:36 +0530 Subject: [PATCH 276/295] [Fix] Purchase Register --- .../purchase_register/purchase_register.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index d6233a41e0..6bc35218c9 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -40,9 +40,10 @@ def execute(filters=None): # invoice details purchase_order = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_order", []))) purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", []))) + project_name = list(set(invoice_po_pr_map.get(inv.name, {}).get("project_name", []))) row = [inv.name, inv.posting_date, inv.supplier, inv.credit_to, - account_map.get(inv.credit_to), inv.project_name, inv.bill_no, inv.bill_date, + account_map.get(inv.credit_to), ", ".join(project_name), inv.bill_no, inv.bill_date, inv.remarks, ", ".join(purchase_order), ", ".join(purchase_receipt)] # map expense values @@ -105,11 +106,10 @@ def get_conditions(filters): def get_invoices(filters): conditions = get_conditions(filters) - return webnotes.conn.sql("""select pi.name, pi.posting_date, pi.credit_to, - pii.project_name, pi.supplier, pi.bill_no, pi.bill_date, pi.remarks, pi.net_total, - pi.total_tax, pi.grand_total, pi.outstanding_amount - from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii - where pii.parent = pi.name and pi.docstatus = 1 %s + return webnotes.conn.sql("""select name, posting_date, credit_to, + supplier, bill_no, bill_date, remarks, net_total, + total_tax, grand_total, outstanding_amount + from `tabPurchase Invoice` where docstatus = 1 %s order by posting_date desc, name desc""" % conditions, filters, as_dict=1) @@ -138,8 +138,8 @@ def get_invoice_tax_map(invoice_list): return invoice_tax_map def get_invoice_po_pr_map(invoice_list): - pi_items = webnotes.conn.sql("""select parent, purchase_order, purchase_receipt - from `tabPurchase Invoice Item` where parent in (%s) + pi_items = webnotes.conn.sql("""select parent, purchase_order, purchase_receipt, + project_name from `tabPurchase Invoice Item` where parent in (%s) and (ifnull(purchase_order, '') != '' or ifnull(purchase_receipt, '') != '')""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) @@ -151,6 +151,11 @@ def get_invoice_po_pr_map(invoice_list): if d.purchase_receipt: invoice_po_pr_map.setdefault(d.parent, webnotes._dict()).setdefault( "purchase_receipt", []).append(d.purchase_receipt) + if d.project_name: + invoice_po_pr_map.setdefault(d.parent, webnotes._dict()).setdefault( + "project_name", []).append(d.project_name) + + webnotes.errprint(invoice_po_pr_map) return invoice_po_pr_map From f1845cf7c977ef3dbf5c34dc7ee25675dc9810d9 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Thu, 27 Jun 2013 11:06:11 +0530 Subject: [PATCH 277/295] Removed errprint from purchase register --- accounts/report/purchase_register/purchase_register.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 6bc35218c9..655cf8ced0 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -154,8 +154,6 @@ def get_invoice_po_pr_map(invoice_list): if d.project_name: invoice_po_pr_map.setdefault(d.parent, webnotes._dict()).setdefault( "project_name", []).append(d.project_name) - - webnotes.errprint(invoice_po_pr_map) return invoice_po_pr_map From c35dd968af8e21a7e60c2eaa192bd84be9276382 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 27 Jun 2013 12:21:34 +0530 Subject: [PATCH 278/295] [fix] invoice with zero values --- accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index c17654a601..956867d7ab 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -149,7 +149,7 @@ class DocType(SellingController): ret = get_obj('Sales Common').get_item_details(item.fields, self) for fieldname, value in ret.items(): if self.meta.get_field(fieldname, parentfield="entries") and \ - not item.fields.get(fieldname): + item.fields.get(fieldname) is None: item.fields[fieldname] = value # fetch pos details, if they are not fetched From d6cc5cd08f9e65b706f7a56a94a099dc1b21b58f Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 28 Jun 2013 10:09:46 +0530 Subject: [PATCH 279/295] [Trends Teports] cleanup --- .../purchase_invoice_trends/purchase_invoice_trends.py | 3 +-- .../report/sales_invoice_trends/sales_invoice_trends.py | 3 +-- .../report/purchase_order_trends/purchase_order_trends.py | 3 +-- selling/report/quotation_trends/quotation_trends.py | 3 +-- selling/report/sales_order_trends/sales_order_trends.py | 3 +-- stock/report/delivery_note_trends/delivery_note_trends.py | 3 +-- .../purchase_receipt_trends/purchase_receipt_trends.py | 8 ++------ 7 files changed, 8 insertions(+), 18 deletions(-) diff --git a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py index a558a6cecf..659cb85398 100644 --- a/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py +++ b/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py @@ -21,8 +21,7 @@ from controllers.trends import get_columns,get_data def execute(filters=None): if not filters: filters ={} data = [] - trans = "Purchase Invoice" - conditions = get_columns(filters, trans) + conditions = get_columns(filters, "Purchase Invoice") data = get_data(filters, conditions) return conditions["columns"], data \ No newline at end of file diff --git a/accounts/report/sales_invoice_trends/sales_invoice_trends.py b/accounts/report/sales_invoice_trends/sales_invoice_trends.py index a1068340ec..9a5eaf2f8d 100644 --- a/accounts/report/sales_invoice_trends/sales_invoice_trends.py +++ b/accounts/report/sales_invoice_trends/sales_invoice_trends.py @@ -21,8 +21,7 @@ from controllers.trends import get_columns,get_data def execute(filters=None): if not filters: filters ={} data = [] - trans = "Sales Invoice" - conditions = get_columns(filters, trans) + conditions = get_columns(filters, "Sales Invoice") data = get_data(filters, conditions) return conditions["columns"], data \ No newline at end of file diff --git a/buying/report/purchase_order_trends/purchase_order_trends.py b/buying/report/purchase_order_trends/purchase_order_trends.py index 9b294656c3..9dc986e456 100644 --- a/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/buying/report/purchase_order_trends/purchase_order_trends.py @@ -21,8 +21,7 @@ from controllers.trends import get_columns,get_data def execute(filters=None): if not filters: filters ={} data = [] - trans = "Purchase Order" - conditions = get_columns(filters, trans) + conditions = get_columns(filters, "Purchase Order") data = get_data(filters, conditions) return conditions["columns"], data \ No newline at end of file diff --git a/selling/report/quotation_trends/quotation_trends.py b/selling/report/quotation_trends/quotation_trends.py index d08e0f87f1..29f9ef82c2 100644 --- a/selling/report/quotation_trends/quotation_trends.py +++ b/selling/report/quotation_trends/quotation_trends.py @@ -21,8 +21,7 @@ from controllers.trends import get_columns, get_data def execute(filters=None): if not filters: filters ={} data = [] - trans = "Quotation" - conditions = get_columns(filters, trans) + conditions = get_columns(filters, "Quotation") data = get_data(filters, conditions) return conditions["columns"], data \ No newline at end of file diff --git a/selling/report/sales_order_trends/sales_order_trends.py b/selling/report/sales_order_trends/sales_order_trends.py index 455fbd4e63..ac7c3ab97b 100644 --- a/selling/report/sales_order_trends/sales_order_trends.py +++ b/selling/report/sales_order_trends/sales_order_trends.py @@ -21,8 +21,7 @@ from controllers.trends import get_columns,get_data def execute(filters=None): if not filters: filters ={} data = [] - trans = "Sales Order" - conditions = get_columns(filters, trans) + conditions = get_columns(filters, "Sales Order") data = get_data(filters, conditions) return conditions["columns"], data \ No newline at end of file diff --git a/stock/report/delivery_note_trends/delivery_note_trends.py b/stock/report/delivery_note_trends/delivery_note_trends.py index 9878ecc867..e5b2cd094b 100644 --- a/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/stock/report/delivery_note_trends/delivery_note_trends.py @@ -21,8 +21,7 @@ from controllers.trends import get_columns,get_data def execute(filters=None): if not filters: filters ={} data = [] - trans = "Delivery Note" - conditions = get_columns(filters, trans) + conditions = get_columns(filters, "Delivery Note") data = get_data(filters, conditions) return conditions["columns"], data \ No newline at end of file diff --git a/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 56c0023a2f..b41ce281f7 100644 --- a/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -21,11 +21,7 @@ from controllers.trends import get_columns,get_data def execute(filters=None): if not filters: filters ={} data = [] - trans = "Purchase Receipt" - conditions = get_columns(filters, trans) - data = get_data(filters, tab, conditions) - - if not data : - webnotes.msgprint("Data not found for selected criterias") + conditions = get_columns(filters, "Purchase Receipt") + data = get_data(filters, conditions) return conditions["columns"], data \ No newline at end of file From b164edc16f6a85a16445fc0c36be61b9eada4d82 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 28 Jun 2013 13:03:39 +0530 Subject: [PATCH 280/295] [fix] cleanup data --- utilities/cleanup_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/cleanup_data.py b/utilities/cleanup_data.py index ed04a94bfa..c9de4a5e23 100644 --- a/utilities/cleanup_data.py +++ b/utilities/cleanup_data.py @@ -27,7 +27,7 @@ def delete_transactions(): 'Maintenance Schedule', 'Leave Application', 'Leave Allocation', 'Lead', 'Journal Voucher', 'Installation Note', 'Material Request', 'GL Entry', 'Expense Claim', 'Opportunity', 'Delivery Note', 'Customer Issue', 'Bin', 'Authorization Rule', 'Attendance', 'C-Form', - 'Appraisal', 'Installation Note', 'Communication'] + 'Appraisal', 'Installation Note', 'Communication', "Supplier Quotation"] for d in trans: for t in webnotes.conn.sql("select options from tabDocField where parent='%s' and fieldtype='Table'" % d): webnotes.conn.sql("delete from `tab%s`" % (t)) From ac24d0fa986431980510a2e0ba0487831788a576 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 1 Jul 2013 14:03:08 +0530 Subject: [PATCH 281/295] [fixes] ignore single doctypes in chekcing linked documents --- patches/patch_list.py | 1 + utilities/cleanup_data.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/patches/patch_list.py b/patches/patch_list.py index efd4d51cce..4955c75e6c 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -242,4 +242,5 @@ patch_list = [ "execute:webnotes.delete_doc('DocType', 'System Console')", "patches.june_2013.p04_fix_event_for_lead_oppty_project", "patches.june_2013.p05_remove_search_criteria_reports", + "execute:webnotes.delete_doc('DocType', 'Update Delivery Date')", ] \ No newline at end of file diff --git a/utilities/cleanup_data.py b/utilities/cleanup_data.py index c9de4a5e23..df7f4be159 100644 --- a/utilities/cleanup_data.py +++ b/utilities/cleanup_data.py @@ -20,14 +20,16 @@ import webnotes def delete_transactions(): print "Deleting transactions..." - trans = ['Timesheet', 'Task', 'Support Ticket', 'Stock Reconciliation', 'Stock Ledger Entry', + trans = ['Task', 'Support Ticket', 'Stock Reconciliation', 'Stock Ledger Entry', 'Stock Entry', 'Sales Order', 'Salary Slip','Sales Invoice', 'Quotation', 'Quality Inspection', 'Purchase Receipt', 'Purchase Order', 'Production Order', 'POS Setting', 'Period Closing Voucher', 'Purchase Invoice', 'Maintenance Visit', 'Maintenance Schedule', 'Leave Application', 'Leave Allocation', 'Lead', 'Journal Voucher', 'Installation Note', 'Material Request', 'GL Entry', 'Expense Claim', 'Opportunity', 'Delivery Note', 'Customer Issue', 'Bin', 'Authorization Rule', 'Attendance', 'C-Form', - 'Appraisal', 'Installation Note', 'Communication', "Supplier Quotation"] + 'Appraisal', 'Installation Note', 'Communication', "Supplier Quotation", "Newsletter", + "Job Applicant", "Web Page", "Website Slideshow", "Blog Post", "Blog Category", "Blogger", + "Time Log", "Time Log Batch", "Workflow"] for d in trans: for t in webnotes.conn.sql("select options from tabDocField where parent='%s' and fieldtype='Table'" % d): webnotes.conn.sql("delete from `tab%s`" % (t)) @@ -66,6 +68,7 @@ def delete_masters(): 'Item Group': ['All Item Groups', 'Default'], 'Item': '', 'Holiday List': '', + 'Activity Type': '', 'Grade': '', 'Feed': '', 'Expense Claim Type': ['Travel', 'Medical', 'Calls', 'Food', 'Others'], From 3f30e3132bcf5076379cb3742cf082387498778b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 4 Jul 2013 14:36:16 +0530 Subject: [PATCH 282/295] [mapper] reload --- patches/patch_list.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patches/patch_list.py b/patches/patch_list.py index 4955c75e6c..fc4fd738fe 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -243,4 +243,6 @@ patch_list = [ "patches.june_2013.p04_fix_event_for_lead_oppty_project", "patches.june_2013.p05_remove_search_criteria_reports", "execute:webnotes.delete_doc('DocType', 'Update Delivery Date')", + "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Order-Purchase Invoice')", + "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Receipt-Purchase Invoice')", ] \ No newline at end of file From e2dd8009994e2cf67438be56f477870aecaea580 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 4 Jul 2013 14:37:58 +0530 Subject: [PATCH 283/295] Update purchase_register.py --- accounts/report/purchase_register/purchase_register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 655cf8ced0..0fbe451035 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -58,7 +58,7 @@ def execute(filters=None): row.append(invoice_tax_map.get(inv.name, {}).get(tax_acc)) # total tax, grand total, outstanding amount & rounded total - row += [inv.other_charges_total, inv.grand_total, flt(inv.grand_total, 2), \ + row += [inv.total_tax, inv.grand_total, flt(inv.grand_total, 2), \ inv.outstanding_amount] data.append(row) @@ -164,4 +164,4 @@ def get_account_details(invoice_list): where name in (%s)""" % ", ".join(["%s"]*len(accounts)), tuple(accounts), as_dict=1): account_map[acc.name] = acc.parent_account - return account_map \ No newline at end of file + return account_map From bce622d723f494b7f48b9e58940d3faa9a9061a9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 4 Jul 2013 14:40:21 +0530 Subject: [PATCH 284/295] Update patch_list.py --- patches/patch_list.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/patches/patch_list.py b/patches/patch_list.py index fc4fd738fe..9fdf39b539 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -243,6 +243,6 @@ patch_list = [ "patches.june_2013.p04_fix_event_for_lead_oppty_project", "patches.june_2013.p05_remove_search_criteria_reports", "execute:webnotes.delete_doc('DocType', 'Update Delivery Date')", - "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Order-Purchase Invoice')", - "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Receipt-Purchase Invoice')", -] \ No newline at end of file + "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Order-Purchase Invoice') # 2013-07-04", + "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Receipt-Purchase Invoice') # 2013-07-04", +] From 7dc77f764fe19c20f862df1ef5103e05225836c7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 4 Jul 2013 14:56:29 +0530 Subject: [PATCH 285/295] Update Purchase Order-Purchase Invoice.txt --- .../Purchase Order-Purchase Invoice.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/DocType Mapper/Purchase Order-Purchase Invoice/Purchase Order-Purchase Invoice.txt b/accounts/DocType Mapper/Purchase Order-Purchase Invoice/Purchase Order-Purchase Invoice.txt index ce45824e40..031abd216d 100644 --- a/accounts/DocType Mapper/Purchase Order-Purchase Invoice/Purchase Order-Purchase Invoice.txt +++ b/accounts/DocType Mapper/Purchase Order-Purchase Invoice/Purchase Order-Purchase Invoice.txt @@ -4,7 +4,7 @@ "docstatus": 0, "creation": "2010-08-08 17:09:35", "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" + "modified": "2013-07-04 12:49:50" }, { "name": "__common__", @@ -120,4 +120,4 @@ "to_table": "Purchase Taxes and Charges", "validation_logic": "docstatus =1" } -] \ No newline at end of file +] From 19f2396f9567004db577d8db2eb0797779f04790 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 4 Jul 2013 14:56:50 +0530 Subject: [PATCH 286/295] Update Purchase Receipt-Purchase Invoice.txt --- .../Purchase Receipt-Purchase Invoice.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/DocType Mapper/Purchase Receipt-Purchase Invoice/Purchase Receipt-Purchase Invoice.txt b/accounts/DocType Mapper/Purchase Receipt-Purchase Invoice/Purchase Receipt-Purchase Invoice.txt index f6c87e266c..5ed4b2aced 100644 --- a/accounts/DocType Mapper/Purchase Receipt-Purchase Invoice/Purchase Receipt-Purchase Invoice.txt +++ b/accounts/DocType Mapper/Purchase Receipt-Purchase Invoice/Purchase Receipt-Purchase Invoice.txt @@ -4,7 +4,7 @@ "docstatus": 0, "creation": "2010-08-08 17:09:35", "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" + "modified": "2013-07-04 12:49:50" }, { "name": "__common__", @@ -141,4 +141,4 @@ "to_table": "Purchase Taxes and Charges", "validation_logic": "docstatus=1" } -] \ No newline at end of file +] From fa8d69c7cabb50a3403b4a87295ef26b6fcf97ff Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 4 Jul 2013 15:02:23 +0530 Subject: [PATCH 287/295] Update patch_list.py --- patches/patch_list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/patch_list.py b/patches/patch_list.py index 9fdf39b539..56cb93c456 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -243,6 +243,6 @@ patch_list = [ "patches.june_2013.p04_fix_event_for_lead_oppty_project", "patches.june_2013.p05_remove_search_criteria_reports", "execute:webnotes.delete_doc('DocType', 'Update Delivery Date')", - "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Order-Purchase Invoice') # 2013-07-04", - "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Receipt-Purchase Invoice') # 2013-07-04", + "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Order-Purchase Invoice') # 2013-07-04 3:00", + "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Receipt-Purchase Invoice') # 2013-07-04 3:00", ] From 3525481823428aeaee0c51992a8220ff9ac343fd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 5 Jul 2013 11:27:26 +0530 Subject: [PATCH 288/295] [patch] restore defaults --- patches/july_2013/__init__.py | 0 patches/july_2013/restore_tree_roots.py | 3 +++ startup/install.py | 6 +++--- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 patches/july_2013/__init__.py create mode 100644 patches/july_2013/restore_tree_roots.py diff --git a/patches/july_2013/__init__.py b/patches/july_2013/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/patches/july_2013/restore_tree_roots.py b/patches/july_2013/restore_tree_roots.py new file mode 100644 index 0000000000..ea3a9913ea --- /dev/null +++ b/patches/july_2013/restore_tree_roots.py @@ -0,0 +1,3 @@ +def execute(): + from startup.install import import_defaults + import_defaults() \ No newline at end of file diff --git a/startup/install.py b/startup/install.py index 5ddbf094c8..1553e1b72f 100644 --- a/startup/install.py +++ b/startup/install.py @@ -176,6 +176,6 @@ def import_defaults(): ] for r in records: - doc = webnotes.doc(r) - doc.insert() - \ No newline at end of file + if not webnotes.conn.exists(r['doctype'], r['name']): + bean = webnotes.bean(r) + bean.insert() \ No newline at end of file From 00d344b93459a48f119aac992f195981dbdc7c2b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 5 Jul 2013 11:32:18 +0530 Subject: [PATCH 289/295] [fixes] default records while install --- startup/install.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/startup/install.py b/startup/install.py index 1553e1b72f..204df87913 100644 --- a/startup/install.py +++ b/startup/install.py @@ -139,8 +139,10 @@ def import_defaults(): {'doctype': 'Supplier Type', 'name': 'Default Supplier Type', 'supplier_type': 'Default Supplier Type'}, # Price List - {'doctype': 'Price List', 'name': 'Default Price List', 'price_list_name': 'Default Price List'}, - {'doctype': 'Price List', 'name': 'Standard', 'price_list_name': 'Standard'}, + {'doctype': 'Price List', 'name': 'Default Price List', + 'price_list_name': 'Default Price List', "valid_for_all_countries": 1}, + {'doctype': 'Price List', 'name': 'Standard', 'price_list_name': 'Standard', + "valid_for_all_countries": 1}, # warehouse type {'doctype': 'Warehouse Type', 'name': 'Default Warehouse Type', 'warehouse_type': 'Default Warehouse Type'}, From 1daad6b1ca52eef33c892647c7309be414a0bb71 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jul 2013 11:28:59 +0530 Subject: [PATCH 290/295] [report] added some extra columns --- .../delivered_items_to_be_billed.txt | 6 +++--- .../ordered_items_to_be_delivered.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt b/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt index e84c597b1d..a9a18e7293 100644 --- a/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt +++ b/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-25 10:38:57", + "creation": "2013-05-02 15:20:25", "docstatus": 0, - "modified": "2013-05-01 11:56:43", + "modified": "2013-07-08 11:08:23", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,7 +10,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select \n `tabDelivery Note`.`name` as \"Delivery Note:Link/Delivery Note:120\",\n`tabDelivery Note`.`customer` as \"Customer:Link/Customer:120\",\n`tabDelivery Note`.`status` as \"Status\",\n `tabDelivery Note`.`posting_date` as \"Date:Date\",\n `tabDelivery Note`.`project_name` as \"Project\",\n `tabDelivery Note Item`.item_code as \"Item:Link/Item:120\",\n `tabDelivery Note Item`.amount as \"Amount:Currency:110\",\n (`tabDelivery Note Item`.billed_amt * ifnull(`tabDelivery Note`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (ifnull(`tabDelivery Note Item`.amount,0) - (ifnull(`tabDelivery Note Item`.billed_amt,0) * ifnull(`tabDelivery Note`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabDelivery Note Item`.item_name as \"Item Name::150\",\n `tabDelivery Note Item`.description as \"Description:Data:200\"\nfrom\n `tabDelivery Note`, `tabDelivery Note Item`\nwhere\n `tabDelivery Note Item`.`parent` = `tabDelivery Note`.`name`\n and `tabDelivery Note`.docstatus = 1\n and `tabDelivery Note`.status != \"Stopped\"\n and ifnull(`tabDelivery Note Item`.billed_amt,0) < ifnull(`tabDelivery Note Item`.export_amount,0)\norder by `tabDelivery Note`.posting_date asc", + "query": "select \n `tabDelivery Note`.`name` as \"Delivery Note:Link/Delivery Note:120\",\n`tabDelivery Note`.`customer` as \"Customer:Link/Customer:120\",\n`tabDelivery Note`.`status` as \"Status\",\n `tabDelivery Note`.`posting_date` as \"Date:Date\",\n `tabDelivery Note`.`project_name` as \"Project\",\n `tabDelivery Note Item`.item_code as \"Item:Link/Item:120\",\n `tabDelivery Note Item`.amount as \"Amount:Currency:110\",\n (`tabDelivery Note Item`.billed_amt * ifnull(`tabDelivery Note`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (ifnull(`tabDelivery Note Item`.amount,0) - (ifnull(`tabDelivery Note Item`.billed_amt,0) * ifnull(`tabDelivery Note`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabDelivery Note Item`.item_name as \"Item Name::150\",\n `tabDelivery Note Item`.description as \"Description:Data:200\",\n `tabDelivery Note Item`.prevdoc_docname as \"Sales Order:Link/Sales Order:120\",\n `tabDelivery Note Item`.prevdoc_date as \"SO Date:Date:100\",\n `tabDelivery Note`.address_display as \"Customer Address::150\"\nfrom\n `tabDelivery Note`, `tabDelivery Note Item`\nwhere\n `tabDelivery Note Item`.`parent` = `tabDelivery Note`.`name`\n and `tabDelivery Note`.docstatus = 1\n and `tabDelivery Note`.status != \"Stopped\"\n and ifnull(`tabDelivery Note Item`.billed_amt,0) < ifnull(`tabDelivery Note Item`.export_amount,0)\norder by `tabDelivery Note`.posting_date asc", "ref_doctype": "Sales Invoice", "report_name": "Delivered Items To Be Billed", "report_type": "Query Report" diff --git a/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.txt b/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.txt index 730e3d724c..65ee389f07 100644 --- a/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.txt +++ b/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-21 14:26:49", + "creation": "2013-02-22 18:01:55", "docstatus": 0, - "modified": "2013-02-22 15:53:01", + "modified": "2013-07-08 11:17:54", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,7 +10,7 @@ "doctype": "Report", "is_standard": "Yes", "name": "__common__", - "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Stopped\"\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", + "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.amount as \"Amount:Float:140\",\n `tabSales Order`.`delivery_date` as \"Expected Delivery Date:Date:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Stopped\"\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", "ref_doctype": "Delivery Note", "report_name": "Ordered Items To Be Delivered", "report_type": "Query Report" From 70eda07bcfe58769f55220e4dbdbeffbb66a0861 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jul 2013 11:33:09 +0530 Subject: [PATCH 291/295] [report] deleted duplicate report --- patches/patch_list.py | 1 + selling/page/selling_home/selling_home.js | 4 ++-- .../__init__.py | 0 .../sales_orders_pending_to_be_delivered.txt | 22 ------------------- 4 files changed, 3 insertions(+), 24 deletions(-) delete mode 100644 selling/report/sales_orders_pending_to_be_delivered/__init__.py delete mode 100644 selling/report/sales_orders_pending_to_be_delivered/sales_orders_pending_to_be_delivered.txt diff --git a/patches/patch_list.py b/patches/patch_list.py index 56cb93c456..74eae984fe 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -245,4 +245,5 @@ patch_list = [ "execute:webnotes.delete_doc('DocType', 'Update Delivery Date')", "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Order-Purchase Invoice') # 2013-07-04 3:00", "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Receipt-Purchase Invoice') # 2013-07-04 3:00", + "execute: webnotes.delete_doc('Report', 'Sales Orders Pending To Be Delivered')", ] diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 90f8a9465c..e912fd8b61 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -149,8 +149,8 @@ wn.module_page["Selling"] = [ route: "query-report/Customer Addresses And Contacts" }, { - "label":wn._("Sales Orders Pending to be Delivered"), - route: "query-report/Sales Orders Pending To Be Delivered" + "label":wn._("Ordered Items To Be Delivered"), + route: "query-report/Ordered Items To Be Delivered", }, { "label":wn._("Sales Person-wise Transaction Summary"), diff --git a/selling/report/sales_orders_pending_to_be_delivered/__init__.py b/selling/report/sales_orders_pending_to_be_delivered/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/selling/report/sales_orders_pending_to_be_delivered/sales_orders_pending_to_be_delivered.txt b/selling/report/sales_orders_pending_to_be_delivered/sales_orders_pending_to_be_delivered.txt deleted file mode 100644 index c145f4ea39..0000000000 --- a/selling/report/sales_orders_pending_to_be_delivered/sales_orders_pending_to_be_delivered.txt +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "creation": "2013-02-21 14:26:46", - "docstatus": 0, - "modified": "2013-02-22 15:53:24", - "modified_by": "Administrator", - "owner": "Administrator" - }, - { - "doctype": "Report", - "is_standard": "Yes", - "name": "__common__", - "query": "select \n `tabSales Order`.`name` as \"S.O. No.:Link/Sales Order:120\",\n `tabSales Order`.`transaction_date` as \"S.O. Date:Date\",\n `tabSales Order`.`delivery_date` as \"Expected Delivery Date:Date\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order Item`.item_code as \"Item Code:Link/Item:120\",\n `tabSales Order Item`.description as \"Description\",\n `tabSales Order Item`.qty as \"Qty:Float\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float\",\n `tabSales Order`.`po_no` as \"P.O. No.\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Stopped\"\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", - "ref_doctype": "Sales Order", - "report_name": "Sales Orders Pending To Be Delivered", - "report_type": "Query Report" - }, - { - "doctype": "Report", - "name": "Sales Orders Pending To Be Delivered" - } -] \ No newline at end of file From 690c2f5ffe6a82a88b24982b04d3d054a35bf7ea Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 8 Jul 2013 11:55:50 +0530 Subject: [PATCH 292/295] [patch] [fix] delete report --- patches/patch_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/patch_list.py b/patches/patch_list.py index 74eae984fe..812f641fcb 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -245,5 +245,5 @@ patch_list = [ "execute:webnotes.delete_doc('DocType', 'Update Delivery Date')", "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Order-Purchase Invoice') # 2013-07-04 3:00", "execute:webnotes.reload_doc('accounts', 'DocType Mapper', 'Purchase Receipt-Purchase Invoice') # 2013-07-04 3:00", - "execute: webnotes.delete_doc('Report', 'Sales Orders Pending To Be Delivered')", + "execute:webnotes.delete_doc('Report', 'Sales Orders Pending To Be Delivered')", ] From d3c74c1a1bbb023fd4b2314a098c659624fca7dd Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 9 Jul 2013 11:51:24 +0530 Subject: [PATCH 293/295] [Cleanup][Report] Added company in Warehouse-wise Stock Balance & removed filters from both reports --- .../batch_wise_balance_history.js | 21 ---------- .../batch_wise_balance_history.py | 11 ------ .../warehouse_wise_stock_balance.js | 14 ------- .../warehouse_wise_stock_balance.py | 38 ++++++++----------- 4 files changed, 16 insertions(+), 68 deletions(-) diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.js b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js index 0ba1938a59..98293e45f7 100644 --- a/stock/report/batch_wise_balance_history/batch_wise_balance_history.js +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js @@ -1,26 +1,5 @@ wn.query_reports["Batch-Wise Balance History"] = { "filters": [ - { - "fieldname":"item_code", - "label": "Item", - "fieldtype": "Link", - "options": "Item", - "width": "80" - }, - { - "fieldname":"warehouse", - "label": "Warehouse", - "fieldtype": "Link", - "options": "Warehouse", - "width": "80" - }, - { - "fieldname":"batch_no", - "label": "Batch", - "fieldtype": "Link", - "options": "Batch", - "width": "80" - }, { "fieldname":"from_date", "label": "From Date", diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index ca3e775f72..530465e9ae 100644 --- a/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -49,15 +49,6 @@ def get_columns(filters): def get_conditions(filters): conditions = "" - if filters.get("item_code"): - conditions += " and item_code='%s'" % filters["item_code"] - - if filters.get("warehouse"): - conditions += " and warehouse='%s'" % filters["warehouse"] - - if filters.get("batch_no"): - conditions += " and batch_no='%s'" % filters["batch_no"] - if not filters.get("from_date"): webnotes.msgprint("Please enter From Date", raise_exception=1) @@ -100,8 +91,6 @@ def get_item_warehouse_batch_map(filters): return iwb_map def get_item_details(filters): - if filters.get("item_code"): - conditions = " and name = '%s'" % filters["item_code"] item_map = {} for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): item_map.setdefault(d.name, d) diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js index 5e1eb3a7b2..2ce5b4b4ab 100644 --- a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js @@ -1,19 +1,5 @@ wn.query_reports["Warehouse-Wise Stock Balance"] = { "filters": [ - { - "fieldname":"item_code", - "label": "Item", - "fieldtype": "Link", - "options": "Item", - "width": "80" - }, - { - "fieldname":"warehouse", - "label": "Warehouse", - "fieldtype": "Link", - "options": "Warehouse", - "width": "80" - }, { "fieldname":"from_date", "label": "From Date", diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py index 324bbe3e8c..4389aa59e4 100644 --- a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py @@ -26,34 +26,29 @@ def execute(filters=None): iwb_map = get_item_warehouse_map(filters) data = [] - for item in sorted(iwb_map): - for wh in sorted(iwb_map[item]): - qty_dict = iwb_map[item][wh] - data.append([item, item_map[item]["item_name"], - item_map[item]["description"], wh, - qty_dict.opening_qty, qty_dict.in_qty, - qty_dict.out_qty, qty_dict.bal_qty - ]) + for company in sorted(iwb_map): + for item in sorted(iwb_map[company]): + for wh in sorted(iwb_map[company][item]): + qty_dict = iwb_map[company][item][wh] + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], wh, + qty_dict.opening_qty, qty_dict.in_qty, + qty_dict.out_qty, qty_dict.bal_qty, company + ]) return columns, data def get_columns(filters): """return columns based on filters""" - columns = ["Item:Link/Item:100"] + ["Item Name::150"] + ["Description::150"] + \ - ["Warehouse:Link/Warehouse:100"] + ["Opening Qty::90"] + \ - ["In Qty::80"] + ["Out Qty::80"] + ["Balance Qty::90"] + columns = ["Item:Link/Item:100", "Item Name::150", "Description::150", \ + "Warehouse:Link/Warehouse:100", "Opening Qty::90", \ + "In Qty::80", "Out Qty::80", "Balance Qty::90", "Company:Link/Company:100"] return columns def get_conditions(filters): conditions = "" - if filters.get("item_code"): - conditions += " and item_code='%s'" % filters["item_code"] - - if filters.get("warehouse"): - conditions += " and warehouse='%s'" % filters["warehouse"] - if not filters.get("from_date"): webnotes.msgprint("Please enter From Date", raise_exception=1) @@ -68,7 +63,7 @@ def get_conditions(filters): def get_stock_ledger_entries(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select item_code, warehouse, - posting_date, actual_qty + posting_date, actual_qty, company from `tabStock Ledger Entry` where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % conditions, as_dict=1) @@ -78,10 +73,11 @@ def get_item_warehouse_map(filters): iwb_map = {} for d in sle: - iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, webnotes._dict({\ + iwb_map.setdefault(d.company, {}).setdefault(d.item_code, {}).\ + setdefault(d.warehouse, webnotes._dict({\ "opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0 })) - qty_dict = iwb_map[d.item_code][d.warehouse] + qty_dict = iwb_map[d.company][d.item_code][d.warehouse] if d.posting_date < filters["from_date"]: qty_dict.opening_qty += flt(d.actual_qty) elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]: @@ -95,8 +91,6 @@ def get_item_warehouse_map(filters): return iwb_map def get_item_details(filters): - if filters.get("item_code"): - conditions = " and name = '%s'" % filters["item_code"] item_map = {} for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): item_map.setdefault(d.name, d) From 5c33e59e7a5795e5c83a9a4c3598a90de65e0683 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 9 Jul 2013 15:38:40 +0530 Subject: [PATCH 294/295] [fix][report] sales/purchase register, same accounts as income and tax account --- .../purchase_register/purchase_register.py | 31 ++++++++++++------ .../report/sales_register/sales_register.py | 32 +++++++++++++------ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 0fbe451035..6fec6608a3 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -23,7 +23,7 @@ def execute(filters=None): if not filters: filters = {} invoice_list = get_invoices(filters) - columns, expense_accounts, tax_accounts = get_columns(invoice_list) + columns, expense_accounts, tax_accounts, renamed_columns = get_columns(invoice_list) if not invoice_list: @@ -31,7 +31,7 @@ def execute(filters=None): return columns, invoice_list invoice_expense_map = get_invoice_expense_map(invoice_list) - invoice_tax_map = get_invoice_tax_map(invoice_list) + invoice_tax_map = get_invoice_tax_map(invoice_list, renamed_columns) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) account_map = get_account_details(invoice_list) @@ -73,7 +73,9 @@ def get_columns(invoice_list): "Project:Link/Project:80", "Bill No::120", "Bill Date:Date:80", "Remarks::150", "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100" ] - expense_accounts = tax_accounts = [] + expense_accounts = tax_accounts = expense_columns = tax_columns = [] + renamed_columns = {} + if invoice_list: expense_accounts = webnotes.conn.sql_list("""select distinct expense_head from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' @@ -85,13 +87,23 @@ def get_columns(invoice_list): and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) order by account_head""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + + + expense_columns = [(account + ":Currency:120") for account in expense_accounts] + for account in tax_accounts: + if account in expense_accounts: + new_account = account + " (Tax)" + renamed_columns[account] = new_account + tax_columns.append(new_account + ":Currency:120") + else: + tax_columns.append(account + ":Currency:120") - columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ - ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ + columns = columns + expense_columns + \ + ["Net Total:Currency:120"] + tax_columns + \ ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + \ ["Rounded Total:Currency:120"] + ["Outstanding Amount:Currency:120"] - return columns, expense_accounts, tax_accounts + return columns, expense_accounts, tax_accounts, renamed_columns def get_conditions(filters): conditions = "" @@ -125,15 +137,16 @@ def get_invoice_expense_map(invoice_list): return invoice_expense_map -def get_invoice_tax_map(invoice_list): +def get_invoice_tax_map(invoice_list, renamed_columns): tax_details = webnotes.conn.sql("""select parent, account_head, sum(tax_amount) as tax_amount from `tabPurchase Taxes and Charges` where parent in (%s) group by parent, account_head""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) invoice_tax_map = {} for d in tax_details: - invoice_tax_map.setdefault(d.parent, webnotes._dict()).setdefault(d.account_head, []) - invoice_tax_map[d.parent][d.account_head] = flt(d.tax_amount) + account = renamed_columns.get(d.account_head) or d.account_head + invoice_tax_map.setdefault(d.parent, webnotes._dict()).setdefault(account, []) + invoice_tax_map[d.parent][account] = flt(d.tax_amount) return invoice_tax_map diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 3eebc33e9c..4c49cc86e8 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -23,14 +23,14 @@ def execute(filters=None): if not filters: filters = {} invoice_list = get_invoices(filters) - columns, income_accounts, tax_accounts = get_columns(invoice_list) + columns, income_accounts, tax_accounts, renamed_columns = get_columns(invoice_list) if not invoice_list: msgprint(_("No record found")) return columns, invoice_list invoice_income_map = get_invoice_income_map(invoice_list) - invoice_tax_map = get_invoice_tax_map(invoice_list) + invoice_tax_map = get_invoice_tax_map(invoice_list, renamed_columns) invoice_so_dn_map = get_invoice_so_dn_map(invoice_list) customer_map = get_customer_deatils(invoice_list) @@ -74,7 +74,9 @@ def get_columns(invoice_list): "Remarks::150", "Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100" ] - income_accounts = tax_accounts = [] + income_accounts = tax_accounts = income_columns = tax_columns = [] + renamed_columns = {} + if invoice_list: income_accounts = webnotes.conn.sql_list("""select distinct income_account from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) @@ -83,15 +85,24 @@ def get_columns(invoice_list): tax_accounts = webnotes.conn.sql_list("""select distinct account_head from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' - and docstatus = 1 and parent in (%s) order by account_head""" % + and docstatus = 1 and ifnull(tax_amount, 0) > 0 + and parent in (%s) order by account_head""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + + income_columns = [(account + ":Currency:120") for account in income_accounts] + for account in tax_accounts: + if account in income_accounts: + new_account = account + " (Tax)" + renamed_columns[account] = new_account + tax_columns.append(new_account + ":Currency:120") + else: + tax_columns.append(account + ":Currency:120") - columns = columns + [(account + ":Currency:120") for account in income_accounts] + \ - ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ + columns = columns + income_columns + ["Net Total:Currency:120"] + tax_columns + \ ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + \ ["Rounded Total:Currency:120"] + ["Outstanding Amount:Currency:120"] - return columns, income_accounts, tax_accounts + return columns, income_accounts, tax_accounts, renamed_columns def get_conditions(filters): conditions = "" @@ -124,15 +135,16 @@ def get_invoice_income_map(invoice_list): return invoice_income_map -def get_invoice_tax_map(invoice_list): +def get_invoice_tax_map(invoice_list, renamed_columns): tax_details = webnotes.conn.sql("""select parent, account_head, sum(tax_amount) as tax_amount from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) invoice_tax_map = {} for d in tax_details: - invoice_tax_map.setdefault(d.parent, webnotes._dict()).setdefault(d.account_head, []) - invoice_tax_map[d.parent][d.account_head] = flt(d.tax_amount) + account = renamed_columns.get(d.account_head) or d.account_head + invoice_tax_map.setdefault(d.parent, webnotes._dict()).setdefault(account, []) + invoice_tax_map[d.parent][account] = flt(d.tax_amount) return invoice_tax_map From 8faa90fb203e00df38436695c8aab397c9e96c5c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 12 Jul 2013 17:50:34 +0530 Subject: [PATCH 295/295] [fix] [minor] add customer_name/supplier_name in sales/purchase register --- .../item_wise_purchase_register.py | 8 ++++---- .../item_wise_sales_register/item_wise_sales_register.py | 9 ++++----- accounts/report/purchase_register/purchase_register.py | 9 ++++----- accounts/report/sales_register/sales_register.py | 6 +++--- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index f7afb3dfb6..9f5f071e02 100644 --- a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -26,9 +26,9 @@ def execute(filters=None): data = [] for d in item_list: expense_head = d.expense_head or aii_account_map.get(d.company) - data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, d.supplier, - d.credit_to, d.project_name, d.company, d.purchase_order, d.purchase_receipt, - expense_head, d.qty, d.rate, d.amount]) + data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, + d.supplier_name, d.credit_to, d.project_name, d.company, d.purchase_order, + d.purchase_receipt, expense_head, d.qty, d.rate, d.amount]) return columns, data @@ -59,7 +59,7 @@ def get_items(filters): return webnotes.conn.sql("""select pi.name, pi.posting_date, pi.credit_to, pi.company, pi.supplier, pi.remarks, pi_item.item_code, pi_item.item_name, pi_item.item_group, pi_item.project_name, pi_item.purchase_order, pi_item.purchase_receipt, - pi_item.expense_head, pi_item.qty, pi_item.rate, pi_item.amount + pi_item.expense_head, pi_item.qty, pi_item.rate, pi_item.amount, pi.supplier_name from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item where pi.name = pi_item.parent and pi.docstatus = 1 %s order by pi.posting_date desc, pi_item.item_code desc""" % conditions, filters, as_dict=1) diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/accounts/report/item_wise_sales_register/item_wise_sales_register.py index f3ed2a1242..4d099e0b97 100644 --- a/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -16,7 +16,6 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import flt def execute(filters=None): if not filters: filters = {} @@ -26,9 +25,9 @@ def execute(filters=None): data = [] for d in item_list: - data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, d.customer, - d.debit_to, d.territory, d.project_name, d.company, d.sales_order, d.delivery_note, - d.income_account, d.qty, d.basic_rate, d.amount]) + data.append([d.item_code, d.item_name, d.item_group, d.name, d.posting_date, + d.customer_name, d.debit_to, d.territory, d.project_name, d.company, d.sales_order, + d.delivery_note, d.income_account, d.qty, d.basic_rate, d.amount]) return columns, data @@ -61,7 +60,7 @@ def get_items(filters): return webnotes.conn.sql("""select si.name, si.posting_date, si.debit_to, si.project_name, si.customer, si.remarks, si.territory, si.company, si_item.item_code, si_item.item_name, si_item.item_group, si_item.sales_order, si_item.delivery_note, si_item.income_account, - si_item.qty, si_item.basic_rate, si_item.amount + si_item.qty, si_item.basic_rate, si_item.amount, si.customer_name from `tabSales Invoice` si, `tabSales Invoice Item` si_item where si.name = si_item.parent and si.docstatus = 1 %s order by si.posting_date desc, si_item.item_code desc""" % conditions, filters, as_dict=1) \ No newline at end of file diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 6fec6608a3..705a65436d 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -42,7 +42,7 @@ def execute(filters=None): purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", []))) project_name = list(set(invoice_po_pr_map.get(inv.name, {}).get("project_name", []))) - row = [inv.name, inv.posting_date, inv.supplier, inv.credit_to, + row = [inv.name, inv.posting_date, inv.supplier_name, inv.credit_to, account_map.get(inv.credit_to), ", ".join(project_name), inv.bill_no, inv.bill_date, inv.remarks, ", ".join(purchase_order), ", ".join(purchase_receipt)] @@ -68,7 +68,7 @@ def execute(filters=None): def get_columns(invoice_list): """return columns based on filters""" columns = [ - "Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier:Link/Supplier:120", + "Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier::120", "Supplier Account:Link/Account:120", "Account Group:LInk/Account:120", "Project:Link/Project:80", "Bill No::120", "Bill Date:Date:80", "Remarks::150", "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100" @@ -118,9 +118,8 @@ def get_conditions(filters): def get_invoices(filters): conditions = get_conditions(filters) - return webnotes.conn.sql("""select name, posting_date, credit_to, - supplier, bill_no, bill_date, remarks, net_total, - total_tax, grand_total, outstanding_amount + return webnotes.conn.sql("""select name, posting_date, credit_to, supplier, supplier_name, + bill_no, bill_date, remarks, net_total, total_tax, grand_total, outstanding_amount from `tabPurchase Invoice` where docstatus = 1 %s order by posting_date desc, name desc""" % conditions, filters, as_dict=1) diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 4c49cc86e8..2bc28509c1 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -42,7 +42,7 @@ def execute(filters=None): sales_order = list(set(invoice_so_dn_map.get(inv.name, {}).get("sales_order", []))) delivery_note = list(set(invoice_so_dn_map.get(inv.name, {}).get("delivery_note", []))) - row = [inv.name, inv.posting_date, inv.customer, inv.debit_to, + row = [inv.name, inv.posting_date, inv.customer_name, inv.debit_to, account_map.get(inv.debit_to), customer_map.get(inv.customer), inv.project_name, inv.remarks, ", ".join(sales_order), ", ".join(delivery_note)] @@ -68,7 +68,7 @@ def execute(filters=None): def get_columns(invoice_list): """return columns based on filters""" columns = [ - "Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer:Link/Customer:120", + "Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer::120", "Customer Account:Link/Account:120", "Account Group:LInk/Account:120", "Territory:Link/Territory:80", "Project:Link/Project:80", "Remarks::150", "Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100" @@ -118,7 +118,7 @@ def get_conditions(filters): def get_invoices(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select name, posting_date, debit_to, project_name, customer, - remarks, net_total, other_charges_total, grand_total, rounded_total, + customer_name, remarks, net_total, other_charges_total, grand_total, rounded_total, outstanding_amount from `tabSales Invoice` where docstatus = 1 %s order by posting_date desc, name desc""" % conditions, filters, as_dict=1)

    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 Date: Thu, 25 Apr 2013 11:06:17 +0530 Subject: [PATCH 038/295] [patch] [file data] reset permissions --- patches/patch_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/patches/patch_list.py b/patches/patch_list.py index abee4f0cbc..a6dd0a217b 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -246,4 +246,5 @@ patch_list = [ "execute:webnotes.delete_doc('DocType Mapper', 'Delivery Note-Packing Slip')", "execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')", "patches.april_2013.p06_default_cost_center", + "execute:webnotes.reset_perms('File Data')", ] \ No newline at end of file From 205f7ce62023f9da04edf7ff604605966dd579f1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Apr 2013 13:35:06 +0530 Subject: [PATCH 039/295] validation for stock and nonstock items in purchase cycle --- .../purchase_invoice/test_purchase_invoice.py | 35 +++++++++++++++++-- controllers/buying_controller.py | 20 ++++++++++- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 621604b96b..a70c932d9b 100644 --- a/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -72,7 +72,38 @@ class TestPurchaseInvoice(unittest.TestCase): ["Stock Received But Not Billed - _TC", 750.0, 0], ["_Test Account Shipping Charges - _TC", 100.0, 0], ["_Test Account VAT - _TC", 120.0, 0], - ["Expenses Included In Valuation - _TC", 0, 250.0] + ["Expenses Included In Valuation - _TC", 0, 250.0], + ]) + + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_values[i][0], gle.account) + self.assertEquals(expected_values[i][1], gle.debit) + self.assertEquals(expected_values[i][2], gle.credit) + + webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + + def test_gl_entries_with_aia_for_non_stock_items(self): + webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1) + + pi = webnotes.bean(copy=test_records[1]) + pi.doclist[1].item_code = "_Test Non Stock Item" + pi.doclist[1].expense_head = "_Test Account Cost for Goods Sold - _TC" + pi.doclist.pop(2) + pi.doclist.pop(3) + pi.run_method("calculate_taxes_and_totals") + pi.insert() + pi.submit() + + gl_entries = webnotes.conn.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s + order by account asc""", pi.doc.name, as_dict=1) + self.assertTrue(gl_entries) + + expected_values = sorted([ + ["_Test Supplier - _TC", 0, 620], + ["_Test Account Cost for Goods Sold - _TC", 500.0, 0], + ["_Test Account VAT - _TC", 120.0, 0], ]) for i, gle in enumerate(gl_entries): @@ -106,7 +137,6 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(tax.account_head, expected_values[i][0]) self.assertEqual(tax.tax_amount, expected_values[i][1]) self.assertEqual(tax.total, expected_values[i][2]) - # print tax.account_head, tax.tax_amount, tax.item_wise_tax_detail expected_values = [ ["_Test Item Home Desktop 100", 90, 59], @@ -142,7 +172,6 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(tax.account_head, expected_values[i][0]) self.assertEqual(tax.tax_amount, expected_values[i][1]) self.assertEqual(tax.total, expected_values[i][2]) - # print tax.account_head, tax.tax_amount, tax.item_wise_tax_detail expected_values = [ ["_Test FG Item", 90, 7059], diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 429737e53e..85d8b9d9ea 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -29,6 +29,7 @@ from controllers.stock_controller import StockController class BuyingController(StockController): def validate(self): super(BuyingController, self).validate() + self.validate_stock_or_nonstock_items() if self.meta.get_field("currency"): self.company_currency = get_company_currency(self.doc.company) self.validate_conversion_rate("currency", "conversion_rate") @@ -41,7 +42,24 @@ class BuyingController(StockController): # set total in words self.set_total_in_words() - + + def validate_stock_or_nonstock_items(self): + items = [d.item_code for d in self.doclist.get({"parentfield": self.fname})] + if self.stock_items and len(items) > len(self.stock_items): + nonstock_items = list(set(items) - set(self.stock_items)) + webnotes.msgprint(_("Stock and non-stock items can not be entered at the same ") + + self.doc.doctype + _(""". You should make separate documents for them. + Stock Items: """) + ", ".join(self.stock_items) + _(""" + Non-stock Items: """) + ", ".join(nonstock_items), raise_exception=1) + + elif items and not self.stock_items: + tax_for_valuation = [d.account_head for d in + self.doclist.get({"parentfield": "purchase_tax_details"}) + if d.category in ["Valuation", "Valuation and Total"]] + if tax_for_valuation: + webnotes.msgprint(_("""Tax Category can not be 'Valuation' or 'Valuation and Total' + as all items are non-stock items"""), raise_exception=1) + def update_item_details(self): for item in self.doclist.get({"parentfield": self.fname}): ret = get_item_details({ From 04c1f94b4e53a19bde20efe687aac6eab3b7959a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Apr 2013 16:27:51 +0530 Subject: [PATCH 040/295] [DN][usability] copy cost center in each row --- controllers/buying_controller.py | 2 +- stock/doctype/delivery_note/delivery_note.js | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 85d8b9d9ea..94e3d56ddc 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -47,7 +47,7 @@ class BuyingController(StockController): items = [d.item_code for d in self.doclist.get({"parentfield": self.fname})] if self.stock_items and len(items) > len(self.stock_items): nonstock_items = list(set(items) - set(self.stock_items)) - webnotes.msgprint(_("Stock and non-stock items can not be entered at the same ") + + webnotes.msgprint(_("Stock and non-stock items can not be entered in the same ") + self.doc.doctype + _(""". You should make separate documents for them. Stock Items: """) + ", ".join(self.stock_items) + _(""" Non-stock Items: """) + ", ".join(nonstock_items), raise_exception=1) diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index 8d09cbd676..0a31dfe80e 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -326,6 +326,17 @@ if (sys_defaults.auto_inventory_accounting) { } // cost center + cur_frm.cscript.cost_center = function(doc, cdt, cdn){ + var d = locals[cdt][cdn]; + if(d.cost_center) { + var cl = getchildren('Delivery Note Item', doc.name, cur_frm.cscript.fname, doc.doctype); + for(var i = 0; i < cl.length; i++){ + if(!cl[i].cost_center) cl[i].cost_center = d.cost_center; + } + } + refresh_field(cur_frm.cscript.fname); + } + cur_frm.fields_dict.delivery_note_details.grid.get_field("cost_center").get_query = function(doc) { return { query: "accounts.utils.get_cost_center_list", From 612424776535c588163bc0146a93160f366ed285 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Apr 2013 16:29:06 +0530 Subject: [PATCH 041/295] [bom] set fetched values at server side --- manufacturing/doctype/bom/bom.js | 37 +++++++++----------- manufacturing/doctype/bom/bom.py | 58 ++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/manufacturing/doctype/bom/bom.js b/manufacturing/doctype/bom/bom.js index 4e8fbc95d1..f0c15fa2a9 100644 --- a/manufacturing/doctype/bom/bom.js +++ b/manufacturing/doctype/bom/bom.js @@ -59,27 +59,22 @@ cur_frm.fields_dict["bom_operations"].grid.on_row_delete = function(cdt, cdn){ set_operation_no(doc); } -cur_frm.cscript.item = function(doc, dt, dn) { - if (doc.item) { - get_server_fields('get_item_details', doc.item, '', doc, dt, dn, 1); - } -} +cur_frm.add_fetch("item", "description", "description"); +cur_frm.add_fetch("item", "stock_uom", "uom"); cur_frm.cscript.workstation = function(doc,dt,dn) { var d = locals[dt][dn]; - if (d.workstation) { - var callback = function(r, rt) { - calculate_op_cost(doc, dt, dn); - calculate_total(doc); - } - get_server_fields('get_workstation_details', d.workstation, - 'bom_operations', doc, dt, dn, 1, callback); - } + wn.model.with_doc("Workstation", d.workstation, function(i, v) { + d.hour_rate = v.hour_rate; + refresh_field("hour_rate"); + calculate_op_cost(doc); + calculate_total(doc); + }); } cur_frm.cscript.hour_rate = function(doc, dt, dn) { - calculate_op_cost(doc, dt, dn); + calculate_op_cost(doc); calculate_total(doc); } @@ -114,7 +109,7 @@ var get_bom_material_detail= function(doc, cdt, cdn) { $.extend(d, r.message); refresh_field("bom_materials"); doc = locals[doc.doctype][doc.name]; - calculate_rm_cost(doc, cdt, cdn); + calculate_rm_cost(doc); calculate_total(doc); }, freeze: true @@ -124,7 +119,7 @@ var get_bom_material_detail= function(doc, cdt, cdn) { cur_frm.cscript.qty = function(doc, cdt, cdn) { - calculate_rm_cost(doc, cdt, cdn); + calculate_rm_cost(doc); calculate_total(doc); } @@ -134,12 +129,12 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) { msgprint("You can not change rate if BOM mentioned agianst any item"); get_bom_material_detail(doc, cdt, cdn); } else { - calculate_rm_cost(doc, cdt, cdn); + calculate_rm_cost(doc); calculate_total(doc); } } -var calculate_op_cost = function(doc, dt, dn) { +var calculate_op_cost = function(doc) { var op = getchildren('BOM Operation', doc.name, 'bom_operations'); total_op_cost = 0; for(var i=0;i Date: Fri, 26 Apr 2013 17:21:49 +0530 Subject: [PATCH 042/295] [sales invoice] [fetching] fetch pos values and customer address contact values --- .../doctype/sales_invoice/sales_invoice.py | 154 ++++++++---------- .../sales_invoice/test_sales_invoice.py | 2 +- utilities/doctype/address/test_address.py | 14 ++ utilities/transaction_base.py | 19 ++- 4 files changed, 104 insertions(+), 85 deletions(-) create mode 100644 utilities/doctype/address/test_address.py diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index f44a787de9..f0222a0ac7 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -46,6 +46,14 @@ class DocType(SellingController): def validate(self): super(DocType, self).validate() + if not (self.doc.contact_person and self.doc.customer_address): + for fieldname, val in self.get_default_address_and_contact("customer").items(): + if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): + self.doc.fields[fieldname] = val + + if cint(self.doc.is_pos): + self.set_pos_fields(for_validate=True) + self.validate_posting_time() self.so_dn_required() self.validate_proj_cust() @@ -153,60 +161,42 @@ class DocType(SellingController): webnotes.msgprint(_("Time Log Batch status must be 'Submitted'") + ":" + d.time_log_batch, raise_exception=True) - def set_pos_fields(self): + def set_pos_fields(self, for_validate=False): """Set retail related fields from pos settings""" - pos = self.pos_details - - if pos: - val = webnotes.conn.sql("""select name from `tabAccount` - where name = %s and docstatus != 2""", - (cstr(self.doc.customer) + " - " + self.get_company_abbr())) - - val = val and val[0][0] or '' - if not val: val = pos[0]['customer_account'] or '' + if cint(self.doc.is_pos) != 1: + return + + if self.pos_settings: + pos = self.pos_settings[0] + + self.doc.conversion_rate = flt(pos.conversion_rate) if not self.doc.debit_to: - webnotes.conn.set(self.doc,'debit_to',val) - - lst = ['territory', 'naming_series', 'currency', 'charge', 'letter_head', 'tc_name', - 'price_list_name', 'company', 'select_print_heading', 'cash_bank_account'] + self.doc.debit_to = self.doc.customer and webnotes.conn.get_value("Account", { + "name": self.doc.customer + " - " + self.get_company_abbr(), + "docstatus": ["!=", 2] + }) or pos.customer_account - for i in lst: - self.doc.fields[i] = pos[0][i] or '' + for fieldname in ('territory', 'naming_series', 'currency', 'charge', 'letter_head', 'tc_name', + 'price_list_name', 'company', 'select_print_heading', 'cash_bank_account'): + if (not for_validate) or (for_validate and not self.doc.fields.get(fieldname)): + self.doc.fields[fieldname] = pos.get(fieldname) - self.set_pos_item_values() - - self.doc.conversion_rate = flt(pos[0]['conversion_rate']) or 0 + # set pos values in items + for doc in self.doclist.get({"parentfield": "entries"}): + if doc.fields.get('item_code'): + for fieldname, val in self.apply_pos_settings(doc.fields).items(): + if (not for_validate) or (for_validate and not self.doc.fields.get(fieldname)): + doc.fields[fieldname] = val - #fetch terms - if self.doc.tc_name: + # fetch terms + if self.doc.tc_name and not self.doc.terms: self.get_tc_details() - #fetch charges - if self.doc.charge: + # fetch charges + if self.doc.charge and not len(self.doclist.get({"parentfield": "other_charges"})): self.get_other_charges() - - def set_pos_item_values(self): - """Set default values related to pos for previously created sales invoice.""" - if cint(self.doc.is_pos) == 1: - dtl = self.pos_details - - for d in getlist(self.doclist,'entries'): - # overwrite if mentioned in item - item = webnotes.conn.sql("""select default_income_account, - default_sales_cost_center, default_warehouse, purchase_account - from tabItem where name = %s""", (d.item_code,), as_dict=1) - - d.income_account = (item and item[0]['default_income_account']) \ - or (dtl and dtl[0]['income_account']) or d.income_account - d.cost_center = (item and item[0]['default_sales_cost_center']) \ - or (dtl and dtl[0]['cost_center']) or d.cost_center - d.warehouse = (item and item[0]['default_warehouse']) \ - or (dtl and dtl[0]['warehouse']) or d.warehouse - d.expense_account = (item and item[0].purchase_account) \ - or (dtl and dtl[0].expense_account) or d.expense_account - def get_customer_account(self): """Get Account Head to which amount needs to be Debited based on Customer""" if not self.doc.company: @@ -299,60 +289,58 @@ class DocType(SellingController): args = args and json.loads(args) or {} if args.get('item_code'): ret = get_obj('Sales Common').get_item_details(args, self) - return self.get_pos_details(args, ret) - else: - for doc in self.doclist: + + if cint(self.doc.is_pos) == 1 and self.pos_settings: + ret = self.apply_pos_settings(args, ret) + + return ret + elif cint(self.doc.is_pos) == 1 and self.pos_settings: + for doc in self.doclist.get({"parentfield": "entries"}): if doc.fields.get('item_code'): - arg = { - 'item_code':doc.fields.get('item_code'), - 'income_account':doc.fields.get('income_account'), - 'cost_center': doc.fields.get('cost_center'), - 'warehouse': doc.fields.get('warehouse'), - 'expense_account': doc.fields.get('expense_account'), - } - - ret = self.get_pos_details(arg) + ret = self.apply_pos_settings(doc.fields) for r in ret: if not doc.fields.get(r): doc.fields[r] = ret[r] @property - def pos_details(self): - if not hasattr(self, "_pos_details"): + def pos_settings(self): + if not hasattr(self, "_pos_settings"): dtl = webnotes.conn.sql("""select * from `tabPOS Setting` where user = %s and company = %s""", (webnotes.session['user'], self.doc.company), as_dict=1) if not dtl: dtl = webnotes.conn.sql("""select * from `tabPOS Setting` where ifnull(user,'') = '' and company = %s""", self.doc.company, as_dict=1) - self._pos_details = dtl + self._pos_settings = dtl - return self._pos_details + return self._pos_settings - def get_pos_details(self, args, ret = {}): - if args['item_code'] and cint(self.doc.is_pos) == 1: - dtl = self.pos_details - - item = webnotes.conn.sql("""select default_income_account, default_sales_cost_center, - default_warehouse, purchase_account from tabItem where name = %s""", - args['item_code'], as_dict=1) - - ret['income_account'] = item and item[0].get('default_income_account') \ - or (dtl and dtl[0].get('income_account') or args.get('income_account')) - - ret['cost_center'] = item and item[0].get('default_sales_cost_center') \ - or (dtl and dtl[0].get('cost_center') or args.get('cost_center')) + def apply_pos_settings(self, args, ret=None): + if not ret: ret = {} + + pos = self.pos_settings[0] + + item = webnotes.conn.sql("""select default_income_account, default_sales_cost_center, + default_warehouse, purchase_account from tabItem where name = %s""", + args.get('item_code'), as_dict=1) + + if item: + item = item[0] - ret['warehouse'] = item and item[0].get('default_warehouse') \ - or (dtl and dtl[0].get('warehouse') or args.get('warehouse')) + ret.update({ + "income_account": item.get("default_income_account") \ + or pos.get("income_account") or args.get("income_account"), + "cost_center": item.get("default_sales_cost_center") \ + or pos.get("cost_center") or args.get("cost_center"), + "warehouse": item.get("default_warehouse") \ + or pos.get("warehouse") or args.get("warehouse"), + "expense_account": item.get("purchase_account") \ + or pos.get("expense_account") or args.get("expense_account") + }) - ret['expense_account'] = item and item[0].get('purchase_account') \ - or (dtl and dtl[0].get('expense_account') or args.get('expense_account')) - - if ret['warehouse']: - actual_qty = webnotes.conn.sql("""select actual_qty from `tabBin` - where item_code = %s and warehouse = %s""", - (args['item_code'], ret['warehouse'])) - ret['actual_qty']= actual_qty and flt(actual_qty[0][0]) or 0 + if ret.get("warehouse"): + ret["actual_qty"] = flt(webnotes.conn.get_value("Bin", + {"item_code": args.get("item_code"), "warehouse": args.get("warehouse")}, + "actual_qty")) return ret def get_barcode_details(self, barcode): diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index fd8dc648d2..31bf024e7f 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -395,7 +395,7 @@ class TestSalesInvoice(unittest.TestCase): for i in xrange(count): base_si = _test(i) -test_dependencies = ["Journal Voucher", "POS Setting"] +test_dependencies = ["Journal Voucher", "POS Setting", "Contact", "Address"] test_records = [ [ diff --git a/utilities/doctype/address/test_address.py b/utilities/doctype/address/test_address.py new file mode 100644 index 0000000000..eddd9c754d --- /dev/null +++ b/utilities/doctype/address/test_address.py @@ -0,0 +1,14 @@ +test_records = [ + [{ + "doctype": "Address", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "address_type": "Office", + "address_title": "_Test Address", + "address_line1": "_Test Address Line 1", + "city": "_Test City", + "country": "India", + "phone": "+91 0000000000", + "is_primary_address": 1 + }], +] \ No newline at end of file diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 4b34ba1286..5d7d1a84b1 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -22,6 +22,23 @@ from webnotes.model.doc import addchild from webnotes.model.controller import DocListController class TransactionBase(DocListController): + 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""" + ret = {} + + # {customer: self.doc.fields.get("customer")} + args = {party_type: self.doc.fields.get(party_type)} + + address_text, address_name = self.get_address_text(**args) + ret.update({ + # customer_address + (party_type + "_address"): address_name, + "address_display": address_text + }) + ret.update(self.get_contact_text(**args)) + return ret + # Get Customer Default Primary Address - first load def get_default_customer_address(self, args=''): address_text, address_name = self.get_address_text(customer=self.doc.customer) @@ -73,7 +90,7 @@ class TransactionBase(DocListController): details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_shipping_address desc, is_primary_address desc limit 1" % cond, as_dict = 1) else: details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_primary_address desc limit 1" % cond, as_dict = 1) - + extract = lambda x: details and details[0] and details[0].get(x,'') or '' address_fields = [('','address_line1'),('\n','address_line2'),('\n','city'),('\n','state'),(' ','pincode'),('\n','country'),('\nPhone: ','phone'),('\nFax: ', 'fax')] address_display = ''.join([a[0]+extract(a[1]) for a in address_fields if extract(a[1])]) From d58d1b5a1867d141c06a8a596c0c1b69e6856ad4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Apr 2013 17:25:44 +0530 Subject: [PATCH 043/295] validation for stock and nonstock items in purchase cycle --- controllers/buying_controller.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 94e3d56ddc..e167dc57b7 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -45,12 +45,13 @@ class BuyingController(StockController): def validate_stock_or_nonstock_items(self): items = [d.item_code for d in self.doclist.get({"parentfield": self.fname})] - if self.stock_items and len(items) > len(self.stock_items): + if self.stock_items: nonstock_items = list(set(items) - set(self.stock_items)) - webnotes.msgprint(_("Stock and non-stock items can not be entered in the same ") + - self.doc.doctype + _(""". You should make separate documents for them. - Stock Items: """) + ", ".join(self.stock_items) + _(""" - Non-stock Items: """) + ", ".join(nonstock_items), raise_exception=1) + if nonstock_items: + webnotes.msgprint(_("Stock and non-stock items can not be entered in the same ") + + self.doc.doctype + _(""". You should make separate documents for them. + Stock Items: """) + ", ".join(self.stock_items) + _(""" + Non-stock Items: """) + ", ".join(nonstock_items), raise_exception=1) elif items and not self.stock_items: tax_for_valuation = [d.account_head for d in From 6198c28fd352f597194c2cea460b9830b417f11f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Apr 2013 17:29:05 +0530 Subject: [PATCH 044/295] validation for stock and nonstock items in purchase cycle --- manufacturing/doctype/bom/bom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manufacturing/doctype/bom/bom.py b/manufacturing/doctype/bom/bom.py index 1408a3ad5c..5f42f4da5d 100644 --- a/manufacturing/doctype/bom/bom.py +++ b/manufacturing/doctype/bom/bom.py @@ -89,7 +89,7 @@ class DocType: "qty": item.qty }) for r in ret: - if not item.fields[r]: + if not item.fields.get(r): item.fields[r] = ret[r] def get_bom_material_detail(self, args=None): @@ -117,7 +117,7 @@ class DocType: def get_rm_rate(self, arg): """ Get raw material rate as per selected method, if bom exists takes bom cost """ - + rate = 0 if arg['bom_no']: rate = self.get_bom_unitcost(arg['bom_no']) elif arg and (arg['is_purchase_item'] == 'Yes' or arg['is_sub_contracted_item'] == 'Yes'): From 319e0f43da2d8f6168408067c3aabaddd69a1f68 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 08:53:12 +0530 Subject: [PATCH 045/295] [latest updates] added default cost center and update manager --- home/page/latest_updates/latest_updates.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index 949849d8f1..a4d99f69e4 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,5 +1,7 @@ erpnext.updates = [ + ["18th April", ["Cost Center: Set a default Cost Center for a Company"]], ["12th April", ["Employee: List of Leave Approvers who can approve the Employee's Leave Applications"]], + ["3rd April", ["Update Manager: Open source users can update their ERPNext instance from Setup > Update Manager"]], ["27th March", ["Rename multiple items together. Go to Setup > Rename Tool"]], ["26th March", ["Added project to Stock Ledger and Balance", "Added Default Cash Account in Company."]], From 4cbeda1ec7ced13f5227948641467f2c66db148d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 10:48:45 +0530 Subject: [PATCH 046/295] [patch] [fix] commented out export_to_files call in p05_update_file_data patch --- patches/april_2013/p05_update_file_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index a5540cc3cd..986e5b9b53 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -26,7 +26,7 @@ def execute(): webnotes.conn.sql("""delete from tabDocField where fieldname='file_list' and parent=%s""", doctype) - export_to_files([["DocType", doctype]]) + # export_to_files([["DocType", doctype]]) def update_for_doc(doctype, doc): for filedata in doc.file_list.split("\n"): From e81b0e2d669d766e685d6859400172a211da0d23 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 29 Apr 2013 11:45:22 +0530 Subject: [PATCH 047/295] [stock ageing][fixes] for serialized items --- stock/page/stock_ageing/stock_ageing.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stock/page/stock_ageing/stock_ageing.js b/stock/page/stock_ageing/stock_ageing.js index edad9a76a1..456f5f1367 100644 --- a/stock/page/stock_ageing/stock_ageing.js +++ b/stock/page/stock_ageing/stock_ageing.js @@ -95,6 +95,8 @@ erpnext.StockAgeing = erpnext.StockGridReport.extend({ this.data = [].concat(this._data); + this.serialized_buying_rates = this.get_serialized_buying_rates(); + $.each(this.data, function(i, d) { me.reset_item_values(d); }); From 99883420e78c6f6e54df7dc205e5eaf866449e56 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 29 Apr 2013 12:15:35 +0530 Subject: [PATCH 048/295] [general ledger][fixes] for against_account value --- accounts/page/general_ledger/general_ledger.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index 21be3a05dd..a462b70e93 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -186,7 +186,6 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ var totals = this.make_summary_row("Totals", this.account); var grouped_ledgers = {}; - $.each(data, function(i, item) { if((me.is_default("company") ? true : me.apply_filter(item, "company")) && (me.account ? me.is_child_account(me.account, item.account) @@ -217,8 +216,7 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ grouped_ledgers[item.account].totals.debit += item.debit; grouped_ledgers[item.account].totals.credit += item.credit; } - - if(me.account) { + if(item.account) { item.against_account = me.voucher_accounts[item.voucher_type + ":" + item.voucher_no][(item.debit > 0 ? "credits" : "debits")].join(", "); } From ce3296fc87298466c32b6de471d9b1f78aa31ccc Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 12:28:19 +0530 Subject: [PATCH 049/295] [rename tool] [fix] condition to validate that content exists in a row of the uploaded file --- utilities/doctype/rename_tool/rename_tool.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utilities/doctype/rename_tool/rename_tool.py b/utilities/doctype/rename_tool/rename_tool.py index 2e368ced70..5accf3c6b7 100644 --- a/utilities/doctype/rename_tool/rename_tool.py +++ b/utilities/doctype/rename_tool/rename_tool.py @@ -34,7 +34,8 @@ def upload(select_doctype=None, rows=None): rename_log = [] for row in rows: - if len(row) > 2: + # if row has some content + if len(row) > 1 and row[0] and row[1]: try: if rename_doc(select_doctype, row[0], row[1]): rename_log.append(_("Successful: ") + row[0] + " -> " + row[1]) @@ -45,5 +46,5 @@ def upload(select_doctype=None, rows=None): rename_log.append("" + \ _("Failed: ") + row[0] + " -> " + row[1] + "") rename_log.append("" + repr(e) + "") - + return rename_log \ No newline at end of file From 457cd7d1f446b7a823eef77c4df5c53c1e90d8c6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 13:01:44 +0530 Subject: [PATCH 050/295] [gl entry] [validation] company match validation should be done only for is_cancelled = No --- accounts/doctype/gl_entry/gl_entry.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py index 64d84b0459..429cc104a7 100644 --- a/accounts/doctype/gl_entry/gl_entry.py +++ b/accounts/doctype/gl_entry/gl_entry.py @@ -108,8 +108,8 @@ class DocType: and not 'Accounts Manager' in webnotes.user.get_roles(): msgprint(_("Account") + ": " + self.doc.account + _(" has been freezed. \ Only Accounts Manager can do transaction against this account"), raise_exception=1) - - if ret and ret[0]["company"] != self.doc.company: + + if self.doc.is_cancelled in ("No", None) and ret and ret[0]["company"] != self.doc.company: msgprint(_("Account") + ": " + self.doc.account + _(" does not belong to the company") \ + ": " + self.doc.company, raise_exception=1) @@ -124,9 +124,10 @@ class DocType: return self.cost_center_company[self.doc.cost_center] - if self.doc.cost_center and _get_cost_center_company() != self.doc.company: - msgprint(_("Cost Center") + ": " + self.doc.cost_center \ - + _(" does not belong to the company") + ": " + self.doc.company, raise_exception=True) + if self.doc.is_cancelled in ("No", None) and \ + self.doc.cost_center and _get_cost_center_company() != self.doc.company: + msgprint(_("Cost Center") + ": " + self.doc.cost_center \ + + _(" does not belong to the company") + ": " + self.doc.company, raise_exception=True) def check_freezing_date(self, adv_adj): """ From e5a36a287f8069025d13b7b2547f580965d69525 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 14:12:27 +0530 Subject: [PATCH 051/295] [patch] [file data] patch for custom fields with name file_list --- patches/april_2013/p05_update_file_data.py | 40 +++++++++++++--------- patches/patch_list.py | 1 + 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index 986e5b9b53..1ff48fa60e 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -5,28 +5,36 @@ def execute(): webnotes.reload_doc("core", "doctype", "file_data") webnotes.reset_perms("File Data") - singles = webnotes.conn.sql_list("""select name from tabDocType - where ifnull(issingle,0)=1""") + singles = get_single_doctypes() + for doctype in webnotes.conn.sql_list("""select parent from tabDocField where fieldname='file_list' and fieldtype='Text'"""): - if doctype in singles: - doc = webnotes.doc(doctype, doctype) - if doc.file_list: - update_for_doc(doctype, doc) - webnotes.conn.set_value(doctype, None, "file_list", None) - else: - try: - for doc in webnotes.conn.sql("""select name, file_list from `tab%s` where - ifnull(file_list, '')!=''""" % doctype, as_dict=True): - update_for_doc(doctype, doc) - webnotes.conn.commit() - webnotes.conn.sql("""alter table `tab%s` drop column file_list""" % doctype) - except Exception, e: - if e.args[0]!=1054: raise e + update_file_list(doctype, singles) webnotes.conn.sql("""delete from tabDocField where fieldname='file_list' and parent=%s""", doctype) + # export_to_files([["DocType", doctype]]) + +def get_single_doctypes(): + return webnotes.conn.sql_list("""select name from tabDocType + where ifnull(issingle,0)=1""") + +def update_file_list(doctype, singles): + if doctype in singles: + doc = webnotes.doc(doctype, doctype) + if doc.file_list: + update_for_doc(doctype, doc) + webnotes.conn.set_value(doctype, None, "file_list", None) + else: + try: + for doc in webnotes.conn.sql("""select name, file_list from `tab%s` where + ifnull(file_list, '')!=''""" % doctype, as_dict=True): + update_for_doc(doctype, doc) + webnotes.conn.commit() + webnotes.conn.sql("""alter table `tab%s` drop column file_list""" % doctype) + except Exception, e: + if e.args[0]!=1054: raise e def update_for_doc(doctype, doc): for filedata in doc.file_list.split("\n"): diff --git a/patches/patch_list.py b/patches/patch_list.py index a6dd0a217b..096fec80ab 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -247,4 +247,5 @@ patch_list = [ "execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')", "patches.april_2013.p06_default_cost_center", "execute:webnotes.reset_perms('File Data')", + "patches.april_2013.p07_update_file_data_custom_field", ] \ No newline at end of file From 123f59dc89cf3858678ba8a71bfd72e9a3b9438b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 14:21:42 +0530 Subject: [PATCH 052/295] [patches] [file data] fixes in file data patch --- patches/april_2013/p05_update_file_data.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index 1ff48fa60e..d3877417e2 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -53,10 +53,17 @@ def update_for_doc(doctype, doc): exists = False if exists: - webnotes.conn.sql("""update `tabFile Data` - set attached_to_doctype=%s, attached_to_name=%s - where name=%s""", (doctype, doc.name, fileid)) - + if webnotes.conn.exists("File Data", fileid): + fd = webnotes.bean("File Data", fileid) + if not (fd.doc.attached_to_doctype and fd.doc.attached_to_name): + fd.doc.attached_to_doctype = doctype + fd.doc.attached_to_name = doc.name + fd.save() + else: + fd = webnotes.bean("File Data", copy=fd.doclist) + fd.doc.attached_to_doctype = doctype + fd.doc.attached_to_name = doc.name + fd.insert() else: webnotes.conn.sql("""delete from `tabFile Data` where name=%s""", - fileid) \ No newline at end of file + fileid) \ No newline at end of file From 7bd7c8769ef6eb472b9b0dde5653859ccaa7150d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 14:21:58 +0530 Subject: [PATCH 053/295] [patch] [file data] files in update file data patch --- .../april_2013/p07_update_file_data_custom_field.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 patches/april_2013/p07_update_file_data_custom_field.py diff --git a/patches/april_2013/p07_update_file_data_custom_field.py b/patches/april_2013/p07_update_file_data_custom_field.py new file mode 100644 index 0000000000..909df0554d --- /dev/null +++ b/patches/april_2013/p07_update_file_data_custom_field.py @@ -0,0 +1,11 @@ +import webnotes +def execute(): + from patches.april_2013.p05_update_file_data import update_file_list, get_single_doctypes + singles = get_single_doctypes() + for doctype in webnotes.conn.sql("""select parent from `tabCustom Field` where + fieldname='file_list' and fieldtype='Text'"""): + update_file_list(doctype, singles) + + webnotes.conn.sql("""delete from `tabCustom Field` where fieldname='file_list' + and parent=%s""", doctype) + \ No newline at end of file From 345c8fbfa29dd51e7f0a637ec50818dd7b975b16 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 29 Apr 2013 14:35:11 +0530 Subject: [PATCH 054/295] [patch] rebuild sales browser --- patches/april_2013/rebuild_sales_browser.py | 4 ++++ patches/patch_list.py | 1 + 2 files changed, 5 insertions(+) create mode 100644 patches/april_2013/rebuild_sales_browser.py diff --git a/patches/april_2013/rebuild_sales_browser.py b/patches/april_2013/rebuild_sales_browser.py new file mode 100644 index 0000000000..917ae68467 --- /dev/null +++ b/patches/april_2013/rebuild_sales_browser.py @@ -0,0 +1,4 @@ +import webnotes +def execute(): + from patches.january_2013 import rebuild_tree + rebuild_tree.execute() \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index a6dd0a217b..1b874d63ff 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -247,4 +247,5 @@ patch_list = [ "execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')", "patches.april_2013.p06_default_cost_center", "execute:webnotes.reset_perms('File Data')", + "patches.april_2013.rebuild_sales_browser", ] \ No newline at end of file From 7ee7eae3ae8848eb597bcab6557dc278fbb6784b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 29 Apr 2013 15:10:39 +0530 Subject: [PATCH 055/295] [item][validation] some validation only after saving --- stock/doctype/item/item.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index 4767742e3e..fce6f32543 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -55,12 +55,13 @@ class DocType(DocListController): ch.conversion_factor = 1 def check_stock_uom_with_bin(self): - bin = webnotes.conn.sql("select stock_uom from `tabBin` where item_code = %s", - self.doc.item_code) - if self.doc.stock_uom and bin and cstr(bin[0][0]) \ - and cstr(bin[0][0]) != cstr(self.doc.stock_uom): - msgprint(_("Please Update Stock UOM with the help of Stock UOM Replace Utility."), - raise_exception=1) + if not self.doc.__islocal: + bin = webnotes.conn.sql("select stock_uom from `tabBin` where item_code = %s", + self.doc.name) + if self.doc.stock_uom and bin and cstr(bin[0][0]) \ + and cstr(bin[0][0]) != cstr(self.doc.stock_uom): + msgprint(_("Please Update Stock UOM with the help of Stock UOM Replace Utility."), + raise_exception=1) def validate_conversion_factor(self): check_list = [] @@ -154,13 +155,14 @@ class DocType(DocListController): def validate_barcode(self): if self.doc.barcode: - duplicate = webnotes.conn.sql("select name from tabItem where barcode = %s and name != %s", (self.doc.barcode, self.doc.name)) + duplicate = webnotes.conn.sql("""select name from tabItem where barcode = %s + and name != %s""", (self.doc.barcode, self.doc.name)) if duplicate: msgprint("Barcode: %s already used in item: %s" % (self.doc.barcode, cstr(duplicate[0][0])), raise_exception = 1) def check_non_asset_warehouse(self): - if self.doc.is_asset_item == "Yes": + if not self.doc.__islocal and self.doc.is_asset_item == "Yes": existing_qty = webnotes.conn.sql("select t1.warehouse, t1.actual_qty from tabBin t1, tabWarehouse t2 where t1.item_code=%s and (t2.warehouse_type!='Fixed Asset' or t2.warehouse_type is null) and t1.warehouse=t2.name and t1.actual_qty > 0", self.doc.name) for e in existing_qty: msgprint("%s Units exist in Warehouse %s, which is not an Asset Warehouse." % From eda17eb20a7808f24faae12b612e40f101059557 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 15:21:30 +0530 Subject: [PATCH 056/295] [patch] [fiel data] new patch p07_update_file_data_2 --- patches/april_2013/p05_update_file_data.py | 20 ++++++++++++------- patches/april_2013/p07_update_file_data_2.py | 15 ++++++++++++++ .../p07_update_file_data_custom_field.py | 11 ---------- patches/patch_list.py | 2 +- 4 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 patches/april_2013/p07_update_file_data_2.py delete mode 100644 patches/april_2013/p07_update_file_data_custom_field.py diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index d3877417e2..a5d2f7a07a 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -8,7 +8,7 @@ def execute(): singles = get_single_doctypes() for doctype in webnotes.conn.sql_list("""select parent from tabDocField where - fieldname='file_list' and fieldtype='Text'"""): + fieldname='file_list'"""): update_file_list(doctype, singles) webnotes.conn.sql("""delete from tabDocField where fieldname='file_list' @@ -32,9 +32,11 @@ def update_file_list(doctype, singles): ifnull(file_list, '')!=''""" % doctype, as_dict=True): update_for_doc(doctype, doc) webnotes.conn.commit() - webnotes.conn.sql("""alter table `tab%s` drop column file_list""" % doctype) + webnotes.conn.sql("""alter table `tab%s` drop column `file_list`""" % doctype) except Exception, e: - if e.args[0]!=1054: raise e + print webnotes.getTraceback() + if (e.args and e.args[0]!=1054) or not e.args: + raise e def update_for_doc(doctype, doc): for filedata in doc.file_list.split("\n"): @@ -60,10 +62,14 @@ def update_for_doc(doctype, doc): fd.doc.attached_to_name = doc.name fd.save() else: - fd = webnotes.bean("File Data", copy=fd.doclist) - fd.doc.attached_to_doctype = doctype - fd.doc.attached_to_name = doc.name - fd.insert() + try: + fd = webnotes.bean("File Data", copy=fd.doclist) + fd.doc.attached_to_doctype = doctype + fd.doc.attached_to_name = doc.name + fd.doc.name = None + fd.insert() + except webnotes.DuplicateEntryError: + pass else: webnotes.conn.sql("""delete from `tabFile Data` where name=%s""", fileid) \ No newline at end of file diff --git a/patches/april_2013/p07_update_file_data_2.py b/patches/april_2013/p07_update_file_data_2.py new file mode 100644 index 0000000000..7b89a9de77 --- /dev/null +++ b/patches/april_2013/p07_update_file_data_2.py @@ -0,0 +1,15 @@ +import webnotes +def execute(): + from patches.april_2013.p05_update_file_data import update_file_list, get_single_doctypes + + 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): + doctype = doctype[3:] + update_file_list(doctype, singles) + + webnotes.conn.sql("""delete from `tabCustom Field` where fieldname='file_list' + and parent=%s""", doctype) + webnotes.conn.sql("""delete from `tabDocField` where fieldname='file_list' + and parent=%s""", doctype) + \ No newline at end of file diff --git a/patches/april_2013/p07_update_file_data_custom_field.py b/patches/april_2013/p07_update_file_data_custom_field.py deleted file mode 100644 index 909df0554d..0000000000 --- a/patches/april_2013/p07_update_file_data_custom_field.py +++ /dev/null @@ -1,11 +0,0 @@ -import webnotes -def execute(): - from patches.april_2013.p05_update_file_data import update_file_list, get_single_doctypes - singles = get_single_doctypes() - for doctype in webnotes.conn.sql("""select parent from `tabCustom Field` where - fieldname='file_list' and fieldtype='Text'"""): - update_file_list(doctype, singles) - - webnotes.conn.sql("""delete from `tabCustom Field` where fieldname='file_list' - and parent=%s""", doctype) - \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 096fec80ab..70aeb4a6ff 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -247,5 +247,5 @@ patch_list = [ "execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')", "patches.april_2013.p06_default_cost_center", "execute:webnotes.reset_perms('File Data')", - "patches.april_2013.p07_update_file_data_custom_field", + "patches.april_2013.p07_update_file_data_2", ] \ No newline at end of file From 28eae6e9ff1d002e8232a60bcfc9c451a11fa745 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 15:25:52 +0530 Subject: [PATCH 057/295] [patch] [file data] fix --- patches/april_2013/p07_update_file_data_2.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/patches/april_2013/p07_update_file_data_2.py b/patches/april_2013/p07_update_file_data_2.py index 7b89a9de77..2405e80d65 100644 --- a/patches/april_2013/p07_update_file_data_2.py +++ b/patches/april_2013/p07_update_file_data_2.py @@ -6,6 +6,9 @@ def execute(): 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): doctype = doctype[3:] + + if not webnotes.conn.exists("DocType", doctype): continue + update_file_list(doctype, singles) webnotes.conn.sql("""delete from `tabCustom Field` where fieldname='file_list' From fe5fb33cc2e8c7f9a59e1f9d6d859b16623c93ec Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 15:27:57 +0530 Subject: [PATCH 058/295] [patch] [file data] fix --- patches/april_2013/p05_update_file_data.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index a5d2f7a07a..787991251e 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -56,20 +56,20 @@ def update_for_doc(doctype, doc): if exists: if webnotes.conn.exists("File Data", fileid): - fd = webnotes.bean("File Data", fileid) - if not (fd.doc.attached_to_doctype and fd.doc.attached_to_name): - fd.doc.attached_to_doctype = doctype - fd.doc.attached_to_name = doc.name - fd.save() - else: - try: + try: + fd = webnotes.bean("File Data", fileid) + if not (fd.doc.attached_to_doctype and fd.doc.attached_to_name): + fd.doc.attached_to_doctype = doctype + fd.doc.attached_to_name = doc.name + fd.save() + else: fd = webnotes.bean("File Data", copy=fd.doclist) fd.doc.attached_to_doctype = doctype fd.doc.attached_to_name = doc.name fd.doc.name = None fd.insert() - except webnotes.DuplicateEntryError: - pass + except webnotes.DuplicateEntryError: + pass else: webnotes.conn.sql("""delete from `tabFile Data` where name=%s""", fileid) \ No newline at end of file From fe9c1eb5df29b744016844c67f9ceb682034cdc1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 16:36:59 +0530 Subject: [PATCH 059/295] [support ticket] clear assign to on closing ticket --- support/doctype/support_ticket/support_ticket.js | 14 +++++--------- support/doctype/support_ticket/support_ticket.py | 4 ++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/support/doctype/support_ticket/support_ticket.js b/support/doctype/support_ticket/support_ticket.js index 3da81d9efb..4ee4c1d968 100644 --- a/support/doctype/support_ticket/support_ticket.js +++ b/support/doctype/support_ticket/support_ticket.js @@ -29,17 +29,13 @@ $.extend(cur_frm.cscript, { erpnext.hide_naming_series(); cur_frm.cscript.make_listing(doc); if(!doc.__islocal) { - if(in_list(user_roles,'System Manager')) { - if(doc.status!='Closed') cur_frm.add_custom_button('Close Ticket', cur_frm.cscript['Close Ticket']); - if(doc.status=='Closed') cur_frm.add_custom_button('Re-Open Ticket', cur_frm.cscript['Re-Open Ticket']); - }else if(doc.allocated_to) { - cur_frm.set_df_property('status','read_only', 1); - if(user==doc.allocated_to && doc.status!='Closed') cur_frm.add_custom_button('Close Ticket', cur_frm.cscript['Close Ticket']); + if(user_roles.indexOf("Support Manager")!==-1) { + if(doc.status!='Closed') cur_frm.add_custom_button('Close Ticket', cur_frm.cscript['Close Ticket']); + if(doc.status=='Closed') cur_frm.add_custom_button('Re-Open Ticket', cur_frm.cscript['Re-Open Ticket']); } - cur_frm.set_df_property('subject','read_only', 1); - cur_frm.set_df_property('description','hidden', 1); - cur_frm.set_df_property('raised_by','read_only', 1); + cur_frm.toggle_enable(["subject", "raised_by"], false); + cur_frm.toggle_display("description", false); } refresh_field('status'); }, diff --git a/support/doctype/support_ticket/support_ticket.py b/support/doctype/support_ticket/support_ticket.py index 5625f1179c..8a705f41c7 100644 --- a/support/doctype/support_ticket/support_ticket.py +++ b/support/doctype/support_ticket/support_ticket.py @@ -45,6 +45,10 @@ class DocType(TransactionBase): def validate(self): self.update_status() + if self.doc.status == "Closed": + from webnotes.widgets.form.assign_to import clear + clear(self.doc.doctype, self.doc.name) + def on_communication_sent(self, comm): webnotes.conn.set(self.doc, 'status', 'Waiting for Customer') if comm.lead and not self.doc.lead: From d7ad13a8a7d98e7977d25d1e8d2b1e281a3cdd2d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 29 Apr 2013 19:38:34 +0530 Subject: [PATCH 060/295] [price list] [country] specify country for a price list --- home/page/latest_updates/latest_updates.js | 1 + patches/april_2013/p08_price_list_country.py | 4 + patches/patch_list.py | 1 + setup/doctype/price_list/price_list.js | 31 ++++++++ setup/doctype/price_list/price_list.py | 75 +++---------------- setup/doctype/price_list/price_list.txt | 39 +++++++++- setup/doctype/price_list_country/__init__.py | 0 .../price_list_country/price_list_country.py | 8 ++ .../price_list_country/price_list_country.txt | 36 +++++++++ stock/doctype/item_price/item_price.txt | 8 +- 10 files changed, 132 insertions(+), 71 deletions(-) create mode 100644 patches/april_2013/p08_price_list_country.py create mode 100644 setup/doctype/price_list_country/__init__.py create mode 100644 setup/doctype/price_list_country/price_list_country.py create mode 100644 setup/doctype/price_list_country/price_list_country.txt diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index b768f1aa1e..4a09c5e2ff 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,4 +1,5 @@ erpnext.updates = [ + ["30th April", ["Price List: Valid for all countries or only valid for specific countries"]], ["18th April", ["Cost Center: Set a default Cost Center for a Company"]], ["12th April", ["Employee: List of Leave Approvers who can approve the Employee's Leave Applications"]], ["10th April", ["Redesigned File Uploads and added File Manager in Setup"]], diff --git a/patches/april_2013/p08_price_list_country.py b/patches/april_2013/p08_price_list_country.py new file mode 100644 index 0000000000..1179e1da5b --- /dev/null +++ b/patches/april_2013/p08_price_list_country.py @@ -0,0 +1,4 @@ +import webnotes + +def execute(): + webnotes.conn.sql("""update `tabPrice List` set valid_for_all_countries=1""") \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index c9a0ab5aed..83fb43fd14 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -249,4 +249,5 @@ patch_list = [ "execute:webnotes.reset_perms('File Data')", "patches.april_2013.p07_update_file_data_2", "patches.april_2013.rebuild_sales_browser", + "patches.april_2013.p08_price_list_country", ] \ No newline at end of file diff --git a/setup/doctype/price_list/price_list.js b/setup/doctype/price_list/price_list.js index 75d3d0f1c1..0020edaf20 100644 --- a/setup/doctype/price_list/price_list.js +++ b/setup/doctype/price_list/price_list.js @@ -14,10 +14,41 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +cur_frm.cscript.onload = function() { + cur_frm.cscript.show_item_prices(); +} + cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.set_intro(""); if(doc.__islocal) { cur_frm.set_intro("Save this list to begin."); return; + } else { + cur_frm.cscript.show_item_prices(); } } + +cur_frm.cscript.show_item_prices = function() { + cur_frm.toggle_display("item_prices_section", cur_frm.doc.__item_price && cur_frm.doc.__item_price.length); + $(cur_frm.fields_dict.item_prices.wrapper).empty(); + if (!cur_frm.doc.__item_price) return; + var out = '\ + \ + \ + \ + \ + \ + \ + ' + + $.map(cur_frm.doc.__item_price.sort(function(a, b) { return a.parent.localeCompare(b.parent); }), function(d) { + return '' + + '' + + '' + + '' + + '' + + '' + }).join("\n") + + '\ +
    ' + wn._("Item Code") + '' + wn._("Price") + '' + wn._("Valid For Selling") + '' + wn._("Valid For Buying") + '
    ' + d.parent + '' + format_currency(d.ref_rate, d.ref_currency) + '' + (cint(d.selling) ? '' : "") + '' + (cint(d.buying) ? '' : "") + '
    '; + $(out).appendTo($(cur_frm.fields_dict.item_prices.wrapper)); +} diff --git a/setup/doctype/price_list/price_list.py b/setup/doctype/price_list/price_list.py index a88309b6db..9fef9154d7 100644 --- a/setup/doctype/price_list/price_list.py +++ b/setup/doctype/price_list/price_list.py @@ -16,73 +16,20 @@ from __future__ import unicode_literals import webnotes - -from webnotes.model.doc import Document -from webnotes import msgprint +from webnotes import msgprint, _ +from webnotes.utils import cint class DocType: def __init__(self, d, dl): self.doc, self.doclist = d, dl - self.cl = [] - - # validate currency - def is_currency_valid(self, currency): - if currency in self.cl: - return 1 - - if webnotes.conn.sql("select name from tabCurrency where name=%s", currency): - self.cl.append(currency) - return 1 - else: - return 0 - - def download_template(self, arg=None): - """download 3 column template with all Items""" - default_currency = webnotes.conn.get_default('currency') - item_list = webnotes.conn.sql("""select name from tabItem where - (ifnull(is_sales_item,'')='Yes' or ifnull(is_service_item,'')='Yes')""") - data = [self.get_price(i[0], default_currency) for i in item_list] - return [['Item', 'Rate', 'Currency']] + data - - def get_price(self, item, default_currency): - rate = webnotes.conn.sql("""select ref_rate, ref_currency from `tabItem Price` - where parent=%s and price_list_name=%s""", (item, self.doc.name)) - return [item, rate and rate[0][0] or 0, rate and rate[0][1] or default_currency] - - # update prices in Price List - def update_prices(self): - from webnotes.utils.datautils import read_csv_content_from_attached_file - data = read_csv_content_from_attached_file(self.doc) - webnotes.conn.auto_commit_on_many_writes = 1 - - updated = 0 - - for line in data: - if line and len(line)==3 and line[0]!='Item': - # if item exists - if webnotes.conn.sql("select name from tabItem where name=%s", line[0]): - if self.is_currency_valid(line[2]): - # if price exists - ref_ret_detail = webnotes.conn.sql("select name from `tabItem Price` where parent=%s and price_list_name=%s and ref_currency=%s", \ - (line[0], self.doc.name, line[2])) - if ref_ret_detail: - webnotes.conn.sql("update `tabItem Price` set ref_rate=%s where name=%s", (line[1], ref_ret_detail[0][0])) - else: - d = Document('Item Price') - d.parent = line[0] - d.parentfield = 'ref_rate_details' - d.parenttype = 'Item' - d.price_list_name = self.doc.name - d.ref_rate = line[1] - d.ref_currency = line[2] - d.save(1) - updated += 1 - else: - msgprint("[Ignored] Unknown currency '%s' for Item '%s'" % (line[2], line[0])) - else: - msgprint("[Ignored] Did not find Item '%s'" % line[1]) - - msgprint("%s items updated" % updated) - webnotes.conn.auto_commit_on_many_writes = 0 \ No newline at end of file + def onload(self): + self.doc.fields["__item_price"] = webnotes.conn.sql("""select parent, ref_rate, ref_currency, selling, buying + from `tabItem Price` where price_list_name=%s""", self.doc.name, as_dict=True) + + def validate(self): + if not (cint(self.doc.valid_for_all_countries) or len(self.doclist.get({"parentfield": "valid_for_countries"}))): + msgprint(_("""Please check "Valid For All Countries" or \ + enter atlease one row in the "Countries" table."""), raise_exception=True) + diff --git a/setup/doctype/price_list/price_list.txt b/setup/doctype/price_list/price_list.txt index a230f5b98c..de945b4dd1 100644 --- a/setup/doctype/price_list/price_list.txt +++ b/setup/doctype/price_list/price_list.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-25 11:35:09", "docstatus": 0, - "modified": "2013-01-22 14:56:41", + "modified": "2013-04-29 19:32:15", "modified_by": "Administrator", "owner": "Administrator" }, @@ -53,7 +53,41 @@ "reqd": 1 }, { - "depends_on": "price_list_name", + "default": "1", + "doctype": "DocField", + "fieldname": "valid_for_all_countries", + "fieldtype": "Check", + "label": "Valid for all countries" + }, + { + "description": "A list of Countries, for which, this Price List is valid", + "doctype": "DocField", + "fieldname": "valid_for_countries", + "fieldtype": "Table", + "label": "Valid for the following countries", + "options": "Price List Country" + }, + { + "doctype": "DocField", + "fieldname": "item_prices_section", + "fieldtype": "Section Break", + "label": "Item Prices" + }, + { + "doctype": "DocField", + "fieldname": "item_prices", + "fieldtype": "HTML", + "label": "Item Prices" + }, + { + "depends_on": "eval:!doc.__islocal", + "doctype": "DocField", + "fieldname": "section_break_1", + "fieldtype": "Section Break", + "label": "How to upload" + }, + { + "depends_on": "eval:!doc.__islocal", "doctype": "DocField", "fieldname": "how_to_upload", "fieldtype": "HTML", @@ -78,7 +112,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "role": "Sales Master Manager", "write": 1 } diff --git a/setup/doctype/price_list_country/__init__.py b/setup/doctype/price_list_country/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/setup/doctype/price_list_country/price_list_country.py b/setup/doctype/price_list_country/price_list_country.py new file mode 100644 index 0000000000..928aa9ff9f --- /dev/null +++ b/setup/doctype/price_list_country/price_list_country.py @@ -0,0 +1,8 @@ +# For license information, please see license.txt + +from __future__ import unicode_literals +import webnotes + +class DocType: + def __init__(self, d, dl): + self.doc, self.doclist = d, dl \ No newline at end of file diff --git a/setup/doctype/price_list_country/price_list_country.txt b/setup/doctype/price_list_country/price_list_country.txt new file mode 100644 index 0000000000..640b0a8052 --- /dev/null +++ b/setup/doctype/price_list_country/price_list_country.txt @@ -0,0 +1,36 @@ +[ + { + "creation": "2013-04-29 18:24:32", + "docstatus": 0, + "modified": "2013-04-29 18:24:32", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "autoname": "PLCNTRY-.#####", + "doctype": "DocType", + "istable": 1, + "module": "Setup", + "name": "__common__" + }, + { + "doctype": "DocField", + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "name": "__common__", + "options": "Country", + "parent": "Price List Country", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "reqd": 1 + }, + { + "doctype": "DocType", + "name": "Price List Country" + }, + { + "doctype": "DocField" + } +] \ No newline at end of file diff --git a/stock/doctype/item_price/item_price.txt b/stock/doctype/item_price/item_price.txt index 721902bc72..ad0b840c11 100644 --- a/stock/doctype/item_price/item_price.txt +++ b/stock/doctype/item_price/item_price.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-08 15:37:16", + "creation": "2013-04-29 15:18:04", "docstatus": 0, - "modified": "2013-03-21 17:29:15", + "modified": "2013-04-29 19:16:45", "modified_by": "Administrator", "owner": "Administrator" }, @@ -68,13 +68,13 @@ "doctype": "DocField", "fieldname": "selling", "fieldtype": "Check", - "label": "For Selling" + "label": "Valid For Selling" }, { "description": "Allow this price in purchase related forms", "doctype": "DocField", "fieldname": "buying", "fieldtype": "Check", - "label": "For Buying" + "label": "Valid For Buying" } ] \ No newline at end of file From 98cfe688a85db05e22c9b5efe0d78029d8bc9cb1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 30 Apr 2013 17:22:43 +0530 Subject: [PATCH 061/295] [script report] purchase register in new style --- accounts/page/accounts_home/accounts_home.js | 5 + accounts/report/purchase_register/__init__.py | 0 .../purchase_register/purchase_register.js | 43 +++++ .../purchase_register/purchase_register.py | 147 ++++++++++++++++++ .../purchase_register/purchase_register.txt | 21 +++ .../report/sales_register/sales_register.js | 1 + .../report/sales_register/sales_register.py | 4 +- .../bank_clearance_report.txt | 39 ++--- 8 files changed, 240 insertions(+), 20 deletions(-) create mode 100644 accounts/report/purchase_register/__init__.py create mode 100644 accounts/report/purchase_register/purchase_register.js create mode 100644 accounts/report/purchase_register/purchase_register.py create mode 100644 accounts/report/purchase_register/purchase_register.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 2703d27f69..6a9f91a719 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -154,6 +154,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Sales Register", doctype: "Sales Invoice" }, + { + "label":wn._("Purchase Register"), + route: "query-report/Purchase Register", + doctype: "Purchase Invoice" + }, ] }, { diff --git a/accounts/report/purchase_register/__init__.py b/accounts/report/purchase_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/purchase_register/purchase_register.js b/accounts/report/purchase_register/purchase_register.js new file mode 100644 index 0000000000..f943e84db1 --- /dev/null +++ b/accounts/report/purchase_register/purchase_register.js @@ -0,0 +1,43 @@ +wn.query_reports["Purchase Register"] = { + "add_total_row": true, + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname":"account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = wn.query_report.filters_by_name.company.get_value(); + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Credit", + "company": company, + "master_type": "Supplier" + } + } + } + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + } + ] +} \ No newline at end of file diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py new file mode 100644 index 0000000000..01dc93700b --- /dev/null +++ b/accounts/report/purchase_register/purchase_register.py @@ -0,0 +1,147 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + columns, expense_accounts, tax_accounts = get_columns() + + invoice_list = get_invoices(filters) + invoice_expense_map = get_invoice_expense_map(invoice_list) + invoice_tax_map = get_invoice_tax_map(invoice_list) + invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) + account_map = get_account_details(invoice_list) + + data = [] + for inv in invoice_list: + # invoice details + purchase_order = ", ".join(invoice_po_pr_map.get(inv.name, {}).get("purchase_order", [])) + purchase_receipt = ", ".join(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", [])) + row = [inv.name, inv.posting_date, inv.supplier, inv.credit_to, + account_map.get(inv.credit_to), inv.project_name, inv.bill_no, inv.bill_date, + inv.remarks, purchase_order, purchase_receipt] + + # map expense values + for expense_acc in expense_accounts: + row.append(invoice_expense_map.get(inv.name, {}).get(expense_acc)) + + # net total + row.append(inv.net_total) + + # tax account + for tax_acc in tax_accounts: + row.append(invoice_tax_map.get(inv.name, {}).get(tax_acc)) + + # total tax, grand total + row += [inv.total_tax, inv.grand_total] + data.append(row) + + return columns, data + + +def get_columns(): + """return columns based on filters""" + columns = [ + "Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier:Link/Supplier:120", + "Supplier Account:Link/Account:120", "Account Group:LInk/Account:120", + "Project:Link/Project:80", "Bill No::120", "Bill Date:Date:80", "Remarks::150", + "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100" + ] + + expense_accounts = webnotes.conn.sql_list("""select distinct expense_head + from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' + order by expense_head""") + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' + and docstatus = 1 and ifnull(account_head, '') != '' order by account_head""") + + columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ + ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ + ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + + return columns, expense_accounts, tax_accounts + +def get_conditions(filters): + conditions = "" + + if filters.get("company"): conditions += " and company=%(company)s" + if filters.get("account"): conditions += " and account = %(account)s" + + if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s" + + return conditions + +def get_invoices(filters): + conditions = get_conditions(filters) + return webnotes.conn.sql("""select name, posting_date, credit_to, project_name, supplier, + bill_no, bill_date, remarks, net_total, total_tax, grand_total + from `tabPurchase Invoice` where docstatus = 1 %s + order by posting_date desc, name desc""" % conditions, filters, as_dict=1) + +def get_invoice_expense_map(invoice_list): + expense_details = webnotes.conn.sql("""select parent, expense_head, sum(amount) as amount + from `tabPurchase Invoice Item` where parent in (%s) group by parent, expense_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_expense_map = {} + for d in expense_details: + invoice_expense_map.setdefault(d.parent, webnotes._dict()).setdefault(d.expense_head, []) + invoice_expense_map[d.parent][d.expense_head] = flt(d.amount) + + return invoice_expense_map + +def get_invoice_tax_map(invoice_list): + tax_details = webnotes.conn.sql("""select parent, account_head, sum(tax_amount) as tax_amount + from `tabPurchase Taxes and Charges` where parent in (%s) group by parent, account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_tax_map = {} + for d in tax_details: + invoice_tax_map.setdefault(d.parent, webnotes._dict()).setdefault(d.account_head, []) + invoice_tax_map[d.parent][d.account_head] = flt(d.tax_amount) + + return invoice_tax_map + +def get_invoice_po_pr_map(invoice_list): + pi_items = webnotes.conn.sql("""select parent, purchase_order, purchase_receipt + from `tabPurchase Invoice Item` where parent in (%s) + and (ifnull(purchase_order, '') != '' or ifnull(purchase_receipt, '') != '')""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_po_pr_map = {} + for d in pi_items: + if d.purchase_order: + invoice_po_pr_map.setdefault(d.parent, webnotes._dict()).setdefault( + "purchase_order", []).append(d.purchase_order) + if d.purchase_receipt: + invoice_po_pr_map.setdefault(d.parent, webnotes._dict()).setdefault( + "purchase_receipt", []).append(d.purchase_receipt) + + return invoice_po_pr_map + +def get_account_details(invoice_list): + account_map = {} + accounts = list(set([inv.credit_to for inv in invoice_list])) + for acc in webnotes.conn.sql("""select name, parent_account from tabAccount + where name in (%s)""" % ", ".join(["%s"]*len(accounts)), tuple(accounts), as_dict=1): + account_map.setdefault(acc.name, "") + account_map[acc.name] = acc.parent_account + + return account_map \ No newline at end of file diff --git a/accounts/report/purchase_register/purchase_register.txt b/accounts/report/purchase_register/purchase_register.txt new file mode 100644 index 0000000000..799971ef0a --- /dev/null +++ b/accounts/report/purchase_register/purchase_register.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-04-29 16:13:11", + "docstatus": 0, + "modified": "2013-04-29 16:13:11", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Invoice", + "report_name": "Purchase Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Purchase Register" + } +] \ No newline at end of file diff --git a/accounts/report/sales_register/sales_register.js b/accounts/report/sales_register/sales_register.js index 82246198c9..cf4543a713 100644 --- a/accounts/report/sales_register/sales_register.js +++ b/accounts/report/sales_register/sales_register.js @@ -1,4 +1,5 @@ wn.query_reports["Sales Register"] = { + "add_total_row": true, "filters": [ { "fieldname":"from_date", diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 07772cb4e2..a0020c45a0 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -94,8 +94,8 @@ def get_conditions(filters): def get_invoices(filters): conditions = get_conditions(filters) - return webnotes.conn.sql("""select name, posting_date, territory, debit_to, territory, - project_name, customer, remarks, net_total, other_charges_total, grand_total + return webnotes.conn.sql("""select name, posting_date, debit_to, project_name, customer, + remarks, net_total, other_charges_total, grand_total from `tabSales Invoice` where docstatus = 1 %s order by posting_date desc, name desc""" % conditions, filters, as_dict=1) diff --git a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt b/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt index 00384386f5..8aa94bf50a 100644 --- a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt +++ b/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt @@ -1,29 +1,32 @@ [ { - "owner": "Administrator", + "creation": "2012-05-14 18:05:41", "docstatus": 0, - "creation": "2012-04-03 12:49:50", + "modified": "2013-04-30 14:49:06", "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" + "owner": "Administrator" }, { - "description": "Bank Clearance report", - "parent_doc_type": "Journal Voucher", - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Journal Voucher\u0001Submitted':1,'Journal Voucher\u0001Voucher Type':'','Journal Voucher\u0001Is Opening':'','Journal Voucher\u0001Fiscal Year':'','Journal Voucher\u0001Company':'','Journal Voucher\u0001TDS Applicable':'','Journal Voucher\u0001TDS Category':''}", - "dis_filters": "fiscal_year", - "doc_type": "Journal Voucher Detail", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "ID", - "page_len": 50, + "columns": "Journal Voucher\u0001ID,Journal Voucher Detail\u0001Account,Journal Voucher Detail\u0001Debit,Journal Voucher Detail\u0001Credit,Journal Voucher\u0001Clearance Date,Journal Voucher\u0001Cheque No,Journal Voucher\u0001Cheque Date,Journal Voucher\u0001Voucher Date,Journal Voucher\u0001Posting Date,Journal Voucher Detail\u0001Against Payable,Journal Voucher Detail\u0001Against Receivable", "criteria_name": "Bank Clearance report", - "columns": "Journal Voucher\u0001ID,Journal Voucher Detail\u0001Account,Journal Voucher Detail\u0001Debit,Journal Voucher Detail\u0001Credit,Journal Voucher\u0001Clearance Date,Journal Voucher\u0001Cheque No,Journal Voucher\u0001Cheque Date,Journal Voucher\u0001Voucher Date,Journal Voucher\u0001Posting Date,Journal Voucher Detail\u0001Against Payable,Journal Voucher Detail\u0001Against Receivable" + "custom_query": "", + "description": "Bank Clearance report", + "dis_filters": "fiscal_year", + "disabled": 0, + "doc_type": "Journal Voucher Detail", + "doctype": "Search Criteria", + "filters": "{'Journal Voucher\u0001Submitted':1,'Journal Voucher\u0001Voucher Type':'','Journal Voucher\u0001Is Opening':'','Journal Voucher\u0001Fiscal Year':'','Journal Voucher\u0001Company':'','Journal Voucher\u0001TDS Applicable':'','Journal Voucher\u0001TDS Category':''}", + "module": "Accounts", + "name": "__common__", + "page_len": 50, + "parent_doc_type": "Journal Voucher", + "report_script": null, + "sort_by": "ID", + "sort_order": "DESC", + "standard": "Yes" }, { - "name": "bank_clearance_report", - "doctype": "Search Criteria" + "doctype": "Search Criteria", + "name": "bank_clearance_report" } ] \ No newline at end of file From 84e2af2212bf3025a06ae9dbb5b565d36e62afd0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 30 Apr 2013 18:08:43 +0530 Subject: [PATCH 062/295] [report] add total row --- .../report/accounts_payable/accounts_payable.py | 12 ++---------- .../report/accounts_payable/accounts_payable.txt | 3 ++- .../accounts_receivable/accounts_receivable.py | 13 ++----------- .../accounts_receivable/accounts_receivable.txt | 3 ++- .../report/purchase_register/purchase_register.js | 1 - .../report/purchase_register/purchase_register.txt | 3 ++- accounts/report/sales_register/sales_register.js | 1 - accounts/report/sales_register/sales_register.txt | 3 ++- 8 files changed, 12 insertions(+), 27 deletions(-) diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index 34c8ccd2be..a10dc7e7e7 100644 --- a/accounts/report/accounts_payable/accounts_payable.py +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -19,7 +19,6 @@ def execute(filters=None): and nowdate() or filters.get("report_date") data = [] - total_invoiced_amount = total_paid = total_outstanding = 0 for gle in entries: if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: @@ -36,7 +35,7 @@ def execute(filters=None): paid_amount = get_paid_amount(gle, filters.get("report_date") or nowdate(), entries_after_report_date) outstanding_amount = invoiced_amount - paid_amount - + if abs(flt(outstanding_amount)) > 0.01: row = [gle.posting_date, gle.account, gle.voucher_type, gle.voucher_no, gle.remarks, account_supplier_type_map.get(gle.account), due_date, bill_no, @@ -47,16 +46,9 @@ def execute(filters=None): ageing_based_on_date = due_date else: ageing_based_on_date = gle.posting_date + row += get_ageing_data(ageing_based_on_date, age_on, outstanding_amount) - - # Add to total - total_invoiced_amount += flt(invoiced_amount) - total_paid += flt(paid_amount) - total_outstanding += flt(outstanding_amount) data.append(row) - if data: - data.append(["", "", "", "", "", "", "", "Total", "", total_invoiced_amount, total_paid, - total_outstanding, "", "", "", ""]) return columns, data diff --git a/accounts/report/accounts_payable/accounts_payable.txt b/accounts/report/accounts_payable/accounts_payable.txt index 8920a0bf2e..6de97f64e6 100644 --- a/accounts/report/accounts_payable/accounts_payable.txt +++ b/accounts/report/accounts_payable/accounts_payable.txt @@ -2,11 +2,12 @@ { "creation": "2013-04-22 16:16:03", "docstatus": 0, - "modified": "2013-04-23 14:54:27", + "modified": "2013-04-30 17:55:54", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "doctype": "Report", "is_standard": "Yes", "name": "__common__", diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index d385b36b90..a8c6d2311e 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -18,7 +18,6 @@ def execute(filters=None): and nowdate() or filters.get("report_date") data = [] - total_invoiced_amount = total_payment = total_outstanding = 0 for gle in entries: if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: @@ -41,17 +40,9 @@ def execute(filters=None): else: ageing_based_on_date = gle.posting_date row += get_ageing_data(ageing_based_on_date, age_on, outstanding_amount) - - # Add to total - total_invoiced_amount += flt(invoiced_amount) - total_payment += flt(payment_amount) - total_outstanding += flt(outstanding_amount) - + data.append(row) - if data: - data.append(["", "", "", "", "", "", "Total", total_invoiced_amount, total_payment, - total_outstanding, "", "", "", ""]) - + return columns, data def get_columns(): diff --git a/accounts/report/accounts_receivable/accounts_receivable.txt b/accounts/report/accounts_receivable/accounts_receivable.txt index 28f7e4f44d..7eb344fc08 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.txt +++ b/accounts/report/accounts_receivable/accounts_receivable.txt @@ -2,11 +2,12 @@ { "creation": "2013-04-16 11:31:13", "docstatus": 0, - "modified": "2013-04-16 11:31:13", + "modified": "2013-04-30 17:54:47", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "doctype": "Report", "is_standard": "Yes", "name": "__common__", diff --git a/accounts/report/purchase_register/purchase_register.js b/accounts/report/purchase_register/purchase_register.js index f943e84db1..21e0547f4a 100644 --- a/accounts/report/purchase_register/purchase_register.js +++ b/accounts/report/purchase_register/purchase_register.js @@ -1,5 +1,4 @@ wn.query_reports["Purchase Register"] = { - "add_total_row": true, "filters": [ { "fieldname":"from_date", diff --git a/accounts/report/purchase_register/purchase_register.txt b/accounts/report/purchase_register/purchase_register.txt index 799971ef0a..847f5f3b6c 100644 --- a/accounts/report/purchase_register/purchase_register.txt +++ b/accounts/report/purchase_register/purchase_register.txt @@ -2,11 +2,12 @@ { "creation": "2013-04-29 16:13:11", "docstatus": 0, - "modified": "2013-04-29 16:13:11", + "modified": "2013-04-30 17:51:19", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "doctype": "Report", "is_standard": "Yes", "name": "__common__", diff --git a/accounts/report/sales_register/sales_register.js b/accounts/report/sales_register/sales_register.js index cf4543a713..82246198c9 100644 --- a/accounts/report/sales_register/sales_register.js +++ b/accounts/report/sales_register/sales_register.js @@ -1,5 +1,4 @@ wn.query_reports["Sales Register"] = { - "add_total_row": true, "filters": [ { "fieldname":"from_date", diff --git a/accounts/report/sales_register/sales_register.txt b/accounts/report/sales_register/sales_register.txt index 5aef814596..dbf41af0d0 100644 --- a/accounts/report/sales_register/sales_register.txt +++ b/accounts/report/sales_register/sales_register.txt @@ -2,11 +2,12 @@ { "creation": "2013-04-23 18:15:29", "docstatus": 0, - "modified": "2013-04-23 18:15:29", + "modified": "2013-04-30 17:53:10", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "doctype": "Report", "is_standard": "Yes", "name": "__common__", From 76646fb279849deafe52ffc3451ac6d1945a6b08 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 1 May 2013 11:23:44 +0530 Subject: [PATCH 063/295] [script report] bank reconciliation statement in new style --- accounts/page/accounts_home/accounts_home.js | 5 ++ .../bank_reconciliation_statement/__init__.py | 0 .../bank_reconciliation_statement.js | 25 ++++++++ .../bank_reconciliation_statement.py | 63 +++++++++++++++++++ .../bank_reconciliation_statement.txt | 22 +++++++ 5 files changed, 115 insertions(+) create mode 100644 accounts/report/bank_reconciliation_statement/__init__.py create mode 100644 accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js create mode 100644 accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py create mode 100644 accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 6a9f91a719..5779307f70 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -187,6 +187,11 @@ wn.module_page["Accounts"] = [ right: true, icon: "icon-list", items: [ + { + "label":wn._("Bank Reconciliation Statement"), + route: "query-report/Bank Reconciliation Statement", + doctype: "Journal Voucher" + }, { "label":wn._("Delivered Items To Be Billed"), route: "query-report/Delivered Items To Be Billed", diff --git a/accounts/report/bank_reconciliation_statement/__init__.py b/accounts/report/bank_reconciliation_statement/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js new file mode 100644 index 0000000000..28ac9205a6 --- /dev/null +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -0,0 +1,25 @@ +wn.query_reports["Bank Reconciliation Statement"] = { + "filters": [ + { + "fieldname":"account", + "label": "Bank Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "account_type": "Bank or Cash" + } + } + } + }, + { + "fieldname":"report_date", + "label": "Date", + "fieldtype": "Date", + "default": get_today() + }, + ] +} \ No newline at end of file diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py new file mode 100644 index 0000000000..4275958e5d --- /dev/null +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -0,0 +1,63 @@ +from __future__ import unicode_literals +import webnotes +from webnotes import _, msgprint +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + data = get_entries(filters) + + from accounts.utils import get_balance_on + balance_as_per_company = get_balance_on(filters["account"], filters["report_date"]) + + total_debit, total_credit = 0,0 + for d in data: + total_debit += flt(d[4]) + total_credit += flt(d[5]) + + if webnotes.conn.get_value("Account", filters["account"], "debit_or_credit") == 'Debit': + bank_bal = flt(balance_as_per_company) - flt(total_debit) + flt(total_credit) + else: + bank_bal = flt(balance_as_per_company) + flt(total_debit) - flt(total_credit) + + data += [ + ["", "", "", "Balance as per company books", balance_as_per_company, ""], + ["", "", "", "Amounts not reflected in bank", total_debit, total_credit], + ["", "", "", "Balance as per bank", bank_bal, ""] + ] + + return columns, data + + +def get_columns(): + return ["Journal Voucher:Link/Journal Voucher:140", "Posting Date:Date:100", + "Clearance Date:Date:110", "Against Account:Link/Account:200", + "Debit:Currency:120", "Credit:Currency:120" + ] + +def get_conditions(filters): + conditions = "" + if not filters.get("account"): + msgprint(_("Please select Bank Account"), raise_exception=1) + else: + conditions += " and jvd.account = %(account)s" + + if not filters.get("report_date"): + msgprint(_("Please select Date on which you want to run the report"), raise_exception=1) + else: + conditions += """ and jv.posting_date <= %(report_date)s + and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s""" + + return conditions + +def get_entries(filters): + conditions = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jv.posting_date, jv.clearance_date, + jvd.against_account, jvd.debit, jvd.credit + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 and ifnull(jv.cheque_no, '')!= '' %s + order by jv.name DESC""" % conditions, filters, as_list=1) + + return entries \ No newline at end of file diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.txt b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.txt new file mode 100644 index 0000000000..9867c5ded7 --- /dev/null +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-04-30 18:30:21", + "docstatus": 0, + "modified": "2013-05-01 10:53:12", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 0, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Bank Reconciliation Statement", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Bank Reconciliation Statement" + } +] \ No newline at end of file From 91e4c144705e1c2417e77100ac9678120c20dc97 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 1 May 2013 12:00:44 +0530 Subject: [PATCH 064/295] [fixes] [__islocal] use doc.fields.get('__islocal') instead of doc.__islocal --- accounts/doctype/cost_center/cost_center.py | 2 +- stock/doctype/item/item.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/doctype/cost_center/cost_center.py b/accounts/doctype/cost_center/cost_center.py index e57b6a3758..a7672452aa 100644 --- a/accounts/doctype/cost_center/cost_center.py +++ b/accounts/doctype/cost_center/cost_center.py @@ -81,7 +81,7 @@ class DocType(DocTypeNestedSet): """ Cost Center name must be unique """ - if (self.doc.__islocal or not self.doc.name) and webnotes.conn.sql("select name from `tabCost Center` where cost_center_name = %s and company_name=%s", (self.doc.cost_center_name, self.doc.company_name)): + if (self.doc.fields.get("__islocal") or not self.doc.name) and webnotes.conn.sql("select name from `tabCost Center` where cost_center_name = %s and company_name=%s", (self.doc.cost_center_name, self.doc.company_name)): msgprint("Cost Center Name already exists, please rename", raise_exception=1) self.validate_mandatory() diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index fce6f32543..6b6dfb3700 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -55,7 +55,7 @@ class DocType(DocListController): ch.conversion_factor = 1 def check_stock_uom_with_bin(self): - if not self.doc.__islocal: + if not self.doc.fields.get("__islocal"): bin = webnotes.conn.sql("select stock_uom from `tabBin` where item_code = %s", self.doc.name) if self.doc.stock_uom and bin and cstr(bin[0][0]) \ From 4120ffefa1657255eae64d89b5cc276afe4ed200 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 1 May 2013 12:14:57 +0530 Subject: [PATCH 065/295] [project] [query] removed debug --- projects/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/utils.py b/projects/utils.py index a7a016ab3e..7a45b08d9d 100644 --- a/projects/utils.py +++ b/projects/utils.py @@ -5,4 +5,4 @@ import webnotes @webnotes.whitelist() def get_time_log_list(doctype, txt, searchfield, start, page_len, filters): - return webnotes.conn.get_values("Time Log", filters, ["name", "activity_type", "owner"], debug=True) \ No newline at end of file + return webnotes.conn.get_values("Time Log", filters, ["name", "activity_type", "owner"]) \ No newline at end of file From e388465e17acf20a00bf54c2870c7944d3524aa0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 1 May 2013 12:23:07 +0530 Subject: [PATCH 066/295] [script report] bank clearance summary --- accounts/page/accounts_home/accounts_home.js | 5 ++ .../report/bank_clearance_summary/__init__.py | 0 .../bank_clearance_summary.js | 32 +++++++++++ .../bank_clearance_summary.py | 54 +++++++++++++++++++ .../bank_clearance_summary.txt | 21 ++++++++ .../bank_reconciliation_statement.py | 16 ++++++ .../delivered_items_to_be_billed.txt | 4 +- 7 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 accounts/report/bank_clearance_summary/__init__.py create mode 100644 accounts/report/bank_clearance_summary/bank_clearance_summary.js create mode 100644 accounts/report/bank_clearance_summary/bank_clearance_summary.py create mode 100644 accounts/report/bank_clearance_summary/bank_clearance_summary.txt diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 5779307f70..99d92b576c 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -202,6 +202,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Ordered Items To Be Billed", doctype: "Sales Invoice" }, + { + "label":wn._("Bank Clearance Summary"), + route: "query-report/Bank Clearance Summary", + doctype: "Journal Voucher" + }, ] } ] diff --git a/accounts/report/bank_clearance_summary/__init__.py b/accounts/report/bank_clearance_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/accounts/report/bank_clearance_summary/bank_clearance_summary.js new file mode 100644 index 0000000000..76adfd3174 --- /dev/null +++ b/accounts/report/bank_clearance_summary/bank_clearance_summary.js @@ -0,0 +1,32 @@ +wn.query_reports["Bank Clearance Summary"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname":"account", + "label": "Bank Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "account_type": "Bank or Cash" + } + } + } + }, + ] +} \ No newline at end of file diff --git a/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/accounts/report/bank_clearance_summary/bank_clearance_summary.py new file mode 100644 index 0000000000..49ac1a46c8 --- /dev/null +++ b/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -0,0 +1,54 @@ +# 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 import _, msgprint + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + data = get_entries(filters) + + return columns, data + +def get_columns(): + return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", + "Posting Date:Date:100", "Clearance Date:Date:110", "Against Account:Link/Account:200", + "Debit:Currency:120", "Credit:Currency:120" + ] + +def get_conditions(filters): + conditions = "" + if not filters.get("account"): + msgprint(_("Please select Bank Account"), raise_exception=1) + else: + conditions += " and jvd.account = %(account)s" + + if filters.get("from_date"): conditions += " and jv.posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and jv.posting_date<=%(to_date)s" + + return conditions + +def get_entries(filters): + conditions = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jvd.account, jv.posting_date, + jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 %s + order by jv.name DESC""" % conditions, filters, as_list=1) + return entries \ No newline at end of file diff --git a/accounts/report/bank_clearance_summary/bank_clearance_summary.txt b/accounts/report/bank_clearance_summary/bank_clearance_summary.txt new file mode 100644 index 0000000000..3dd2079f3a --- /dev/null +++ b/accounts/report/bank_clearance_summary/bank_clearance_summary.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-05-01 12:13:25", + "docstatus": 0, + "modified": "2013-05-01 12:13:25", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Bank Clearance Summary", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Bank Clearance Summary" + } +] \ No newline at end of file diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py index 4275958e5d..1345cd8006 100644 --- a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -1,3 +1,19 @@ +# 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 import _, msgprint diff --git a/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt b/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt index c84854f39d..e84c597b1d 100644 --- a/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt +++ b/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 17:55:23", + "creation": "2013-02-25 10:38:57", "docstatus": 0, - "modified": "2013-02-23 14:35:28", + "modified": "2013-05-01 11:56:43", "modified_by": "Administrator", "owner": "Administrator" }, From e97b07e43fe436049daf8b9e9620dd151d16d1fd Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 1 May 2013 14:12:19 +0530 Subject: [PATCH 067/295] [backup manager] [fix] manage mysql connection --- setup/doctype/backup_manager/backup_dropbox.py | 2 ++ setup/doctype/backup_manager/backup_googledrive.py | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/setup/doctype/backup_manager/backup_dropbox.py b/setup/doctype/backup_manager/backup_dropbox.py index ac07824a88..054d2b26b6 100644 --- a/setup/doctype/backup_manager/backup_dropbox.py +++ b/setup/doctype/backup_manager/backup_dropbox.py @@ -85,6 +85,7 @@ def backup_to_dropbox(): os.path.basename(backup.backup_path_db)) upload_file_to_dropbox(filename, "/database", dropbox_client) + webnotes.conn.close() response = dropbox_client.metadata("/files") # upload files to files folder @@ -108,6 +109,7 @@ def backup_to_dropbox(): did_not_upload.append(filename) error_log.append(cstr(e)) + webnotes.connect() return did_not_upload, list(set(error_log)) def get_dropbox_session(): diff --git a/setup/doctype/backup_manager/backup_googledrive.py b/setup/doctype/backup_manager/backup_googledrive.py index 7d980debe7..a705f0efa9 100644 --- a/setup/doctype/backup_manager/backup_googledrive.py +++ b/setup/doctype/backup_manager/backup_googledrive.py @@ -34,7 +34,6 @@ def get_gdrive_authorize_url(): "authorize_url": authorize_url, } -@webnotes.whitelist() def upload_files(name, mimetype, service, folder_id): if not webnotes.conn: webnotes.connect() @@ -78,6 +77,9 @@ def backup_to_gdrive(): did_not_upload = [] error_log = [] + files_folder_id = webnotes.conn.get_value("Backup Manager", None, "files_folder_id") + + webnotes.conn.close() path = os.path.join(get_base_path(), "public", "files") for filename in os.listdir(path): found = False @@ -91,9 +93,7 @@ def backup_to_gdrive(): #Compare Local File with Server File param = {} - children = drive_service.children().list( - folderId=webnotes.conn.get_value("Backup Manager", None, "files_folder_id"), - **param).execute() + children = drive_service.children().list(folderId=files_folder_id, **param).execute() for child in children.get('items', []): file = drive_service.files().get(fileId=child['id']).execute() if filename == file['title'] and size == int(file['fileSize']): @@ -101,12 +101,12 @@ def backup_to_gdrive(): break if not found: try: - upload_files(filepath, mimetype, drive_service, - webnotes.conn.get_value("Backup Manager", None, "files_folder_id")) + upload_files(filepath, mimetype, drive_service, files_folder_id) except Exception, e: did_not_upload.append(filename) error_log.append(cstr(e)) + webnotes.connect() return did_not_upload, list(set(error_log)) def get_gdrive_flow(): From 006d074b260574d5d1b556b0f8e9fb96d2c9d327 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 1 May 2013 15:05:29 +0530 Subject: [PATCH 068/295] [script report] collection report --- .../accounts_payable/accounts_payable.py | 5 +- .../accounts_receivable.py | 7 +- accounts/report/collection_report/__init__.py | 0 .../collection_report/collection_report.js | 42 +++++++++ .../collection_report/collection_report.py | 86 +++++++++++++++++++ .../collection_report/collection_report.txt | 22 +++++ .../purchase_register/purchase_register.py | 1 - .../report/sales_register/sales_register.py | 2 - 8 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 accounts/report/collection_report/__init__.py create mode 100644 accounts/report/collection_report/collection_report.js create mode 100644 accounts/report/collection_report/collection_report.py create mode 100644 accounts/report/collection_report/collection_report.txt diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index a10dc7e7e7..5e944a1b84 100644 --- a/accounts/report/accounts_payable/accounts_payable.py +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -67,8 +67,7 @@ def get_gl_entries(filters, before_report_date=True): gl_entries = [] gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` where ifnull(is_cancelled, 'No') = 'No' %s order by posting_date, account""" % - (conditions) % (", ".join(['%s']*len(supplier_accounts))), - tuple(supplier_accounts), as_dict=1) + (conditions), tuple(supplier_accounts), as_dict=1) return gl_entries def get_conditions(filters, before_report_date=True): @@ -85,7 +84,7 @@ def get_conditions(filters, before_report_date=True): conditions, filters) if supplier_accounts: - conditions += " and account in (%s)" + conditions += " and account in (%s)" % (", ".join(['%s']*len(supplier_accounts))) if filters.get("report_date"): if before_report_date: diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index a8c6d2311e..47908c3f13 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -58,8 +58,7 @@ def get_gl_entries(filters, upto_report_date=True): conditions, customer_accounts = get_conditions(filters, upto_report_date) return webnotes.conn.sql("""select * from `tabGL Entry` where ifnull(is_cancelled, 'No') = 'No' %s order by posting_date, account""" % - (conditions) % (", ".join(['%s']*len(customer_accounts))), - tuple(customer_accounts), as_dict=1) + (conditions), tuple(customer_accounts), as_dict=1) def get_conditions(filters, upto_report_date=True): conditions = "" @@ -75,7 +74,7 @@ def get_conditions(filters, upto_report_date=True): conditions, filters) if customer_accounts: - conditions += " and account in (%s)" + conditions += " and account in (%s)" % (", ".join(['%s']*len(customer_accounts))) if filters.get("report_date"): if upto_report_date: @@ -96,7 +95,7 @@ def get_account_territory_map(): def get_si_due_date_map(): """ get due_date from sales invoice """ si_due_date_map = {} - for t in webnotes.conn.sql("""select name, due_date from `tabSales Invoice` group by name"""): + for t in webnotes.conn.sql("""select name, due_date from `tabSales Invoice`"""): si_due_date_map[t[0]] = t[1] return si_due_date_map diff --git a/accounts/report/collection_report/__init__.py b/accounts/report/collection_report/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/collection_report/collection_report.js b/accounts/report/collection_report/collection_report.js new file mode 100644 index 0000000000..697a779c16 --- /dev/null +++ b/accounts/report/collection_report/collection_report.js @@ -0,0 +1,42 @@ +wn.query_reports["Collection Report"] = { + "filters": [ + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date"), + "width": "80" + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": get_today() + }, + { + "fieldname":"account", + "label": "Customer Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = wn.query_report.filters_by_name.company.get_value(); + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "debit_or_credit": "Debit", + "company": company, + "master_type": "Customer" + } + } + } + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + }, + ] +} \ No newline at end of file diff --git a/accounts/report/collection_report/collection_report.py b/accounts/report/collection_report/collection_report.py new file mode 100644 index 0000000000..50f74e0e82 --- /dev/null +++ b/accounts/report/collection_report/collection_report.py @@ -0,0 +1,86 @@ +# 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 accounts.report.accounts_receivable.accounts_receivable import get_ageing_data + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + entries = get_entries(filters) + si_posting_date_map = get_si_posting_date_map() + + data = [] + for d in entries: + against_invoice_date = d.against_invoice and si_posting_date_map[d.against_invoice] or "" + + row = [d.name, d.account, d.posting_date, d.against_invoice, against_invoice_date, + d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark] + + if d.against_invoice: + row += get_ageing_data(against_invoice_date, d.posting_date, d.credit or -1*d.debit) + else: + row += ["", "", "", "", ""] + + data.append(row) + + return columns, data + +def get_columns(): + return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", + "Posting Date:Date:100", "Against Invoice:Link/Sales Invoice:130", + "Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120", + "Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40", + "0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" + ] + +def get_conditions(filters): + conditions = "" + + customer_accounts = [] + if filters.get("account"): + customer_accounts = [filters["account"]] + elif filters.get("company"): + customer_accounts = webnotes.conn.sql_list("""select name from `tabAccount` + where ifnull(master_type, '') = 'Customer' and docstatus < 2 + and company = %s""", filters["company"]) + + if customer_accounts: + conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(customer_accounts))) + + if filters.get("from_date"): conditions += " and jv.posting_date >= '%s'" % filters["from_date"] + if filters.get("to_date"): conditions += " and jv.posting_date <= '%s'" % filters["to_date"] + + return conditions, customer_accounts + +def get_entries(filters): + conditions, customer_accounts = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jvd.account, jv.posting_date, + jvd.against_invoice, jvd.debit, jvd.credit, jv.cheque_no, jv.cheque_date, jv.remark + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" % + (conditions), tuple(customer_accounts), as_dict=1) + + return entries + +def get_si_posting_date_map(): + si_posting_date_map = {} + for t in webnotes.conn.sql("""select name, posting_date from `tabSales Invoice`"""): + si_posting_date_map[t[0]] = t[1] + + return si_posting_date_map \ No newline at end of file diff --git a/accounts/report/collection_report/collection_report.txt b/accounts/report/collection_report/collection_report.txt new file mode 100644 index 0000000000..3933dee3a6 --- /dev/null +++ b/accounts/report/collection_report/collection_report.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-01 12:29:12", + "docstatus": 0, + "modified": "2013-05-01 12:29:12", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Collection Report", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Collection Report" + } +] \ No newline at end of file diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 01dc93700b..c131c17b4b 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -141,7 +141,6 @@ def get_account_details(invoice_list): accounts = list(set([inv.credit_to for inv in invoice_list])) for acc in webnotes.conn.sql("""select name, parent_account from tabAccount where name in (%s)""" % ", ".join(["%s"]*len(accounts)), tuple(accounts), as_dict=1): - account_map.setdefault(acc.name, "") account_map[acc.name] = acc.parent_account return account_map \ No newline at end of file diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index a0020c45a0..23d2227fc2 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -145,7 +145,6 @@ def get_customer_deatils(invoice_list): customers = list(set([inv.customer for inv in invoice_list])) for cust in webnotes.conn.sql("""select name, territory from `tabCustomer` where name in (%s)""" % ", ".join(["%s"]*len(customers)), tuple(customers), as_dict=1): - customer_map.setdefault(cust.name, "") customer_map[cust.name] = cust.territory return customer_map @@ -155,7 +154,6 @@ def get_account_details(invoice_list): accounts = list(set([inv.debit_to for inv in invoice_list])) for acc in webnotes.conn.sql("""select name, parent_account from tabAccount where name in (%s)""" % ", ".join(["%s"]*len(accounts)), tuple(accounts), as_dict=1): - account_map.setdefault(acc.name, "") account_map[acc.name] = acc.parent_account return account_map \ No newline at end of file From 81bbfba0cc212ce38c29ca1094e7168935c4f9d4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 2 May 2013 11:27:25 +0530 Subject: [PATCH 069/295] [hr] allowed rename in earning and deduction type --- .../accounts_payable/accounts_payable.py | 19 ++-------- .../collection_report/collection_report.js | 2 +- hr/doctype/deduction_type/deduction_type.txt | 5 +-- hr/doctype/earning_type/earning_type.txt | 5 +-- stock/doctype/batch/batch.txt | 35 +++++-------------- 5 files changed, 18 insertions(+), 48 deletions(-) diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index 5e944a1b84..4e9b2c88ba 100644 --- a/accounts/report/accounts_payable/accounts_payable.py +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import getdate, nowdate, flt, cstr +from accounts.report.accounts_receivable.accounts_receivable import get_ageing_data def execute(filters=None): if not filters: filters = {} @@ -124,20 +125,4 @@ def get_paid_amount(gle, report_date, entries_after_report_date): and against_voucher = %s and name != %s and ifnull(is_cancelled, 'No') = 'No'""", (gle.account, report_date, gle.voucher_type, gle.voucher_no, gle.name))[0][0] - return flt(paid_amount) - -def get_ageing_data(ageing_based_on_date, age_on, outstanding_amount): - val1 = val2 = val3 = val4 = diff = 0 - diff = age_on and ageing_based_on_date \ - and (getdate(age_on) - getdate(ageing_based_on_date)).days or 0 - - if diff <= 30: - val1 = outstanding_amount - elif 30 < diff <= 60: - val2 = outstanding_amount - elif 60 < diff <= 90: - val3 = outstanding_amount - elif diff > 90: - val4 = outstanding_amount - - return [diff, val1, val2, val3, val4] \ No newline at end of file + return flt(paid_amount) \ No newline at end of file diff --git a/accounts/report/collection_report/collection_report.js b/accounts/report/collection_report/collection_report.js index 697a779c16..b370453c5f 100644 --- a/accounts/report/collection_report/collection_report.js +++ b/accounts/report/collection_report/collection_report.js @@ -1,7 +1,7 @@ wn.query_reports["Collection Report"] = { "filters": [ { - "fieldname":"from_date", + "fieldname": "from_date", "label": "From Date", "fieldtype": "Date", "default": wn.defaults.get_user_default("year_start_date"), diff --git a/hr/doctype/deduction_type/deduction_type.txt b/hr/doctype/deduction_type/deduction_type.txt index 4c9ad7ac4c..f3467936f5 100644 --- a/hr/doctype/deduction_type/deduction_type.txt +++ b/hr/doctype/deduction_type/deduction_type.txt @@ -1,12 +1,13 @@ [ { - "creation": "2013-01-10 16:34:13", + "creation": "2013-01-22 16:50:30", "docstatus": 0, - "modified": "2013-01-22 14:25:38", + "modified": "2013-05-02 11:22:59", "modified_by": "Administrator", "owner": "Administrator" }, { + "allow_rename": 1, "autoname": "field:deduction_name", "doctype": "DocType", "document_type": "Master", diff --git a/hr/doctype/earning_type/earning_type.txt b/hr/doctype/earning_type/earning_type.txt index 18cac6821a..d69f48619c 100644 --- a/hr/doctype/earning_type/earning_type.txt +++ b/hr/doctype/earning_type/earning_type.txt @@ -1,12 +1,13 @@ [ { - "creation": "2013-01-10 16:34:13", + "creation": "2013-01-24 11:03:32", "docstatus": 0, - "modified": "2013-01-23 16:32:07", + "modified": "2013-05-02 11:22:48", "modified_by": "Administrator", "owner": "Administrator" }, { + "allow_rename": 1, "autoname": "field:earning_name", "doctype": "DocType", "document_type": "Master", diff --git a/stock/doctype/batch/batch.txt b/stock/doctype/batch/batch.txt index 4722996c0f..c6ed3c370a 100644 --- a/stock/doctype/batch/batch.txt +++ b/stock/doctype/batch/batch.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-01 19:09:43", + "creation": "2013-03-05 14:50:38", "docstatus": 0, - "modified": "2013-03-01 08:22:16", + "modified": "2013-05-01 15:49:47", "modified_by": "Administrator", "owner": "harshada@webnotestech.com" }, @@ -24,14 +24,19 @@ "permlevel": 0 }, { + "cancel": 1, + "create": 1, "doctype": "DocPerm", "name": "__common__", "parent": "Batch", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, + "report": 1, "role": "Material Master Manager", - "submit": 0 + "submit": 0, + "write": 1 }, { "doctype": "DocType", @@ -94,28 +99,6 @@ "oldfieldtype": "Date" }, { - "doctype": "DocField", - "fieldname": "trash_reason", - "fieldtype": "Small Text", - "label": "Trash Reason", - "oldfieldname": "trash_reason", - "oldfieldtype": "Small Text", - "read_only": 1 - }, - { - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1 + "doctype": "DocPerm" } ] \ No newline at end of file From ecb36f2cb0486055ea076432878e99a3fddd5cfb Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 2 May 2013 11:34:37 +0530 Subject: [PATCH 070/295] [purchase] [validation] validate warehouse belongs to company if set --- .../doctype/purchase_order/test_purchase_order.py | 6 ++++++ controllers/buying_controller.py | 13 ++++++++++++- .../material_request/test_material_request.py | 6 ++++++ stock/doctype/warehouse/test_warehouse.py | 3 ++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py index bd27f07687..be2e2946a9 100644 --- a/buying/doctype/purchase_order/test_purchase_order.py +++ b/buying/doctype/purchase_order/test_purchase_order.py @@ -27,6 +27,12 @@ class TestPurchaseOrder(unittest.TestCase): po.insert() self.assertEquals(len(po.doclist.get({"parentfield": "po_raw_material_details"})), 2) + def test_warehouse_company_validation(self): + from controllers.buying_controller import WrongWarehouseCompany + po = webnotes.bean(copy=test_records[0]) + po.doc.company = "_Test Company 1" + self.assertRaises(WrongWarehouseCompany, po.insert) + test_dependencies = ["BOM"] diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index e167dc57b7..cd822e6ecc 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -26,10 +26,13 @@ from webnotes.model.utils import round_floats_in_doc from controllers.stock_controller import StockController +class WrongWarehouseCompany(Exception): pass + class BuyingController(StockController): def validate(self): super(BuyingController, self).validate() self.validate_stock_or_nonstock_items() + self.validate_warehouse_belongs_to_company() if self.meta.get_field("currency"): self.company_currency = get_company_currency(self.doc.company) self.validate_conversion_rate("currency", "conversion_rate") @@ -42,7 +45,15 @@ class BuyingController(StockController): # set total in words self.set_total_in_words() - + + def validate_warehouse_belongs_to_company(self): + for d in self.doclist.get({"warehouse": True}): + warehouse_company = webnotes.conn.get_value("Warehouse", d.warehouse, "company") + if warehouse_company and warehouse_company != self.doc.company: + webnotes.msgprint(_("Warehouse must belong to company") + \ + (": %s (%s, %s)" % (d.warehouse, warehouse_company, self.doc.company)), + raise_exception=WrongWarehouseCompany) + def validate_stock_or_nonstock_items(self): items = [d.item_code for d in self.doclist.get({"parentfield": self.fname})] if self.stock_items: diff --git a/stock/doctype/material_request/test_material_request.py b/stock/doctype/material_request/test_material_request.py index 080989f2d9..f5dbb52bbe 100644 --- a/stock/doctype/material_request/test_material_request.py +++ b/stock/doctype/material_request/test_material_request.py @@ -263,6 +263,12 @@ class TestMaterialRequest(unittest.TestCase): se = webnotes.bean(copy=se_doclist) self.assertRaises(webnotes.MappingMismatchError, se.insert) + def test_warehouse_company_validation(self): + from controllers.buying_controller import WrongWarehouseCompany + mr = webnotes.bean(copy=test_records[0]) + mr.doc.company = "_Test Company 1" + self.assertRaises(WrongWarehouseCompany, mr.insert) + test_records = [ [ { diff --git a/stock/doctype/warehouse/test_warehouse.py b/stock/doctype/warehouse/test_warehouse.py index 26501beab9..f3a04581a9 100644 --- a/stock/doctype/warehouse/test_warehouse.py +++ b/stock/doctype/warehouse/test_warehouse.py @@ -2,7 +2,8 @@ test_records = [ [{ "doctype": "Warehouse", "warehouse_name": "_Test Warehouse", - "warehouse_type": "_Test Warehouse Type" + "warehouse_type": "_Test Warehouse Type", + "company": "_Test Company" }], [{ "doctype": "Warehouse", From 3aeed95e0a5f51e3751ec9aa24912edccd5418c2 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 2 May 2013 11:39:46 +0530 Subject: [PATCH 071/295] [customer/supplier] [address] extended list to 5 --- buying/doctype/supplier/supplier.js | 4 ++-- selling/doctype/customer/customer.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/buying/doctype/supplier/supplier.js b/buying/doctype/supplier/supplier.js index 3834bda92c..202be71dfe 100644 --- a/buying/doctype/supplier/supplier.js +++ b/buying/doctype/supplier/supplier.js @@ -47,7 +47,7 @@ cur_frm.cscript.make_address = function() { if(!cur_frm.address_list) { cur_frm.address_list = new wn.ui.Listing({ parent: cur_frm.fields_dict['address_html'].wrapper, - page_length: 2, + page_length: 5, new_doctype: "Address", custom_new_doc: function(doctype) { var address = wn.model.make_new_doc_and_get_name('Address'); @@ -78,7 +78,7 @@ cur_frm.cscript.make_contact = function() { if(!cur_frm.contact_list) { cur_frm.contact_list = new wn.ui.Listing({ parent: cur_frm.fields_dict['contact_html'].wrapper, - page_length: 2, + page_length: 5, new_doctype: "Contact", custom_new_doc: function(doctype) { var contact = wn.model.make_new_doc_and_get_name('Contact'); diff --git a/selling/doctype/customer/customer.js b/selling/doctype/customer/customer.js index 403b83f582..40a7846430 100644 --- a/selling/doctype/customer/customer.js +++ b/selling/doctype/customer/customer.js @@ -57,7 +57,7 @@ cur_frm.cscript.make_address = function() { if(!cur_frm.address_list) { cur_frm.address_list = new wn.ui.Listing({ parent: cur_frm.fields_dict['address_html'].wrapper, - page_length: 2, + page_length: 5, new_doctype: "Address", custom_new_doc: function(doctype) { var address = wn.model.make_new_doc_and_get_name('Address'); @@ -88,7 +88,7 @@ cur_frm.cscript.make_contact = function() { if(!cur_frm.contact_list) { cur_frm.contact_list = new wn.ui.Listing({ parent: cur_frm.fields_dict['contact_html'].wrapper, - page_length: 2, + page_length: 5, custom_new_doc: function(doctype) { var contact = wn.model.make_new_doc_and_get_name('Contact'); contact = locals['Contact'][contact]; From f2edbbd5cb4b0f3c80eeee58634f38cb33e0cf7b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 2 May 2013 11:49:02 +0530 Subject: [PATCH 072/295] [serial no][validation] status delivered -> in store, allowed for material receipt --- stock/doctype/stock_ledger/stock_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py index 3c83de3260..8fdb50e7a4 100644 --- a/stock/doctype/stock_ledger/stock_ledger.py +++ b/stock/doctype/stock_ledger/stock_ledger.py @@ -114,7 +114,8 @@ class DocType: def update_serial_purchase_details(self, obj, d, serial_no, is_submit, purpose = '', rejected=None): exists = webnotes.conn.sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no)) if is_submit: - if exists and exists[0][2] != 2 and purpose not in ['Material Transfer', 'Sales Return']: + if exists and exists[0][2] != 2 and \ + purpose not in ['Material Transfer', "Material Receipt", 'Sales Return']: msgprint("Serial No: %s already %s" % (serial_no, exists and exists[0][1]), raise_exception = 1) elif exists: s = Document('Serial No', exists and exists[0][0]) From d7889cd5d505e4104aa60afb5192500f69cd805c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 2 May 2013 12:14:59 +0530 Subject: [PATCH 073/295] [script report] payment collection with ageing --- accounts/page/accounts_home/accounts_home.js | 5 +++++ .../__init__.py | 0 .../payment_collection_with_ageing.js} | 2 +- .../payment_collection_with_ageing.py} | 0 .../payment_collection_with_ageing.txt} | 8 ++++---- 5 files changed, 10 insertions(+), 5 deletions(-) rename accounts/report/{collection_report => payment_collection_with_ageing}/__init__.py (100%) rename accounts/report/{collection_report/collection_report.js => payment_collection_with_ageing/payment_collection_with_ageing.js} (94%) rename accounts/report/{collection_report/collection_report.py => payment_collection_with_ageing/payment_collection_with_ageing.py} (100%) rename accounts/report/{collection_report/collection_report.txt => payment_collection_with_ageing/payment_collection_with_ageing.txt} (63%) diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 99d92b576c..7038fe9720 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -207,6 +207,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Bank Clearance Summary", doctype: "Journal Voucher" }, + { + "label":wn._("Payment Collection With Ageing"), + route: "query-report/Payment Collection With Ageing", + doctype: "Journal Voucher" + }, ] } ] diff --git a/accounts/report/collection_report/__init__.py b/accounts/report/payment_collection_with_ageing/__init__.py similarity index 100% rename from accounts/report/collection_report/__init__.py rename to accounts/report/payment_collection_with_ageing/__init__.py diff --git a/accounts/report/collection_report/collection_report.js b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.js similarity index 94% rename from accounts/report/collection_report/collection_report.js rename to accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.js index b370453c5f..d608fbd1d8 100644 --- a/accounts/report/collection_report/collection_report.js +++ b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.js @@ -1,4 +1,4 @@ -wn.query_reports["Collection Report"] = { +wn.query_reports["Payment Collection With Ageing"] = { "filters": [ { "fieldname": "from_date", diff --git a/accounts/report/collection_report/collection_report.py b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py similarity index 100% rename from accounts/report/collection_report/collection_report.py rename to accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py diff --git a/accounts/report/collection_report/collection_report.txt b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.txt similarity index 63% rename from accounts/report/collection_report/collection_report.txt rename to accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.txt index 3933dee3a6..3405d197a7 100644 --- a/accounts/report/collection_report/collection_report.txt +++ b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-01 12:29:12", + "creation": "2013-05-02 12:09:51", "docstatus": 0, - "modified": "2013-05-01 12:29:12", + "modified": "2013-05-02 12:09:51", "modified_by": "Administrator", "owner": "Administrator" }, @@ -12,11 +12,11 @@ "is_standard": "Yes", "name": "__common__", "ref_doctype": "Journal Voucher", - "report_name": "Collection Report", + "report_name": "Payment Collection With Ageing", "report_type": "Script Report" }, { "doctype": "Report", - "name": "Collection Report" + "name": "Payment Collection With Ageing" } ] \ No newline at end of file From 76b3fcdb45128cce92dc2a9936f988396f883f10 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 2 May 2013 14:14:38 +0530 Subject: [PATCH 074/295] [backup manager] [fix] hide google drive settings and show proper intro messages --- .../backup_manager/backup_googledrive.py | 3 +- .../doctype/backup_manager/backup_manager.js | 151 ++++++++++-------- .../doctype/backup_manager/backup_manager.py | 8 +- .../doctype/backup_manager/backup_manager.txt | 5 +- 4 files changed, 95 insertions(+), 72 deletions(-) diff --git a/setup/doctype/backup_manager/backup_googledrive.py b/setup/doctype/backup_manager/backup_googledrive.py index a705f0efa9..533b37ddb0 100644 --- a/setup/doctype/backup_manager/backup_googledrive.py +++ b/setup/doctype/backup_manager/backup_googledrive.py @@ -92,8 +92,7 @@ def backup_to_gdrive(): mimetype = mimetypes.types_map.get("." + ext) or "application/octet-stream" #Compare Local File with Server File - param = {} - children = drive_service.children().list(folderId=files_folder_id, **param).execute() + children = drive_service.children().list(folderId=files_folder_id).execute() for child in children.get('items', []): file = drive_service.files().get(fileId=child['id']).execute() if filename == file['title'] and size == int(file['fileSize']): diff --git a/setup/doctype/backup_manager/backup_manager.js b/setup/doctype/backup_manager/backup_manager.js index 8d2d8de853..c78be3a1eb 100644 --- a/setup/doctype/backup_manager/backup_manager.js +++ b/setup/doctype/backup_manager/backup_manager.js @@ -1,67 +1,90 @@ -cur_frm.cscript.refresh = function(doc) { - cur_frm.disable_save(); -} - -//dropbox -cur_frm.cscript.allow_dropbox_access = function(doc) { - if (doc.send_notifications_to == '') { - msgprint("Please enter email address.") - } - else { - wn.call({ - method: "setup.doctype.backup_manager.backup_dropbox.get_dropbox_authorize_url", - callback: function(r) { - if(!r.exc) { - cur_frm.set_value("dropbox_access_secret", r.message.secret); - cur_frm.set_value("dropbox_access_key", r.message.key); - cur_frm.save(null, function() { - window.open(r.message.url); - }); - } +$.extend(cur_frm.cscript, { + refresh: function() { + cur_frm.disable_save(); + + if(!(cint(cur_frm.doc.dropbox_access_allowed) || + cint(cur_frm.doc.gdrive_access_allowed))) { + cur_frm.set_intro(wn._("You can start by selecting backup frequency and \ + granting access for sync")); + } else { + var services = { + "dropbox": wn._("Dropbox"), + "gdrive": wn._("Google Drive") + } + var active_services = []; + + $.each(services, function(service, label) { + var access_allowed = cint(cur_frm.doc[service + "_access_allowed"]); + var frequency = cur_frm.doc["upload_backups_to_" + service]; + if(access_allowed && frequency && frequency !== "Never") { + active_services.push(label + " [" + frequency + "]"); + } + }); + + if(active_services.length > 0) { + cur_frm.set_intro(wn._("Backups will be uploaded to") + ": " + + wn.utils.comma_and(active_services)); + } else { + cur_frm.set_intro(""); } - }) - } -} - -cur_frm.cscript.backup_right_now = function(doc) { - msgprint("Backing up and uploading. This may take a few minutes.") - wn.call({ - method: "setup.doctype.backup_manager.backup_manager.take_backups_dropbox", - callback: function(r) { - msgprint("Backups taken. Please check your email for the response.") } - }) -} -//gdrive -cur_frm.cscript.allow_gdrive_access = function(doc) { - if (doc.send_notifications_to == '') { - msgprint("Please enter email address.") - } - else { - wn.call({ - method: "setup.doctype.backup_manager.backup_googledrive.get_gdrive_authorize_url", - callback: function(r) { - if(!r.exc) { - window.open(r.message.authorize_url); + + }, + + validate_send_notifications_to: function() { + if(!cur_frm.doc.send_notifications_to) { + msgprint(wn._("Please specify") + ": " + + wn._(wn.meta.get_label(cur_frm.doctype, "send_notifications_to"))); + return false; + } + + return true; + }, + + allow_dropbox_access: function() { + if(cur_frm.cscript.validate_send_notifications_to()) { + wn.call({ + method: "setup.doctype.backup_manager.backup_dropbox.get_dropbox_authorize_url", + callback: function(r) { + if(!r.exc) { + cur_frm.set_value("dropbox_access_secret", r.message.secret); + cur_frm.set_value("dropbox_access_key", r.message.key); + cur_frm.save(null, function() { + window.open(r.message.url); + }); + } } - } - }) - } -} - -cur_frm.cscript.validate_gdrive = function(doc) { - wn.call({ - method: "setup.doctype.backup_manager.backup_googledrive.gdrive_callback", - args: { - verification_code: doc.verification_code - }, - }); -} - -cur_frm.cscript.upload_backups_to_dropbox = function(doc) { - cur_frm.save() -} - -cur_frm.cscript.upload_backups_to_gdrive = function(doc) { - cur_frm.save() -} + }); + } + }, + + allow_gdrive_access: function() { + if(cur_frm.cscript.validate_send_notifications_to()) { + wn.call({ + method: "setup.doctype.backup_manager.backup_googledrive.get_gdrive_authorize_url", + callback: function(r) { + if(!r.exc) { + window.open(r.message.authorize_url); + } + } + }); + } + }, + + validate_gdrive: function() { + wn.call({ + method: "setup.doctype.backup_manager.backup_googledrive.gdrive_callback", + args: { + verification_code: cur_frm.doc.verification_code + }, + }); + }, + + upload_backups_to_dropbox: function() { + cur_frm.save(); + }, + + upload_backups_to_gdrive: function() { + cur_frm.save(); + }, +}); \ No newline at end of file diff --git a/setup/doctype/backup_manager/backup_manager.py b/setup/doctype/backup_manager/backup_manager.py index a8ecd636db..c91cf1110f 100644 --- a/setup/doctype/backup_manager/backup_manager.py +++ b/setup/doctype/backup_manager/backup_manager.py @@ -20,8 +20,8 @@ def take_backups_if(freq): if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_dropbox")==freq: take_backups_dropbox() - if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_gdrive")==freq: - take_backups_gdrive() + # if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_gdrive")==freq: + # take_backups_gdrive() @webnotes.whitelist() def take_backups_dropbox(): @@ -35,7 +35,7 @@ def take_backups_dropbox(): except Exception: file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] error_message = ("\n".join(file_and_error) + "\n" + webnotes.getTraceback()) - print error_message + webnotes.errprint(error_message) send_email(False, "Dropbox", error_message) #backup to gdrive @@ -51,7 +51,7 @@ def take_backups_gdrive(): except Exception: file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] error_message = ("\n".join(file_and_error) + "\n" + webnotes.getTraceback()) - print error_message + webnotes.errprint(error_message) send_email(False, "Google Drive", error_message) def send_email(success, service_name, error_status=None): diff --git a/setup/doctype/backup_manager/backup_manager.txt b/setup/doctype/backup_manager/backup_manager.txt index 9a43f34746..2d6f191e5e 100644 --- a/setup/doctype/backup_manager/backup_manager.txt +++ b/setup/doctype/backup_manager/backup_manager.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-15 11:06:59", + "creation": "2013-04-30 12:58:38", "docstatus": 0, - "modified": "2013-03-15 17:27:33", + "modified": "2013-05-02 11:42:08", "modified_by": "Administrator", "owner": "Administrator" }, @@ -109,6 +109,7 @@ "doctype": "DocField", "fieldname": "sync_with_gdrive", "fieldtype": "Section Break", + "hidden": 1, "label": "Sync with Google Drive" }, { From c711598940c04ed8cb7b7f873c343ab3665c21ad Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 2 May 2013 14:22:40 +0530 Subject: [PATCH 075/295] [doclist] new pattern / [item] pricelist must be unique / [formatter] show null floats as empty string --- controllers/buying_controller.py | 10 +++++----- stock/doctype/item/item.py | 12 ++++++------ stock/doctype/item/test_item.py | 17 ++++++++++++++++- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index cd822e6ecc..0c25b9855e 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -47,13 +47,13 @@ class BuyingController(StockController): self.set_total_in_words() def validate_warehouse_belongs_to_company(self): - for d in self.doclist.get({"warehouse": True}): - warehouse_company = webnotes.conn.get_value("Warehouse", d.warehouse, "company") - if warehouse_company and warehouse_company != self.doc.company: + for warehouse, company in webnotes.conn.get_values("Warehouse", + self.doclist.get_distinct_values("warehouse"), "company").items(): + if company and company != self.doc.company: webnotes.msgprint(_("Warehouse must belong to company") + \ - (": %s (%s, %s)" % (d.warehouse, warehouse_company, self.doc.company)), + (": %s (%s, %s)" % (warehouse, company, self.doc.company)), raise_exception=WrongWarehouseCompany) - + def validate_stock_or_nonstock_items(self): items = [d.item_code for d in self.doclist.get({"parentfield": self.fname})] if self.stock_items: diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index 6b6dfb3700..c6bf017647 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -23,6 +23,7 @@ from webnotes.model.bean import getlist from webnotes import msgprint, _ from webnotes.model.controller import DocListController + class DocType(DocListController): def validate(self): if not self.doc.stock_uom: @@ -124,13 +125,12 @@ class DocType(DocListController): def check_ref_rate_detail(self): check_list=[] for d in getlist(self.doclist,'ref_rate_details'): - if [cstr(d.price_list_name), cstr(d.ref_currency), - cint(d.selling), cint(d.buying)] in check_list: - msgprint("Ref Rate is entered twice for Price List : '%s' and Currency : '%s'." % - (d.price_list_name,d.ref_currency), raise_exception=1) + if d.price_list_name in check_list: + msgprint(_("Cannot have two prices for same Price List") + ": " + d.price_list_name, + raise_exception= webnotes.DuplicateEntryError) else: - check_list.append([cstr(d.price_list_name),cstr(d.ref_currency)]) - + check_list.append(d.price_list_name) + def fill_customer_code(self): """ Append all the customer codes and insert into "customer_code" field of item table """ cust_code=[] diff --git a/stock/doctype/item/test_item.py b/stock/doctype/item/test_item.py index dbbeecc85b..a59747c3e3 100644 --- a/stock/doctype/item/test_item.py +++ b/stock/doctype/item/test_item.py @@ -20,6 +20,14 @@ import webnotes test_ignore = ["BOM"] +class TestItem(unittest.TestCase): + def test_duplicate_price_list(self): + item = webnotes.bean(copy=test_records[0]) + item.doc.item_code = "_Test Item 10" + item_price = item.doclist.get({"doctype": "Item Price"})[0] + item.doclist.append(webnotes.doc(item_price.fields.copy())) + self.assertRaises(webnotes.DuplicateEntryError, item.insert) + test_records = [ [{ "doctype": "Item", @@ -45,7 +53,14 @@ test_records = [ "warehouse": "_Test Warehouse", "warehouse_reorder_level": 20, "warehouse_reorder_qty": 20 - }], + }, { + "doctype": "Item Price", + "parentfield": "ref_rate_details", + "price_list_name": "_Test Price List", + "ref_rate": 100, + "ref_currency": "INR" + } + ], [{ "doctype": "Item", "item_code": "_Test Item Home Desktop 100", From 3487e0a0f3a3ff265add2eebb729a44b306b79e2 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 2 May 2013 14:46:12 +0530 Subject: [PATCH 076/295] [price list] added currency property, validates currency, duplication in Item Price --- home/page/latest_updates/latest_updates.js | 2 ++ setup/doctype/price_list/price_list.txt | 13 ++++++++++--- setup/doctype/price_list/test_price_list.py | 3 ++- stock/doctype/item/item.js | 2 ++ stock/doctype/item/item.py | 18 +++++++++++++----- stock/doctype/item/test_item.py | 8 ++++++++ 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index b768f1aa1e..e11b9c9243 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,4 +1,6 @@ erpnext.updates = [ + ["2nd May", ["Buying: Warehouse must belong to same company as transaction", + "Price List": "Added Currency Field. One price list can have only one currency"]], ["18th April", ["Cost Center: Set a default Cost Center for a Company"]], ["12th April", ["Employee: List of Leave Approvers who can approve the Employee's Leave Applications"]], ["10th April", ["Redesigned File Uploads and added File Manager in Setup"]], diff --git a/setup/doctype/price_list/price_list.txt b/setup/doctype/price_list/price_list.txt index a230f5b98c..bce8aae6e7 100644 --- a/setup/doctype/price_list/price_list.txt +++ b/setup/doctype/price_list/price_list.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-25 11:35:09", "docstatus": 0, - "modified": "2013-01-22 14:56:41", + "modified": "2013-05-02 14:44:24", "modified_by": "Administrator", "owner": "Administrator" }, @@ -52,13 +52,21 @@ "oldfieldtype": "Data", "reqd": 1 }, + { + "doctype": "DocField", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, { "depends_on": "price_list_name", "doctype": "DocField", "fieldname": "how_to_upload", "fieldtype": "HTML", "label": "How to upload", - "options": "