Merge pull request #22048 from nextchamp-saqib/update-items-permission-fix
fix: submitted sales order can be updated with no permission
This commit is contained in:
commit
b7a2f8731f
@ -1376,6 +1376,12 @@
|
|||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Purchase Manager",
|
"role": "Purchase Manager",
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"role": "Accounts User"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "status, transaction_date, supplier,grand_total",
|
"search_fields": "status, transaction_date, supplier,grand_total",
|
||||||
|
@ -185,6 +185,23 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEquals(len(po.get('items')), 1)
|
self.assertEquals(len(po.get('items')), 1)
|
||||||
self.assertEqual(po.status, 'To Receive and Bill')
|
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):
|
def test_update_qty(self):
|
||||||
po = create_purchase_order()
|
po = create_purchase_order()
|
||||||
|
|
||||||
|
@ -1137,8 +1137,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname,
|
|||||||
child_item.item_name = item.item_name
|
child_item.item_name = item.item_name
|
||||||
child_item.description = item.description
|
child_item.description = item.description
|
||||||
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
|
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.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)
|
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||||
if not child_item.warehouse:
|
if not child_item.warehouse:
|
||||||
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
||||||
@ -1157,8 +1157,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
|
|||||||
child_item.item_name = item.item_name
|
child_item.item_name = item.item_name
|
||||||
child_item.description = item.description
|
child_item.description = item.description
|
||||||
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
|
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.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_rate = 1 # Initiallize value will update in parent validation
|
||||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||||
return child_item
|
return child_item
|
||||||
@ -1190,6 +1190,26 @@ def check_and_delete_children(parent, data):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
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)
|
data = json.loads(trans_items)
|
||||||
|
|
||||||
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
||||||
@ -1201,20 +1221,29 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
new_child_flag = False
|
new_child_flag = False
|
||||||
if not d.get("docname"):
|
if not d.get("docname"):
|
||||||
new_child_flag = True
|
new_child_flag = True
|
||||||
if parent_doctype == "Sales Order":
|
check_permissions(parent, 'create')
|
||||||
child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
|
child_item = get_new_child_item(d)
|
||||||
if parent_doctype == "Purchase Order":
|
|
||||||
child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
|
|
||||||
else:
|
else:
|
||||||
|
check_permissions(parent, 'write')
|
||||||
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
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
|
continue
|
||||||
|
|
||||||
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
|
validate_quantity(child_item, d)
|
||||||
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"))
|
|
||||||
|
|
||||||
child_item.qty = flt(d.get("qty"))
|
child_item.qty = flt(d.get("qty"))
|
||||||
precision = child_item.precision("rate") or 2
|
precision = child_item.precision("rate") or 2
|
||||||
@ -1225,6 +1254,18 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
else:
|
else:
|
||||||
child_item.rate = flt(d.get("rate"))
|
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.price_list_rate):
|
||||||
if flt(child_item.rate) > 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
|
# if rate is greater than price_list_rate, set margin
|
||||||
|
@ -487,7 +487,14 @@ erpnext.utils.update_child_items = function(opts) {
|
|||||||
fieldtype: 'Date',
|
fieldtype: 'Date',
|
||||||
fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date",
|
fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date",
|
||||||
in_list_view: 1,
|
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 @@ erpnext.utils.update_child_items = function(opts) {
|
|||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"delivery_date": d.delivery_date,
|
"delivery_date": d.delivery_date,
|
||||||
"schedule_date": d.schedule_date,
|
"schedule_date": d.schedule_date,
|
||||||
|
"conversion_factor": d.conversion_factor,
|
||||||
"qty": d.qty,
|
"qty": d.qty,
|
||||||
"rate": d.rate,
|
"rate": d.rate,
|
||||||
});
|
});
|
||||||
|
@ -400,6 +400,23 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}])
|
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)
|
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):
|
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 1 - _TC", "test@example.com")
|
||||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
||||||
|
@ -769,22 +769,23 @@
|
|||||||
"label": "Against Blanket Order"
|
"label": "Against Blanket Order"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "bom_no",
|
"fieldname": "bom_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "BOM No",
|
"label": "BOM No",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "BOM",
|
"options": "BOM",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "manufacturing_section_section",
|
"fieldname": "manufacturing_section_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Manufacturing Section"
|
"label": "Manufacturing Section"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2020-05-15 18:13:43.006493",
|
"links": [],
|
||||||
|
"modified": "2020-05-29 20:54:32.309460",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Order Item",
|
"name": "Sales Order Item",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user