Merge pull request #22048 from nextchamp-saqib/update-items-permission-fix
fix: submitted sales order can be updated with no permission
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 7145fea..13f5cb0 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -1376,6 +1376,12 @@
"read": 1,
"role": "Purchase Manager",
"write": 1
+ },
+ {
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "Accounts User"
}
],
"search_fields": "status, transaction_date, supplier,grand_total",
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 1712369..813286f 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -118,7 +118,7 @@
self.assertEqual(po.get("items")[0].amount, 1400)
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
-
+
def test_add_new_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4
@@ -144,7 +144,7 @@
self.assertEquals(len(po.get('items')), 2)
self.assertEqual(po.status, 'To Receive and Bill')
-
+
def test_remove_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4
@@ -185,6 +185,23 @@
self.assertEquals(len(po.get('items')), 1)
self.assertEqual(po.status, 'To Receive and Bill')
+ def test_update_child_qty_rate_perm(self):
+ po = create_purchase_order(item_code= "_Test Item", qty=4)
+
+ user = 'test@example.com'
+ test_user = frappe.get_doc('User', user)
+ test_user.add_roles("Accounts User")
+ frappe.set_user(user)
+
+ # update qty
+ trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}])
+ self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
+
+ # add new item
+ trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
+ self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
+ frappe.set_user("Administrator")
+
def test_update_qty(self):
po = create_purchase_order()
@@ -689,7 +706,7 @@
po.save()
self.assertEqual(po.schedule_date, add_days(nowdate(), 2))
-
+
def test_po_optional_blanket_order(self):
"""
Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1.
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index eecb143..f54b593 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1137,8 +1137,8 @@
child_item.item_name = item.item_name
child_item.description = item.description
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
+ child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.uom = item.stock_uom
- child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
if not child_item.warehouse:
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
@@ -1157,8 +1157,8 @@
child_item.item_name = item.item_name
child_item.description = item.description
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
+ child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.uom = item.stock_uom
- child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.base_rate = 1 # Initiallize value will update in parent validation
child_item.base_amount = 1 # Initiallize value will update in parent validation
return child_item
@@ -1190,6 +1190,26 @@
@frappe.whitelist()
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
+ def check_permissions(doc, perm_type='create'):
+ try:
+ doc.check_permission(perm_type)
+ except:
+ action = "add" if perm_type == 'create' else "update"
+ frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
+
+ def get_new_child_item(item_row):
+ if parent_doctype == "Sales Order":
+ return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
+ if parent_doctype == "Purchase Order":
+ return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
+
+ def validate_quantity(child_item, d):
+ if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
+ frappe.throw(_("Cannot set quantity less than delivered quantity"))
+
+ if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
+ frappe.throw(_("Cannot set quantity less than received quantity"))
+
data = json.loads(trans_items)
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
@@ -1201,20 +1221,29 @@
new_child_flag = False
if not d.get("docname"):
new_child_flag = True
- if parent_doctype == "Sales Order":
- child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
- if parent_doctype == "Purchase Order":
- child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
+ check_permissions(parent, 'create')
+ child_item = get_new_child_item(d)
else:
+ check_permissions(parent, 'write')
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
- if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")):
+
+ prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
+ prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
+ prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
+
+ if parent_doctype == 'Sales Order':
+ prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
+ elif parent_doctype == 'Purchase Order':
+ prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date")
+
+ rate_unchanged = prev_rate == new_rate
+ qty_unchanged = prev_qty == new_qty
+ conversion_factor_unchanged = prev_con_fac == new_con_fac
+ date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
+ if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged:
continue
- if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
- frappe.throw(_("Cannot set quantity less than delivered quantity"))
-
- if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
- frappe.throw(_("Cannot set quantity less than received quantity"))
+ validate_quantity(child_item, d)
child_item.qty = flt(d.get("qty"))
precision = child_item.precision("rate") or 2
@@ -1225,6 +1254,18 @@
else:
child_item.rate = flt(d.get("rate"))
+ if d.get("conversion_factor"):
+ if child_item.stock_uom == child_item.uom:
+ child_item.conversion_factor = 1
+ else:
+ child_item.conversion_factor = flt(d.get('conversion_factor'))
+
+ if d.get("delivery_date") and parent_doctype == 'Sales Order':
+ child_item.delivery_date = d.get('delivery_date')
+
+ if d.get("schedule_date") and parent_doctype == 'Purchase Order':
+ child_item.schedule_date = d.get('schedule_date')
+
if flt(child_item.price_list_rate):
if flt(child_item.rate) > flt(child_item.price_list_rate):
# if rate is greater than price_list_rate, set margin
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 2cd79b5..bcab0d8 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -487,7 +487,14 @@
fieldtype: 'Date',
fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date",
in_list_view: 1,
- label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date")
+ label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"),
+ reqd: 1
+ })
+ fields.splice(3, 0, {
+ fieldtype: 'Float',
+ fieldname: "conversion_factor",
+ in_list_view: 1,
+ label: __("Conversion Factor")
})
}
@@ -536,6 +543,7 @@
"item_code": d.item_code,
"delivery_date": d.delivery_date,
"schedule_date": d.schedule_date,
+ "conversion_factor": d.conversion_factor,
"qty": d.qty,
"rate": d.rate,
});
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index b8b0d40..74e742f 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -400,6 +400,23 @@
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
+ def test_update_child_qty_rate_perm(self):
+ so = make_sales_order(item_code= "_Test Item", qty=4)
+
+ user = 'test@example.com'
+ test_user = frappe.get_doc('User', user)
+ test_user.add_roles("Accounts User")
+ frappe.set_user(user)
+
+ # update qty
+ trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
+ self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
+
+ # add new item
+ trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
+ self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
+ frappe.set_user("Administrator")
+
def test_warehouse_user(self):
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index e593499..eff17f8 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -769,22 +769,23 @@
"label": "Against Blanket Order"
},
{
- "fieldname": "bom_no",
- "fieldtype": "Link",
- "label": "BOM No",
- "no_copy": 1,
- "options": "BOM",
- "print_hide": 1
- },
- {
- "fieldname": "manufacturing_section_section",
- "fieldtype": "Section Break",
- "label": "Manufacturing Section"
- }
+ "fieldname": "bom_no",
+ "fieldtype": "Link",
+ "label": "BOM No",
+ "no_copy": 1,
+ "options": "BOM",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "manufacturing_section_section",
+ "fieldtype": "Section Break",
+ "label": "Manufacturing Section"
+ }
],
"idx": 1,
"istable": 1,
- "modified": "2020-05-15 18:13:43.006493",
+ "links": [],
+ "modified": "2020-05-29 20:54:32.309460",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",