blob: a120a49902c3972783bdb86792c1dda8dc6eff84 [file] [log] [blame]
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +05301# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2# License: GNU General Public License v3. See license.txt
3
Chillar Anand915b3432021-09-02 16:44:59 +05304
mbauskarc482aed2017-05-02 12:53:12 +05305import json
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +05306
Chillar Anand915b3432021-09-02 16:44:59 +05307import frappe
8from frappe import _
marination982a2462022-03-31 11:47:20 +05309from frappe.utils import cint, cstr, flt, getdate
Chillar Anand915b3432021-09-02 16:44:59 +053010
11from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life
12
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053013
marination982a2462022-03-31 11:47:20 +053014def update_last_purchase_rate(doc, is_submit) -> None:
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053015 """updates last_purchase_rate in item table for each item"""
Ankush Menat494bd9e2022-03-28 18:52:46 +053016
marination982a2462022-03-31 11:47:20 +053017 this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053018
19 for d in doc.get("items"):
20 # get last purchase details
21 last_purchase_details = get_last_purchase_details(d.item_code, doc.name)
22
23 # compare last purchase date and this transaction's date
24 last_purchase_rate = None
Ankush Menat494bd9e2022-03-28 18:52:46 +053025 if last_purchase_details and (
26 doc.get("docstatus") == 2 or last_purchase_details.purchase_date > this_purchase_date
27 ):
28 last_purchase_rate = last_purchase_details["base_net_rate"]
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053029 elif is_submit == 1:
30 # even if this transaction is the latest one, it should be submitted
31 # for it to be considered for latest purchase rate
32 if flt(d.conversion_factor):
Saqib290253f2019-11-22 12:12:29 +053033 last_purchase_rate = flt(d.base_net_rate) / flt(d.conversion_factor)
Deepesh Garg8b0302b2019-08-05 10:14:19 +053034 # Check if item code is present
35 # Conversion factor should not be mandatory for non itemized items
36 elif d.item_code:
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053037 frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
38
39 # update last purchsae rate
Ankush Menat494bd9e2022-03-28 18:52:46 +053040 frappe.db.set_value("Item", d.item_code, "last_purchase_rate", flt(last_purchase_rate))
Mohammad Hasnain Mohsin Rajan00981202021-01-14 19:23:18 +053041
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053042
marination982a2462022-03-31 11:47:20 +053043def validate_for_items(doc) -> None:
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053044 items = []
45 for d in doc.get("items"):
marination982a2462022-03-31 11:47:20 +053046 set_stock_levels(row=d) # update with latest quantities
47 item = validate_item_and_get_basic_data(row=d)
48 validate_stock_item_warehouse(row=d, item=item)
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053049 validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
50
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053051 items.append(cstr(d.item_code))
52
Ankush Menat494bd9e2022-03-28 18:52:46 +053053 if (
54 items
55 and len(items) != len(set(items))
56 and not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0)
57 ):
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053058 frappe.throw(_("Same item cannot be entered multiple times."))
59
Ankush Menat494bd9e2022-03-28 18:52:46 +053060
marination982a2462022-03-31 11:47:20 +053061def set_stock_levels(row) -> None:
62 projected_qty = frappe.db.get_value(
63 "Bin",
64 {
65 "item_code": row.item_code,
66 "warehouse": row.warehouse,
67 },
68 "projected_qty",
69 )
70
71 qty_data = {
72 "projected_qty": flt(projected_qty),
73 "ordered_qty": 0,
74 "received_qty": 0,
75 }
76 if row.doctype in ("Purchase Receipt Item", "Purchase Invoice Item"):
77 qty_data.pop("received_qty")
78
79 for field in qty_data:
80 if row.meta.get_field(field):
81 row.set(field, qty_data[field])
82
83
Akhil Narang3effaf22024-03-27 11:37:26 +053084def validate_item_and_get_basic_data(row) -> dict:
marination982a2462022-03-31 11:47:20 +053085 item = frappe.db.get_values(
86 "Item",
87 filters={"name": row.item_code},
88 fieldname=["is_stock_item", "is_sub_contracted_item", "end_of_life", "disabled"],
89 as_dict=1,
90 )
91 if not item:
92 frappe.throw(_("Row #{0}: Item {1} does not exist").format(row.idx, frappe.bold(row.item_code)))
93
94 return item[0]
95
96
97def validate_stock_item_warehouse(row, item) -> None:
Akhil Narang3effaf22024-03-27 11:37:26 +053098 if item.is_stock_item == 1 and row.qty and not row.warehouse and not row.get("delivered_by_supplier"):
marination982a2462022-03-31 11:47:20 +053099 frappe.throw(
100 _("Row #{1}: Warehouse is mandatory for stock Item {0}").format(
101 frappe.bold(row.item_code), row.idx
102 )
103 )
104
105
106def check_on_hold_or_closed_status(doctype, docname) -> None:
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530107 status = frappe.db.get_value(doctype, docname, "status")
108
Mangesh-Khairnara1afd782019-03-11 17:15:05 +0530109 if status in ("Closed", "On Hold"):
Akhil Narang3effaf22024-03-27 11:37:26 +0530110 frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530111
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530112
mbauskarc482aed2017-05-02 12:53:12 +0530113@frappe.whitelist()
114def get_linked_material_requests(items):
115 items = json.loads(items)
116 mr_list = []
117 for item in items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530118 material_request = frappe.db.sql(
119 """SELECT distinct mr.name AS mr_name,
Deepesh Garg8b0302b2019-08-05 10:14:19 +0530120 (mr_item.qty - mr_item.ordered_qty) AS qty,
mbauskarc482aed2017-05-02 12:53:12 +0530121 mr_item.item_code AS item_code,
Deepesh Garg8b0302b2019-08-05 10:14:19 +0530122 mr_item.name AS mr_item
mbauskarc482aed2017-05-02 12:53:12 +0530123 FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
124 WHERE mr.name = mr_item.parent
Deepesh Garg8b0302b2019-08-05 10:14:19 +0530125 AND mr_item.item_code = %(item)s
mbauskarc482aed2017-05-02 12:53:12 +0530126 AND mr.material_request_type = 'Purchase'
127 AND mr.per_ordered < 99.99
128 AND mr.docstatus = 1
129 AND mr.status != 'Stopped'
Ankush Menat494bd9e2022-03-28 18:52:46 +0530130 ORDER BY mr_item.item_code ASC""",
131 {"item": item},
132 as_dict=1,
133 )
mbauskarc482aed2017-05-02 12:53:12 +0530134 if material_request:
135 mr_list.append(material_request)
Deepesh Garg8b0302b2019-08-05 10:14:19 +0530136
mbauskarc482aed2017-05-02 12:53:12 +0530137 return mr_list