Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 1 | # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors |
| 2 | # License: GNU General Public License v3. See license.txt |
| 3 | |
Chillar Anand | 915b343 | 2021-09-02 16:44:59 +0530 | [diff] [blame] | 4 | |
mbauskar | c482aed | 2017-05-02 12:53:12 +0530 | [diff] [blame] | 5 | import json |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 6 | |
Chillar Anand | 915b343 | 2021-09-02 16:44:59 +0530 | [diff] [blame] | 7 | import frappe |
| 8 | from frappe import _ |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 9 | from frappe.utils import cint, cstr, flt, getdate |
Chillar Anand | 915b343 | 2021-09-02 16:44:59 +0530 | [diff] [blame] | 10 | |
| 11 | from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life |
| 12 | |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 13 | |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 14 | def update_last_purchase_rate(doc, is_submit) -> None: |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 15 | """updates last_purchase_rate in item table for each item""" |
Ankush Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 16 | |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 17 | this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date")) |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 18 | |
| 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 Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 25 | 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 Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 29 | 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): |
Saqib | 290253f | 2019-11-22 12:12:29 +0530 | [diff] [blame] | 33 | last_purchase_rate = flt(d.base_net_rate) / flt(d.conversion_factor) |
Deepesh Garg | 8b0302b | 2019-08-05 10:14:19 +0530 | [diff] [blame] | 34 | # Check if item code is present |
| 35 | # Conversion factor should not be mandatory for non itemized items |
| 36 | elif d.item_code: |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 37 | frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx)) |
| 38 | |
| 39 | # update last purchsae rate |
Ankush Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 40 | frappe.db.set_value("Item", d.item_code, "last_purchase_rate", flt(last_purchase_rate)) |
Mohammad Hasnain Mohsin Rajan | 0098120 | 2021-01-14 19:23:18 +0530 | [diff] [blame] | 41 | |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 42 | |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 43 | def validate_for_items(doc) -> None: |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 44 | items = [] |
| 45 | for d in doc.get("items"): |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 46 | 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 Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 49 | validate_end_of_life(d.item_code, item.end_of_life, item.disabled) |
| 50 | |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 51 | items.append(cstr(d.item_code)) |
| 52 | |
Ankush Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 53 | 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 Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 58 | frappe.throw(_("Same item cannot be entered multiple times.")) |
| 59 | |
Ankush Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 60 | |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 61 | def 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 Narang | 3effaf2 | 2024-03-27 11:37:26 +0530 | [diff] [blame] | 84 | def validate_item_and_get_basic_data(row) -> dict: |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 85 | 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 | |
| 97 | def validate_stock_item_warehouse(row, item) -> None: |
Akhil Narang | 3effaf2 | 2024-03-27 11:37:26 +0530 | [diff] [blame] | 98 | if item.is_stock_item == 1 and row.qty and not row.warehouse and not row.get("delivered_by_supplier"): |
marination | 982a246 | 2022-03-31 11:47:20 +0530 | [diff] [blame] | 99 | 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 | |
| 106 | def check_on_hold_or_closed_status(doctype, docname) -> None: |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 107 | status = frappe.db.get_value(doctype, docname, "status") |
| 108 | |
Mangesh-Khairnar | a1afd78 | 2019-03-11 17:15:05 +0530 | [diff] [blame] | 109 | if status in ("Closed", "On Hold"): |
Akhil Narang | 3effaf2 | 2024-03-27 11:37:26 +0530 | [diff] [blame] | 110 | frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError) |
Ankush Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 111 | |
Rushabh Mehta | cc8b2b2 | 2017-03-31 12:44:29 +0530 | [diff] [blame] | 112 | |
mbauskar | c482aed | 2017-05-02 12:53:12 +0530 | [diff] [blame] | 113 | @frappe.whitelist() |
| 114 | def get_linked_material_requests(items): |
| 115 | items = json.loads(items) |
| 116 | mr_list = [] |
| 117 | for item in items: |
Ankush Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 118 | material_request = frappe.db.sql( |
| 119 | """SELECT distinct mr.name AS mr_name, |
Deepesh Garg | 8b0302b | 2019-08-05 10:14:19 +0530 | [diff] [blame] | 120 | (mr_item.qty - mr_item.ordered_qty) AS qty, |
mbauskar | c482aed | 2017-05-02 12:53:12 +0530 | [diff] [blame] | 121 | mr_item.item_code AS item_code, |
Deepesh Garg | 8b0302b | 2019-08-05 10:14:19 +0530 | [diff] [blame] | 122 | mr_item.name AS mr_item |
mbauskar | c482aed | 2017-05-02 12:53:12 +0530 | [diff] [blame] | 123 | FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item |
| 124 | WHERE mr.name = mr_item.parent |
Deepesh Garg | 8b0302b | 2019-08-05 10:14:19 +0530 | [diff] [blame] | 125 | AND mr_item.item_code = %(item)s |
mbauskar | c482aed | 2017-05-02 12:53:12 +0530 | [diff] [blame] | 126 | AND mr.material_request_type = 'Purchase' |
| 127 | AND mr.per_ordered < 99.99 |
| 128 | AND mr.docstatus = 1 |
| 129 | AND mr.status != 'Stopped' |
Ankush Menat | 494bd9e | 2022-03-28 18:52:46 +0530 | [diff] [blame] | 130 | ORDER BY mr_item.item_code ASC""", |
| 131 | {"item": item}, |
| 132 | as_dict=1, |
| 133 | ) |
mbauskar | c482aed | 2017-05-02 12:53:12 +0530 | [diff] [blame] | 134 | if material_request: |
| 135 | mr_list.append(material_request) |
Deepesh Garg | 8b0302b | 2019-08-05 10:14:19 +0530 | [diff] [blame] | 136 | |
mbauskar | c482aed | 2017-05-02 12:53:12 +0530 | [diff] [blame] | 137 | return mr_list |