Merge pull request #4766 from nabinhait/permlevel1
[fix] Read permission to All for permlevel 1 fields
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
index 06c7693..d01f1ba 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
@@ -6,36 +6,36 @@
frappe.ui.form.on("Leave Allocation", {
onload: function(frm) {
if(!frm.doc.from_date) frm.set_value("from_date", get_today());
-
+
frm.set_query("employee", function() {
return {
query: "erpnext.controllers.queries.employee_query"
}
})
},
-
+
employee: function(frm) {
frm.trigger("calculate_total_leaves_allocated");
},
-
+
leave_type: function(frm) {
frm.trigger("calculate_total_leaves_allocated");
},
-
+
carry_forward: function(frm) {
frm.trigger("calculate_total_leaves_allocated");
},
-
+
carry_forwarded_leaves: function(frm) {
- frm.set_value("total_leaves_allocated",
+ frm.set_value("total_leaves_allocated",
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated));
},
-
+
new_leaves_allocated: function(frm) {
- frm.set_value("total_leaves_allocated",
+ frm.set_value("total_leaves_allocated",
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated));
},
-
+
calculate_total_leaves_allocated: function(frm) {
if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) {
return frappe.call({
@@ -49,7 +49,7 @@
callback: function(r) {
if (!r.exc && r.message) {
frm.set_value('carry_forwarded_leaves', r.message);
- frm.set_value("total_leaves_allocated",
+ frm.set_value("total_leaves_allocated",
flt(r.message) + flt(frm.doc.new_leaves_allocated));
}
}
@@ -59,4 +59,4 @@
frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated));
}
}
-})
\ No newline at end of file
+})
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 9e638cc..b63a167 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -62,7 +62,7 @@
def validate_dates(self):
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
frappe.throw(_("To date cannot be before from date"))
-
+
if not is_lwp(self.leave_type):
self.validate_dates_acorss_allocation()
self.validate_back_dated_application()
@@ -118,7 +118,7 @@
frappe.throw(_("The day(s) on which you are applying for leave are holidays. You need not apply for leave."))
if not is_lwp(self.leave_type):
- self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date,
+ self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date,
consider_all_leaves_in_the_allocation_period=True)
if self.status != "Rejected" and self.leave_balance < self.total_leave_days:
@@ -145,28 +145,31 @@
}, as_dict = 1):
if d['total_leave_days']==0.5 and cint(self.half_day)==1:
- sum_leave_days=frappe.db.sql("""select sum(total_leave_days) from `tabLeave Application`
- where employee = %(employee)s
- and docstatus < 2
- and status in ("Open", "Approved")
- and (from_date between %(from_date)s and %(to_date)s
- or to_date between %(from_date)s and %(to_date)s
- or %(from_date)s between from_date and to_date)
- and name != %(name)s""", {
- "employee": self.employee,
- "from_date": self.from_date,
- "to_date": self.to_date,
- "name": self.name
- })[0][0]
- if sum_leave_days==1:
- frappe.msgprint(_("Employee {0} has already applied this day").format(self.employee))
- frappe.throw('<a href="#Form/Leave Application/{0}">{0}</a>'.format(d["name"]), OverlapError)
- else:
- frappe.msgprint(_("Employee {0} has already applied for {1} between {2} and {3}")
- .format(self.employee, cstr(d['leave_type']),formatdate(d['from_date']), formatdate(d['to_date'])))
+ sum_leave_days = self.get_total_leaves_on_half_day()
+ if sum_leave_days==1:
+ self.throw_overlap_error(d)
+ else:
+ self.throw_overlap_error(d)
- frappe.throw("""Exising Application: <a href="#Form/Leave Application/{0}">{0}</a>""".format(d["name"]), OverlapError)
+ def throw_overlap_error(self, d):
+ msg = _("Employee {0} has already applied for {1} between {2} and {3}").format(self.employee,
+ d['leave_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \
+ + """ <br><b><a href="#Form/Leave Application/{0}">{0}</a></b>""".format(d["name"])
+ frappe.throw(msg, OverlapError)
+ def get_total_leaves_on_half_day(self):
+ return frappe.db.sql("""select sum(total_leave_days) from `tabLeave Application`
+ where employee = %(employee)s
+ and docstatus < 2
+ and status in ("Open", "Approved")
+ and from_date = %(from_date)s
+ and to_date = %(to_date)s
+ and name != %(name)s""", {
+ "employee": self.employee,
+ "from_date": self.from_date,
+ "to_date": self.to_date,
+ "name": self.name
+ })[0][0]
def validate_max_days(self):
max_days = frappe.db.get_value("Leave Type", self.leave_type, "max_days_allowed")
@@ -259,13 +262,13 @@
return number_of_days
@frappe.whitelist()
-def get_leave_balance_on(employee, leave_type, date, allocation_records=None,
+def get_leave_balance_on(employee, leave_type, date, allocation_records=None,
consider_all_leaves_in_the_allocation_period=False):
if allocation_records == None:
allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict())
allocation = allocation_records.get(leave_type, frappe._dict())
-
+
if consider_all_leaves_in_the_allocation_period:
date = allocation.to_date
leaves_taken = get_approved_leaves_for_period(employee, leave_type, allocation.from_date, date)
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index 67e211e..7580f39 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -44,11 +44,11 @@
]
-class TestLeaveApplication(unittest.TestCase):
+class TestLeaveApplication(unittest.TestCase):
def setUp(self):
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
frappe.db.sql("delete from `tab%s`" % dt)
-
+
def tearDown(self):
frappe.set_user("Administrator")
@@ -101,7 +101,7 @@
frappe.db.set_value("Department", "_Test Department",
"leave_block_list", "_Test Leave Block List")
-
+
make_allocation_record()
application = self.get_application(_test_records[0])
@@ -128,7 +128,7 @@
frappe.set_user("test@example.com")
make_allocation_record()
-
+
application = self.get_application(_test_records[0])
application.leave_approver = "test2@example.com"
application.insert()
@@ -137,6 +137,60 @@
application.leave_approver = "test2@example.com"
self.assertRaises(OverlapError, application.insert)
+ def test_overlap_with_half_day(self):
+ self._clear_roles()
+ self._clear_applications()
+
+ from frappe.utils.user import add_role
+ add_role("test@example.com", "Employee")
+ add_role("test2@example.com", "Leave Approver")
+
+ frappe.set_user("test@example.com")
+
+ make_allocation_record()
+
+ # allow second half-day on the same day if available
+ application = self.get_application(_test_records[0])
+ application.leave_approver = "test2@example.com"
+ application.half_day = 1
+ application.insert()
+
+ # allow second half-day on the same day if available
+ application = self.get_application(_test_records[0])
+ application.leave_approver = "test2@example.com"
+ application.half_day = 1
+ application.insert()
+
+ application = self.get_application(_test_records[0])
+ application.leave_approver = "test2@example.com"
+ application.half_day = 1
+
+ self.assertRaises(OverlapError, application.insert)
+
+ def test_overlap_with_half_day_not_applicable(self):
+ self._clear_roles()
+ self._clear_applications()
+
+ from frappe.utils.user import add_role
+ add_role("test@example.com", "Employee")
+ add_role("test2@example.com", "Leave Approver")
+
+ frappe.set_user("test@example.com")
+
+ make_allocation_record()
+
+ # allow second half-day on the same day if available
+ application = self.get_application(_test_records[0])
+ application.leave_approver = "test2@example.com"
+ application.insert()
+
+ # allow second half-day on the same day if available
+ application = self.get_application(_test_records[0])
+ application.leave_approver = "test2@example.com"
+ application.half_day = 1
+
+ self.assertRaises(OverlapError, application.insert)
+
def test_global_block_list(self):
self._clear_roles()
@@ -144,9 +198,9 @@
add_role("test1@example.com", "Employee")
add_role("test@example.com", "Leave Approver")
self._add_employee_leave_approver("_T-Employee-0002", "test@example.com")
-
+
make_allocation_record(employee="_T-Employee-0002")
-
+
application = self.get_application(_test_records[1])
application.leave_approver = "test@example.com"
@@ -186,9 +240,9 @@
# create leave application as Employee
frappe.set_user("test@example.com")
-
+
make_allocation_record()
-
+
application = self.get_application(_test_records[0])
application.leave_approver = "test1@example.com"
application.insert()
@@ -228,9 +282,9 @@
# create leave application as employee
# but submit as invalid leave approver - should raise exception
frappe.set_user("test@example.com")
-
+
make_allocation_record()
-
+
application = self.get_application(_test_records[0])
application.leave_approver = "test2@example.com"
application.insert()
@@ -251,9 +305,9 @@
frappe.db.set_value("Employee", "_T-Employee-0001", "department", None)
frappe.set_user("test@example.com")
-
+
make_allocation_record()
-
+
application = self.get_application(_test_records[0])
application.leave_approver = "test2@example.com"
application.insert()
@@ -269,10 +323,10 @@
"_T-Employee-0001")
frappe.db.set_value("Employee", "_T-Employee-0001", "department", original_department)
-
+
def make_allocation_record(employee=None, leave_type=None):
frappe.db.sql("delete from `tabLeave Allocation`")
-
+
allocation = frappe.get_doc({
"doctype": "Leave Allocation",
"employee": employee or "_T-Employee-0001",
@@ -281,6 +335,6 @@
"to_date": "2015-12-31",
"new_leaves_allocated": 30
})
-
+
allocation.insert(ignore_permissions=True)
allocation.submit()
diff --git a/erpnext/hr/doctype/leave_type/leave_type.js b/erpnext/hr/doctype/leave_type/leave_type.js
new file mode 100644
index 0000000..88c87ea
--- /dev/null
+++ b/erpnext/hr/doctype/leave_type/leave_type.js
@@ -0,0 +1,8 @@
+frappe.ui.form.on("Leave Type", {
+ refresh: function(frm) {
+ frm.add_custom_button(__("Allocations"), function() {
+ frappe.set_route("List", "Leave Allocation",
+ {"leave_type": frm.doc.name});
+ });
+ }
+});
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index dbd3250..59d371a 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -423,6 +423,8 @@
@frappe.whitelist()
def get_default_warehouse():
- wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
- fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse")
+ wip_warehouse = frappe.db.get_single_value("Manufacturing Settings",
+ "default_wip_warehouse")
+ fg_warehouse = frappe.db.get_single_value("Manufacturing Settings",
+ "default_fg_warehouse")
return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index b73a32e..2239f0a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -247,3 +247,4 @@
execute:frappe.db.sql("update `tabPricing Rule` set title=name where title='' or title is null") #2016-01-27
erpnext.patches.v6_20.set_party_account_currency_in_orders
erpnext.patches.v6_19.comment_feed_communication
+erpnext.patches.v6_21.fix_reorder_level
diff --git a/erpnext/patches/v6_21/__init__.py b/erpnext/patches/v6_21/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/patches/v6_21/__init__.py
diff --git a/erpnext/patches/v6_21/fix_reorder_level.py b/erpnext/patches/v6_21/fix_reorder_level.py
new file mode 100644
index 0000000..602978b
--- /dev/null
+++ b/erpnext/patches/v6_21/fix_reorder_level.py
@@ -0,0 +1,24 @@
+from __future__ import unicode_literals
+
+import frappe
+from erpnext.stock.doctype.item.item import DuplicateReorderRows
+
+def execute():
+ if frappe.db.has_column("Item", "re_order_level"):
+ for item in frappe.db.sql("""select name, default_warehouse, re_order_level, re_order_qty
+ from tabItem
+ where ifnull(re_order_level, 0) != 0
+ and ifnull(re_order_qty, 0) != 0""", as_dict=1):
+
+ item_doc = frappe.get_doc("Item", item.name)
+ item_doc.append("reorder_levels", {
+ "warehouse": item.default_warehouse,
+ "warehouse_reorder_level": item.re_order_level,
+ "warehouse_reorder_qty": item.re_order_qty,
+ "material_request_type": "Purchase" if item_doc.is_purchase_item else "Transfer"
+ })
+
+ try:
+ item_doc.save()
+ except DuplicateReorderRows:
+ pass
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index f804f27..506ccff 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -49,7 +49,7 @@
# reset series
naming_series = meta.get_field("naming_series")
- if naming_series:
+ if naming_series and naming_series.options:
prefixes = sorted(naming_series.options.split("\n"), lambda a, b: len(b) - len(a))
for prefix in prefixes:
diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py
index 7e209b6..4dc326e 100644
--- a/erpnext/startup/report_data_map.py
+++ b/erpnext/startup/report_data_map.py
@@ -46,8 +46,7 @@
# Stock
"Item": {
"columns": ["name", "if(item_name=name, '', item_name) as item_name", "description",
- "item_group as parent_item_group", "stock_uom", "brand", "valuation_method",
- "re_order_level", "re_order_qty"],
+ "item_group as parent_item_group", "stock_uom", "brand", "valuation_method"],
# "conditions": ["docstatus < 2"],
"order_by": "name",
"links": {
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 9ce603c..a691f2f 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -72,8 +72,6 @@
(frm.doc.__onload && frm.doc.__onload.sle_exists=="exists") ? false : true);
}
- erpnext.item.toggle_reqd(frm);
-
erpnext.item.toggle_attributes(frm);
},
@@ -102,7 +100,6 @@
},
is_stock_item: function(frm) {
- erpnext.item.toggle_reqd(frm);
if(frm.doc.is_pro_applicable && !frm.doc.is_stock_item)
frm.set_value("is_pro_applicable", 0);
},
@@ -180,10 +177,6 @@
},
- toggle_reqd: function(frm) {
- frm.toggle_reqd("default_warehouse", frm.doc.is_stock_item);
- },
-
make_dashboard: function(frm) {
frm.dashboard.reset();
if(frm.doc.__islocal)
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 79789b0..96b4350 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -790,108 +790,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "depends_on": "eval:(doc.is_stock_item && !doc.apply_warehouse_wise_reorder_level)",
- "description": "Automatically create Material Request if quantity falls below this level",
- "fieldname": "re_order_level",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Re-order Level",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "re_order_level",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "depends_on": "eval:(doc.is_stock_item && !doc.apply_warehouse_wise_reorder_level)",
- "fieldname": "re_order_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Re-order Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "depends_on": "is_stock_item",
- "fieldname": "apply_warehouse_wise_reorder_level",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Apply Warehouse-wise Reorder Level",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": "reorder_levels",
- "depends_on": "eval:(doc.is_stock_item && doc.apply_warehouse_wise_reorder_level)",
- "fieldname": "section_break_31",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Reorder level based on Warehouse",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "depends_on": "eval:(doc.is_stock_item && doc.apply_warehouse_wise_reorder_level)",
+ "depends_on": "",
"description": "Will also apply for variants unless overrridden",
"fieldname": "reorder_levels",
"fieldtype": "Table",
@@ -1467,6 +1366,35 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "default": "",
+ "depends_on": "eval:doc.is_sales_item",
+ "description": "Allow in Sales Order of type \"Service\"",
+ "fieldname": "is_service_item",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "label": "Is Service Item",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "is_service_item",
+ "oldfieldtype": "Select",
+ "options": "",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"default": "0",
"description": "Publish Item to hub.erpnext.com",
"fieldname": "publish_in_hub",
@@ -2309,7 +2237,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
- "modified": "2016-01-26 05:31:58.950718",
+ "modified": "2016-02-08 01:15:52.177625",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
@@ -2329,7 +2257,7 @@
"print": 1,
"read": 1,
"report": 1,
- "role": "Material Master Manager",
+ "role": "Item Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
@@ -2349,7 +2277,7 @@
"print": 1,
"read": 1,
"report": 1,
- "role": "Material Manager",
+ "role": "Stock Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
@@ -2357,7 +2285,7 @@
},
{
"amend": 0,
- "apply_user_permissions": 1,
+ "apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
@@ -2369,7 +2297,7 @@
"print": 1,
"read": 1,
"report": 1,
- "role": "Material User",
+ "role": "Stock User",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
@@ -2377,7 +2305,7 @@
},
{
"amend": 0,
- "apply_user_permissions": 1,
+ "apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
@@ -2397,7 +2325,7 @@
},
{
"amend": 0,
- "apply_user_permissions": 1,
+ "apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
@@ -2417,7 +2345,7 @@
},
{
"amend": 0,
- "apply_user_permissions": 1,
+ "apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
@@ -2437,7 +2365,7 @@
},
{
"amend": 0,
- "apply_user_permissions": 1,
+ "apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
@@ -2457,7 +2385,7 @@
},
{
"amend": 0,
- "apply_user_permissions": 1,
+ "apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 3e3c13e..f18bc8b 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -14,7 +14,7 @@
from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
from erpnext.controllers.item_variant import get_variant, copy_attributes_to_variant, ItemVariantExistsError
-class WarehouseNotSet(frappe.ValidationError): pass
+class DuplicateReorderRows(frappe.ValidationError): pass
class Item(WebsiteGenerator):
website = frappe._dict(
@@ -58,7 +58,6 @@
if not self.stock_uom:
msgprint(_("Please enter default Unit of Measure"), raise_exception=1)
- self.check_warehouse_is_set_for_stock_item()
self.validate_uom()
self.add_default_uom_in_conversion_factor_table()
self.validate_conversion_factor()
@@ -303,11 +302,6 @@
if not find_variant(combination):
context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1])
- def check_warehouse_is_set_for_stock_item(self):
- if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"):
- frappe.msgprint(_("Default Warehouse is mandatory for stock Item."),
- raise_exception=WarehouseNotSet)
-
def add_default_uom_in_conversion_factor_table(self):
uom_conv_list = [d.uom for d in self.get("uoms")]
if self.stock_uom not in uom_conv_list:
@@ -409,22 +403,14 @@
you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'"))
def validate_reorder_level(self):
- if cint(self.apply_warehouse_wise_reorder_level):
- self.re_order_level, self.re_order_qty = 0, 0
- else:
- self.set("reorder_levels", [])
-
- if self.re_order_level or len(self.get("reorder_levels", {"material_request_type": "Purchase"})):
+ if len(self.get("reorder_levels", {"material_request_type": "Purchase"})):
if not (self.is_purchase_item or self.is_pro_applicable):
frappe.throw(_("""To set reorder level, item must be a Purchase Item or Manufacturing Item"""))
- if self.re_order_level and not self.re_order_qty:
- frappe.throw(_("Please set reorder quantity"))
for d in self.get("reorder_levels"):
if d.warehouse_reorder_level and not d.warehouse_reorder_qty:
frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx))
-
def validate_warehouse_for_reorder(self):
warehouse = []
for i in self.get("reorder_levels"):
@@ -432,7 +418,7 @@
warehouse += [i.get("warehouse")]
else:
frappe.throw(_("Row {0}: An Reorder entry already exists for this warehouse {1}")
- .format(i.idx, i.warehouse))
+ .format(i.idx, i.warehouse), DuplicateReorderRows)
def check_if_sle_exists(self):
sle = frappe.db.sql("""select name from `tabStock Ledger Entry`
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 51d1079..a66362d 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -6,7 +6,6 @@
import frappe
from frappe.test_runner import make_test_records
-from erpnext.stock.doctype.item.item import WarehouseNotSet
from erpnext.controllers.item_variant import create_variant, ItemVariantExistsError, InvalidItemAttributeValueError
test_ignore = ["BOM"]
@@ -45,18 +44,6 @@
item = frappe.get_doc("Item", item_code)
return item
- # def test_template_cannot_have_stock(self):
- # item = self.get_item(10)
- # make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, basic_rate=1)
- # item.has_variants = 1
- # self.assertRaises(ItemTemplateCannotHaveStock, item.save)
-
- def test_default_warehouse(self):
- item = frappe.copy_doc(test_records[0])
- item.is_stock_item = 1
- item.default_warehouse = None
- self.assertRaises(WarehouseNotSet, item.insert)
-
def test_get_item_details(self):
from erpnext.stock.get_item_details import get_item_details
to_check = {
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 0bb4815..922eb5a 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -102,6 +102,8 @@
fields: [
{"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"),
options:"BOM", reqd: 1},
+ {"fieldname":"warehouse", "fieldtype":"Link", "label":__("Warehouse"),
+ options:"Warehouse", reqd: 1, label:"For Warehouse"},
{"fieldname":"fetch_exploded", "fieldtype":"Check",
"label":__("Fetch exploded BOM (including sub-assemblies)"), "default":1},
{fieldname:"fetch", "label":__("Get Items from BOM"), "fieldtype":"Button"}
@@ -119,7 +121,7 @@
var d = frappe.model.add_child(cur_frm.doc, "Material Request Item", "items");
d.item_code = item.item_code;
d.description = item.description;
- d.warehouse = item.default_warehouse;
+ d.warehouse = d.warehouse;
d.uom = item.stock_uom;
d.qty = item.qty;
});
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 9b51bfd..cca2456 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
import frappe
-from frappe.utils import flt, cstr, nowdate, add_days, cint
+from frappe.utils import flt, nowdate, add_days, cint
from frappe import _
def reorder_item():
@@ -26,10 +26,9 @@
and (is_purchase_item=1 or is_sub_contracted_item=1)
and disabled=0
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)
- and ((re_order_level is not null and re_order_level > 0)
- or exists (select name from `tabItem Reorder` ir where ir.parent=item.name)
+ and (exists (select name from `tabItem Reorder` ir where ir.parent=item.name)
or (variant_of is not null and variant_of != ''
- and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of))
+ and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of))
)""",
{"today": nowdate()})
@@ -73,10 +72,6 @@
add_to_material_request(item_code, d.warehouse, d.warehouse_reorder_level,
d.warehouse_reorder_qty, d.material_request_type)
- else:
- # raise for default warehouse
- add_to_material_request(item_code, item.default_warehouse, item.re_order_level, item.re_order_qty, "Purchase")
-
if material_requests:
return create_material_request(material_requests)
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index aef6c99..fd9ff5d 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -42,19 +42,15 @@
re_order_level = re_order_qty = 0
- if bin.warehouse==item.default_warehouse:
- re_order_level = item.re_order_level or 0
- re_order_qty = item.re_order_qty or 0
-
for d in item.get("reorder_levels"):
if d.warehouse == bin.warehouse:
re_order_level = d.warehouse_reorder_level
re_order_qty = d.warehouse_reorder_qty
-
+
shortage_qty = re_order_level - flt(bin.projected_qty) if re_order_level else 0
- data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
- item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty,
+ data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
+ item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty,
bin.reserved_qty, bin.projected_qty, re_order_level, re_order_qty, shortage_qty])
return data
diff --git a/erpnext/utilities/doctype/address/address.py b/erpnext/utilities/doctype/address/address.py
index da529fb..7f47731 100644
--- a/erpnext/utilities/doctype/address/address.py
+++ b/erpnext/utilities/doctype/address/address.py
@@ -8,6 +8,7 @@
from frappe.utils import cstr
from frappe.model.document import Document
+from jinja2 import TemplateSyntaxError
class Address(Document):
def __setup__(self):
@@ -85,16 +86,22 @@
if not isinstance(address_dict, dict):
address_dict = frappe.db.get_value("Address", address_dict, "*", as_dict=True) or {}
- template = frappe.db.get_value("Address Template", \
- {"country": address_dict.get("country")}, "template")
- if not template:
- template = frappe.db.get_value("Address Template", \
- {"is_default": 1}, "template")
+ data = frappe.db.get_value("Address Template", \
+ {"country": address_dict.get("country")}, ["name", "template"])
+ if not data:
+ data = frappe.db.get_value("Address Template", \
+ {"is_default": 1}, ["name", "template"])
- if not template:
+ if not data:
frappe.throw(_("No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template."))
- return frappe.render_template(template, address_dict)
+ name, template = data
+
+ try:
+ return frappe.render_template(template, address_dict)
+ except TemplateSyntaxError:
+ frappe.throw(_("There is an error in your Address Template {0}").format(name))
+
def get_territory_from_address(address):
"""Tries to match city, state and country of address to existing territory"""
diff --git a/erpnext/utilities/doctype/address_template/address_template.py b/erpnext/utilities/doctype/address_template/address_template.py
index aa6ef25..e759be4 100644
--- a/erpnext/utilities/doctype/address_template/address_template.py
+++ b/erpnext/utilities/doctype/address_template/address_template.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from frappe.utils.jinja import validate_template
from frappe import _
class AddressTemplate(Document):
@@ -14,6 +15,8 @@
self.is_default = 1
frappe.msgprint(_("Setting this Address Template as default as there is no other default"))
+ validate_template(self.template)
+
def on_update(self):
if self.is_default and self.defaults:
for d in self.defaults: