Merge remote-tracking branch 'webnotes/4.0.0-wip' into permissions
Conflicts:
erpnext/accounts/page/accounts_browser/accounts_browser.css
erpnext/controllers/buying_controller.py
erpnext/manufacturing/doctype/production_order/production_order.py
erpnext/patches/patch_list.py
erpnext/selling/doctype/customer/customer.txt
erpnext/selling/doctype/sales_order/sales_order.py
erpnext/selling/doctype/sales_order/test_sales_order.py
erpnext/setup/doctype/features_setup/features_setup.txt
erpnext/stock/doctype/stock_entry/test_stock_entry.py
erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
startup/query_handlers.py
diff --git a/erpnext/utilities/README.md b/erpnext/utilities/README.md
new file mode 100644
index 0000000..871f9d4
--- /dev/null
+++ b/erpnext/utilities/README.md
@@ -0,0 +1 @@
+Common utilities / DocTypes.
\ No newline at end of file
diff --git a/erpnext/utilities/__init__.py b/erpnext/utilities/__init__.py
new file mode 100644
index 0000000..b047537
--- /dev/null
+++ b/erpnext/utilities/__init__.py
@@ -0,0 +1,33 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes import _, msgprint
+from webnotes.utils import cint, comma_or
+
+def validate_status(status, options):
+ if status not in options:
+ msgprint(_("Status must be one of ") + comma_or(options), raise_exception=True)
+
+def build_filter_conditions(filters):
+ conditions, filter_values = [], []
+ for key in filters:
+ conditions.append('`' + key + '` = %s')
+ filter_values.append(filters[key])
+
+ conditions = conditions and " and " + " and ".join(conditions) or ""
+ return conditions, filter_values
\ No newline at end of file
diff --git a/erpnext/utilities/cleanup_data.py b/erpnext/utilities/cleanup_data.py
new file mode 100644
index 0000000..630a54e
--- /dev/null
+++ b/erpnext/utilities/cleanup_data.py
@@ -0,0 +1,203 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+#!/usr/bin/python
+
+# This script is for cleaning up of all data from system including
+# all transactions and masters (excludes default masters).
+# Basically after running this file, system will reset to it's
+# initial state.
+# This script can be executed from lib/wnf.py using
+# lib/wnf.py --cleanup-data
+
+from __future__ import unicode_literals
+import sys
+sys.path.append("lib/py")
+sys.path.append(".")
+sys.path.append("erpnext")
+
+import webnotes
+
+#--------------------------------
+
+def delete_transactions():
+ print "Deleting transactions..."
+
+ 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", "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))
+ webnotes.conn.sql("delete from `tab%s`" % (d))
+ print "Deleted " + d
+
+
+
+def delete_masters():
+ print "Deleting masters...."
+ masters = {
+ 'Workstation': ['Default Workstation'],
+ 'Warehouse': ['Default Warehouse'],
+ 'UOM': ['Kg', 'Mtr', 'Box', 'Ltr', 'Nos', 'Ft', 'Pair', 'Set'],
+ 'Territory': ['All Territories', 'Default Territory'],
+ 'Terms and Conditions': '',
+ 'Tag': '',
+ 'Supplier Type': ['Default Supplier Type'],
+ 'Supplier': '',
+ 'Serial No': '',
+ 'Sales Person': ['Sales Team'],
+ 'Sales Partner': '',
+ 'Sales BOM': '',
+ 'Salary Structure': '',
+ 'Purchase Taxes and Charges Master': '',
+ 'Project': '',
+ 'Print Heading': '',
+ 'Price List': ['Default Price List'],
+ 'Sales Taxes and Charges Master': '',
+ 'Letter Head': '',
+ 'Leave Type': ['Leave Without Pay', 'Privilege Leave', 'Casual Leave', 'PL', 'CL', 'LWP',
+ 'Compensatory Off', 'Sick Leave'],
+ 'Appraisal Template': '',
+ 'Item Group': ['All Item Groups', 'Default'],
+ 'Item': '',
+ 'Holiday List': '',
+ 'Activity Type': '',
+ 'Grade': '',
+ 'Feed': '',
+ 'Expense Claim Type': ['Travel', 'Medical', 'Calls', 'Food', 'Others'],
+ 'Event': '',
+ 'Employment Type': '',
+ 'Employee': '',
+ 'Earning Type': ['Basic', 'Conveyance', 'House Rent Allowance', 'Dearness Allowance',
+ 'Medical Allowance', 'Telephone'],
+ 'Designation': '',
+ 'Department': '',
+ 'Deduction Type': ['Income Tax', 'Professional Tax', 'Provident Fund', 'Leave Deduction'],
+ 'Customer Group': ['All Customer Groups', 'Default Customer Group'],
+ 'Customer': '',
+ 'Cost Center': '',
+ 'Contact': '',
+ 'Campaign': '',
+ 'Budget Distribution': '',
+ 'Brand': '',
+ 'Branch': '',
+ 'Batch': '',
+ 'Appraisal': '',
+ 'Account': '',
+ 'BOM': ''
+ }
+ for d in masters.keys():
+ 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))
+ lst = '"'+'","'.join(masters[d])+ '"'
+ webnotes.conn.sql("delete from `tab%s` where name not in (%s)" % (d, lst))
+ print "Deleted " + d
+
+
+
+def reset_all_series():
+ # Reset master series
+ webnotes.conn.sql("""update tabSeries set current = 0 where name not in
+ ('Ann/', 'BSD', 'DEF', 'DF', 'EV', 'Event Updates/', 'FileData-',
+ 'FL', 'FMD/', 'GLM Detail', 'Login Page/', 'MDI', 'MDR', 'MI', 'MIR',
+ 'PERM', 'PR', 'SRCH/C/', 'TD', 'TIC/', 'TMD/', 'TW', 'UR', '_FEED',
+ '_SRCH', '_TRIGGER', '__NSO', 'CustomField', 'Letter')
+ """)
+ print "Series updated"
+
+def reset_transaction_series():
+ webnotes.conn.sql("""update tabSeries set current = 0 where name in
+ ('JV', 'INV', 'BILL', 'SO', 'DN', 'PO', 'LEAD', 'ENQUIRY', 'ENQ', 'CI',
+ 'IN', 'PS', 'IDT', 'QAI', 'QTN', 'STE', 'SQTN', 'SUP', 'SR',
+ 'POS', 'LAP', 'LAL', 'EXP')""")
+ print "Series updated"
+
+
+def delete_main_masters():
+ main_masters = ['Fiscal Year', 'Company', 'DefaultValue']
+ for d in main_masters:
+ 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))
+ webnotes.conn.sql("delete from `tab%s`" % (d))
+ print "Deleted " + d
+
+def reset_global_defaults():
+ flds = {
+ 'default_company': None,
+ 'default_currency': None,
+ 'current_fiscal_year': None,
+ 'date_format': 'dd-mm-yyyy',
+ 'sms_sender_name': None,
+ 'default_item_group': 'Default',
+ 'default_stock_uom': 'Nos',
+ 'default_valuation_method': 'FIFO',
+ 'tolerance': None,
+ 'acc_frozen_upto': None,
+ 'bde_auth_role': None,
+ 'credit_controller': None,
+ 'default_customer_group': 'Default Customer Group',
+ 'default_territory': 'Default',
+ 'default_price_list': 'Standard',
+ 'default_supplier_type': 'Default Supplier Type',
+ 'hide_currency_symbol': None,
+ 'default_price_list_currency': None,
+ }
+
+ from webnotes.model.code import get_obj
+ gd = get_obj('Global Defaults', 'Global Defaults')
+ for d in flds:
+ gd.doc.fields[d] = flds[d]
+ gd.doc.save()
+
+ webnotes.clear_cache()
+
+
+def run():
+ webnotes.connect()
+ webnotes.conn.begin()
+
+ # Confirmation from user
+ confirm = ''
+ while not confirm:
+ confirm = raw_input("Are you sure you want to delete the data from the system (N/Y)?")
+ if confirm.lower() != 'y':
+ raise Exception
+
+ cleanup_type = ''
+ while cleanup_type not in ['1', '2']:
+ cleanup_type = raw_input("""\nWhat type of cleanup you want ot perform?
+ 1. Only Transactions
+ 2. Both Masters and Transactions
+
+ Please enter your choice (1/2):
+ """)
+
+ # delete
+ delete_transactions()
+
+ if cleanup_type == '1':
+ print "Reset Transaction Series"
+ reset_transaction_series()
+ else:
+ delete_masters()
+ print "Reset All Series"
+ reset_all_series()
+ delete_main_masters()
+ reset_global_defaults()
+
+ print "System cleaned up succesfully"
+ webnotes.conn.commit()
+ webnotes.conn.close()
+
+
+if __name__ == '__main__':
+ run()
diff --git a/erpnext/utilities/demo/__init__.py b/erpnext/utilities/demo/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/utilities/demo/__init__.py
diff --git a/erpnext/utilities/demo/demo-login.css b/erpnext/utilities/demo/demo-login.css
new file mode 100644
index 0000000..f3464a3
--- /dev/null
+++ b/erpnext/utilities/demo/demo-login.css
@@ -0,0 +1,3 @@
+body, #container, .outer {
+ background-color: #888 !important;
+}
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo-login.html b/erpnext/utilities/demo/demo-login.html
new file mode 100644
index 0000000..4595cb7
--- /dev/null
+++ b/erpnext/utilities/demo/demo-login.html
@@ -0,0 +1,25 @@
+<div class="container">
+ <div class="row" style="margin-top: 100px;">
+ <div class="col-sm-offset-3 col-sm-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ Start ERPNext Demo
+ </div>
+ <div class="panel-body">
+ <p>
+ <input id="lead-email" type="email"
+ class="form-control" placeholder="Your Email Id (optional)">
+ </p>
+ <p>
+ <button type="submit" id="login_btn"
+ class="btn btn-primary btn-large">Launch Demo</button>
+ </p>
+ <hr>
+ <p class="text-muted small">Some functionality is disabled for the demo app. The demo data will be cleared regulary. To start your own ERPNext Trial, <a href="https://erpnext.com/pricing-and-signup">click here</a></p>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ </div>
+</div>
diff --git a/erpnext/utilities/demo/demo-login.js b/erpnext/utilities/demo/demo-login.js
new file mode 100644
index 0000000..509057b
--- /dev/null
+++ b/erpnext/utilities/demo/demo-login.js
@@ -0,0 +1,27 @@
+$(document).ready(function() {
+ $(".navbar, footer, .banner, #user-tools").toggle(false);
+
+ $("#login_btn").click(function() {
+ var me = this;
+ $(this).html("Logging In...").prop("disabled", true);
+ wn.call({
+ "method": "login",
+ args: {
+ usr: "demo@erpnext.com",
+ pwd: "demo",
+ lead_email: $("#lead-email").val(),
+ },
+ callback: function(r) {
+ $(me).prop("disabled", false);
+ if(r.exc) {
+ alert("Error, please contact support@erpnext.com");
+ } else {
+ console.log("Logged In");
+ window.location.href = "app.html";
+ }
+ }
+ })
+ return false;
+ })
+ .prop("disabled", false);
+})
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_control_panel.py b/erpnext/utilities/demo/demo_control_panel.py
new file mode 100644
index 0000000..cb80373
--- /dev/null
+++ b/erpnext/utilities/demo/demo_control_panel.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+import webnotes
+
+class CustomDocType(DocType):
+ def on_login(self):
+ from webnotes.utils import validate_email_add
+ from webnotes import conf
+ if "demo_notify_url" in conf:
+ if webnotes.form_dict.lead_email and validate_email_add(webnotes.form_dict.lead_email):
+ import requests
+ response = requests.post(conf.demo_notify_url, data={
+ "cmd":"erpnext.templates.utils.send_message",
+ "subject":"Logged into Demo",
+ "sender": webnotes.form_dict.lead_email,
+ "message": "via demo.erpnext.com"
+ })
diff --git a/erpnext/utilities/demo/demo_docs/Address.csv b/erpnext/utilities/demo/demo_docs/Address.csv
new file mode 100644
index 0000000..51c34b5
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Address.csv
@@ -0,0 +1,46 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,,
+Table:,Address,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,
+Column Labels,ID,Address Type,Address Title,Address Line1,City/Town,Country,Phone,Customer,Customer Name,Supplier,Supplier Name,Address Line2,Pincode,State,Email Id,Fax,Preferred Billing Address,Preferred Shipping Address,Sales Partner,Lead,Lead Name
+Column Name:,name,address_type,address_title,address_line1,city,country,phone,customer,customer_name,supplier,supplier_name,address_line2,pincode,state,email_id,fax,is_primary_address,is_shipping_address,sales_partner,lead,lead_name
+Mandatory:,Yes,Yes,Yes,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No
+Type:,Data (text),Select,Data,Data,Data,Select,Data,Link,Data,Link,Data,Data,Data,Data,Data,Data,Check,Check,Link,Link,Data
+Info:,,"One of: Billing, Shipping, Office, Personal, Plant, Postal, Shop, Subsidiary, Warehouse, Other",,,,Valid Country,,Valid Customer,,Valid Supplier,,,,,,,0 or 1,0 or 1,Valid Sales Partner,Valid Lead,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,
+,,Office,,254 Theotokopoulou Str.,Larnaka,Cyprus,23566775757,Adaptas,Adaptas,,,,,,,,,,,,
+,,Office,,R Patrão Caramelho 116,Fajozes,Portugal,23566775757,Asian Fusion,Asian Fusion,,,,,,,,,,,,
+,,Office,,30 Fulford Road,PENTRE-PIOD,United Kingdom,23566775757,Asian Junction,Asian Junction,,,,,,,,,,,,
+,,Office,,Schoenebergerstrasse 13,Raschau,Germany,23566775757,Big D Supermarkets,Big D Supermarkets,,,,,,,,,,,,
+,,Office,,Hoheluftchaussee 43,Kieritzsch,Germany,23566775757,Buttrey Food & Drug,Buttrey Food & Drug,,,,,,,,,,,,
+,,Office,,R Cimo Vila 6,Rebordosa,Portugal,23566775757,Chi-Chis,Chi-Chis,,,,,,,,,,,,
+,,Office,,R 5 Outubro 9,Quinta Nova São Domingos,Portugal,23566775757,Choices,Choices,,,,,,,,,,,,
+,,Office,,Avenida Macambira 953,Goiânia,Brazil,23566775757,Consumers and Consumers Express,Consumers and Consumers Express,,,,,,,,,,,,
+,,Office,,2342 Goyeau Ave,Windsor,Canada,23566775757,Crafts Canada,Crafts Canada,,,,,,,,,,,,
+,,Office,,Laukaantie 82,KOKKOLA,Finland,23566775757,Endicott Shoes,Endicott Shoes,,,,,,,,,,,,
+,,Office,,9 Brown Street,PETERSHAM,Australia,23566775757,Fayva,Fayva,,,,,,,,,,,,
+,,Office,,Via Donnalbina 41,Cala Gonone,Italy,23566775757,Intelacard,Intelacard,,,,,,,,,,,,
+,,Office,,Liljerum Grenadjärtorpet 69,TOMTEBODA,Sweden,23566775757,Landskip Yard Care,Landskip Yard Care,,,,,,,,,,,,
+,,Office,,72 Bishopgate Street,SEAHAM,United Kingdom,23566775757,Life Plan Counselling,Life Plan Counselling,,,,,,,,,,,,
+,,Office,,Σκαφίδια 105,ΠΑΡΕΚΚΛΗΣΙΑ,Cyprus,23566775757,Mr Fables,Mr Fables,,,,,,,,,,,,
+,,Office,,Mellemvej 7,Aabybro,Denmark,23566775757,Nelson Brothers,Nelson Brothers,,,,,,,,,,,,
+,,Office,,Plouggårdsvej 98,Karby,Denmark,23566775757,Netobill,Netobill,,,,,,,,,,,,
+,,Office,,176 Michalakopoulou Street,Agio Georgoudi,Cyprus,23566775757,,,Helios Air,Helios Air,,,,,,,,,,
+,,Office,,Fibichova 1102,Kokorín,Czech Republic,23566775757,,,Ks Merchandise,Ks Merchandise,,,,,,,,,,
+,,Office,,Zahradní 888,Cechtín,Czech Republic,23566775757,,,HomeBase,HomeBase,,,,,,,,,,
+,,Office,,ul. Grochowska 94,Warszawa,Poland,23566775757,,,Scott Ties,Scott Ties,,,,,,,,,,
+,,Office,,Norra Esplanaden 87,HELSINKI,Finland,23566775757,,,Reliable Investments,Reliable Investments,,,,,,,,,,
+,,Office,,2038 Fallon Drive,Dresden,Canada,23566775757,,,Nan Duskin,Nan Duskin,,,,,,,,,,
+,,Office,,77 cours Franklin Roosevelt,MARSEILLE,France,23566775757,,,Rainbow Records,Rainbow Records,,,,,,,,,,
+,,Office,,ul. Tuwima Juliana 85,Łódź,Poland,23566775757,,,New World Realty,New World Realty,,,,,,,,,,
+,,Office,,Gl. Sygehusvej 41,Narsaq,Greenland,23566775757,,,Asiatic Solutions,Asiatic Solutions,,,,,,,,,,
+,,Office,,Gosposka ulica 50,Nova Gorica,Slovenia,23566775757,,,Eagle Hardware,Eagle Hardware,,,,,,,,,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/BOM.csv b/erpnext/utilities/demo/demo_docs/BOM.csv
new file mode 100644
index 0000000..ab0d6f5
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/BOM.csv
@@ -0,0 +1,47 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Table:,BOM,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+DocType:,BOM,,,,,,,,,,,,,,,~,BOM Operation,bom_operations,,,,,,~,BOM Item,bom_materials,,,,,,,,,~,BOM Explosion Item,flat_bom_details,,,,,,
+Column Labels:,ID,Item,Quantity,Is Active,Is Default,With Operations,Rate Of Materials Based On,Price List,Total Cost,Total Raw Material Cost,Total Operating Cost,Item UOM,Project Name,Item Desription,Amended From,,ID,Operation No,Operation Description,Workstation,Hour Rate,Operation Time (mins),Operating Cost,,ID,Item Code,Qty,Stock UOM,Operation No,BOM No,Rate,Amount,Scrap %,Item Description,,ID,Item Code,Description,Qty,Rate,Amount,Stock UOM,Qty Consumed Per Unit
+Column Name:,name,item,quantity,is_active,is_default,with_operations,rm_cost_as_per,buying_price_list,total_cost,raw_material_cost,operating_cost,uom,project_name,description,amended_from,~,name,operation_no,opn_description,workstation,hour_rate,time_in_mins,operating_cost,~,name,item_code,qty,stock_uom,operation_no,bom_no,rate,amount,scrap,description,~,name,item_code,description,qty,rate,amount,stock_uom,qty_consumed_per_unit
+Mandatory:,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,,Yes,Yes,Yes,No,No,No,No,,Yes,Yes,Yes,Yes,No,No,No,No,No,No,,Yes,No,No,No,No,No,No,No
+Type:,Data (text),Link,Float,Check,Check,Check,Select,Link,Float,Float,Float,Select,Link,Small Text,Link,,Data,Data,Text,Link,Float,Float,Float,,Data,Link,Float,Link,Select,Link,Float,Float,Float,Text,,Data,Link,Text,Float,Float,Float,Link,Float
+Info:,,Valid Item,,0 or 1,0 or 1,0 or 1,"One of: Valuation Rate, Last Purchase Rate, Price List",Valid Price List,,,,Valid UOM,Valid Project,,Valid BOM,,Leave blank for new records,,,Valid Workstation,,,,,Leave blank for new records,Valid Item,,Valid UOM,,Valid BOM,,,,,,Leave blank for new records,Valid Item,,,,,Valid UOM,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,BOM/Bearing Assembly/001,Bearing Assembly,1,1,1,,Price List,Standard Buying,130,130,0,Nos,,Bearing Assembly,,,,,,,,,,,,Base Bearing Plate,1,Nos,,,15,15,,1/4 in. x 6 in. x 6 in. Mild Steel Plate,,,Bearing Pipe,1.5 in. Diameter x 36 in. Mild Steel Tubing,1,15,15,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Block,1,Nos,,,10,10,,"CAST IRON, MCMASTER PART NO. 3710T13",,,Bearing Collar,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,2,20,40,Nos,2
+,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Collar,2,Nos,,,20,40,,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,,,Bearing Block,"CAST IRON, MCMASTER PART NO. 3710T13",1,10,10,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Pipe,1,Nos,,,15,15,,1.5 in. Diameter x 36 in. Mild Steel Tubing,,,Upper Bearing Plate,3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate,1,50,50,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Upper Bearing Plate,1,Nos,,,50,50,,3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate,,,Base Bearing Plate,1/4 in. x 6 in. x 6 in. Mild Steel Plate,1,15,15,Nos,1
+,BOM/Wind Mill A Series/001,Wind Mill A Series,1,1,1,,Price List,Standard Buying,223,223,0,Nos,,Wind Mill A Series for Home Use 9ft,,,,,,,,,,,,Base Bearing Plate,1,Nos,,,15,15,,1/4 in. x 6 in. x 6 in. Mild Steel Plate,,,Shaft,1.25 in. Diameter x 6 ft. Mild Steel Tubing,1,30,30,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Base Plate,1,Nos,,,20,20,,3/4 in. x 2 ft. x 4 ft. Pine Plywood,,,Base Bearing Plate,1/4 in. x 6 in. x 6 in. Mild Steel Plate,1,15,15,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Block,1,Nos,,,10,10,,"CAST IRON, MCMASTER PART NO. 3710T13",,,External Disc,15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing,1,45,45,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Pipe,1,Nos,,,15,15,,1.5 in. Diameter x 36 in. Mild Steel Tubing,,,Bearing Pipe,1.5 in. Diameter x 36 in. Mild Steel Tubing,1,15,15,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,External Disc,1,Nos,,,45,45,,15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing,,,Wing Sheet,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,4,22,88,Nos,4
+,,,,,,,,,,,,,,,,,,,,,,,,,,Shaft,1,Nos,,,30,30,,1.25 in. Diameter x 6 ft. Mild Steel Tubing,,,Base Plate,3/4 in. x 2 ft. x 4 ft. Pine Plywood,1,20,20,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Wing Sheet,4,Nos,,,22,88,,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,,,Bearing Block,"CAST IRON, MCMASTER PART NO. 3710T13",1,10,10,Nos,1
+,BOM/Wind MIll C Series/001,Wind MIll C Series,1,1,1,,Price List,Standard Buying,314,314,0,Nos,,Wind Mill C Series for Commercial Use 18ft,,,,,,,,,,,,Base Plate,2,Nos,,,20,40,,3/4 in. x 2 ft. x 4 ft. Pine Plywood,,,Base Bearing Plate,1/4 in. x 6 in. x 6 in. Mild Steel Plate,1,15,15,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Internal Disc,1,Nos,,,33,33,,For Bearing Collar,,,Bearing Collar,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,2,20,40,Nos,2
+,,,,,,,,,,,,,,,,,,,,,,,,,,External Disc,1,Nos,,,45,45,,15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing,,,Base Plate,3/4 in. x 2 ft. x 4 ft. Pine Plywood,2,20,40,Nos,2
+,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Assembly,1,Nos,,BOM/Bearing Assembly/001,130,130,,Bearing Assembly,,,Bearing Pipe,1.5 in. Diameter x 36 in. Mild Steel Tubing,1,15,15,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Wing Sheet,3,Nos,,,22,66,,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,,,Internal Disc,For Bearing Collar,1,33,33,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Upper Bearing Plate,3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate,1,50,50,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Wing Sheet,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,3,22,66,Nos,3
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,External Disc,15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing,1,45,45,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Block,"CAST IRON, MCMASTER PART NO. 3710T13",1,10,10,Nos,1
+,BOM/Wind Turbine/001,Wind Turbine,1,1,1,,Price List,Standard Buying,139,139,0,Nos,,Small Wind Turbine for Home Use,,,,,,,,,,,,Base Bearing Plate,1,Nos,,,15,15,,1/4 in. x 6 in. x 6 in. Mild Steel Plate,,,Shaft,1.25 in. Diameter x 6 ft. Mild Steel Tubing,1,30,30,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Base Plate,1,Nos,,,20,20,,3/4 in. x 2 ft. x 4 ft. Pine Plywood,,,Bearing Collar,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,1,20,20,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Bearing Collar,1,Nos,,,20,20,,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,,,Base Plate,3/4 in. x 2 ft. x 4 ft. Pine Plywood,1,20,20,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Blade Rib,1,Nos,,,10,10,,1/2 in. x 2 ft. x 4 ft. Pine Plywood,,,Base Bearing Plate,1/4 in. x 6 in. x 6 in. Mild Steel Plate,1,15,15,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Shaft,1,Nos,,,30,30,,1.25 in. Diameter x 6 ft. Mild Steel Tubing,,,Blade Rib,1/2 in. x 2 ft. x 4 ft. Pine Plywood,1,10,10,Nos,1
+,,,,,,,,,,,,,,,,,,,,,,,,,,Wing Sheet,2,Nos,,,22,44,,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,,,Wing Sheet,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,2,22,44,Nos,2
diff --git a/erpnext/utilities/demo/demo_docs/Contact.csv b/erpnext/utilities/demo/demo_docs/Contact.csv
new file mode 100644
index 0000000..be3dddc
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Contact.csv
@@ -0,0 +1,46 @@
+Data Import Template,,,,,,,,,,,,,,,
+Table:,Contact,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,
+Column Labels,First Name,Email Id,Phone,Last Name,Status,Customer,Customer Name,Supplier,Supplier Name,Sales Partner,Is Primary Contact,Mobile No,Department,Designation,Unsubscribed
+Column Name:,first_name,email_id,phone,last_name,status,customer,customer_name,supplier,supplier_name,sales_partner,is_primary_contact,mobile_no,department,designation,unsubscribed
+Mandatory:,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No
+Type:,Data,Data,Data,Data,Select,Link,Data,Link,Data,Link,Check,Data,Data,Data,Check
+Info:,,,,,"One of: Open, Replied",Valid Customer,,Valid Supplier,,Valid Sales Partner,0 or 1,,,,0 or 1
+Start entering data below this line,,,,,,,,,,,,,,,
+,Jan,JanVaclavik@dayrep.com,456675757,Václavík,,Adaptas,Adaptas,,,,,,,,
+,Chidumaga,ChidumagaTobeolisa@armyspy.com,456675757,Tobeolisa,,Asian Fusion,Asian Fusion,,,,,,,,
+,Jana,JanaKubanova@superrito.com,456675757,Kubáňová,,Asian Junction,Asian Junction,,,,,,,,
+,紹萱,XuChaoXuan@gustr.com,456675757,于,,Big D Supermarkets,Big D Supermarkets,,,,,,,,
+,Özlem,OzlemVerwijmeren@dayrep.com,456675757,Verwijmeren,,Buttrey Food & Drug,Buttrey Food & Drug,,,,,,,,
+,Hans,HansRasmussen@superrito.com,456675757,Rasmussen,,Chi-Chis,Chi-Chis,,,,,,,,
+,Satomi,SatomiShigeki@superrito.com,456675757,Shigeki,,Choices,Choices,,,,,,,,
+,Simon,SimonVJessen@superrito.com,456675757,Jessen,,Consumers and Consumers Express,Consumers and Consumers Express,,,,,,,,
+,نگارین,NeguaranShahsaah@teleworm.us,456675757,شاه سیاه,,Crafts Canada,Crafts Canada,,,,,,,,
+,Lom-Ali,Lom-AliBataev@dayrep.com,456675757,Bataev,,Endicott Shoes,Endicott Shoes,,,,,,,,
+,Tiên,VanNgocTien@einrot.com,456675757,Văn,,Fayva,Fayva,,,,,,,,
+,Quimey,QuimeyOsorioRuelas@gustr.com,456675757,Osorio,,Intelacard,Intelacard,,,,,,,,
+,Edgarda,EdgardaSalcedoRaya@teleworm.us,456675757,Salcedo,,Landskip Yard Care,Landskip Yard Care,,,,,,,,
+,Hafsteinn,HafsteinnBjarnarsonar@armyspy.com,456675757,Bjarnarsonar,,Life Plan Counselling,Life Plan Counselling,,,,,,,,
+,Даниил,@superrito.com,456675757,Коновалов,,Mr Fables,Mr Fables,,,,,,,,
+,Selma,SelmaMAndersen@teleworm.us,456675757,Andersen,,Nelson Brothers,Nelson Brothers,,,,,,,,
+,Ladislav,LadislavKolaja@armyspy.com,456675757,Kolaja,,Netobill,Netobill,,,,,,,,
+,Tewolde,TewoldeAbaalom@teleworm.us,456675757,Abaalom,,,,Helios Air,Helios Air,,,,,,
+,Leila,LeilaFernandesRodrigues@gustr.com,456675757,Rodrigues,,,,Ks Merchandise,Ks Merchandise,,,,,,
+,Dmitry,DmitryBulgakov@gustr.com,456675757,Bulgakov,,,,HomeBase,HomeBase,,,,,,
+,Haiduc,HaiducWhitfoot@dayrep.com,456675757,Whitfoot,,,,Scott Ties,Scott Ties,,,,,,
+,Sesselja,SesseljaPetursdottir@cuvox.de,456675757,Pétursdóttir,,,,Reliable Investments,Reliable Investments,,,,,,
+,Hajdar,HajdarPignar@superrito.com,456675757,Pignar,,,,Nan Duskin,Nan Duskin,,,,,,
+,Gustava,GustavaLorenzo@teleworm.us,456675757,Lorenzo,,,,Rainbow Records,Rainbow Records,,,,,,
+,Bethany,BethanyWood@teleworm.us,456675757,Wood,,,,New World Realty,New World Realty,,,,,,
+,Gloriana,GlorianaBrownlock@cuvox.de,456675757,Brownlock,,,,Asiatic Solutions,Asiatic Solutions,,,,,,
+,Jenson,JensonFraser@gustr.com,456675757,Fraser,,,,Eagle Hardware,Eagle Hardware,,,,,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/Customer.csv b/erpnext/utilities/demo/demo_docs/Customer.csv
new file mode 100644
index 0000000..2ed0e1c
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Customer.csv
@@ -0,0 +1,38 @@
+Data Import Template,,,,,,,,,,,,,,,,
+Table:,Customer,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,
+Column Labels,ID,Customer Name,Customer Type,Customer Group,Territory,Company,Series,Lead Ref,Default Price List,Default Currency,Customer Details,Credit Days,Credit Limit,Website,Default Sales Partner,Default Commission Rate
+Column Name:,name,customer_name,customer_type,customer_group,territory,company,naming_series,lead_name,default_price_list,default_currency,customer_details,credit_days,credit_limit,website,default_sales_partner,default_commission_rate
+Mandatory:,Yes,Yes,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No
+Type:,Data (text),Data,Select,Link,Link,Link,Select,Link,Link,Link,Text,Int,Currency,Data,Link,Float
+Info:,,,"One of: Company, Individual",Valid Customer Group,Valid Territory,Valid Company,"One of: CUST, CUSTMUM",Valid Lead,Valid Price List,Valid Currency,,Integer,,,Valid Sales Partner,
+Start entering data below this line,,,,,,,,,,,,,,,,
+,,Asian Junction,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Life Plan Counselling,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Two Pesos,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Mr Fables,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Intelacard,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Big D Supermarkets,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Adaptas,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Nelson Brothers,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Landskip Yard Care,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Buttrey Food & Drug,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Fayva,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Asian Fusion,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Crafts Canada,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Consumers and Consumers Express,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Netobill,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Choices,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Chi-Chis,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Red Food,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
+,,Endicott Shoes,Company,Commercial,Rest Of The World,Wind Power LLC,,,,,,,,,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/Employee.csv b/erpnext/utilities/demo/demo_docs/Employee.csv
new file mode 100644
index 0000000..c9b340a
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Employee.csv
@@ -0,0 +1,40 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Table:,Employee,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+DocType:,Employee,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Column Labels:,ID,Full Name,Date of Joining,Date of Birth,Gender,Company,Status,Naming Series,Salutation,Image,User ID,Employee Number,Employment Type,Holiday List,Scheduled Confirmation Date,Final Confirmation Date,Contract End Date,Date Of Retirement,Branch,Department,Designation,Grade,Email (By company),Notice - Number of Days,Salary Mode,Bank Name,Bank A/C No.,ESIC CARD No,PF Number,Gratuity LIC ID,Reports to,Cell Number,Personal Email,Person To Be Contacted,Relation,Emergency Phone Number,Permanent Accommodation Type,Permanent Address,Current Accommodation Type,Current Address,Bio,PAN Number,Passport Number,Date of Issue,Valid Upto,Place of Issue,Marital Status,Blood Group,Family Background,Health Details,Resignation Letter Date,Relieving Date,Reason for Leaving,Leave Encashed?,Encashment Date,Held On,Reason for Resignation,New Workplace,Feedback
+Column Name:,name,employee_name,date_of_joining,date_of_birth,gender,company,status,naming_series,salutation,image,user_id,employee_number,employment_type,holiday_list,scheduled_confirmation_date,final_confirmation_date,contract_end_date,date_of_retirement,branch,department,designation,grade,company_email,notice_number_of_days,salary_mode,bank_name,bank_ac_no,esic_card_no,pf_number,gratuity_lic_id,reports_to,cell_number,personal_email,person_to_be_contacted,relation,emergency_phone_number,permanent_accommodation_type,permanent_address,current_accommodation_type,current_address,bio,pan_number,passport_number,date_of_issue,valid_upto,place_of_issue,marital_status,blood_group,family_background,health_details,resignation_letter_date,relieving_date,reason_for_leaving,leave_encashed,encashment_date,held_on,reason_for_resignation,new_workplace,feedback
+Mandatory:,Yes,Yes,Yes,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No
+Type:,Data (text),Data,Date,Date,Select,Select,Select,Select,Select,Select,Link,Data,Link,Link,Date,Date,Date,Date,Link,Link,Link,Link,Data,Int,Select,Data,Data,Data,Data,Data,Link,Data,Data,Data,Data,Data,Select,Small Text,Select,Small Text,Text Editor,Data,Data,Date,Date,Data,Select,Select,Small Text,Small Text,Date,Date,Data,Select,Date,Date,Select,Data,Small Text
+Info:,,,,,"One of: Male, Female",Valid Company,"One of: Active, Left",One of: EMP/,"One of: Mr, Ms",One of: attach_files:,Valid Profile,,Valid Employment Type,Valid Holiday List,,,,,Valid Branch,Valid Department,Valid Designation,Valid Grade,,Integer,"One of: Bank, Cash, Cheque",,,,,,Valid Employee,,,,,,"One of: Rented, Owned",,"One of: Rented, Owned",,,,,,,,"One of: Single, Married, Divorced, Widowed","One of: A+, A-, B+, B-, AB+, AB-, O+, O-",,,,,,"One of: Yes, No",,,"One of: Better Prospects, Health Concerns",,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Dikman Shervashidze Shervashidze,10-10-2001,03-01-1982,Female,Wind Power LLC,Active,EMP/,,,DikmanShervashidze@armyspy.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Zukutakitoteka ,09-16-1976,03-02-1959,Female,Wind Power LLC,Active,EMP/,,,Zukutakitoteka@teleworm.us,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Hatsue Kashiwagi,06-16-2000,03-03-1982,Female,Wind Power LLC,Active,EMP/,,,HatsueKashiwagi@cuvox.de,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Nuran Verkleij,07-01-1969,04-04-1945,Female,Wind Power LLC,Active,EMP/,,,NuranVerkleij@einrot.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Дмитрий Пирогов,12-24-1999,03-05-1978,Male,Wind Power LLC,Active,EMP/,,,aromn@armyspy.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Tilde Lindqvist,08-05-1981,03-06-1964,Female,Wind Power LLC,Active,EMP/,,,TildeLindqvist@cuvox.de,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Michał Sobczak,06-10-2006,03-07-1982,Male,Wind Power LLC,Active,EMP/,,,MichalSobczak@teleworm.us,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Gabrielle Loftus,10-21-1993,03-08-1969,Female,Wind Power LLC,Active,EMP/,,,GabrielleLoftus@superrito.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Vakhita Ryzaev,09-06-2005,03-09-1982,Male,Wind Power LLC,Active,EMP/,,,VakhitaRyzaev@teleworm.us,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Charmaine Gaudreau,12-25-2007,03-10-1985,Female,Wind Power LLC,Active,EMP/,,,CharmaineGaudreau@cuvox.de,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Rafaëla Maartens,09-02-2002,03-11-1982,Female,Wind Power LLC,Active,EMP/,,,RafaelaMaartens@cuvox.de,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Nuguse Yohannes,08-24-1984,07-12-1966,Male,Wind Power LLC,Active,EMP/,,,NuguseYohannes@dayrep.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Раиса Белякова,12-04-2002,03-13-1982,Female,Wind Power LLC,Active,EMP/,,,panca@armyspy.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,胤隆 蔡,03-14-2011,06-14-1991,Male,Wind Power LLC,Active,EMP/,,,CaYinLong@gustr.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Freddie Scott,12-14-2004,03-15-1982,Male,Wind Power LLC,Active,EMP/,,,FreddieScott@armyspy.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Bergþóra Vigfúsdóttir,05-17-2004,03-16-1982,Female,Wind Power LLC,Active,EMP/,,,BergoraVigfusdottir@superrito.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Ward Kalb,05-13-1994,03-17-1973,Male,Wind Power LLC,Active,EMP/,,,WardNajmalDinKalb@cuvox.de,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Wan Mai,12-15-2003,09-18-1982,Female,Wind Power LLC,Active,EMP/,,,WanMai@teleworm.us,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Leon Abdulov,03-15-1999,03-19-1982,Male,Wind Power LLC,Active,EMP/,,,LeonAbdulov@superrito.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Sabina Novotná,01-25-1991,03-20-1974,Female,Wind Power LLC,Active,EMP/,,,SabinaNovotna@superrito.com,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/Fiscal_Year.csv b/erpnext/utilities/demo/demo_docs/Fiscal_Year.csv
new file mode 100644
index 0000000..d56b1b9
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Fiscal_Year.csv
@@ -0,0 +1,23 @@
+Data Import Template,,,,
+Table:,Fiscal Year,,,
+,,,,
+,,,,
+Notes:,,,,
+Please do not change the template headings.,,,,
+First data column must be blank.,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,
+"For updating, you can update only selective columns.",,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,
+,,,,
+DocType:,Fiscal Year,,,
+Column Labels:,ID,Year Name,Year Start Date,Year Closed
+Column Name:,name,year,year_start_date,year_end_date,is_fiscal_year_closed
+Mandatory:,Yes,Yes,Yes,Yes,No
+Type:,Data (text),Data,Date,Date,Select
+Info:,,,,,"One of: No, Yes"
+Start entering data below this line,,,,
+,,2009,01-01-2009,31-12-2009,No
+,,2010,01-01-2010,31-12-2010,No
+,,2011,01-01-2011,31-12-2011,No
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/Item.csv b/erpnext/utilities/demo/demo_docs/Item.csv
new file mode 100644
index 0000000..eeb18c5
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Item.csv
@@ -0,0 +1,37 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Table:,Item,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+DocType:,Item,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,~,Item Reorder,item_reorder,,,,~,UOM Conversion Detail,uom_conversion_details,,~,Item Supplier,item_supplier_details,,~,Item Customer Detail,item_customer_details,,~,Item Tax,item_tax,,~,Item Quality Inspection Parameter,item_specification_details,,~,Website Item Group,website_item_groups,~,Item Website Specification,item_website_specifications,
+Column Labels:,ID,Item Name,Item Group,Default Unit of Measure,Description,Is Stock Item,Is Asset Item,Has Batch No,Has Serial No,Is Purchase Item,Is Sales Item,Is Service Item,Inspection Required,Allow Bill of Materials,Allow Production Order,Is Sub Contracted Item,Document Numbering Series,Item Code,Brand,Barcode,Image,Description HTML,Default Warehouse,Allowance Percent,Valuation Method,Minimum Order Qty,Serial Number Series,Warranty Period (in days),End of Life,Net Weight,Weight UOM,Re-Order Level,Re-Order Qty,Default Supplier,Lead Time Days,Default Expense Account,Default Cost Center,Last Purchase Rate,Standard Rate,Manufacturer,Manufacturer Part Number,Max Discount (%),Default Income Account,Cost Center,Default BOM,Show in Website,Page Name,Weightage,Slideshow,Image,Website Warehouse,Website Description,,ID,Warehouse,Re-order Level,Material Request Type,Re-order Qty,,ID,UOM,Conversion Factor,,ID,Supplier,Supplier Part Number,,ID,Customer Name,Ref Code,,ID,Tax,Tax Rate,,ID,Parameter,Acceptance Criteria,,ID,Item Group,,ID,Label,Description
+Column Name:,name,item_name,item_group,stock_uom,description,is_stock_item,is_asset_item,has_batch_no,has_serial_no,is_purchase_item,is_sales_item,is_service_item,inspection_required,is_manufactured_item,is_pro_applicable,is_sub_contracted_item,naming_series,item_code,brand,barcode,image,description_html,default_warehouse,tolerance,valuation_method,min_order_qty,serial_no_series,warranty_period,end_of_life,net_weight,weight_uom,re_order_level,re_order_qty,default_supplier,lead_time_days,purchase_account,cost_center,last_purchase_rate,standard_rate,manufacturer,manufacturer_part_no,max_discount,default_income_account,default_sales_cost_center,default_bom,show_in_website,page_name,weightage,slideshow,website_image,website_warehouse,web_long_description,~,name,warehouse,warehouse_reorder_level,material_request_type,warehouse_reorder_qty,~,name,uom,conversion_factor,~,name,supplier,supplier_part_no,~,name,customer_name,ref_code,~,name,tax_type,tax_rate,~,name,specification,value,~,name,item_group,~,name,label,description
+Mandatory:,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,,Yes,Yes,Yes,Yes,No,,Yes,No,No,,Yes,No,No,,Yes,Yes,Yes,,Yes,Yes,No,,Yes,Yes,No,,Yes,No,,Yes,No,No
+Type:,Data (text),Data,Link,Link,Small Text,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Data,Link,Data,Select,Small Text,Link,Float,Select,Float,Data,Data,Date,Float,Link,Float,Float,Link,Int,Link,Link,Float,Float,Data,Data,Float,Link,Link,Link,Check,Data,Int,Link,Select,Link,Text Editor,,Data,Link,Float,Select,Float,,Data,Link,Float,,Data,Link,Data,,Data,Link,Data,,Data,Link,Float,,Data,Data,Data,,Data,Link,,Data,Data,Text
+Info:,,,Valid Item Group,Valid UOM,,"One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No",One of: ITEM,,Valid Brand,,One of: attach_files:,,Valid Warehouse,,"One of: FIFO, Moving Average",,,,,,Valid UOM,,,Valid Supplier,Integer,Valid Account,Valid Cost Center,,,,,,Valid Account,Valid Cost Center,Valid BOM,0 or 1,,Integer,Valid Website Slideshow,One of: attach_files:,Valid Warehouse,,,Leave blank for new records,Valid Warehouse,,"One of: Purchase, Transfer",,,Leave blank for new records,Valid UOM,,,Leave blank for new records,Valid Supplier,,,Leave blank for new records,Valid Customer,,,Leave blank for new records,Valid Account,,,Leave blank for new records,,,,Leave blank for new records,Valid Item Group,,Leave blank for new records,,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,Base Bearing Plate,Base Bearing Plate,Raw Material,Nos,1/4 in. x 6 in. x 6 in. Mild Steel Plate,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Base Bearing Plate,,,,,Stores - WP,0,,0,,,,0,,0,0,Eagle Hardware,0,,,15,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Base Plate,Base Plate,Raw Material,Nos,3/4 in. x 2 ft. x 4 ft. Pine Plywood,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Base Plate,,,,,Stores - WP,0,,0,,,,0,,0,0,HomeBase,0,,,20,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Bearing Assembly,Bearing Assembly,Sub Assemblies,Nos,Bearing Assembly,Yes,No,No,No,No,Yes,No,No,Yes,Yes,No,,Bearing Assembly,,,,,Stores - WP,0,,0,,,,0,,0,0,Asiatic Solutions,0,,,0,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Bearing Block,Bearing Block,Raw Material,Nos,"CAST IRON, MCMASTER PART NO. 3710T13",Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Bearing Block,,,,,Stores - WP,0,,0,,,,0,,0,0,Nan Duskin,0,,,10,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Bearing Collar,Bearing Collar,Raw Material,Nos,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Bearing Collar,,,,,Stores - WP,0,,0,,,,0,,0,0,Eagle Hardware,0,,,20,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Bearing Pipe,Bearing Pipe,Raw Material,Nos,1.5 in. Diameter x 36 in. Mild Steel Tubing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Bearing Pipe,,,,,Stores - WP,0,,0,,,,0,,0,0,HomeBase,0,,,15,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Blade Rib,Blade Rib,Raw Material,Nos,1/2 in. x 2 ft. x 4 ft. Pine Plywood,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Blade Rib,,,,,Stores - WP,0,,0,,,,0,,0,0,Ks Merchandise,0,,,0,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Disc Collars,Disc Collars,Raw Material,Nos,For Upper Bearing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Disc Collars,,,,,Stores - WP,0,,0,,,,0,,0,0,Asiatic Solutions,0,,,0,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,External Disc,External Disc,Raw Material,Nos,15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,External Disc,,,,,Stores - WP,0,,0,,,,0,,0,0,HomeBase,0,,,45,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Internal Disc,Internal Disc,Raw Material,Nos,For Bearing Collar,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Internal Disc,,,,,Stores - WP,0,,0,,,,0,,0,0,HomeBase,0,,,33,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Shaft,Shaft,Raw Material,Nos,1.25 in. Diameter x 6 ft. Mild Steel Tubing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Shaft,,,,,Stores - WP,0,,0,,,,0,,0,0,Eagle Hardware,0,,,30,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Stand,Stand,Raw Material,Nos,N/A,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Stand,,,,,Stores - WP,0,,0,,,,0,,0,0,Scott Ties,0,,,0,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Upper Bearing Plate,Upper Bearing Plate,Raw Material,Nos,3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Upper Bearing Plate,,,,,Stores - WP,0,,0,,,,0,,0,0,Eagle Hardware,0,,,50,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Wind Mill A Series,Wind Mill A Series,Products,Nos,Wind Mill A Series for Home Use 9ft,Yes,No,No,Yes,No,Yes,Yes,No,Yes,Yes,No,,Wind Mill A Series,,,,,Finished Goods - WP,0,,0,WMA,,,0,,0,0,,0,,,0,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Wind MIll C Series,Wind MIll C Series,Products,Nos,Wind Mill C Series for Commercial Use 18ft,Yes,No,No,Yes,No,Yes,Yes,No,Yes,Yes,No,,Wind MIll C Series,,,,,Finished Goods - WP,0,,0,WMC,,,0,,0,0,,0,,,0,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Wind Turbine,Wind Turbine,Products,Nos,Small Wind Turbine for Home Use,Yes,No,No,Yes,No,Yes,Yes,No,Yes,Yes,No,,Wind Turbine,,,,,Finished Goods - WP,0,,0,WTU,,,0,,0,0,,0,,,0,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
+,Wing Sheet,Wing Sheet,Raw Material,Nos,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Wing Sheet,,,,,Stores - WP,0,,0,,,,0,,0,0,New World Realty,0,,,22,0,,,0,,,,0,,0,,,,,,,,,,,,,Nos,1,,,,,,,,,,,,,,,,,,,,,,,
diff --git a/erpnext/utilities/demo/demo_docs/Item_Price.csv b/erpnext/utilities/demo/demo_docs/Item_Price.csv
new file mode 100644
index 0000000..d70b7b3
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Item_Price.csv
@@ -0,0 +1,49 @@
+Data Import Template,,,,,,,
+Table:,Item Price,,,,,,
+,,,,,,,
+,,,,,,,
+Notes:,,,,,,,
+Please do not change the template headings.,,,,,,,
+First data column must be blank.,,,,,,,
+"If you are uploading new records, leave the ""name"""" (ID) column blank.""",,,,,,,
+"If you are uploading new records, ""Naming Series"""" becomes mandatory"," if present.""",,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,
+,,,,,,,
+DocType:,Item Price,,,,,,
+Column Labels:,ID,Price List,Item Code,Rate,Valid for Buying or Selling?,Item Name,Item Description
+Column Name:,name,price_list,item_code,ref_rate,buying_or_selling,item_name,item_description
+Mandatory:,Yes,Yes,Yes,Yes,No,No,No
+Type:,Data (text),Link,Link,Currency,Select,Data,Text
+Info:,,Valid Price List,Valid Item,,"One of: Selling, Buying",,
+Start entering data below this line,,,,,,,
+,,Standard Buying,Base Bearing Plate,15,Buying,,
+,,Standard Buying,Base Plate,20,Buying,,
+,,Standard Buying,Bearing Block,10,Buying,,
+,,Standard Buying,Bearing Collar,20,Buying,,
+,,Standard Buying,Bearing Pipe,15,Buying,,
+,,Standard Buying,Blade Rib,10,Buying,,
+,,Standard Buying,Disc Collars,74,Buying,,
+,,Standard Buying,External Disc,45,Buying,,
+,,Standard Buying,Internal Disc,33,Buying,,
+,,Standard Buying,Shaft,30,Buying,,
+,,Standard Buying,Stand,40,Buying,,
+,,Standard Buying,Upper Bearing Plate,50,Buying,,
+,,Standard Buying,Wing Sheet,22,Buying,,
+,,Standard Selling,Wind Turbine,21,Selling,,
+,,Standard Selling,Wind Mill A Series,28,Selling,,
+,,Standard Selling,Wind MIll C Series,14,Selling,,
+,,Standard Selling,Base Bearing Plate,28,Selling,,
+,,Standard Selling,Base Plate,21,Selling,,
+,,Standard Selling,Bearing Block,14,Selling,,
+,,Standard Selling,Bearing Collar,103.6,Selling,,
+,,Standard Selling,Bearing Pipe,63,Selling,,
+,,Standard Selling,Blade Rib,46.2,Selling,,
+,,Standard Selling,Disc Collars,42,Selling,,
+,,Standard Selling,External Disc,56,Selling,,
+,,Standard Selling,Internal Disc,70,Selling,,
+,,Standard Selling,Shaft,340,Selling,,
+,,Standard Selling,Stand,400,Selling,,
+,,Standard Selling,Upper Bearing Plate,300,Selling,,
+,,Standard Selling,Wing Sheet,30.8,Selling,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/Lead.csv b/erpnext/utilities/demo/demo_docs/Lead.csv
new file mode 100644
index 0000000..948a68d
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Lead.csv
@@ -0,0 +1,68 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,
+Table:,Lead,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"""" (ID) column blank.""",,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"""" becomes mandatory"," if present.""",,,,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,
+Column Labels,ID,Contact Name,Status,Naming Series,Company Name,Email Id,Source,From Customer,Campaign Name,Phone,Mobile No.,Fax,Website,Territory,Lead Type,Lead Owner,Market Segment,Industry,Request Type,Next Contact By,Next Contact Date,Company,Unsubscribed,Blog Subscriber
+Column Name:,name,lead_name,status,naming_series,company_name,email_id,source,customer,campaign_name,phone,mobile_no,fax,website,territory,type,lead_owner,market_segment,industry,request_type,contact_by,contact_date,company,unsubscribed,blog_subscriber
+Mandatory:,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No
+Type:,Data (text),Data,Select,Select,Data,Data,Select,Link,Link,Data,Data,Data,Data,Link,Select,Link,Select,Link,Select,Link,Date,Link,Check,Check
+Info:,,,"Lead, Open, Replied, Opportunity, Interested, Converted, Do Not Contact","One of: LEAD, LEAD/10-11/, LEAD/MUMBAI/",,,"One of: Advertisement, Blog Post, Campaign, Call, Customer, Exhibition, Supplier, Website, Email",Valid Customer,Valid Campaign,,,,,Valid Territory,"One of: Client, Channel Partner, Consultant",Valid Profile,"One of: Lower Income, Middle Income, Upper Income",Valid Industry Type,"One of: Product Enquiry, Request for Information, Suggestions, Other",Valid Profile,,Valid Company,0 or 1,0 or 1
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,
+,,Mart Lakeman,Lead,,Zany Brainy,MartLakeman@einrot.com,,,,,,,,,,,,,,,,,,
+,,Saga Lundqvist,Lead,,Patterson-Fletcher,SagaLundqvist@dayrep.com,,,,,,,,,,,,,,,,,,
+,,Adna Sjöberg,Lead,,Griff's Hamburgers,AdnaSjoberg@gustr.com,,,,,,,,,,,,,,,,,,
+,,Ida Svendsen,Lead,,Rhodes Furniture,IdaDSvendsen@superrito.com,,,,,,,,,,,,,,,,,,
+,,Emppu Hämeenniemi,Lead,,Burger Chef,EmppuHameenniemi@teleworm.us,,,,,,,,,,,,,,,,,,
+,,Eugenio Pisano,Lead,,Stratabiz,EugenioPisano@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Semhar Hagos,Lead,,Home Quarters Warehouse,SemharHagos@einrot.com,,,,,,,,,,,,,,,,,,
+,,Branimira Ivanković,Lead,,Enviro Architectural Designs,BranimiraIvankovic@einrot.com,,,,,,,,,,,,,,,,,,
+,,Shelly Fields,Lead,,Ideal Garden Management,ShellyLFields@superrito.com,,,,,,,,,,,,,,,,,,
+,,Leo Mikulić,Lead,,Listen Up,LeoMikulic@gustr.com,,,,,,,,,,,,,,,,,,
+,,Denisa Jarošová,Lead,,I. Magnin,DenisaJarosova@teleworm.us,,,,,,,,,,,,,,,,,,
+,,Janek Rutkowski,Lead,,First Rate Choice,JanekRutkowski@dayrep.com,,,,,,,,,,,,,,,,,,
+,,美月 宇藤,Lead,,Multi Tech Development,mm@gustr.com,,,,,,,,,,,,,,,,,,
+,,Даниил Афанасьев,Lead,,National Auto Parts,dd@einrot.com,,,,,,,,,,,,,,,,,,
+,,Zorislav Petković,Lead,,Integra Investment Plan,ZorislavPetkovic@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Nanao Niwa,Lead,,The Lawn Guru,NanaoNiwa@superrito.com,,,,,,,,,,,,,,,,,,
+,,Hreiðar Jörundsson,Lead,,Buena Vista Realty Service,HreiarJorundsson@armyspy.com,,,,,,,,,,,,,,,,,,
+,,Lai Chu,Lead,,Bountiful Harvest Health Food Store,ChuThiBichLai@einrot.com,,,,,,,,,,,,,,,,,,
+,,Victor Aksakov,Lead,,P. Samuels Men's Clothiers,VictorAksakov@dayrep.com,,,,,,,,,,,,,,,,,,
+,,Saidalim Bisliev,Lead,,Vinyl Fever,SaidalimBisliev@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Totte Jakobsson,Lead,,Garden Master,TotteJakobsson@armyspy.com,,,,,,,,,,,,,,,,,,
+,,Naná Armas,Lead,,Big Apple,NanaArmasRobles@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Walerian Duda,Lead,,Monk House Sales,WalerianDuda@dayrep.com,,,,,,,,,,,,,,,,,,
+,,Moarimikashi ,Lead,,ManCharm,Moarimikashi@teleworm.us,,,,,,,,,,,,,,,,,,
+,,Dobromił Dąbrowski ,Lead,,Custom Lawn Care,DobromilDabrowski@dayrep.com,,,,,,,,,,,,,,,,,,
+,,Teigan Sinclair,Lead,,The Serendipity Dip,TeiganSinclair@gustr.com,,,,,,,,,,,,,,,,,,
+,,Fahad Guirguis,Lead,,Cavages,FahadSaidGuirguis@gustr.com,,,,,,,,,,,,,,,,,,
+,,Morten Olsen,Lead,,Gallenkamp,MortenJOlsen@armyspy.com,,,,,,,,,,,,,,,,,,
+,,Christian Baecker,Lead,,Webcom Business Services,ChristianBaecker@armyspy.com,,,,,,,,,,,,,,,,,,
+,,Sebastianus Dohmen,Lead,,Accord Investments,SebastianusDohmen@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Eero Koskinen,Lead,,American Appliance,EeroKoskinen@superrito.com,,,,,,,,,,,,,,,,,,
+,,富奎 盧,Lead,,Bettendorf's,LuFuKui@teleworm.us,,,,,,,,,,,,,,,,,,
+,,Milica Jelić,Lead,,House Of Denmark,MilicaJelic@dayrep.com,,,,,,,,,,,,,,,,,,
+,,Barbora Holubová,Lead,,10000 Auto Parts,BarboraHolubova@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Marta Kos,Lead,,Mages,MartaKos@einrot.com,,,,,,,,,,,,,,,,,,
+,,Simret Zula,Lead,,CSK Auto,SimretZula@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Kamil Chlubna,Lead,,Eagle Hardware & Garden,KamilChlubna@einrot.com,,,,,,,,,,,,,,,,,,
+,,Aceline Bolduc,Lead,,Rustler Steak House,AcelineBolduc@armyspy.com,,,,,,,,,,,,,,,,,,
+,,Lucie Stupková,Lead,,ABCO Foods,LucieStupkova@gustr.com,,,,,,,,,,,,,,,,,,
+,,Roland Solvik,Lead,,Trak Auto,RolandSolvik@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Mekirinzukushitakufu ,Lead,,Choices,Mekirinzukushitakufu@teleworm.us,,,,,,,,,,,,,,,,,,
+,,Mukharbek Sultanovich,Lead,,Megatronic,MukharbekSultanovich@cuvox.de,,,,,,,,,,,,,,,,,,
+,,Osman Amanuel,Lead,,Handy Dan,OsmanAmanuel@dayrep.com,,,,,,,,,,,,,,,,,,
+,,幸子 阪部,Lead,,Channel Home Centers,dd@armyspy.com,,,,,,,,,,,,,,,,,,
+,,Masakazu Kamitani,Lead,,Honest Air Group,MasakazuKamitani@superrito.com,,,,,,,,,,,,,,,,,,
+,,Omran Sabbagh,Lead,,Pleasures and Pasttimes,OmranNuhaidSabbagh@einrot.com,,,,,,,,,,,,,,,,,,
+,,Rikako Matsumura,Lead,,Lazysize,RikakoMatsumura@einrot.com,,,,,,,,,,,,,,,,,,
+,,Anayolisa Chukwukadibia,Lead,,Prestiga-Biz,AnayolisaChukwukadibia@einrot.com,,,,,,,,,,,,,,,,,,
+,,Gudmunda Hinna,Lead,,Childs Restaurants,GudmundaHinna@armyspy.com,,,,,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/Profile.csv b/erpnext/utilities/demo/demo_docs/Profile.csv
new file mode 100644
index 0000000..eb456c1
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Profile.csv
@@ -0,0 +1,40 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,
+Table:,Profile,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,
+DocType:,Profile,,,,,,,,,,,,,,,,,,,
+Column Labels:,ID,Email,First Name,User Type,Enabled,Middle Name (Optional),Last Name,Language,Birth Date,Gender,New Password,User Image,Background Image,Bio,Email Signature,Login After,Login Before,Restrict IP,Last Login,Last IP
+Column Name:,name,email,first_name,user_type,enabled,middle_name,last_name,language,birth_date,gender,new_password,user_image,background_image,bio,email_signature,login_after,login_before,restrict_ip,last_login,last_ip
+Mandatory:,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No
+Type:,Data (text),Data,Data,Select,Check,Data,Data,Select,Date,Select,Password,Select,Select,Small Text,Small Text,Int,Int,Data,Read Only,Read Only
+Info:,,,,"One of: System User, Website User",0 or 1,,,"One of: العربية, Deutsch, english, español, français, हिंदी, Hrvatski, nederlands, português, português brasileiro, српски, தமிழ், ไทย",,"One of: Male, Female, Other",,One of: attach_files:,One of: attach_files:,,,Integer,Integer,,,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,
+,,DikmanShervashidze@armyspy.com,Dikman,System User,1,V,Shervashidze,,,,testpass,,,,,,,,,
+,,Zukutakitoteka@teleworm.us,Zukutakitoteka,System User,1,,,,,,testpass,,,,,,,,,
+,,HatsueKashiwagi@cuvox.de,Hatsue,System User,1,H,Kashiwagi,,,,testpass,,,,,,,,,
+,,NuranVerkleij@einrot.com,Nuran,System User,1,T,Verkleij,,,,testpass,,,,,,,,,
+,,aromn@armyspy.com,Дмитрий,System User,1,З,Пирогов,,,,testpass,,,,,,,,,
+,,TildeLindqvist@cuvox.de,Tilde,System User,1,T,Lindqvist,,,,testpass,,,,,,,,,
+,,MichalSobczak@teleworm.us,Michał,System User,1,S,Sobczak,,,,testpass,,,,,,,,,
+,,GabrielleLoftus@superrito.com,Gabrielle,System User,1,J,Loftus,,,,testpass,,,,,,,,,
+,,VakhitaRyzaev@teleworm.us,Vakhita,System User,1,A,Ryzaev,,,,testpass,,,,,,,,,
+,,CharmaineGaudreau@cuvox.de,Charmaine,System User,1,D,Gaudreau,,,,testpass,,,,,,,,,
+,,RafaelaMaartens@cuvox.de,Rafaëla,System User,1,Z,Maartens,,,,testpass,,,,,,,,,
+,,NuguseYohannes@dayrep.com,Nuguse,System User,0,S,Yohannes,,,,testpass,,,,,,,,,
+,,panca@armyspy.com,Раиса,System User,0,В,Белякова,,,,testpass,,,,,,,,,
+,,CaYinLong@gustr.com,胤隆,System User,1,婷,蔡,,,,testpass,,,,,,,,,
+,,FreddieScott@armyspy.com,Freddie,System User,1,A,Scott,,,,testpass,,,,,,,,,
+,,BergoraVigfusdottir@superrito.com,Bergþóra,System User,1,Ö,Vigfúsdóttir,,,,testpass,,,,,,,,,
+,,WardNajmalDinKalb@cuvox.de,Ward,System User,1,N,Kalb,,,,testpass,,,,,,,,,
+,,WanMai@teleworm.us,Wan,System User,1,A,Mai,,,,testpass,,,,,,,,,
+,,LeonAbdulov@superrito.com,Leon,System User,1,A,Abdulov,,,,testpass,,,,,,,,,
+,,SabinaNovotna@superrito.com,Sabina,System User,1,J,Novotná,,,,testpass,,,,,,,,,
diff --git a/erpnext/utilities/demo/demo_docs/Salary_Structure.csv b/erpnext/utilities/demo/demo_docs/Salary_Structure.csv
new file mode 100644
index 0000000..a74de90
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Salary_Structure.csv
@@ -0,0 +1,24 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,
+Table:,Salary Structure,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,
+DocType:,Salary Structure,,,,,,,,,,,,,,~,Salary Structure Earning,earning_details,,,~,Salary Structure Deduction,deduction_details,,
+Column Labels:,ID,Employee,Is Active,From Date,Company,Employee Name,Branch,Designation,Department,Grade,To Date,Total Earning,Total Deduction,Net Pay,,ID,Type,Amount,Reduce Earning for Leave Without Pay (LWP),,ID,Type,Amount,Reduce Deduction for Leave Without Pay (LWP)
+Column Name:,name,employee,is_active,from_date,company,employee_name,branch,designation,department,grade,to_date,total_earning,total_deduction,net_pay,~,name,e_type,modified_value,depend_on_lwp,~,name,d_type,d_modified_amt,depend_on_lwp
+Mandatory:,Yes,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,,Yes,Yes,No,No,,Yes,Yes,No,No
+Type:,Data (text),Link,Select,Date,Select,Data,Select,Select,Select,Select,Date,Currency,Currency,Currency,,Data,Link,Currency,Check,,Data,Link,Currency,Check
+Info:,,Valid Employee,"One of: Yes, No",,Valid Company,,Valid Branch,Valid Designation,Valid Department,Valid Grade,,,,,,Leave blank for new records,Valid Earning Type,,0 or 1,,Leave blank for new records,Valid Deduction Type,,0 or 1
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,
+,,EMP/0001,Yes,2013-08-06,Wind Power LLC,Dikman Shervashidze Shervashidze,,,,,,5000,400,4600,,,Basic,5000,,,,Income Tax,400,
+,,EMP/0002,Yes,2013-08-06,Wind Power LLC,Zukutakitoteka,,,,,,6700,400,6300,,,Basic,6700,,,,Income Tax,400,
+,,EMP/0003,Yes,2013-08-06,Wind Power LLC,Hatsue Kashiwagi,,,,,,3400,400,3000,,,Basic,3400,,,,Income Tax,400,
+,,EMP/0004,Yes,2013-08-06,Wind Power LLC,Nuran Verkleij,,,,,,6990,566,6424,,,Basic,6990,,,,Income Tax,566,
diff --git a/erpnext/utilities/demo/demo_docs/Stock Reconcilation Template.csv b/erpnext/utilities/demo/demo_docs/Stock Reconcilation Template.csv
new file mode 100644
index 0000000..eddc2bc
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Stock Reconcilation Template.csv
@@ -0,0 +1,25 @@
+Stock Reconciliation,,,,,,
+----,,,,,,
+"Stock Reconciliation can be used to update the stock on a particular date, usually as per physical inventory.",,,,,,
+"When submitted, the system creates difference entries to set the given stock and valuation on this date.",,,,,,
+It can also be used to create opening stock entries and to fix stock value.,,,,,,
+----,,,,,,
+Notes:,,,,,,
+Item Code and Warehouse should already exist.,,,,,,
+You can update either Quantity or Valuation Rate or both.,,,,,,
+"If no change in either Quantity or Valuation Rate, leave the cell blank.",,,,,,
+----,,,,,,
+Item Code,Warehouse,Quantity,Valuation Rate,,,
+Base Bearing Plate,Stores,4,750,,,
+Base Plate,Stores,4,1092,,,
+Bearing Block,Stores,2,355,,,
+Bearing Collar,Stores,4,980,,,
+Bearing Pipe,Stores,5,599,,,
+Blade Rib,Stores,3,430,,,
+Disc Collars,Stores,7,3000,,,
+External Disc,Stores,2,1200,,,
+Internal Disc,Stores,2,1000,,,
+Shaft,Stores,5,600,,,
+Stand,Stores,2,200,,,
+Upper Bearing Plate,Stores,10,400,,,
+Wing Sheet,Stores,20,300,,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/Supplier.csv b/erpnext/utilities/demo/demo_docs/Supplier.csv
new file mode 100644
index 0000000..6f91164
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/Supplier.csv
@@ -0,0 +1,29 @@
+Data Import Template,,,,,,,,,
+Table:,Supplier,,,,,,,,
+,,,,,,,,,
+,,,,,,,,,
+Notes:,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,
+First data column must be blank.,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,
+,,,,,,,,,
+Column Labels,ID,Supplier Name,Supplier Type,Company,Series,Default Currency,Supplier Details,Credit Days,Website
+Column Name:,name,supplier_name,supplier_type,company,naming_series,default_currency,supplier_details,credit_days,website
+Mandatory:,Yes,Yes,Yes,Yes,No,No,No,No,No
+Type:,Data (text),Data,Link,Link,Select,Link,Text,Int,Data
+Info:,,,Valid Supplier Type,Valid Company,"One of: SUPP, SUPP/10-11/",Valid Currency,,Integer,
+Start entering data below this line,,,,,,,,,
+,Helios Air,Helios Air,Raw Material,Wind Power LLC,,,,,
+,Ks Merchandise,Ks Merchandise,Electrical,Wind Power LLC,,,,,
+,HomeBase,HomeBase,Raw Material,Wind Power LLC,,,,,
+,Scott Ties,Scott Ties,Raw Material,Wind Power LLC,,,,,
+,Reliable Investments,Reliable Investments,Services,Wind Power LLC,,,,,
+,Nan Duskin,Nan Duskin,Services,Wind Power LLC,,,,,
+,Rainbow Records,Rainbow Records,Services,Wind Power LLC,,,,,
+,New World Realty,New World Realty,Services,Wind Power LLC,,,,,
+,Asiatic Solutions,Asiatic Solutions,Raw Material,Wind Power LLC,,,,,
+,Eagle Hardware,Eagle Hardware,Raw Material,Wind Power LLC,,,,,
\ No newline at end of file
diff --git a/erpnext/utilities/demo/demo_docs/bearing-block.png b/erpnext/utilities/demo/demo_docs/bearing-block.png
new file mode 100644
index 0000000..b60f2f4
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/bearing-block.png
Binary files differ
diff --git a/erpnext/utilities/demo/demo_docs/wind-turbine.png b/erpnext/utilities/demo/demo_docs/wind-turbine.png
new file mode 100644
index 0000000..81fcfc2
--- /dev/null
+++ b/erpnext/utilities/demo/demo_docs/wind-turbine.png
Binary files differ
diff --git a/erpnext/utilities/demo/make_demo.py b/erpnext/utilities/demo/make_demo.py
new file mode 100644
index 0000000..4d40670
--- /dev/null
+++ b/erpnext/utilities/demo/make_demo.py
@@ -0,0 +1,434 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import webnotes, os, datetime
+import webnotes.utils
+from webnotes.utils import random_string, cstr
+from webnotes.widgets import query_report
+import random
+import json
+
+webnotes.session = webnotes._dict({"user":"Administrator"})
+from webnotes.core.page.data_import_tool.data_import_tool import upload
+
+# fix price list
+# fix fiscal year
+
+company = "Wind Power LLC"
+company_abbr = "WP"
+country = "United States"
+currency = "USD"
+time_zone = "America/New_York"
+start_date = '2013-01-01'
+bank_name = "Citibank"
+runs_for = None
+prob = {
+ "default": { "make": 0.6, "qty": (1,5) },
+ "Sales Order": { "make": 0.4, "qty": (1,3) },
+ "Purchase Order": { "make": 0.7, "qty": (1,15) },
+ "Purchase Receipt": { "make": 0.7, "qty": (1,15) },
+}
+
+def make(reset=False, simulate=True):
+ #webnotes.flags.print_messages = True
+ webnotes.flags.mute_emails = True
+ webnotes.flags.rollback_on_exception = True
+
+ if not webnotes.conf.demo_db_name:
+ raise Exception("conf.py does not have demo_db_name")
+
+ if reset:
+ setup()
+ else:
+ if webnotes.conn:
+ webnotes.conn.close()
+
+ webnotes.connect(db_name=webnotes.conf.demo_db_name)
+
+ if simulate:
+ _simulate()
+
+def setup():
+ install()
+ webnotes.connect(db_name=webnotes.conf.demo_db_name)
+ complete_setup()
+ make_customers_suppliers_contacts()
+ make_items()
+ make_price_lists()
+ make_users_and_employees()
+ make_bank_account()
+ # make_opening_stock()
+ # make_opening_accounts()
+
+def _simulate():
+ global runs_for
+ current_date = webnotes.utils.getdate(start_date)
+
+ # continue?
+ last_posting = webnotes.conn.sql("""select max(posting_date) from `tabStock Ledger Entry`""")
+ if last_posting[0][0]:
+ current_date = webnotes.utils.add_days(last_posting[0][0], 1)
+
+ # run till today
+ if not runs_for:
+ runs_for = webnotes.utils.date_diff(webnotes.utils.nowdate(), current_date)
+
+ for i in xrange(runs_for):
+ print current_date.strftime("%Y-%m-%d")
+ webnotes.local.current_date = current_date
+
+ if current_date.weekday() in (5, 6):
+ current_date = webnotes.utils.add_days(current_date, 1)
+ continue
+
+ run_sales(current_date)
+ run_purchase(current_date)
+ run_manufacturing(current_date)
+ run_stock(current_date)
+ run_accounts(current_date)
+
+ current_date = webnotes.utils.add_days(current_date, 1)
+
+def run_sales(current_date):
+ if can_make("Quotation"):
+ for i in xrange(how_many("Quotation")):
+ make_quotation(current_date)
+
+ if can_make("Sales Order"):
+ for i in xrange(how_many("Sales Order")):
+ make_sales_order(current_date)
+
+def run_accounts(current_date):
+ if can_make("Sales Invoice"):
+ from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
+ report = "Ordered Items to be Billed"
+ for so in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Sales Invoice")]:
+ si = webnotes.bean(make_sales_invoice(so))
+ si.doc.posting_date = current_date
+ si.insert()
+ si.submit()
+ webnotes.conn.commit()
+
+ if can_make("Purchase Invoice"):
+ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
+ report = "Received Items to be Billed"
+ for pr in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Purchase Invoice")]:
+ pi = webnotes.bean(make_purchase_invoice(pr))
+ pi.doc.posting_date = current_date
+ pi.doc.bill_no = random_string(6)
+ pi.insert()
+ pi.submit()
+ webnotes.conn.commit()
+
+ if can_make("Payment Received"):
+ from erpnext.accounts.doctype.journal_voucher.journal_voucher import get_payment_entry_from_sales_invoice
+ report = "Accounts Receivable"
+ for si in list(set([r[4] for r in query_report.run(report, {"report_date": current_date })["result"] if r[3]=="Sales Invoice"]))[:how_many("Payment Received")]:
+ jv = webnotes.bean(get_payment_entry_from_sales_invoice(si))
+ jv.doc.posting_date = current_date
+ jv.doc.cheque_no = random_string(6)
+ jv.doc.cheque_date = current_date
+ jv.insert()
+ jv.submit()
+ webnotes.conn.commit()
+
+ if can_make("Payment Made"):
+ from erpnext.accounts.doctype.journal_voucher.journal_voucher import get_payment_entry_from_purchase_invoice
+ report = "Accounts Payable"
+ for pi in list(set([r[4] for r in query_report.run(report, {"report_date": current_date })["result"] if r[3]=="Purchase Invoice"]))[:how_many("Payment Made")]:
+ jv = webnotes.bean(get_payment_entry_from_purchase_invoice(pi))
+ jv.doc.posting_date = current_date
+ jv.doc.cheque_no = random_string(6)
+ jv.doc.cheque_date = current_date
+ jv.insert()
+ jv.submit()
+ webnotes.conn.commit()
+
+def run_stock(current_date):
+ # make purchase requests
+ if can_make("Purchase Receipt"):
+ from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+ from erpnext.stock.stock_ledger import NegativeStockError
+ report = "Purchase Order Items To Be Received"
+ for po in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Purchase Receipt")]:
+ pr = webnotes.bean(make_purchase_receipt(po))
+ pr.doc.posting_date = current_date
+ pr.doc.fiscal_year = cstr(current_date.year)
+ pr.insert()
+ try:
+ pr.submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
+
+ # make delivery notes (if possible)
+ if can_make("Delivery Note"):
+ from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
+ from erpnext.stock.stock_ledger import NegativeStockError
+ from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError, SerialNoQtyError
+ report = "Ordered Items To Be Delivered"
+ for so in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Delivery Note")]:
+ dn = webnotes.bean(make_delivery_note(so))
+ dn.doc.posting_date = current_date
+ dn.doc.fiscal_year = cstr(current_date.year)
+ dn.insert()
+ try:
+ dn.submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
+ except SerialNoRequiredError: pass
+ except SerialNoQtyError: pass
+
+ # try submitting existing
+ for dn in webnotes.conn.get_values("Delivery Note", {"docstatus": 0}, "name"):
+ b = webnotes.bean("Delivery Note", dn[0])
+ b.submit()
+ webnotes.conn.commit()
+
+def run_purchase(current_date):
+ # make material requests for purchase items that have negative projected qtys
+ if can_make("Material Request"):
+ report = "Items To Be Requested"
+ for row in query_report.run(report)["result"][:how_many("Material Request")]:
+ mr = webnotes.new_bean("Material Request")
+ mr.doc.material_request_type = "Purchase"
+ mr.doc.transaction_date = current_date
+ mr.doc.fiscal_year = cstr(current_date.year)
+ mr.doclist.append({
+ "doctype": "Material Request Item",
+ "parentfield": "indent_details",
+ "schedule_date": webnotes.utils.add_days(current_date, 7),
+ "item_code": row[0],
+ "qty": -row[-1]
+ })
+ mr.insert()
+ mr.submit()
+
+ # make supplier quotations
+ if can_make("Supplier Quotation"):
+ from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation
+ report = "Material Requests for which Supplier Quotations are not created"
+ for row in query_report.run(report)["result"][:how_many("Supplier Quotation")]:
+ if row[0] != "Total":
+ sq = webnotes.bean(make_supplier_quotation(row[0]))
+ sq.doc.transaction_date = current_date
+ sq.doc.fiscal_year = cstr(current_date.year)
+ sq.insert()
+ sq.submit()
+ webnotes.conn.commit()
+
+ # make purchase orders
+ if can_make("Purchase Order"):
+ from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+ report = "Requested Items To Be Ordered"
+ for row in query_report.run(report)["result"][:how_many("Purchase Order")]:
+ if row[0] != "Total":
+ po = webnotes.bean(make_purchase_order(row[0]))
+ po.doc.transaction_date = current_date
+ po.doc.fiscal_year = cstr(current_date.year)
+ po.insert()
+ po.submit()
+ webnotes.conn.commit()
+
+def run_manufacturing(current_date):
+ from erpnext.stock.stock_ledger import NegativeStockError
+ from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, DuplicateEntryForProductionOrderError
+
+ ppt = webnotes.bean("Production Planning Tool", "Production Planning Tool")
+ ppt.doc.company = company
+ ppt.doc.use_multi_level_bom = 1
+ ppt.doc.purchase_request_for_warehouse = "Stores - WP"
+ ppt.run_method("get_open_sales_orders")
+ ppt.run_method("get_items_from_so")
+ ppt.run_method("raise_production_order")
+ ppt.run_method("raise_purchase_request")
+ webnotes.conn.commit()
+
+ # submit production orders
+ for pro in webnotes.conn.get_values("Production Order", {"docstatus": 0}, "name"):
+ b = webnotes.bean("Production Order", pro[0])
+ b.doc.wip_warehouse = "Work in Progress - WP"
+ b.submit()
+ webnotes.conn.commit()
+
+ # submit material requests
+ for pro in webnotes.conn.get_values("Material Request", {"docstatus": 0}, "name"):
+ b = webnotes.bean("Material Request", pro[0])
+ b.submit()
+ webnotes.conn.commit()
+
+ # stores -> wip
+ if can_make("Stock Entry for WIP"):
+ for pro in query_report.run("Open Production Orders")["result"][:how_many("Stock Entry for WIP")]:
+ make_stock_entry_from_pro(pro[0], "Material Transfer", current_date)
+
+ # wip -> fg
+ if can_make("Stock Entry for FG"):
+ for pro in query_report.run("Production Orders in Progress")["result"][:how_many("Stock Entry for FG")]:
+ make_stock_entry_from_pro(pro[0], "Manufacture/Repack", current_date)
+
+ # try posting older drafts (if exists)
+ for st in webnotes.conn.get_values("Stock Entry", {"docstatus":0}, "name"):
+ try:
+ webnotes.bean("Stock Entry", st[0]).submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
+ except IncorrectValuationRateError: pass
+ except DuplicateEntryForProductionOrderError: pass
+
+def make_stock_entry_from_pro(pro_id, purpose, current_date):
+ from erpnext.manufacturing.doctype.production_order.production_order import make_stock_entry
+ from erpnext.stock.stock_ledger import NegativeStockError
+ from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, DuplicateEntryForProductionOrderError
+
+ try:
+ st = webnotes.bean(make_stock_entry(pro_id, purpose))
+ st.doc.posting_date = current_date
+ st.doc.fiscal_year = cstr(current_date.year)
+ for d in st.doclist.get({"parentfield": "mtn_details"}):
+ d.expense_account = "Stock Adjustment - " + company_abbr
+ d.cost_center = "Main - " + company_abbr
+ st.insert()
+ webnotes.conn.commit()
+ st.submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
+ except IncorrectValuationRateError: pass
+ except DuplicateEntryForProductionOrderError: pass
+
+def make_quotation(current_date):
+ b = webnotes.bean([{
+ "creation": current_date,
+ "doctype": "Quotation",
+ "quotation_to": "Customer",
+ "customer": get_random("Customer"),
+ "order_type": "Sales",
+ "transaction_date": current_date,
+ "fiscal_year": cstr(current_date.year)
+ }])
+
+ add_random_children(b, {
+ "doctype": "Quotation Item",
+ "parentfield": "quotation_details",
+ }, rows=3, randomize = {
+ "qty": (1, 5),
+ "item_code": ("Item", {"is_sales_item": "Yes"})
+ }, unique="item_code")
+
+ b.insert()
+ webnotes.conn.commit()
+ b.submit()
+ webnotes.conn.commit()
+
+def make_sales_order(current_date):
+ q = get_random("Quotation", {"status": "Submitted"})
+ if q:
+ from erpnext.selling.doctype.quotation.quotation import make_sales_order
+ so = webnotes.bean(make_sales_order(q))
+ so.doc.transaction_date = current_date
+ so.doc.delivery_date = webnotes.utils.add_days(current_date, 10)
+ so.insert()
+ webnotes.conn.commit()
+ so.submit()
+ webnotes.conn.commit()
+
+def add_random_children(bean, template, rows, randomize, unique=None):
+ for i in xrange(random.randrange(1, rows)):
+ d = template.copy()
+ for key, val in randomize.items():
+ if isinstance(val[0], basestring):
+ d[key] = get_random(*val)
+ else:
+ d[key] = random.randrange(*val)
+
+ if unique:
+ if not bean.doclist.get({"doctype": d["doctype"], unique:d[unique]}):
+ bean.doclist.append(d)
+ else:
+ bean.doclist.append(d)
+
+def get_random(doctype, filters=None):
+ condition = []
+ if filters:
+ for key, val in filters.items():
+ condition.append("%s='%s'" % (key, val))
+ if condition:
+ condition = " where " + " and ".join(condition)
+ else:
+ condition = ""
+
+ out = webnotes.conn.sql("""select name from `tab%s` %s
+ order by RAND() limit 0,1""" % (doctype, condition))
+
+ return out and out[0][0] or None
+
+def can_make(doctype):
+ return random.random() < prob.get(doctype, prob["default"])["make"]
+
+def how_many(doctype):
+ return random.randrange(*prob.get(doctype, prob["default"])["qty"])
+
+def install():
+ print "Creating Fresh Database..."
+ from webnotes.install_lib.install import Installer
+ from webnotes import conf
+ inst = Installer('root')
+ inst.install(conf.demo_db_name, verbose=1, force=1)
+
+def complete_setup():
+ print "Complete Setup..."
+ from erpnext.setup.page.setup_wizard.setup_wizard import setup_account
+ setup_account({
+ "first_name": "Test",
+ "last_name": "User",
+ "fy_start_date": "2013-01-01",
+ "fy_end_date": "2013-12-31",
+ "industry": "Manufacturing",
+ "company_name": company,
+ "company_abbr": company_abbr,
+ "currency": currency,
+ "timezone": time_zone,
+ "country": country
+ })
+
+ import_data("Fiscal_Year")
+
+def make_items():
+ import_data("Item")
+ import_data("BOM", submit=True)
+
+def make_price_lists():
+ import_data("Item_Price", overwrite=True)
+
+def make_customers_suppliers_contacts():
+ import_data(["Customer", "Supplier", "Contact", "Address", "Lead"])
+
+def make_users_and_employees():
+ webnotes.conn.set_value("HR Settings", None, "emp_created_by", "Naming Series")
+ webnotes.conn.commit()
+
+ import_data(["Profile", "Employee", "Salary_Structure"])
+
+def make_bank_account():
+ ba = webnotes.bean({
+ "doctype": "Account",
+ "account_name": bank_name,
+ "account_type": "Bank or Cash",
+ "group_or_ledger": "Ledger",
+ "parent_account": "Bank Accounts - " + company_abbr,
+ "company": company
+ }).insert()
+
+ webnotes.set_value("Company", company, "default_bank_account", ba.doc.name)
+ webnotes.conn.commit()
+
+def import_data(dt, submit=False, overwrite=False):
+ if not isinstance(dt, (tuple, list)):
+ dt = [dt]
+
+ for doctype in dt:
+ print "Importing", doctype.replace("_", " "), "..."
+ webnotes.local.form_dict = webnotes._dict()
+ if submit:
+ webnotes.form_dict["params"] = json.dumps({"_submit": 1})
+ webnotes.uploaded_file = os.path.join(os.path.dirname(__file__), "demo_docs", doctype+".csv")
+ upload(overwrite=overwrite)
diff --git a/erpnext/utilities/demo/make_erpnext_demo.py b/erpnext/utilities/demo/make_erpnext_demo.py
new file mode 100644
index 0000000..a094239
--- /dev/null
+++ b/erpnext/utilities/demo/make_erpnext_demo.py
@@ -0,0 +1,124 @@
+if __name__=="__main__":
+ import sys
+ sys.path.extend([".", "lib", "app"])
+
+import webnotes, os
+import utilities.demo.make_demo
+
+def make_demo_app(site=None):
+ webnotes.init(site=site)
+ webnotes.flags.mute_emails = 1
+
+ utilities.demo.make_demo.make(reset=True, simulate=False)
+ # setup demo user etc so that the site it up faster, while the data loads
+ make_demo_user()
+ make_demo_login_page()
+ make_demo_on_login_script()
+ utilities.demo.make_demo.make(reset=False, simulate=True)
+ webnotes.destroy()
+
+def make_demo_user():
+ from webnotes.auth import _update_password
+
+ roles = ["Accounts Manager", "Analytics", "Expense Approver", "Accounts User",
+ "Leave Approver", "Blogger", "Customer", "Sales Manager", "Employee", "Support Manager",
+ "HR Manager", "HR User", "Maintenance Manager", "Maintenance User", "Material Manager",
+ "Material Master Manager", "Material User", "Manufacturing Manager",
+ "Manufacturing User", "Projects User", "Purchase Manager", "Purchase Master Manager",
+ "Purchase User", "Quality Manager", "Report Manager", "Sales Master Manager",
+ "Sales User", "Supplier", "Support Team"]
+
+ def add_roles(bean):
+ for role in roles:
+ p.doclist.append({
+ "doctype": "UserRole",
+ "parentfield": "user_roles",
+ "role": role
+ })
+
+ # make demo user
+ if webnotes.conn.exists("Profile", "demo@erpnext.com"):
+ webnotes.delete_doc("Profile", "demo@erpnext.com")
+
+ p = webnotes.new_bean("Profile")
+ p.doc.email = "demo@erpnext.com"
+ p.doc.first_name = "Demo"
+ p.doc.last_name = "User"
+ p.doc.enabled = 1
+ p.doc.user_type = "ERPNext Demo"
+ p.insert()
+ add_roles(p)
+ p.save()
+ _update_password("demo@erpnext.com", "demo")
+
+ # make system manager user
+ if webnotes.conn.exists("Profile", "admin@erpnext.com"):
+ webnotes.delete_doc("Profile", "admin@erpnext.com")
+
+ p = webnotes.new_bean("Profile")
+ p.doc.email = "admin@erpnext.com"
+ p.doc.first_name = "Admin"
+ p.doc.last_name = "User"
+ p.doc.enabled = 1
+ p.doc.user_type = "System User"
+ p.insert()
+ roles.append("System Manager")
+ add_roles(p)
+ p.save()
+ _update_password("admin@erpnext.com", "admin010123")
+
+ # only read for newsletter
+ webnotes.conn.sql("""update `tabDocPerm` set `write`=0, `create`=0, `cancel`=0
+ where parent='Newsletter'""")
+ webnotes.conn.sql("""update `tabDocPerm` set `write`=0, `create`=0, `cancel`=0
+ where parent='Profile' and role='All'""")
+
+ webnotes.conn.commit()
+
+def make_demo_login_page():
+ webnotes.conn.set_value("Website Settings", None, "home_page", "")
+
+ webnotes.conn.sql("""delete from `tabWeb Page` where name='demo-login'""")
+ p = webnotes.new_bean("Web Page")
+ p.doc.title = "Demo Login"
+ p.doc.published = 1
+ p.doc.description = "ERPNext Demo Login"
+
+ with open(os.path.join(os.path.dirname(__file__), "demo-login.html"), "r") as dfile:
+ p.doc.main_section = dfile.read()
+
+ p.doc.insert_code = 1
+ with open(os.path.join(os.path.dirname(__file__), "demo-login.js"), "r") as dfile:
+ p.doc.javascript = dfile.read()
+
+ p.doc.insert_style = 1
+ with open(os.path.join(os.path.dirname(__file__), "demo-login.css"), "r") as dfile:
+ p.doc.css = dfile.read()
+
+ p.insert()
+
+ website_settings = webnotes.bean("Website Settings", "Website Settings")
+ website_settings.doc.home_page = "demo-login"
+ website_settings.doc.disable_signup = 1
+ website_settings.save()
+
+ webnotes.conn.commit()
+
+def make_demo_on_login_script():
+ import shutil
+ import webnotes.plugins
+ custom_script_path = webnotes.plugins.get_path("Core", "DocType", "Control Panel")
+ webnotes.create_folder(os.path.dirname(custom_script_path))
+
+ shutil.copyfile(os.path.join(os.path.dirname(__file__), "demo_control_panel.py"), custom_script_path)
+
+ cp = webnotes.bean("Control Panel")
+ cp.doc.custom_startup_code = """wn.ui.toolbar.show_banner('You are using ERPNext Demo. To start your own ERPNext Trial, <a href="https://erpnext.com/pricing-and-signup" target="_blank">click here</a>')"""
+ cp.save()
+
+ webnotes.conn.commit()
+
+if __name__=="__main__":
+ import sys
+ site = sys.argv[1:]
+ make_demo_app(site=site and site[0] or None)
diff --git a/erpnext/utilities/doctype/__init__.py b/erpnext/utilities/doctype/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/utilities/doctype/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/utilities/doctype/address/README.md b/erpnext/utilities/doctype/address/README.md
new file mode 100644
index 0000000..a4efda6
--- /dev/null
+++ b/erpnext/utilities/doctype/address/README.md
@@ -0,0 +1 @@
+Address belonging to a Customer or Supplier.
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/address/__init__.py b/erpnext/utilities/doctype/address/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/utilities/doctype/address/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/utilities/doctype/address/address.js b/erpnext/utilities/doctype/address/address.js
new file mode 100644
index 0000000..f56a709
--- /dev/null
+++ b/erpnext/utilities/doctype/address/address.js
@@ -0,0 +1,4 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+{% include 'controllers/js/contact_address_common.js' %};
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/address/address.py b/erpnext/utilities/doctype/address/address.py
new file mode 100644
index 0000000..ad6e049
--- /dev/null
+++ b/erpnext/utilities/doctype/address/address.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+from webnotes import msgprint
+from webnotes.utils import cstr, cint
+
+class DocType:
+ def __init__(self, doc, doclist=[]):
+ self.doc = doc
+ self.doclist = doclist
+
+ def autoname(self):
+ if not self.doc.address_title:
+ self.doc.address_title = self.doc.customer \
+ or self.doc.supplier or self.doc.sales_partner or self.doc.lead
+
+ if self.doc.address_title:
+ self.doc.name = cstr(self.doc.address_title).strip() + "-" + cstr(self.doc.address_type).strip()
+ else:
+ webnotes.msgprint("""Address Title is mandatory.""" + self.doc.customer, raise_exception=True)
+
+ def validate(self):
+ self.validate_primary_address()
+ self.validate_shipping_address()
+
+ def validate_primary_address(self):
+ """Validate that there can only be one primary address for particular customer, supplier"""
+ if self.doc.is_primary_address == 1:
+ self._unset_other("is_primary_address")
+
+ elif self.doc.is_shipping_address != 1:
+ for fieldname in ["customer", "supplier", "sales_partner", "lead"]:
+ if self.doc.fields.get(fieldname):
+ if not webnotes.conn.sql("""select name from `tabAddress` where is_primary_address=1
+ and `%s`=%s and name!=%s""" % (fieldname, "%s", "%s"),
+ (self.doc.fields[fieldname], self.doc.name)):
+ self.doc.is_primary_address = 1
+ break
+
+ def validate_shipping_address(self):
+ """Validate that there can only be one shipping address for particular customer, supplier"""
+ if self.doc.is_shipping_address == 1:
+ self._unset_other("is_shipping_address")
+
+ def _unset_other(self, is_address_type):
+ for fieldname in ["customer", "supplier", "sales_partner", "lead"]:
+ if self.doc.fields.get(fieldname):
+ webnotes.conn.sql("""update `tabAddress` set `%s`=0 where `%s`=%s and name!=%s""" %
+ (is_address_type, fieldname, "%s", "%s"), (self.doc.fields[fieldname], self.doc.name))
+ break
diff --git a/erpnext/utilities/doctype/address/address.txt b/erpnext/utilities/doctype/address/address.txt
new file mode 100644
index 0000000..669da2b
--- /dev/null
+++ b/erpnext/utilities/doctype/address/address.txt
@@ -0,0 +1,254 @@
+[
+ {
+ "creation": "2013-01-10 16:34:32",
+ "docstatus": 0,
+ "modified": "2013-12-20 19:23:54",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "allow_import": 1,
+ "allow_rename": 1,
+ "doctype": "DocType",
+ "document_type": "Master",
+ "icon": "icon-map-marker",
+ "in_dialog": 0,
+ "module": "Utilities",
+ "name": "__common__",
+ "search_fields": "customer, supplier, sales_partner, country, state"
+ },
+ {
+ "doctype": "DocField",
+ "name": "__common__",
+ "parent": "Address",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0
+ },
+ {
+ "create": 1,
+ "doctype": "DocPerm",
+ "email": 1,
+ "name": "__common__",
+ "parent": "Address",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "Address"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "address_details",
+ "fieldtype": "Section Break",
+ "label": "Address Details",
+ "options": "icon-map-marker"
+ },
+ {
+ "description": "Name of person or organization that this address belongs to.",
+ "doctype": "DocField",
+ "fieldname": "address_title",
+ "fieldtype": "Data",
+ "label": "Address Title",
+ "reqd": 0
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "address_type",
+ "fieldtype": "Select",
+ "label": "Address Type",
+ "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
+ "reqd": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "address_line1",
+ "fieldtype": "Data",
+ "label": "Address Line 1",
+ "reqd": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "address_line2",
+ "fieldtype": "Data",
+ "label": "Address Line 2"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "city",
+ "fieldtype": "Data",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "City/Town",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "state",
+ "fieldtype": "Data",
+ "in_filter": 1,
+ "label": "State",
+ "options": "Suggest",
+ "search_index": 0
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "pincode",
+ "fieldtype": "Data",
+ "in_filter": 1,
+ "label": "Pincode",
+ "search_index": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "country",
+ "fieldtype": "Select",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Country",
+ "options": "link:Country",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "column_break0",
+ "fieldtype": "Column Break",
+ "print_hide": 0,
+ "width": "50%"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "email_id",
+ "fieldtype": "Data",
+ "label": "Email Id"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "phone",
+ "fieldtype": "Data",
+ "label": "Phone",
+ "reqd": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "fax",
+ "fieldtype": "Data",
+ "in_filter": 1,
+ "label": "Fax"
+ },
+ {
+ "default": "0",
+ "description": "Check to make primary address",
+ "doctype": "DocField",
+ "fieldname": "is_primary_address",
+ "fieldtype": "Check",
+ "label": "Preferred Billing Address"
+ },
+ {
+ "default": "0",
+ "description": "Check to make Shipping Address",
+ "doctype": "DocField",
+ "fieldname": "is_shipping_address",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Preferred Shipping Address"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "linked_with",
+ "fieldtype": "Section Break",
+ "label": "Reference",
+ "options": "icon-pushpin"
+ },
+ {
+ "depends_on": "eval:!doc.supplier && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "label": "Customer",
+ "options": "Customer"
+ },
+ {
+ "depends_on": "eval:!doc.supplier && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "customer_name",
+ "fieldtype": "Data",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Customer Name",
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval:!doc.customer && !doc.sales_partner && !doc.lead",
+ "doctype": "DocField",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "label": "Supplier",
+ "options": "Supplier"
+ },
+ {
+ "depends_on": "eval:!doc.customer && !doc.sales_partner && !doc.lead",
+ "doctype": "DocField",
+ "fieldname": "supplier_name",
+ "fieldtype": "Data",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Supplier Name",
+ "read_only": 1,
+ "search_index": 0
+ },
+ {
+ "depends_on": "eval:!doc.customer && !doc.supplier && !doc.lead",
+ "doctype": "DocField",
+ "fieldname": "sales_partner",
+ "fieldtype": "Link",
+ "label": "Sales Partner",
+ "options": "Sales Partner"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "column_break_22",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval:!doc.supplier && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "lead",
+ "fieldtype": "Link",
+ "label": "Lead",
+ "options": "Lead"
+ },
+ {
+ "depends_on": "eval:!doc.supplier && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "lead_name",
+ "fieldtype": "Data",
+ "label": "Lead Name",
+ "read_only": 1
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Sales User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Purchase User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Maintenance User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Accounts User"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/address/test_address.py b/erpnext/utilities/doctype/address/test_address.py
new file mode 100644
index 0000000..befbe82
--- /dev/null
+++ b/erpnext/utilities/doctype/address/test_address.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+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/erpnext/utilities/doctype/contact/README.md b/erpnext/utilities/doctype/contact/README.md
new file mode 100644
index 0000000..484522c
--- /dev/null
+++ b/erpnext/utilities/doctype/contact/README.md
@@ -0,0 +1 @@
+Contact representing a Customer or Supplier.
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/contact/__init__.py b/erpnext/utilities/doctype/contact/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/utilities/doctype/contact/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/utilities/doctype/contact/contact.js b/erpnext/utilities/doctype/contact/contact.js
new file mode 100644
index 0000000..3d3e556
--- /dev/null
+++ b/erpnext/utilities/doctype/contact/contact.js
@@ -0,0 +1,18 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+{% include 'controllers/js/contact_address_common.js' %};
+
+cur_frm.cscript.refresh = function(doc) {
+ cur_frm.communication_view = new wn.views.CommunicationList({
+ list: wn.model.get("Communication", {"parent": doc.name, "parenttype": "Contact"}),
+ parent: cur_frm.fields_dict.communication_html.wrapper,
+ doc: doc,
+ recipients: doc.email_id
+ });
+}
+
+cur_frm.cscript.hide_dialog = function() {
+ if(cur_frm.contact_list)
+ cur_frm.contact_list.run();
+}
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/contact/contact.py b/erpnext/utilities/doctype/contact/contact.py
new file mode 100644
index 0000000..301d7fd
--- /dev/null
+++ b/erpnext/utilities/doctype/contact/contact.py
@@ -0,0 +1,51 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import cstr, extract_email_id
+
+from erpnext.utilities.transaction_base import TransactionBase
+
+class DocType(TransactionBase):
+ def __init__(self, doc, doclist=[]):
+ self.doc = doc
+ self.doclist = doclist
+
+ def autoname(self):
+ # 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.set_status()
+ self.validate_primary_contact()
+
+ def validate_primary_contact(self):
+ if self.doc.is_primary_contact == 1:
+ if self.doc.customer:
+ webnotes.conn.sql("update tabContact set is_primary_contact=0 where customer = '%s'" % (self.doc.customer))
+ elif self.doc.supplier:
+ webnotes.conn.sql("update tabContact set is_primary_contact=0 where supplier = '%s'" % (self.doc.supplier))
+ elif self.doc.sales_partner:
+ webnotes.conn.sql("update tabContact set is_primary_contact=0 where sales_partner = '%s'" % (self.doc.sales_partner))
+ else:
+ if self.doc.customer:
+ if not webnotes.conn.sql("select name from tabContact where is_primary_contact=1 and customer = '%s'" % (self.doc.customer)):
+ self.doc.is_primary_contact = 1
+ elif self.doc.supplier:
+ if not webnotes.conn.sql("select name from tabContact where is_primary_contact=1 and supplier = '%s'" % (self.doc.supplier)):
+ self.doc.is_primary_contact = 1
+ elif self.doc.sales_partner:
+ if not webnotes.conn.sql("select name from tabContact where is_primary_contact=1 and sales_partner = '%s'" % (self.doc.sales_partner)):
+ self.doc.is_primary_contact = 1
+
+ def on_trash(self):
+ webnotes.conn.sql("""update `tabSupport Ticket` set contact='' where contact=%s""",
+ self.doc.name)
diff --git a/erpnext/utilities/doctype/contact/contact.txt b/erpnext/utilities/doctype/contact/contact.txt
new file mode 100644
index 0000000..65c22b3
--- /dev/null
+++ b/erpnext/utilities/doctype/contact/contact.txt
@@ -0,0 +1,292 @@
+[
+ {
+ "creation": "2013-01-10 16:34:32",
+ "docstatus": 0,
+ "modified": "2013-12-20 19:23:59",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "allow_import": 1,
+ "allow_rename": 1,
+ "doctype": "DocType",
+ "document_type": "Master",
+ "icon": "icon-user",
+ "in_create": 0,
+ "in_dialog": 0,
+ "module": "Utilities",
+ "name": "__common__"
+ },
+ {
+ "doctype": "DocField",
+ "name": "__common__",
+ "parent": "Contact",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0
+ },
+ {
+ "create": 1,
+ "doctype": "DocPerm",
+ "email": 1,
+ "name": "__common__",
+ "parent": "Contact",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "Contact"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "contact_section",
+ "fieldtype": "Section Break",
+ "label": "Contact Details",
+ "options": "icon-user"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "first_name",
+ "fieldtype": "Data",
+ "label": "First Name",
+ "oldfieldname": "first_name",
+ "oldfieldtype": "Data",
+ "reqd": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "last_name",
+ "fieldtype": "Data",
+ "label": "Last Name",
+ "oldfieldname": "last_name",
+ "oldfieldtype": "Data"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "cb00",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "Passive",
+ "doctype": "DocField",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "label": "Status",
+ "options": "Passive\nOpen\nReplied"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "email_id",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Email Id",
+ "oldfieldname": "email_id",
+ "oldfieldtype": "Data",
+ "reqd": 0,
+ "search_index": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "phone",
+ "fieldtype": "Data",
+ "label": "Phone",
+ "oldfieldname": "contact_no",
+ "oldfieldtype": "Data",
+ "reqd": 0
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "sb00",
+ "fieldtype": "Section Break",
+ "label": "Communication History",
+ "options": "icon-comments",
+ "print_hide": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "communication_html",
+ "fieldtype": "HTML",
+ "label": "Communication HTML",
+ "print_hide": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "contact_details",
+ "fieldtype": "Section Break",
+ "label": "Reference",
+ "options": "icon-pushpin"
+ },
+ {
+ "depends_on": "eval:!doc.supplier && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "label": "Customer",
+ "oldfieldname": "customer",
+ "oldfieldtype": "Link",
+ "options": "Customer",
+ "print_hide": 0
+ },
+ {
+ "depends_on": "eval:!doc.supplier && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "customer_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Customer Name",
+ "read_only": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "column_break1",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "width": "50%"
+ },
+ {
+ "depends_on": "eval:!doc.customer && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "label": "Supplier",
+ "options": "Supplier"
+ },
+ {
+ "allow_on_submit": 0,
+ "depends_on": "eval:!doc.customer && !doc.sales_partner",
+ "doctype": "DocField",
+ "fieldname": "supplier_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Supplier Name",
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval:!doc.customer && !doc.supplier",
+ "doctype": "DocField",
+ "fieldname": "sales_partner",
+ "fieldtype": "Link",
+ "label": "Sales Partner",
+ "options": "Sales Partner"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:(doc.customer || doc.supplier || doc.sales_partner)",
+ "doctype": "DocField",
+ "fieldname": "is_primary_contact",
+ "fieldtype": "Check",
+ "label": "Is Primary Contact",
+ "oldfieldname": "is_primary_contact",
+ "oldfieldtype": "Select"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "more_info",
+ "fieldtype": "Section Break",
+ "label": "More Info",
+ "options": "icon-file-text"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "mobile_no",
+ "fieldtype": "Data",
+ "label": "Mobile No",
+ "oldfieldname": "mobile_no",
+ "oldfieldtype": "Data"
+ },
+ {
+ "description": "Enter department to which this Contact belongs",
+ "doctype": "DocField",
+ "fieldname": "department",
+ "fieldtype": "Data",
+ "label": "Department",
+ "options": "Suggest"
+ },
+ {
+ "description": "Enter designation of this Contact",
+ "doctype": "DocField",
+ "fieldname": "designation",
+ "fieldtype": "Data",
+ "label": "Designation",
+ "options": "Suggest"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "unsubscribed",
+ "fieldtype": "Check",
+ "label": "Unsubscribed"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "trash_reason",
+ "fieldtype": "Small Text",
+ "label": "Trash Reason",
+ "oldfieldname": "trash_reason",
+ "oldfieldtype": "Small Text",
+ "read_only": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "communications",
+ "fieldtype": "Table",
+ "hidden": 1,
+ "label": "Communications",
+ "options": "Communication",
+ "print_hide": 1
+ },
+ {
+ "cancel": 1,
+ "doctype": "DocPerm",
+ "role": "System Manager"
+ },
+ {
+ "amend": 0,
+ "cancel": 1,
+ "doctype": "DocPerm",
+ "role": "Sales Master Manager"
+ },
+ {
+ "cancel": 1,
+ "doctype": "DocPerm",
+ "role": "Purchase Master Manager"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Sales Manager"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Purchase Manager"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Maintenance Manager"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Accounts Manager"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Sales User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Purchase User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Maintenance User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Accounts User"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/contact/test_contact.py b/erpnext/utilities/doctype/contact/test_contact.py
new file mode 100644
index 0000000..5e7898f
--- /dev/null
+++ b/erpnext/utilities/doctype/contact/test_contact.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+test_records = [
+ [{
+ "doctype": "Contact",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "first_name": "_Test Contact For _Test Customer",
+ "email_id": "test_contact_customer@example.com",
+ "phone": "+91 0000000000",
+ "status": "Open",
+ "is_primary_contact": 1
+ }],
+ [{
+ "doctype": "Contact",
+ "supplier": "_Test Supplier",
+ "supplier_name": "_Test Supplier",
+ "first_name": "_Test Contact For _Test Supplier",
+ "email_id": "test_contact_supplier@example.com",
+ "phone": "+91 0000000000",
+ "status": "Open",
+ "is_primary_contact": 1
+ }]
+]
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/note/README.md b/erpnext/utilities/doctype/note/README.md
new file mode 100644
index 0000000..95d7b33
--- /dev/null
+++ b/erpnext/utilities/doctype/note/README.md
@@ -0,0 +1 @@
+Shared Note. (Page with standard information, links, attachments).
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/note/__init__.py b/erpnext/utilities/doctype/note/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/utilities/doctype/note/__init__.py
diff --git a/erpnext/utilities/doctype/note/note.py b/erpnext/utilities/doctype/note/note.py
new file mode 100644
index 0000000..0ae23a2
--- /dev/null
+++ b/erpnext/utilities/doctype/note/note.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+# 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
+
+ def autoname(self):
+ # replace forbidden characters
+ import re
+ self.doc.name = re.sub("[%'\"#*?`]", "", self.doc.title.strip())
+
+ def onload(self):
+ if not self.doc.public and webnotes.session.user != self.doc.owner:
+ if webnotes.session.user not in [d.user for d in self.doclist if d.doctype=="Note User"]:
+ webnotes.msgprint("You are not authorized to read this record.", raise_exception=True)
+
+ def validate(self):
+ if not self.doc.fields.get("__islocal"):
+ if webnotes.session.user != self.doc.owner:
+ if webnotes.session.user not in webnotes.conn.sql_list("""select user from `tabNote User`
+ where parent=%s and permission='Edit'""", self.doc.name):
+ webnotes.msgprint("You are not authorized to edit this record.", raise_exception=True)
diff --git a/erpnext/utilities/doctype/note/note.txt b/erpnext/utilities/doctype/note/note.txt
new file mode 100644
index 0000000..055619a
--- /dev/null
+++ b/erpnext/utilities/doctype/note/note.txt
@@ -0,0 +1,86 @@
+[
+ {
+ "creation": "2013-05-24 13:41:00",
+ "docstatus": 0,
+ "modified": "2013-12-20 19:24:14",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "allow_rename": 1,
+ "description": "Note is a free page where users can share documents / notes",
+ "doctype": "DocType",
+ "document_type": "Transaction",
+ "icon": "icon-file-text",
+ "module": "Utilities",
+ "name": "__common__",
+ "read_only_onload": 1
+ },
+ {
+ "doctype": "DocField",
+ "name": "__common__",
+ "parent": "Note",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "doctype": "DocPerm",
+ "email": 1,
+ "name": "__common__",
+ "parent": "Note",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "role": "All",
+ "write": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "Note"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "label": "Title",
+ "print_hide": 1
+ },
+ {
+ "description": "Help: To link to another record in the system, use \"#Form/Note/[Note Name]\" as the Link URL. (don't use \"http://\")",
+ "doctype": "DocField",
+ "fieldname": "content",
+ "fieldtype": "Text Editor",
+ "in_list_view": 0,
+ "label": "Content"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "share",
+ "fieldtype": "Section Break",
+ "label": "Share"
+ },
+ {
+ "description": "Everyone can read",
+ "doctype": "DocField",
+ "fieldname": "public",
+ "fieldtype": "Check",
+ "label": "Public",
+ "print_hide": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "share_with",
+ "fieldtype": "Table",
+ "label": "Share With",
+ "options": "Note User",
+ "print_hide": 1
+ },
+ {
+ "doctype": "DocPerm"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/note_user/README.md b/erpnext/utilities/doctype/note_user/README.md
new file mode 100644
index 0000000..a8e066d
--- /dev/null
+++ b/erpnext/utilities/doctype/note_user/README.md
@@ -0,0 +1 @@
+User who can access / edit the parent Note.
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/note_user/__init__.py b/erpnext/utilities/doctype/note_user/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/utilities/doctype/note_user/__init__.py
diff --git a/erpnext/utilities/doctype/note_user/note_user.py b/erpnext/utilities/doctype/note_user/note_user.py
new file mode 100644
index 0000000..e5468e5
--- /dev/null
+++ b/erpnext/utilities/doctype/note_user/note_user.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+# 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/erpnext/utilities/doctype/note_user/note_user.txt b/erpnext/utilities/doctype/note_user/note_user.txt
new file mode 100644
index 0000000..86fdc44
--- /dev/null
+++ b/erpnext/utilities/doctype/note_user/note_user.txt
@@ -0,0 +1,46 @@
+[
+ {
+ "creation": "2013-05-24 14:24:48",
+ "docstatus": 0,
+ "modified": "2013-12-20 19:23:22",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "description": "List of users who can edit a particular Note",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "istable": 1,
+ "module": "Utilities",
+ "name": "__common__"
+ },
+ {
+ "doctype": "DocField",
+ "in_list_view": 1,
+ "name": "__common__",
+ "parent": "Note User",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0
+ },
+ {
+ "doctype": "DocType",
+ "name": "Note User"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "label": "User",
+ "options": "Profile",
+ "reqd": 1
+ },
+ {
+ "default": "Edit",
+ "doctype": "DocField",
+ "fieldname": "permission",
+ "fieldtype": "Select",
+ "label": "Permission",
+ "options": "Edit\nRead"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/rename_tool/README.md b/erpnext/utilities/doctype/rename_tool/README.md
new file mode 100644
index 0000000..38d408c
--- /dev/null
+++ b/erpnext/utilities/doctype/rename_tool/README.md
@@ -0,0 +1 @@
+Tool to rename in bulk by uploading a .csv file.
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/rename_tool/__init__.py b/erpnext/utilities/doctype/rename_tool/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/utilities/doctype/rename_tool/__init__.py
diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.js b/erpnext/utilities/doctype/rename_tool/rename_tool.js
new file mode 100644
index 0000000..90cd1e1
--- /dev/null
+++ b/erpnext/utilities/doctype/rename_tool/rename_tool.js
@@ -0,0 +1,50 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+cur_frm.cscript.refresh = function(doc) {
+ return wn.call({
+ method: "erpnext.utilities.doctype.rename_tool.rename_tool.get_doctypes",
+ callback: function(r) {
+ cur_frm.set_df_property("select_doctype", "options", r.message);
+ cur_frm.cscript.setup_upload();
+ }
+ });
+}
+
+cur_frm.cscript.select_doctype = function() {
+ cur_frm.cscript.setup_upload();
+}
+
+cur_frm.cscript.setup_upload = function() {
+ var me = this;
+ var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty()
+ .html("<hr><div class='alert alert-warning'>" +
+ wn._("Upload a .csv file with two columns: the old name and the new name. Max 500 rows.")
+ + "</div>");
+ var $log = $(cur_frm.fields_dict.rename_log.wrapper).empty();
+
+ // upload
+ wn.upload.make({
+ parent: $wrapper,
+ args: {
+ method: 'utilities.doctype.rename_tool.rename_tool.upload',
+ select_doctype: cur_frm.doc.select_doctype
+ },
+ sample_url: "e.g. http://example.com/somefile.csv",
+ callback: function(fid, filename, r) {
+ $log.empty().html("<hr>");
+ $.each(r.message, function(i, v) {
+ $("<div>" + v + "</div>").appendTo($log);
+ });
+ }
+ });
+
+ // rename button
+ $wrapper.find('form input[type="submit"]')
+ .click(function() {
+ $log.html("Working...");
+ })
+ .addClass("btn-info")
+ .attr('value', 'Upload and Rename')
+
+}
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.py b/erpnext/utilities/doctype/rename_tool/rename_tool.py
new file mode 100644
index 0000000..f38b8d1
--- /dev/null
+++ b/erpnext/utilities/doctype/rename_tool/rename_tool.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes import _
+
+class DocType:
+ def __init__(self, d, dl):
+ self.doc, self.doclist = d, dl
+
+@webnotes.whitelist()
+def get_doctypes():
+ return webnotes.conn.sql_list("""select name from tabDocType
+ where ifnull(allow_rename,0)=1 and module!='Core' order by name""")
+
+@webnotes.whitelist()
+def upload(select_doctype=None, rows=None):
+ from webnotes.utils.datautils import read_csv_content_from_uploaded_file
+ from webnotes.modules import scrub
+ from webnotes.model.rename_doc import rename_doc
+
+ if not select_doctype:
+ select_doctype = webnotes.form_dict.select_doctype
+
+ if not webnotes.has_permission(select_doctype, "write"):
+ raise webnotes.PermissionError
+
+ if not rows:
+ rows = read_csv_content_from_uploaded_file()
+ if not rows:
+ webnotes.msgprint(_("Please select a valid csv file with data."))
+ raise Exception
+
+ if len(rows) > 500:
+ webnotes.msgprint(_("Max 500 rows only."))
+ raise Exception
+
+ rename_log = []
+ for row in rows:
+ # 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])
+ webnotes.conn.commit()
+ else:
+ rename_log.append(_("Ignored: ") + row[0] + " -> " + row[1])
+ except Exception, e:
+ rename_log.append("<span style='color: RED'>" + \
+ _("Failed: ") + row[0] + " -> " + row[1] + "</span>")
+ rename_log.append("<span style='margin-left: 20px;'>" + repr(e) + "</span>")
+
+ return rename_log
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.txt b/erpnext/utilities/doctype/rename_tool/rename_tool.txt
new file mode 100644
index 0000000..e397cf3
--- /dev/null
+++ b/erpnext/utilities/doctype/rename_tool/rename_tool.txt
@@ -0,0 +1,70 @@
+[
+ {
+ "creation": "2012-12-03 10:25:59",
+ "docstatus": 0,
+ "modified": "2013-07-05 14:52:51",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "allow_attach": 0,
+ "allow_email": 1,
+ "allow_print": 1,
+ "doctype": "DocType",
+ "hide_heading": 0,
+ "hide_toolbar": 1,
+ "icon": "icon-magic",
+ "issingle": 1,
+ "max_attachments": 1,
+ "module": "Utilities",
+ "name": "__common__"
+ },
+ {
+ "doctype": "DocField",
+ "name": "__common__",
+ "parent": "Rename Tool",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0
+ },
+ {
+ "create": 1,
+ "doctype": "DocPerm",
+ "name": "__common__",
+ "parent": "Rename Tool",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "read": 1,
+ "report": 0,
+ "role": "System Manager",
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "Rename Tool"
+ },
+ {
+ "description": "Type of document to rename.",
+ "doctype": "DocField",
+ "fieldname": "select_doctype",
+ "fieldtype": "Select",
+ "label": "Select DocType"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "upload_html",
+ "fieldtype": "HTML",
+ "label": "Upload HTML"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "rename_log",
+ "fieldtype": "HTML",
+ "label": "Rename Log"
+ },
+ {
+ "doctype": "DocPerm"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/sms_control/__init__.py b/erpnext/utilities/doctype/sms_control/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_control/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/utilities/doctype/sms_control/sms_control.js b/erpnext/utilities/doctype/sms_control/sms_control.js
new file mode 100644
index 0000000..42025bf
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_control/sms_control.js
@@ -0,0 +1,94 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+function SMSManager() {
+ var me = this;
+ this.get_contact_number = function(contact, key, value) {
+ return $c_obj('SMS Control', 'get_contact_number', {
+ contact_name:contact,
+ value:value,
+ key:key
+ }, function(r,rt) {
+ if(r.exc) { msgprint(r.exc); return; }
+ me.number = r.message;
+ me.show_dialog();
+ }
+ );
+ }
+ this.show = function(contact, key, value, mobile_nos, message) {
+ this.message = message;
+ if (mobile_nos) {
+ me.number = mobile_nos;
+ me.show_dialog();
+ } else if (contact){
+ this.get_contact_number(contact, key, value)
+ } else {
+ me.show_dialog();
+ }
+ }
+ this.show_dialog = function() {
+ if(!me.dialog)
+ me.make_dialog();
+ me.dialog.set_values({
+ 'message': me.message,
+ 'number': me.number
+ })
+ me.dialog.show();
+ }
+ this.make_dialog = function() {
+ var d = new wn.ui.Dialog({
+ title: 'Send SMS',
+ width: 400,
+ fields: [
+ {fieldname:'number', fieldtype:'Data', label:'Mobile Number', reqd:1},
+ {fieldname:'message', fieldtype:'Text', label:'Message', reqd:1},
+ {fieldname:'send', fieldtype:'Button', label:'Send'}
+ ]
+ })
+ d.fields_dict.send.input.onclick = function() {
+ var btn = d.fields_dict.send.input;
+ var v = me.dialog.get_values();
+ if(v) {
+ $(this).set_working();
+ return $c_obj('SMS Control', 'send_form_sms', v, function(r,rt) {
+ $(this).done_working();
+ if(r.exc) {msgprint(r.exc); return; }
+ msgprint('Message Sent');
+ me.dialog.hide();
+ })
+ }
+ }
+ this.dialog = d;
+ }
+}
+
+cur_frm.cscript.send_sms = function(doc,dt,dn) {
+ var doc = cur_frm.doc;
+ var sms_man = new SMSManager();
+ var default_msg = {
+ 'Lead' : '',
+ 'Opportunity' : 'Your enquiry has been logged into the system. Ref No: ' + doc.name,
+ 'Quotation' : 'Quotation ' + doc.name + ' has been sent via email. Thanks!',
+ 'Sales Order' : 'Sales Order ' + doc.name + ' has been created against '
+ + (doc.quotation_no ? ('Quote No:' + doc.quotation_no) : '')
+ + (doc.po_no ? (' for your PO: ' + doc.po_no) : ''),
+ 'Delivery Note' : 'Items has been delivered against delivery note: ' + doc.name
+ + (doc.po_no ? (' for your PO: ' + doc.po_no) : ''),
+ 'Sales Invoice': 'Invoice ' + doc.name + ' has been sent via email '
+ + (doc.po_no ? (' for your PO: ' + doc.po_no) : ''),
+ 'Material Request' : 'Material Request ' + doc.name + ' has been raised in the system',
+ 'Purchase Order' : 'Purchase Order ' + doc.name + ' has been sent via email',
+ 'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name
+ }
+
+ if (in_list(['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype))
+ sms_man.show(doc.contact_person, 'customer', doc.customer, '', default_msg[doc.doctype]);
+ else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype))
+ sms_man.show(doc.contact_person, 'supplier', doc.supplier, '', default_msg[doc.doctype]);
+ else if (doc.doctype == 'Lead')
+ sms_man.show('', '', '', doc.mobile_no, default_msg[doc.doctype]);
+ else if (doc.doctype == 'Opportunity')
+ sms_man.show('', '', '', doc.contact_no, default_msg[doc.doctype]);
+ else if (doc.doctype == 'Material Request')
+ sms_man.show('', '', '', '', default_msg[doc.doctype]);
+}
diff --git a/erpnext/utilities/doctype/sms_control/sms_control.py b/erpnext/utilities/doctype/sms_control/sms_control.py
new file mode 100644
index 0000000..8fbb8fe
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_control/sms_control.py
@@ -0,0 +1,120 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes, json
+
+from webnotes.utils import nowdate, cstr
+from webnotes.model.code import get_obj
+from webnotes.model.doc import Document
+from webnotes import msgprint
+from webnotes.model.bean import getlist, copy_doclist
+
+class DocType:
+ def __init__(self, doc, doclist=[]):
+ self.doc = doc
+ self.doclist = doclist
+
+ def validate_receiver_nos(self,receiver_list):
+ validated_receiver_list = []
+ for d in receiver_list:
+ # remove invalid character
+ invalid_char_list = [' ', '+', '-', '(', ')']
+ for x in invalid_char_list:
+ d = d.replace(x, '')
+
+ validated_receiver_list.append(d)
+
+ if not validated_receiver_list:
+ msgprint("Please enter valid mobile nos", raise_exception=1)
+
+ return validated_receiver_list
+
+
+ def get_sender_name(self):
+ "returns name as SMS sender"
+ sender_name = webnotes.conn.get_value('Global Defaults', None, 'sms_sender_name') or \
+ 'ERPNXT'
+ if len(sender_name) > 6 and \
+ webnotes.conn.get_value("Control Panel", None, "country") == "India":
+ msgprint("""
+ As per TRAI rule, sender name must be exactly 6 characters.
+ Kindly change sender name in Setup --> Global Defaults.
+
+ Note: Hyphen, space, numeric digit, special characters are not allowed.
+ """, raise_exception=1)
+ return sender_name
+
+ def get_contact_number(self, arg):
+ "returns mobile number of the contact"
+ args = json.loads(arg)
+ number = webnotes.conn.sql("""select mobile_no, phone from tabContact where name=%s and %s=%s""" %
+ ('%s', args['key'], '%s'), (args['contact_name'], args['value']))
+ return number and (number[0][0] or number[0][1]) or ''
+
+ def send_form_sms(self, arg):
+ "called from client side"
+ args = json.loads(arg)
+ self.send_sms([str(args['number'])], str(args['message']))
+
+ def send_sms(self, receiver_list, msg, sender_name = ''):
+ receiver_list = self.validate_receiver_nos(receiver_list)
+
+ arg = {
+ 'receiver_list' : receiver_list,
+ 'message' : msg,
+ 'sender_name' : sender_name or self.get_sender_name()
+ }
+
+ if webnotes.conn.get_value('SMS Settings', None, 'sms_gateway_url'):
+ ret = self.send_via_gateway(arg)
+ msgprint(ret)
+
+ def send_via_gateway(self, arg):
+ ss = get_obj('SMS Settings', 'SMS Settings', with_children=1)
+ args = {ss.doc.message_parameter : arg.get('message')}
+ for d in getlist(ss.doclist, 'static_parameter_details'):
+ args[d.parameter] = d.value
+
+ resp = []
+ for d in arg.get('receiver_list'):
+ args[ss.doc.receiver_parameter] = d
+ resp.append(self.send_request(ss.doc.sms_gateway_url, args))
+
+ return resp
+
+ # Send Request
+ # =========================================================
+ def send_request(self, gateway_url, args):
+ import httplib, urllib
+ server, api_url = self.scrub_gateway_url(gateway_url)
+ conn = httplib.HTTPConnection(server) # open connection
+ headers = {}
+ headers['Accept'] = "text/plain, text/html, */*"
+ conn.request('GET', api_url + urllib.urlencode(args), headers = headers) # send request
+ resp = conn.getresponse() # get response
+ resp = resp.read()
+ return resp
+
+ # Split gateway url to server and api url
+ # =========================================================
+ def scrub_gateway_url(self, url):
+ url = url.replace('http://', '').strip().split('/')
+ server = url.pop(0)
+ api_url = '/' + '/'.join(url)
+ if not api_url.endswith('?'):
+ api_url += '?'
+ return server, api_url
+
+
+ # Create SMS Log
+ # =========================================================
+ def create_sms_log(self, arg, sent_sms):
+ sl = Document('SMS Log')
+ sl.sender_name = arg['sender_name']
+ sl.sent_on = nowdate()
+ sl.receiver_list = cstr(arg['receiver_list'])
+ sl.message = arg['message']
+ sl.no_of_requested_sms = len(arg['receiver_list'])
+ sl.no_of_sent_sms = sent_sms
+ sl.save(new=1)
diff --git a/erpnext/utilities/doctype/sms_control/sms_control.txt b/erpnext/utilities/doctype/sms_control/sms_control.txt
new file mode 100644
index 0000000..f8e0fae
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_control/sms_control.txt
@@ -0,0 +1,42 @@
+[
+ {
+ "creation": "2013-01-10 16:34:32",
+ "docstatus": 0,
+ "modified": "2013-12-20 19:21:47",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "DocType",
+ "icon": "icon-mobile-phone",
+ "in_create": 0,
+ "issingle": 1,
+ "module": "Utilities",
+ "name": "__common__"
+ },
+ {
+ "create": 1,
+ "doctype": "DocPerm",
+ "email": 1,
+ "export": 0,
+ "import": 0,
+ "name": "__common__",
+ "parent": "SMS Control",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 0,
+ "role": "System Manager",
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "SMS Control"
+ },
+ {
+ "doctype": "DocPerm"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/sms_log/README.md b/erpnext/utilities/doctype/sms_log/README.md
new file mode 100644
index 0000000..9ee2b79
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_log/README.md
@@ -0,0 +1 @@
+Log of SMS sent via SMS Center.
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/sms_log/__init__.py b/erpnext/utilities/doctype/sms_log/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_log/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/utilities/doctype/sms_log/sms_log.py b/erpnext/utilities/doctype/sms_log/sms_log.py
new file mode 100644
index 0000000..cb6190f
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_log/sms_log.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. 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/erpnext/utilities/doctype/sms_log/sms_log.txt b/erpnext/utilities/doctype/sms_log/sms_log.txt
new file mode 100644
index 0000000..43cb1ff
--- /dev/null
+++ b/erpnext/utilities/doctype/sms_log/sms_log.txt
@@ -0,0 +1,94 @@
+[
+ {
+ "creation": "2012-03-27 14:36:47",
+ "docstatus": 0,
+ "modified": "2013-12-20 19:24:35",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "autoname": "SMSLOG/.########",
+ "doctype": "DocType",
+ "icon": "icon-mobile-phone",
+ "module": "Utilities",
+ "name": "__common__"
+ },
+ {
+ "doctype": "DocField",
+ "name": "__common__",
+ "parent": "SMS Log",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0
+ },
+ {
+ "create": 0,
+ "doctype": "DocPerm",
+ "email": 1,
+ "name": "__common__",
+ "parent": "SMS Log",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "write": 0
+ },
+ {
+ "doctype": "DocType",
+ "name": "SMS Log"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "column_break0",
+ "fieldtype": "Column Break",
+ "width": "50%"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "sender_name",
+ "fieldtype": "Data",
+ "label": "Sender Name"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "sent_on",
+ "fieldtype": "Date",
+ "label": "Sent On"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "receiver_list",
+ "fieldtype": "Small Text",
+ "label": "Receiver List"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "column_break1",
+ "fieldtype": "Column Break",
+ "width": "50%"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "no_of_requested_sms",
+ "fieldtype": "Int",
+ "label": "No of Requested SMS"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "no_of_sent_sms",
+ "fieldtype": "Int",
+ "label": "No of Sent SMS"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "message",
+ "fieldtype": "Small Text",
+ "label": "Message"
+ },
+ {
+ "doctype": "DocPerm"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py
new file mode 100644
index 0000000..cb0ce10
--- /dev/null
+++ b/erpnext/utilities/repost_stock.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+from webnotes.utils import flt
+
+
+def repost(allow_negative_stock=False):
+ """
+ Repost everything!
+ """
+ webnotes.conn.auto_commit_on_many_writes = 1
+
+ if allow_negative_stock:
+ webnotes.conn.set_default("allow_negative_stock", 1)
+
+ for d in webnotes.conn.sql("""select distinct item_code, warehouse from
+ (select item_code, warehouse from tabBin
+ union
+ select item_code, warehouse from `tabStock Ledger Entry`) a"""):
+ repost_stock(d[0], d[1], allow_negative_stock)
+
+ if allow_negative_stock:
+ webnotes.conn.set_default("allow_negative_stock",
+ webnotes.conn.get_value("Stock Settings", None, "allow_negative_stock"))
+ webnotes.conn.auto_commit_on_many_writes = 0
+
+def repost_stock(item_code, warehouse):
+ repost_actual_qty(item_code, warehouse)
+
+ if item_code and warehouse:
+ update_bin(item_code, warehouse, {
+ "reserved_qty": get_reserved_qty(item_code, warehouse),
+ "indented_qty": get_indented_qty(item_code, warehouse),
+ "ordered_qty": get_ordered_qty(item_code, warehouse),
+ "planned_qty": get_planned_qty(item_code, warehouse)
+ })
+
+def repost_actual_qty(item_code, warehouse):
+ from erpnext.stock.stock_ledger import update_entries_after
+ try:
+ update_entries_after({ "item_code": item_code, "warehouse": warehouse })
+ except:
+ pass
+
+def get_reserved_qty(item_code, warehouse):
+ reserved_qty = webnotes.conn.sql("""
+ select
+ sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty))
+ from
+ (
+ (select
+ qty as dnpi_qty,
+ (
+ select qty from `tabSales Order Item`
+ where name = dnpi.parent_detail_docname
+ ) as so_item_qty,
+ (
+ select ifnull(delivered_qty, 0) from `tabSales Order Item`
+ where name = dnpi.parent_detail_docname
+ ) as so_item_delivered_qty,
+ parent, name
+ from
+ (
+ select qty, parent_detail_docname, parent, name
+ from `tabPacked Item` dnpi_in
+ where item_code = %s and warehouse = %s
+ and parenttype="Sales Order"
+ and item_code != parent_item
+ and exists (select * from `tabSales Order` so
+ where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
+ ) dnpi)
+ union
+ (select qty as dnpi_qty, qty as so_item_qty,
+ ifnull(delivered_qty, 0) as so_item_delivered_qty, parent, name
+ from `tabSales Order Item` so_item
+ where item_code = %s and reserved_warehouse = %s
+ and exists(select * from `tabSales Order` so
+ where so.name = so_item.parent and so.docstatus = 1
+ and so.status != 'Stopped'))
+ ) tab
+ where
+ so_item_qty >= so_item_delivered_qty
+ """, (item_code, warehouse, item_code, warehouse))
+
+ return flt(reserved_qty[0][0]) if reserved_qty else 0
+
+def get_indented_qty(item_code, warehouse):
+ indented_qty = webnotes.conn.sql("""select sum(pr_item.qty - ifnull(pr_item.ordered_qty, 0))
+ from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr
+ where pr_item.item_code=%s and pr_item.warehouse=%s
+ and pr_item.qty > ifnull(pr_item.ordered_qty, 0) and pr_item.parent=pr.name
+ and pr.status!='Stopped' and pr.docstatus=1""", (item_code, warehouse))
+
+ return flt(indented_qty[0][0]) if indented_qty else 0
+
+def get_ordered_qty(item_code, warehouse):
+ ordered_qty = webnotes.conn.sql("""
+ select sum((po_item.qty - ifnull(po_item.received_qty, 0))*po_item.conversion_factor)
+ from `tabPurchase Order Item` po_item, `tabPurchase Order` po
+ where po_item.item_code=%s and po_item.warehouse=%s
+ and po_item.qty > ifnull(po_item.received_qty, 0) and po_item.parent=po.name
+ and po.status!='Stopped' and po.docstatus=1""", (item_code, warehouse))
+
+ return flt(ordered_qty[0][0]) if ordered_qty else 0
+
+def get_planned_qty(item_code, warehouse):
+ planned_qty = webnotes.conn.sql("""
+ select sum(ifnull(qty, 0) - ifnull(produced_qty, 0)) from `tabProduction Order`
+ where production_item = %s and fg_warehouse = %s and status != "Stopped"
+ and docstatus=1 and ifnull(qty, 0) > ifnull(produced_qty, 0)""", (item_code, warehouse))
+
+ return flt(planned_qty[0][0]) if planned_qty else 0
+
+
+def update_bin(item_code, warehouse, qty_dict=None):
+ from erpnext.stock.utils import get_bin
+ bin = get_bin(item_code, warehouse)
+ mismatch = False
+ for fld, val in qty_dict.items():
+ if flt(bin.doc.fields.get(fld)) != flt(val):
+ bin.doc.fields[fld] = flt(val)
+ mismatch = True
+
+ if mismatch:
+ bin.doc.projected_qty = flt(bin.doc.actual_qty) + flt(bin.doc.ordered_qty) + \
+ flt(bin.doc.indented_qty) + flt(bin.doc.planned_qty) - flt(bin.doc.reserved_qty)
+
+ bin.doc.save()
\ No newline at end of file
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
new file mode 100644
index 0000000..eed4639
--- /dev/null
+++ b/erpnext/utilities/transaction_base.py
@@ -0,0 +1,498 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes, json
+from webnotes import msgprint, _
+from webnotes.utils import cstr, flt, now_datetime, cint
+from webnotes.model.doc import addchild
+
+from erpnext.controllers.status_updater import StatusUpdater
+
+class TransactionBase(StatusUpdater):
+ def get_default_address_and_contact(self, party_field, party_name=None):
+ """get a dict of default field values of address and contact for a given party type
+ party_type can be one of: customer, supplier"""
+ if not party_name:
+ party_name = self.doc.fields.get(party_field)
+
+ return get_default_address_and_contact(party_field, party_name,
+ fetch_shipping_address=True if self.meta.get_field("shipping_address_name") else False)
+
+ def set_address_fields(self):
+ party_type, party_name = self.get_party_type_and_name()
+
+ if party_type in ("Customer", "Lead"):
+ if self.doc.customer_address:
+ self.doc.address_display = get_address_display(self.doc.customer_address)
+
+ if self.doc.shipping_address_name:
+ self.doc.shipping_address = get_address_display(self.doc.shipping_address_name)
+
+ elif self.doc.supplier_address:
+ self.doc.address_display = get_address_display(self.doc.supplier_address)
+
+ def set_contact_fields(self):
+ party_type, party_name = self.get_party_type_and_name()
+
+ if party_type == "Lead":
+ contact_dict = map_lead_contact_details(party_name)
+ else:
+ contact_dict = map_party_contact_details(self.doc.contact_person, party_type, party_name)
+
+ for fieldname, value in contact_dict.items():
+ if self.meta.get_field(fieldname):
+ self.doc.fields[fieldname] = value
+
+ def get_party_type_and_name(self):
+ if not hasattr(self, "_party_type_and_name"):
+ for party_type in ("Lead", "Customer", "Supplier"):
+ party_field = party_type.lower()
+ if self.meta.get_field(party_field) and self.doc.fields.get(party_field):
+ self._party_type_and_name = (party_type, self.doc.fields.get(party_field))
+ break
+
+ return self._party_type_and_name
+
+ def get_customer_defaults(self):
+ if not self.doc.customer: return {}
+
+ out = self.get_default_address_and_contact("customer")
+
+ customer = webnotes.doc("Customer", self.doc.customer)
+ for f in ['customer_name', 'customer_group', 'territory']:
+ out[f] = customer.fields.get(f)
+
+ # fields prepended with default in Customer doctype
+ for f in ['sales_partner', 'commission_rate', 'currency', 'price_list']:
+ if customer.fields.get("default_" + f):
+ out[f] = customer.fields.get("default_" + f)
+
+ return out
+
+ def set_customer_defaults(self):
+ """
+ For a customer:
+ 1. Sets default address and contact
+ 2. Sets values like Territory, Customer Group, etc.
+ 3. Clears existing Sales Team and fetches the one mentioned in Customer
+ """
+ customer_defaults = self.get_customer_defaults()
+
+ customer_defaults["selling_price_list"] = customer_defaults.get("price_list") or \
+ webnotes.conn.get_value("Customer Group", self.doc.customer_group, "default_price_list") or \
+ self.doc.selling_price_list
+
+ for fieldname, val in customer_defaults.items():
+ if self.meta.get_field(fieldname):
+ self.doc.fields[fieldname] = val
+
+ if self.meta.get_field("sales_team") and self.doc.customer:
+ self.set_sales_team_for_customer()
+
+ def set_sales_team_for_customer(self):
+ from webnotes.model import default_fields
+
+ # clear table
+ self.doclist = self.doc.clear_table(self.doclist, "sales_team")
+
+ sales_team = webnotes.conn.sql("""select * from `tabSales Team`
+ where parenttype="Customer" and parent=%s""", self.doc.customer, as_dict=True)
+ for i, sales_person in enumerate(sales_team):
+ # remove default fields
+ for fieldname in default_fields:
+ if fieldname in sales_person:
+ del sales_person[fieldname]
+
+ sales_person.update({
+ "doctype": "Sales Team",
+ "parentfield": "sales_team",
+ "idx": i+1
+ })
+
+ # add child
+ self.doclist.append(sales_person)
+
+ def get_supplier_defaults(self):
+ out = self.get_default_address_and_contact("supplier")
+
+ supplier = webnotes.doc("Supplier", self.doc.supplier)
+ out["supplier_name"] = supplier.supplier_name
+ if supplier.default_currency:
+ out["currency"] = supplier.default_currency
+ if supplier.default_price_list:
+ out["buying_price_list"] = supplier.default_price_list
+
+ return out
+
+ def set_supplier_defaults(self):
+ for fieldname, val in self.get_supplier_defaults().items():
+ if self.meta.get_field(fieldname):
+ self.doc.fields[fieldname] = val
+
+ def get_lead_defaults(self):
+ out = self.get_default_address_and_contact("lead")
+
+ lead = webnotes.conn.get_value("Lead", self.doc.lead,
+ ["territory", "company_name", "lead_name"], as_dict=True) or {}
+
+ out["territory"] = lead.get("territory")
+ out["customer_name"] = lead.get("company_name") or lead.get("lead_name")
+
+ return out
+
+ def set_lead_defaults(self):
+ self.doc.fields.update(self.get_lead_defaults())
+
+ def get_customer_address(self, args):
+ args = json.loads(args)
+ ret = {
+ 'customer_address' : args["address"],
+ 'address_display' : get_address_display(args["address"]),
+ }
+ if args.get('contact'):
+ ret.update(map_party_contact_details(args['contact']))
+
+ return ret
+
+ def set_customer_address(self, args):
+ self.doc.fields.update(self.get_customer_address(args))
+
+ # TODO deprecate this - used only in sales_order.js
+ def get_shipping_address(self, name):
+ shipping_address = get_default_address("customer", name, is_shipping_address=True)
+ return {
+ 'shipping_address_name' : shipping_address,
+ 'shipping_address' : get_address_display(shipping_address) if shipping_address else None
+ }
+
+ # Get Supplier Default Primary Address - first load
+ # -----------------------
+ def get_default_supplier_address(self, args):
+ if isinstance(args, basestring):
+ args = json.loads(args)
+
+ address_name = get_default_address("supplier", args["supplier"])
+ ret = {
+ 'supplier_address' : address_name,
+ 'address_display' : get_address_display(address_name),
+ }
+ ret.update(map_party_contact_details(None, "supplier", args["supplier"]))
+ ret.update(self.get_supplier_details(args['supplier']))
+ return ret
+
+ # Get Supplier Address
+ # -----------------------
+ def get_supplier_address(self, args):
+ args = json.loads(args)
+ ret = {
+ 'supplier_address' : args['address'],
+ 'address_display' : get_address_display(args["address"]),
+ }
+ ret.update(map_party_contact_details(contact_name=args['contact']))
+ return ret
+
+ def set_supplier_address(self, args):
+ self.doc.fields.update(self.get_supplier_address(args))
+
+ # Get Supplier Details
+ # -----------------------
+ def get_supplier_details(self, name):
+ supplier_details = webnotes.conn.sql("""\
+ select supplier_name, default_currency
+ from `tabSupplier`
+ where name = %s and docstatus < 2""", name, as_dict=1)
+ if supplier_details:
+ return {
+ 'supplier_name': (supplier_details[0]['supplier_name']
+ or self.doc.fields.get('supplier_name')),
+ 'currency': (supplier_details[0]['default_currency']
+ or self.doc.fields.get('currency')),
+ }
+ else:
+ return {}
+
+ # Get Sales Person Details of Customer
+ # ------------------------------------
+ def get_sales_person(self, name):
+ self.doclist = self.doc.clear_table(self.doclist,'sales_team')
+ idx = 0
+ for d in webnotes.conn.sql("select sales_person, allocated_percentage, allocated_amount, incentives from `tabSales Team` where parent = '%s'" % name):
+ ch = addchild(self.doc, 'sales_team', 'Sales Team', self.doclist)
+ ch.sales_person = d and cstr(d[0]) or ''
+ ch.allocated_percentage = d and flt(d[1]) or 0
+ ch.allocated_amount = d and flt(d[2]) or 0
+ ch.incentives = d and flt(d[3]) or 0
+ ch.idx = idx
+ idx += 1
+
+ def load_notification_message(self):
+ dt = self.doc.doctype.lower().replace(" ", "_")
+ if int(webnotes.conn.get_value("Notification Control", None, dt) or 0):
+ self.doc.fields["__notification_message"] = \
+ webnotes.conn.get_value("Notification Control", None, dt + "_message")
+
+ def validate_posting_time(self):
+ if not self.doc.posting_time:
+ self.doc.posting_time = now_datetime().strftime('%H:%M:%S')
+
+ 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) or force:
+
+ 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)),
+ ignore_permissions=True)
+
+ 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()
+
+ def validate_uom_is_integer(self, uom_field, qty_fields):
+ validate_uom_is_integer(self.doclist, uom_field, qty_fields)
+
+ def validate_with_previous_doc(self, source_dt, ref):
+ for key, val in ref.items():
+ is_child = val.get("is_child_table")
+ ref_doc = {}
+ item_ref_dn = []
+ for d in self.doclist.get({"doctype": source_dt}):
+ ref_dn = d.fields.get(val["ref_dn_field"])
+ if ref_dn:
+ if is_child:
+ self.compare_values({key: [ref_dn]}, val["compare_fields"], d)
+ if ref_dn not in item_ref_dn:
+ item_ref_dn.append(ref_dn)
+ elif not val.get("allow_duplicate_prev_row_id"):
+ webnotes.msgprint(_("Row ") + cstr(d.idx + 1) +
+ _(": Duplicate row from same ") + key, raise_exception=1)
+ elif ref_dn:
+ ref_doc.setdefault(key, [])
+ if ref_dn not in ref_doc[key]:
+ ref_doc[key].append(ref_dn)
+ if ref_doc:
+ self.compare_values(ref_doc, val["compare_fields"])
+
+ def compare_values(self, ref_doc, fields, doc=None):
+ for ref_doctype, ref_dn_list in ref_doc.items():
+ for ref_docname in ref_dn_list:
+ prevdoc_values = webnotes.conn.get_value(ref_doctype, ref_docname,
+ [d[0] for d in fields], as_dict=1)
+
+ for field, condition in fields:
+ if prevdoc_values[field] is not None:
+ self.validate_value(field, condition, prevdoc_values[field], doc)
+
+def get_default_address_and_contact(party_field, party_name, fetch_shipping_address=False):
+ out = {}
+
+ # get addresses
+ billing_address = get_default_address(party_field, party_name)
+ if billing_address:
+ out[party_field + "_address"] = billing_address
+ out["address_display"] = get_address_display(billing_address)
+ else:
+ out[party_field + "_address"] = out["address_display"] = None
+
+ if fetch_shipping_address:
+ shipping_address = get_default_address(party_field, party_name, is_shipping_address=True)
+ if shipping_address:
+ out["shipping_address_name"] = shipping_address
+ out["shipping_address"] = get_address_display(shipping_address)
+ else:
+ out["shipping_address_name"] = out["shipping_address"] = None
+
+ # get contact
+ if party_field == "lead":
+ out["customer_address"] = out.get("lead_address")
+ out.update(map_lead_contact_details(party_name))
+ else:
+ out.update(map_party_contact_details(None, party_field, party_name))
+
+ return out
+
+def get_default_address(party_field, party_name, is_shipping_address=False):
+ if is_shipping_address:
+ order_by = "is_shipping_address desc, is_primary_address desc, name asc"
+ else:
+ order_by = "is_primary_address desc, name asc"
+
+ address = webnotes.conn.sql("""select name from `tabAddress` where `%s`=%s order by %s
+ limit 1""" % (party_field, "%s", order_by), party_name)
+
+ return address[0][0] if address else None
+
+def get_default_contact(party_field, party_name):
+ contact = webnotes.conn.sql("""select name from `tabContact` where `%s`=%s
+ order by is_primary_contact desc, name asc limit 1""" % (party_field, "%s"),
+ (party_name,))
+
+ return contact[0][0] if contact else None
+
+def get_address_display(address_dict):
+ if not isinstance(address_dict, dict):
+ address_dict = webnotes.conn.get_value("Address", address_dict, "*", as_dict=True) or {}
+
+ meta = webnotes.get_doctype("Address")
+ sequence = (("", "address_line1"), ("\n", "address_line2"), ("\n", "city"),
+ ("\n", "state"), ("\n" + meta.get_label("pincode") + ": ", "pincode"), ("\n", "country"),
+ ("\n" + meta.get_label("phone") + ": ", "phone"), ("\n" + meta.get_label("fax") + ": ", "fax"))
+
+ display = ""
+ for separator, fieldname in sequence:
+ if address_dict.get(fieldname):
+ display += separator + address_dict.get(fieldname)
+
+ return display.strip()
+
+def map_lead_contact_details(party_name):
+ out = {}
+ for fieldname in ["contact_display", "contact_email", "contact_mobile", "contact_phone"]:
+ out[fieldname] = None
+
+ lead = webnotes.conn.sql("""select * from `tabLead` where name=%s""", party_name, as_dict=True)
+ if lead:
+ lead = lead[0]
+ out.update({
+ "contact_display": lead.get("lead_name"),
+ "contact_email": lead.get("email_id"),
+ "contact_mobile": lead.get("mobile_no"),
+ "contact_phone": lead.get("phone"),
+ })
+
+ return out
+
+def map_party_contact_details(contact_name=None, party_field=None, party_name=None):
+ out = {}
+ for fieldname in ["contact_person", "contact_display", "contact_email",
+ "contact_mobile", "contact_phone", "contact_designation", "contact_department"]:
+ out[fieldname] = None
+
+ if not contact_name and party_field:
+ contact_name = get_default_contact(party_field, party_name)
+
+ if contact_name:
+ contact = webnotes.conn.sql("""select * from `tabContact` where name=%s""",
+ contact_name, as_dict=True)
+
+ if contact:
+ contact = contact[0]
+ out.update({
+ "contact_person": contact.get("name"),
+ "contact_display": " ".join(filter(None,
+ [contact.get("first_name"), contact.get("last_name")])),
+ "contact_email": contact.get("email_id"),
+ "contact_mobile": contact.get("mobile_no"),
+ "contact_phone": contact.get("phone"),
+ "contact_designation": contact.get("designation"),
+ "contact_department": contact.get("department")
+ })
+
+ return out
+
+def get_address_territory(address_doc):
+ territory = None
+ for fieldname in ("city", "state", "country"):
+ value = address_doc.fields.get(fieldname)
+ if value:
+ territory = webnotes.conn.get_value("Territory", value.strip())
+ if territory:
+ break
+
+ return territory
+
+def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
+ """common validation for currency and price list currency"""
+
+ company_currency = webnotes.conn.get_value("Company", company, "default_currency")
+
+ if not conversion_rate:
+ msgprint(_('%(conversion_rate_label)s is mandatory. Maybe Currency Exchange record is not created for %(from_currency)s to %(to_currency)s') % {
+ "conversion_rate_label": conversion_rate_label,
+ "from_currency": currency,
+ "to_currency": company_currency
+ }, raise_exception=True)
+
+def validate_item_fetch(args, item):
+ from erpnext.stock.utils import validate_end_of_life
+ validate_end_of_life(item.name, item.end_of_life)
+
+ # validate company
+ if not args.company:
+ msgprint(_("Please specify Company"), raise_exception=True)
+
+def validate_currency(args, item, meta=None):
+ from webnotes.model.meta import get_field_precision
+ if not meta:
+ meta = webnotes.get_doctype(args.doctype)
+
+ # validate conversion rate
+ if meta.get_field("currency"):
+ validate_conversion_rate(args.currency, args.conversion_rate,
+ meta.get_label("conversion_rate"), args.company)
+
+ # round it
+ args.conversion_rate = flt(args.conversion_rate,
+ get_field_precision(meta.get_field("conversion_rate"),
+ webnotes._dict({"fields": args})))
+
+ # validate price list conversion rate
+ if meta.get_field("price_list_currency") and (args.selling_price_list or args.buying_price_list) \
+ and args.price_list_currency:
+ validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
+ meta.get_label("plc_conversion_rate"), args.company)
+
+ # round it
+ args.plc_conversion_rate = flt(args.plc_conversion_rate,
+ get_field_precision(meta.get_field("plc_conversion_rate"),
+ webnotes._dict({"fields": args})))
+
+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)
+
+class UOMMustBeIntegerError(webnotes.ValidationError): pass
+
+def validate_uom_is_integer(doclist, uom_field, qty_fields):
+ if isinstance(qty_fields, basestring):
+ qty_fields = [qty_fields]
+
+ integer_uoms = filter(lambda uom: webnotes.conn.get_value("UOM", uom,
+ "must_be_whole_number") or None, doclist.get_distinct_values(uom_field))
+
+ if not integer_uoms:
+ return
+
+ for d in doclist:
+ if d.fields.get(uom_field) in integer_uoms:
+ for f in qty_fields:
+ if d.fields.get(f):
+ if cint(d.fields[f])!=d.fields[f]:
+ webnotes.msgprint(_("For UOM") + " '" + d.fields[uom_field] \
+ + "': " + _("Quantity cannot be a fraction.") \
+ + " " + _("In Row") + ": " + str(d.idx),
+ raise_exception=UOMMustBeIntegerError)