2017-03-31 07:14:29 +00:00
|
|
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
|
|
# License: GNU General Public License v3. See license.txt
|
|
|
|
|
2021-09-02 11:14:59 +00:00
|
|
|
|
2017-05-02 07:23:12 +00:00
|
|
|
import json
|
2022-03-31 06:17:20 +00:00
|
|
|
from typing import Dict
|
2021-09-02 11:14:59 +00:00
|
|
|
|
2017-03-31 07:14:29 +00:00
|
|
|
import frappe
|
|
|
|
from frappe import _
|
2022-03-31 06:17:20 +00:00
|
|
|
from frappe.utils import cint, cstr, flt, getdate
|
2021-09-02 11:14:59 +00:00
|
|
|
|
2017-03-31 07:14:29 +00:00
|
|
|
from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life
|
|
|
|
|
|
|
|
|
2022-03-31 06:17:20 +00:00
|
|
|
def update_last_purchase_rate(doc, is_submit) -> None:
|
2017-03-31 07:14:29 +00:00
|
|
|
"""updates last_purchase_rate in item table for each item"""
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2022-03-31 06:17:20 +00:00
|
|
|
this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
|
2017-03-31 07:14:29 +00:00
|
|
|
|
|
|
|
for d in doc.get("items"):
|
|
|
|
# get last purchase details
|
|
|
|
last_purchase_details = get_last_purchase_details(d.item_code, doc.name)
|
|
|
|
|
|
|
|
# compare last purchase date and this transaction's date
|
|
|
|
last_purchase_rate = None
|
|
|
|
if last_purchase_details and (
|
2020-02-27 07:51:14 +00:00
|
|
|
doc.get("docstatus") == 2 or last_purchase_details.purchase_date > this_purchase_date
|
|
|
|
):
|
2019-11-22 06:42:29 +00:00
|
|
|
last_purchase_rate = last_purchase_details["base_net_rate"]
|
2017-03-31 07:14:29 +00:00
|
|
|
elif is_submit == 1:
|
|
|
|
# even if this transaction is the latest one, it should be submitted
|
|
|
|
# for it to be considered for latest purchase rate
|
|
|
|
if flt(d.conversion_factor):
|
2019-11-22 06:42:29 +00:00
|
|
|
last_purchase_rate = flt(d.base_net_rate) / flt(d.conversion_factor)
|
2019-08-05 04:44:19 +00:00
|
|
|
# Check if item code is present
|
|
|
|
# Conversion factor should not be mandatory for non itemized items
|
|
|
|
elif d.item_code:
|
2017-03-31 07:14:29 +00:00
|
|
|
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
|
|
|
|
|
|
|
|
# update last purchsae rate
|
2021-01-14 13:53:18 +00:00
|
|
|
frappe.db.set_value("Item", d.item_code, "last_purchase_rate", flt(last_purchase_rate))
|
|
|
|
|
2017-03-31 07:14:29 +00:00
|
|
|
|
2022-03-31 06:17:20 +00:00
|
|
|
def validate_for_items(doc) -> None:
|
2017-03-31 07:14:29 +00:00
|
|
|
items = []
|
|
|
|
for d in doc.get("items"):
|
|
|
|
if not d.qty:
|
|
|
|
if doc.doctype == "Purchase Receipt" and d.rejected_qty:
|
|
|
|
continue
|
|
|
|
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
|
|
|
|
|
2022-03-31 06:17:20 +00:00
|
|
|
set_stock_levels(row=d) # update with latest quantities
|
|
|
|
item = validate_item_and_get_basic_data(row=d)
|
|
|
|
validate_stock_item_warehouse(row=d, item=item)
|
2017-03-31 07:14:29 +00:00
|
|
|
validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
|
|
|
|
|
|
|
|
items.append(cstr(d.item_code))
|
|
|
|
|
|
|
|
if (
|
|
|
|
items
|
|
|
|
and len(items) != len(set(items))
|
|
|
|
and not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0)
|
|
|
|
):
|
|
|
|
frappe.throw(_("Same item cannot be entered multiple times."))
|
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2022-03-31 06:17:20 +00:00
|
|
|
def set_stock_levels(row) -> None:
|
|
|
|
projected_qty = frappe.db.get_value(
|
|
|
|
"Bin",
|
|
|
|
{
|
|
|
|
"item_code": row.item_code,
|
|
|
|
"warehouse": row.warehouse,
|
|
|
|
},
|
|
|
|
"projected_qty",
|
|
|
|
)
|
|
|
|
|
|
|
|
qty_data = {
|
|
|
|
"projected_qty": flt(projected_qty),
|
|
|
|
"ordered_qty": 0,
|
|
|
|
"received_qty": 0,
|
|
|
|
}
|
|
|
|
if row.doctype in ("Purchase Receipt Item", "Purchase Invoice Item"):
|
|
|
|
qty_data.pop("received_qty")
|
|
|
|
|
|
|
|
for field in qty_data:
|
|
|
|
if row.meta.get_field(field):
|
|
|
|
row.set(field, qty_data[field])
|
|
|
|
|
|
|
|
|
|
|
|
def validate_item_and_get_basic_data(row) -> Dict:
|
|
|
|
item = frappe.db.get_values(
|
|
|
|
"Item",
|
|
|
|
filters={"name": row.item_code},
|
|
|
|
fieldname=["is_stock_item", "is_sub_contracted_item", "end_of_life", "disabled"],
|
|
|
|
as_dict=1,
|
|
|
|
)
|
|
|
|
if not item:
|
|
|
|
frappe.throw(_("Row #{0}: Item {1} does not exist").format(row.idx, frappe.bold(row.item_code)))
|
|
|
|
|
|
|
|
return item[0]
|
|
|
|
|
|
|
|
|
|
|
|
def validate_stock_item_warehouse(row, item) -> None:
|
|
|
|
if (
|
|
|
|
item.is_stock_item == 1
|
|
|
|
and row.qty
|
|
|
|
and not row.warehouse
|
|
|
|
and not row.get("delivered_by_supplier")
|
|
|
|
):
|
|
|
|
frappe.throw(
|
|
|
|
_("Row #{1}: Warehouse is mandatory for stock Item {0}").format(
|
|
|
|
frappe.bold(row.item_code), row.idx
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def check_on_hold_or_closed_status(doctype, docname) -> None:
|
2017-03-31 07:14:29 +00:00
|
|
|
status = frappe.db.get_value(doctype, docname, "status")
|
|
|
|
|
2019-03-11 11:45:05 +00:00
|
|
|
if status in ("Closed", "On Hold"):
|
2017-03-31 07:14:29 +00:00
|
|
|
frappe.throw(
|
|
|
|
_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError
|
|
|
|
)
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2017-03-31 07:14:29 +00:00
|
|
|
|
2017-05-02 07:23:12 +00:00
|
|
|
@frappe.whitelist()
|
|
|
|
def get_linked_material_requests(items):
|
|
|
|
items = json.loads(items)
|
|
|
|
mr_list = []
|
|
|
|
for item in items:
|
2019-08-05 04:44:19 +00:00
|
|
|
material_request = frappe.db.sql(
|
|
|
|
"""SELECT distinct mr.name AS mr_name,
|
|
|
|
(mr_item.qty - mr_item.ordered_qty) AS qty,
|
2017-05-02 07:23:12 +00:00
|
|
|
mr_item.item_code AS item_code,
|
2019-08-05 04:44:19 +00:00
|
|
|
mr_item.name AS mr_item
|
2017-05-02 07:23:12 +00:00
|
|
|
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
|
|
|
WHERE mr.name = mr_item.parent
|
2019-08-05 04:44:19 +00:00
|
|
|
AND mr_item.item_code = %(item)s
|
2017-05-02 07:23:12 +00:00
|
|
|
AND mr.material_request_type = 'Purchase'
|
|
|
|
AND mr.per_ordered < 99.99
|
|
|
|
AND mr.docstatus = 1
|
|
|
|
AND mr.status != 'Stopped'
|
|
|
|
ORDER BY mr_item.item_code ASC""",
|
|
|
|
{"item": item},
|
|
|
|
as_dict=1,
|
|
|
|
)
|
|
|
|
if material_request:
|
|
|
|
mr_list.append(material_request)
|
2019-08-05 04:44:19 +00:00
|
|
|
|
2017-05-02 07:23:12 +00:00
|
|
|
return mr_list
|