選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 

151 行
4.3 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: GNU General Public License v3. See license.txt
  3. import json
  4. from typing import Dict
  5. import frappe
  6. from frappe import _
  7. from frappe.utils import cint, cstr, flt, getdate
  8. from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life
  9. def update_last_purchase_rate(doc, is_submit) -> None:
  10. """updates last_purchase_rate in item table for each item"""
  11. this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
  12. for d in doc.get("items"):
  13. # get last purchase details
  14. last_purchase_details = get_last_purchase_details(d.item_code, doc.name)
  15. # compare last purchase date and this transaction's date
  16. last_purchase_rate = None
  17. if last_purchase_details and (
  18. doc.get("docstatus") == 2 or last_purchase_details.purchase_date > this_purchase_date
  19. ):
  20. last_purchase_rate = last_purchase_details["base_net_rate"]
  21. elif is_submit == 1:
  22. # even if this transaction is the latest one, it should be submitted
  23. # for it to be considered for latest purchase rate
  24. if flt(d.conversion_factor):
  25. last_purchase_rate = flt(d.base_net_rate) / flt(d.conversion_factor)
  26. # Check if item code is present
  27. # Conversion factor should not be mandatory for non itemized items
  28. elif d.item_code:
  29. frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
  30. # update last purchsae rate
  31. frappe.db.set_value("Item", d.item_code, "last_purchase_rate", flt(last_purchase_rate))
  32. def validate_for_items(doc) -> None:
  33. items = []
  34. for d in doc.get("items"):
  35. if not d.qty:
  36. if doc.doctype == "Purchase Receipt" and d.rejected_qty:
  37. continue
  38. frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
  39. set_stock_levels(row=d) # update with latest quantities
  40. item = validate_item_and_get_basic_data(row=d)
  41. validate_stock_item_warehouse(row=d, item=item)
  42. validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
  43. items.append(cstr(d.item_code))
  44. if (
  45. items
  46. and len(items) != len(set(items))
  47. and not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0)
  48. ):
  49. frappe.throw(_("Same item cannot be entered multiple times."))
  50. def set_stock_levels(row) -> None:
  51. projected_qty = frappe.db.get_value(
  52. "Bin",
  53. {
  54. "item_code": row.item_code,
  55. "warehouse": row.warehouse,
  56. },
  57. "projected_qty",
  58. )
  59. qty_data = {
  60. "projected_qty": flt(projected_qty),
  61. "ordered_qty": 0,
  62. "received_qty": 0,
  63. }
  64. if row.doctype in ("Purchase Receipt Item", "Purchase Invoice Item"):
  65. qty_data.pop("received_qty")
  66. for field in qty_data:
  67. if row.meta.get_field(field):
  68. row.set(field, qty_data[field])
  69. def validate_item_and_get_basic_data(row) -> Dict:
  70. item = frappe.db.get_values(
  71. "Item",
  72. filters={"name": row.item_code},
  73. fieldname=["is_stock_item", "is_sub_contracted_item", "end_of_life", "disabled"],
  74. as_dict=1,
  75. )
  76. if not item:
  77. frappe.throw(_("Row #{0}: Item {1} does not exist").format(row.idx, frappe.bold(row.item_code)))
  78. return item[0]
  79. def validate_stock_item_warehouse(row, item) -> None:
  80. if (
  81. item.is_stock_item == 1
  82. and row.qty
  83. and not row.warehouse
  84. and not row.get("delivered_by_supplier")
  85. ):
  86. frappe.throw(
  87. _("Row #{1}: Warehouse is mandatory for stock Item {0}").format(
  88. frappe.bold(row.item_code), row.idx
  89. )
  90. )
  91. def check_on_hold_or_closed_status(doctype, docname) -> None:
  92. status = frappe.db.get_value(doctype, docname, "status")
  93. if status in ("Closed", "On Hold"):
  94. frappe.throw(
  95. _("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError
  96. )
  97. @frappe.whitelist()
  98. def get_linked_material_requests(items):
  99. items = json.loads(items)
  100. mr_list = []
  101. for item in items:
  102. material_request = frappe.db.sql(
  103. """SELECT distinct mr.name AS mr_name,
  104. (mr_item.qty - mr_item.ordered_qty) AS qty,
  105. mr_item.item_code AS item_code,
  106. mr_item.name AS mr_item
  107. FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
  108. WHERE mr.name = mr_item.parent
  109. AND mr_item.item_code = %(item)s
  110. AND mr.material_request_type = 'Purchase'
  111. AND mr.per_ordered < 99.99
  112. AND mr.docstatus = 1
  113. AND mr.status != 'Stopped'
  114. ORDER BY mr_item.item_code ASC""",
  115. {"item": item},
  116. as_dict=1,
  117. )
  118. if material_request:
  119. mr_list.append(material_request)
  120. return mr_list