feat: allow adding & deleting of items in submitted SO & PO (#19911)
* feat: allow adding of items after quotation submission * feat: allow deletion of items from submitted SO & PO * fix: case when items are added and deleted at once * fix: add test cases * For deletion of items while Updating Items after submitting PO & SO
This commit is contained in:
parent
5557b16260
commit
0ad7449ae3
@ -118,6 +118,73 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
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
|
||||
po.save()
|
||||
po.submit()
|
||||
pr = make_pr_against_po(po.name, 2)
|
||||
|
||||
po.load_from_db()
|
||||
first_item_of_po = po.get("items")[0]
|
||||
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
'item_code': first_item_of_po.item_code,
|
||||
'rate': first_item_of_po.rate,
|
||||
'qty': first_item_of_po.qty,
|
||||
'docname': first_item_of_po.name
|
||||
},
|
||||
{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}
|
||||
])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
|
||||
po.reload()
|
||||
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
|
||||
po.save()
|
||||
po.submit()
|
||||
pr = make_pr_against_po(po.name, 2)
|
||||
|
||||
po.reload()
|
||||
first_item_of_po = po.get("items")[0]
|
||||
# add an item
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
'item_code': first_item_of_po.item_code,
|
||||
'rate': first_item_of_po.rate,
|
||||
'qty': first_item_of_po.qty,
|
||||
'docname': first_item_of_po.name
|
||||
},
|
||||
{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
|
||||
po.reload()
|
||||
# check if can remove received item
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name)
|
||||
|
||||
first_item_of_po = po.get("items")[0]
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
'item_code': first_item_of_po.item_code,
|
||||
'rate': first_item_of_po.rate,
|
||||
'qty': first_item_of_po.qty,
|
||||
'docname': first_item_of_po.name
|
||||
}
|
||||
])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
|
||||
po.reload()
|
||||
self.assertEquals(len(po.get('items')), 1)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
def test_update_qty(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
|
@ -1155,6 +1155,25 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
return child_item
|
||||
|
||||
def check_and_delete_children(parent, data):
|
||||
deleted_children = []
|
||||
updated_item_names = [d.get("docname") for d in data]
|
||||
for item in parent.items:
|
||||
if item.name not in updated_item_names:
|
||||
deleted_children.append(item)
|
||||
|
||||
for d in deleted_children:
|
||||
if parent.doctype == "Sales Order" and flt(d.delivered_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(d.idx, d.item_code))
|
||||
|
||||
if parent.doctype == "Purchase Order" and flt(d.received_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code))
|
||||
|
||||
if flt(d.billed_amt):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code))
|
||||
|
||||
d.cancel()
|
||||
d.delete()
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||
@ -1163,6 +1182,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
||||
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
|
||||
check_and_delete_children(parent, data)
|
||||
|
||||
for d in data:
|
||||
new_child_flag = False
|
||||
if not d.get("docname"):
|
||||
|
@ -458,7 +458,8 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
fieldname:"item_code",
|
||||
options: 'Item',
|
||||
in_list_view: 1,
|
||||
read_only: 1,
|
||||
read_only: 0,
|
||||
disabled: 0,
|
||||
label: __('Item Code')
|
||||
}, {
|
||||
fieldtype:'Float',
|
||||
|
@ -321,7 +321,12 @@ class TestSalesOrder(unittest.TestCase):
|
||||
create_dn_against_so(so.name, 4)
|
||||
make_sales_invoice(so.name)
|
||||
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 7}])
|
||||
first_item_of_so = so.get("items")[0]
|
||||
trans_item = json.dumps([
|
||||
{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
|
||||
'qty' : first_item_of_so.qty, 'docname': first_item_of_so.name},
|
||||
{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 7}
|
||||
])
|
||||
update_child_qty_rate('Sales Order', trans_item, so.name)
|
||||
|
||||
so.reload()
|
||||
@ -331,6 +336,48 @@ class TestSalesOrder(unittest.TestCase):
|
||||
self.assertEqual(so.get("items")[-1].amount, 1400)
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
|
||||
def test_remove_item_in_update_child_qty_rate(self):
|
||||
so = make_sales_order(**{
|
||||
"item_list": [{
|
||||
"item_code": '_Test Item',
|
||||
"qty": 5,
|
||||
"rate":1000
|
||||
}]
|
||||
})
|
||||
create_dn_against_so(so.name, 2)
|
||||
make_sales_invoice(so.name)
|
||||
|
||||
# add an item so as to try removing items
|
||||
trans_item = json.dumps([
|
||||
{"item_code": '_Test Item', "qty": 5, "rate":1000, "docname": so.get("items")[0].name},
|
||||
{"item_code": '_Test Item 2', "qty": 2, "rate":500}
|
||||
])
|
||||
update_child_qty_rate('Sales Order', trans_item, so.name)
|
||||
so.reload()
|
||||
self.assertEqual(len(so.get("items")), 2)
|
||||
|
||||
# check if delivered items can be removed
|
||||
trans_item = json.dumps([{
|
||||
"item_code": '_Test Item 2',
|
||||
"qty": 2,
|
||||
"rate":500,
|
||||
"docname": so.get("items")[1].name
|
||||
}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Sales Order', trans_item, so.name)
|
||||
|
||||
#remove last added item
|
||||
trans_item = json.dumps([{
|
||||
"item_code": '_Test Item',
|
||||
"qty": 5,
|
||||
"rate":1000,
|
||||
"docname": so.get("items")[0].name
|
||||
}])
|
||||
update_child_qty_rate('Sales Order', trans_item, so.name)
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(len(so.get("items")), 1)
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
|
||||
|
||||
def test_update_child_qty_rate(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
|
Loading…
x
Reference in New Issue
Block a user