Merge branch 'master' into develop
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index c4c319f..0090e75 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -2,7 +2,7 @@
from __future__ import unicode_literals
import frappe
-__version__ = '7.2.29'
+__version__ = '7.2.30'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 66b3dd0..f8c9c0a 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -169,7 +169,7 @@
item_attribute = frappe.db.sql("""select i.numeric_values, v.abbr
from `tabItem Attribute` i left join `tabItem Attribute Value` v
on (i.name=v.parent)
- where i.name=%(attribute)s and v.attribute_value=%(attribute_value)s""", {
+ where i.name=%(attribute)s and (v.attribute_value=%(attribute_value)s or i.numeric_values = 1)""", {
"attribute": attr.attribute,
"attribute_value": attr.attribute_value
}, as_dict=True)
@@ -180,11 +180,8 @@
# frappe.bold(attr.attribute_value)), title=_('Invalid Attribute'),
# exc=InvalidItemAttributeValueError)
- if item_attribute[0].numeric_values:
- # don't generate item code if one of the attributes is numeric
- return
-
- abbreviations.append(item_attribute[0].abbr)
+ abbr_or_value = cstr(attr.attribute_value) if item_attribute[0].numeric_values else item_attribute[0].abbr
+ abbreviations.append(abbr_or_value)
if abbreviations:
variant.item_code = "{0}-{1}".format(template_item_code, "-".join(abbreviations))
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 4b20ec8..f5bc7db 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -51,7 +51,9 @@
# validate transaction date v/s delivery date
if self.delivery_date:
if getdate(self.transaction_date) > getdate(self.delivery_date):
- frappe.throw(_("Expected Delivery Date cannot be before Sales Order Date"))
+ frappe.msgprint(_("Expected Delivery Date is be before Sales Order Date"),
+ indicator='orange',
+ title=_('Warning'))
def validate_po(self):
# validate p.o date v/s delivery date
@@ -82,7 +84,8 @@
unique_chk_list = set(check_list)
if len(unique_chk_list) != len(check_list) and \
not cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
- frappe.msgprint(_("Warning: Same item has been entered multiple times."))
+ frappe.msgprint(_("Same item has been entered multiple times"),
+ title=_("Warning"), indicator='orange')
def product_bundle_has_stock_item(self, product_bundle):
"""Returns true if product bundle has stock item"""
diff --git a/erpnext/setup/setup_wizard/domainify.py b/erpnext/setup/setup_wizard/domainify.py
index 5963675..69aaf75 100644
--- a/erpnext/setup/setup_wizard/domainify.py
+++ b/erpnext/setup/setup_wizard/domainify.py
@@ -22,8 +22,8 @@
},
'Retail': {
- 'desktop_icons': ['POS', 'Item', 'Customer', 'Sales Invoice', 'Purchase Order', 'Warranty Claim',
- 'Accounts', 'Buying', 'ToDo'],
+ 'desktop_icons': ['POS', 'Item', 'Customer', 'Sales Invoice', 'Purchase Order',
+ 'Warranty Claim', 'Accounts', 'Task', 'Buying', 'ToDo'],
'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User'],
'properties': [
{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'hidden', 'value': 1},
@@ -36,7 +36,7 @@
},
'Distribution': {
- 'desktop_icons': ['Item', 'Customer', 'Supplier', 'Lead', 'Sales Order',
+ 'desktop_icons': ['Item', 'Customer', 'Supplier', 'Lead', 'Sales Order', 'Task',
'Sales Invoice', 'CRM', 'Selling', 'Buying', 'Stock', 'Accounts', 'HR', 'ToDo'],
'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User'],
'set_value': [
@@ -46,8 +46,8 @@
},
'Services': {
- 'desktop_icons': ['Project', 'Timesheet', 'Customer', 'Sales Order', 'Sales Invoice', 'Lead', 'Opportunity',
- 'Expense Claim', 'Employee', 'HR', 'ToDo'],
+ 'desktop_icons': ['Project', 'Timesheet', 'Customer', 'Sales Order', 'Sales Invoice',
+ 'Lead', 'Opportunity', 'Task', 'Expense Claim', 'Employee', 'HR', 'ToDo'],
'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User'],
'properties': [
{'doctype': 'Item', 'fieldname': 'is_stock_item', 'property': 'default', 'value': 0},
@@ -59,10 +59,10 @@
},
'Education': {
'desktop_icons': ['Student', 'Program', 'Course', 'Student Group', 'Instructor',
- 'Fees', 'ToDo', 'Schools'],
+ 'Fees', 'Task', 'ToDo', 'Schools'],
'allow_roles': ['Academics User', 'Accounts User', 'Accounts Manager', 'Item Manager',
'Website Manager', 'HR User', 'HR Manager', 'Purchase User', 'Purchase Manager',
- 'Student'],
+ 'Student', 'Projects User'],
'default_portal_role': 'Student'
},
}
diff --git a/erpnext/setup/setup_wizard/sample_data.py b/erpnext/setup/setup_wizard/sample_data.py
index 955f663..cfc6726 100644
--- a/erpnext/setup/setup_wizard/sample_data.py
+++ b/erpnext/setup/setup_wizard/sample_data.py
@@ -6,9 +6,11 @@
import frappe
from frappe.utils.make_random import add_random_children
import frappe.utils
-import random
+import random, os, json
+from frappe import _
+from markdown2 import markdown
-def make_sample_data():
+def make_sample_data(args):
"""Create a few opportunities, quotes, material requests, issues, todos, projects
to help the user get started"""
items = frappe.get_all("Item", {'is_sales_item': 1})
@@ -22,7 +24,8 @@
make_opportunity(items, customer)
make_quote(items, customer)
- make_projects()
+ make_projects(args.get('domain'))
+ import_email_alert()
if items and warehouses:
make_material_request(frappe.get_all("Item"))
@@ -84,42 +87,91 @@
def make_issue():
pass
-def make_projects():
+def make_projects(domain):
+ current_date = frappe.utils.nowdate()
project = frappe.get_doc({
"doctype": "Project",
"project_name": "ERPNext Implementation",
})
- current_date = frappe.utils.nowdate()
- project.set("tasks", [
+
+ tasks = [
+ {
+ "title": "Explore ERPNext",
+ "start_date": current_date,
+ "end_date": current_date,
+ "file": "explore.md"
+ }]
+
+ if domain == 'Education':
+ tasks += [
{
- "title": "Explore ERPNext",
- "start_date": frappe.utils.add_days(current_date, 1),
- "end_date": frappe.utils.add_days(current_date, 2)
+ "title": _("Setup your School in ERPNext"),
+ "start_date": current_date,
+ "end_date": frappe.utils.add_days(current_date, 1),
+ "file": "school_masters.md"
},
{
- "title": "Run Sales Cycle",
- "start_date": frappe.utils.add_days(current_date, 2),
- "end_date": frappe.utils.add_days(current_date, 3)
+ "title": "Setup Master Data",
+ "start_date": current_date,
+ "end_date": frappe.utils.add_days(current_date, 1),
+ "file": "school_masters.md"
+ }]
+
+ else:
+ tasks += [
+ {
+ "title": "Setup Your Company",
+ "start_date": current_date,
+ "end_date": frappe.utils.add_days(current_date, 1),
+ "file": "masters.md"
},
{
- "title": "Run Billing Cycle",
- "start_date": frappe.utils.add_days(current_date, 3),
- "end_date": frappe.utils.add_days(current_date, 4)
+ "title": "Start Tracking your Sales",
+ "start_date": current_date,
+ "end_date": frappe.utils.add_days(current_date, 2),
+ "file": "sales.md"
},
{
- "title": "Run Purchase Cycle",
- "start_date": frappe.utils.add_days(current_date, 4),
- "end_date": frappe.utils.add_days(current_date, 5)
+ "title": "Start Managing Purchases",
+ "start_date": current_date,
+ "end_date": frappe.utils.add_days(current_date, 3),
+ "file": "purchase.md"
},
{
"title": "Import Data",
- "start_date": frappe.utils.add_days(current_date, 5),
- "end_date": frappe.utils.add_days(current_date, 6)
+ "start_date": current_date,
+ "end_date": frappe.utils.add_days(current_date, 4),
+ "file": "import_data.md"
},
{
"title": "Go Live!",
- "start_date": frappe.utils.add_days(current_date, 6),
- "end_date": frappe.utils.add_days(current_date, 7)
- }])
+ "start_date": current_date,
+ "end_date": frappe.utils.add_days(current_date, 5),
+ "file": "go_live.md"
+ }]
+
+ for t in tasks:
+ with open (os.path.join(os.path.dirname(__file__), "tasks", t['file'])) as f:
+ t['description'] = markdown(f.read())
+ del t['file']
+
+ project.append('tasks', t)
project.insert(ignore_permissions=True)
+
+def import_email_alert():
+ '''Import email alert for task start'''
+ with open (os.path.join(os.path.dirname(__file__), "tasks/task_alert.json")) as f:
+ email_alert = frappe.get_doc(json.loads(f.read())[0])
+ email_alert.insert()
+
+ # trigger the first message!
+ from frappe.email.doctype.email_alert.email_alert import trigger_daily_alerts
+ trigger_daily_alerts()
+
+def test_sample():
+ frappe.db.sql('delete from `tabEmail Alert`')
+ frappe.db.sql('delete from tabProject')
+ frappe.db.sql('delete from tabTask')
+ make_projects('Education')
+ import_email_alert()
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py
index 664c089..26509ed 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -60,7 +60,7 @@
if args.get("add_sample_data"):
try:
- make_sample_data()
+ make_sample_data(args)
frappe.clear_cache()
except:
# clear message
diff --git a/erpnext/setup/setup_wizard/tasks/explore.md b/erpnext/setup/setup_wizard/tasks/explore.md
new file mode 100644
index 0000000..ce6cb60
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/explore.md
@@ -0,0 +1,7 @@
+Thanks for checking this out! ❤️
+
+If you are evaluating an ERP system for the first time, this is going to be quite a task! But don't worry, ERPNext is awesome.
+
+First, get familiar with the surroundings. ERPNext covers a *lot of features*, go to the home page and click on the "Explore" icon.
+
+All the best!
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/go_live.md b/erpnext/setup/setup_wizard/tasks/go_live.md
new file mode 100644
index 0000000..0a934a4
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/go_live.md
@@ -0,0 +1,18 @@
+Ready to go live with ERPNext? 🏁🏁🏁
+
+Here are the steps:
+
+1. Sync up your **Chart of Accounts**
+3. Add your opening stock using **Stock Reconciliation**
+4. Add your open invoices (both sales and purchase)
+3. Add your opening account balances by making a **Journal Entry**
+
+If you need help for going live, sign up for an account at erpnext.com or find a partner to help you with this.
+
+Or you can watch these videos 📺:
+
+Setup your chart of accounts: https://www.youtube.com/watch?v=AcfMCT7wLLo
+
+Add Open Stock: https://www.youtube.com/watch?v=nlHX0ZZ84Lw
+
+Add Opening Balances: https://www.youtube.com/watch?v=nlHX0ZZ84Lw
diff --git a/erpnext/setup/setup_wizard/tasks/import_data.md b/erpnext/setup/setup_wizard/tasks/import_data.md
new file mode 100644
index 0000000..c5b85c9
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/import_data.md
@@ -0,0 +1,5 @@
+Lets import some data! 💪💪
+
+If you are already running a business, you most likely have your Items, Customers or Suppliers in some spreadsheet file somewhere, import it into ERPNext with the Data Import Tool.
+
+Watch this video to get started: https://www.youtube.com/watch?v=Ta2Xx3QoK3E
diff --git a/erpnext/setup/setup_wizard/tasks/masters.md b/erpnext/setup/setup_wizard/tasks/masters.md
new file mode 100644
index 0000000..6ade159
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/masters.md
@@ -0,0 +1,7 @@
+Start building a model of your business in ERPNext by adding your Items and Customers.
+
+These videos 📺 will help you get started:
+
+Adding Customers and Suppliers: https://www.youtube.com/watch?v=zsrrVDk6VBs
+
+Adding Items and Prices: https://www.youtube.com/watch?v=FcOsV-e8ymE
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/purchase.md b/erpnext/setup/setup_wizard/tasks/purchase.md
new file mode 100644
index 0000000..3f3bc3b
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/purchase.md
@@ -0,0 +1,10 @@
+How to manage your purchasing in ERPNext 🛒🛒🛒:
+
+1. Add a few **Suppliers**
+2. Find out what you need by making **Material Requests**.
+3. Now start placing orders via **Purchase Order**.
+4. When your suppliers deliver, make **Purchase Receipts**
+
+Now never run out of stock again! 😎
+
+Watch this video 📺 to get an overview: https://www.youtube.com/watch?v=4TN9kPyfIqM
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/sales.md b/erpnext/setup/setup_wizard/tasks/sales.md
new file mode 100644
index 0000000..15268fa
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/sales.md
@@ -0,0 +1,8 @@
+Start managing your sales with ERPNext 🔔🔔🔔:
+
+1. Add potential business contacts as **Leads**
+2. Udpate your deals in pipeline in **Opportunities**
+3. Send proposals to your leads or customers with **Quotations**
+4. Track confirmed orders with **Sales Orders**
+
+Watch this video 📺 to get an overview: https://www.youtube.com/watch?v=o9XCSZHJfpA
diff --git a/erpnext/setup/setup_wizard/tasks/school_import_data.md b/erpnext/setup/setup_wizard/tasks/school_import_data.md
new file mode 100644
index 0000000..c465b81
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/school_import_data.md
@@ -0,0 +1,5 @@
+Lets import some data! 💪💪
+
+If you are already running a school, you most likely have your Students in some spreadsheet file somewhere. Import it into ERPNext with the Data Import Tool.
+
+Watch this video to get started: https://www.youtube.com/watch?v=Ta2Xx3QoK3E
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/school_masters.md b/erpnext/setup/setup_wizard/tasks/school_masters.md
new file mode 100644
index 0000000..9103935
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/school_masters.md
@@ -0,0 +1,9 @@
+Lets start making things in ERPNext that are representative of your institution.
+
+1. Make a list of **Programs** that you offer
+1. Add a few **Courses** that your programs cover
+1. Create **Academic Terms** and **Academic Years**
+1. Start adding **Students**
+1. Group your students into **Batches**
+
+Watch this video to learn more about ERPNext Schools: https://www.youtube.com/watch?v=f6foQOyGzdA
diff --git a/erpnext/setup/setup_wizard/tasks/task_alert.json b/erpnext/setup/setup_wizard/tasks/task_alert.json
new file mode 100644
index 0000000..dca6845
--- /dev/null
+++ b/erpnext/setup/setup_wizard/tasks/task_alert.json
@@ -0,0 +1,28 @@
+[
+ {
+ "attach_print": 0,
+ "condition": "doc.status in ('Open', 'Overdue')",
+ "date_changed": "exp_end_date",
+ "days_in_advance": 0,
+ "docstatus": 0,
+ "doctype": "Email Alert",
+ "document_type": "Task",
+ "enabled": 1,
+ "event": "Days After",
+ "is_standard": 0,
+ "message": "<p>Task due today:</p>\n\n<div>\n{{ doc.description }}\n</div>\n\n<hr>\n<p style=\"font-size: 85%\">\nThis is a notification for a task that is due today, and a sample <b>Email Alert</b>. In ERPNext you can setup email alerts on anything, Invoices, Orders, Leads, Opportunities, so you never miss a thing.\n<br>To edit this, and setup other alerts, just type <b>Email Alert</b> in the search bar.</p>",
+ "method": null,
+ "modified": "2017-03-09 07:34:58.168370",
+ "module": null,
+ "name": "Task Due Alert",
+ "recipients": [
+ {
+ "cc": null,
+ "condition": null,
+ "email_by_document_field": "owner"
+ }
+ ],
+ "subject": "{{ doc.subject }}",
+ "value_changed": null
+ }
+]
\ No newline at end of file
diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py
index 554243f..58458c6 100644
--- a/erpnext/startup/notifications.py
+++ b/erpnext/startup/notifications.py
@@ -8,7 +8,7 @@
{
"Issue": {"status": "Open"},
"Warranty Claim": {"status": "Open"},
- "Task": {"status": "Overdue"},
+ "Task": {"status": ("in", ("Open", "Overdue"))},
"Project": {"status": "Open"},
"Item": {"total_projected_qty": ("<", 0)},
"Lead": {"status": "Open"},
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 439c729..7068c99 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -68,7 +68,7 @@
frm.set_intro(__("This Item is a Variant of {0} (Template). Attributes will be copied over from the template unless 'No Copy' is set", [frm.doc.variant_of]), true);
}
- if (frappe.defaults.get_default("item_naming_by")!="Naming Series") {
+ if (frappe.defaults.get_default("item_naming_by")!="Naming Series" || frm.doc.variant_of) {
frm.toggle_display("naming_series", false);
} else {
erpnext.toggle_naming_series();
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 706fd5a..15a1118 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -162,14 +162,14 @@
variant = create_variant("_Test Numeric Template Item",
{"Test Size": "Large", "Test Item Length": 1.1})
- self.assertEquals(variant.item_code, None)
+ self.assertEquals(variant.item_code, "_Test Numeric Template Item-L-1.1")
variant.item_code = "_Test Numeric Variant-L-1.1"
variant.item_name = "_Test Numeric Variant Large 1.1m"
self.assertRaises(InvalidItemAttributeValueError, variant.save)
variant = create_variant("_Test Numeric Template Item",
{"Test Size": "Large", "Test Item Length": 1.5})
- self.assertEquals(variant.item_code, None)
+ self.assertEquals(variant.item_code, "_Test Numeric Template Item-L-1.5")
variant.item_code = "_Test Numeric Variant-L-1.5"
variant.item_name = "_Test Numeric Variant Large 1.5m"
variant.save()