fix(assets): preserve asset purchase row links

This commit is contained in:
pandiyan
2026-10-26 12:57:58 +05:30
parent 4ef6cba70b
commit 69f87d832c
3 changed files with 77 additions and 25 deletions
+1
View File
@@ -701,6 +701,7 @@ frappe.ui.form.on("Asset", {
purchase_doc_name: frm.doc.purchase_receipt || frm.doc.purchase_invoice,
item_code: frm.doc.item_code,
doctype: doctype,
asset: frm.is_new() ? null : frm.doc.name,
},
callback: (r) => {
if (r.message) {
+62 -25
View File
@@ -39,6 +39,7 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
update_draft_asset_depr_schedules,
)
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.controllers.buying_controller import get_linked_asset_qty
class Asset(AccountsController):
@@ -229,27 +230,41 @@ class Asset(AccountsController):
if not purchase_doc:
return
linked_item = self.get_linked_item(purchase_doc_type, purchase_doc)
if linked_item:
if purchase_doc_type == "Purchase Receipt":
self.purchase_receipt_item = linked_item
else:
self.purchase_invoice_item = linked_item
self.set(
frappe.scrub(purchase_doc_type) + "_item", self.get_linked_item(purchase_doc_type, purchase_doc)
)
def get_linked_item(self, purchase_doc_type, purchase_doc):
purchase_doc = frappe.get_doc(purchase_doc_type, purchase_doc)
fieldname = frappe.scrub(purchase_doc_type) + "_item"
matching_items = purchase_doc.get("items", {"item_code": self.item_code})
for item in purchase_doc.items:
if self.asset_quantity > 1:
if item.base_net_amount == self.gross_purchase_amount and item.qty == self.asset_quantity:
return item.name
elif item.qty == self.asset_quantity:
return item.name
else:
if item.base_net_rate == self.gross_purchase_amount and item.qty == self.asset_quantity:
for item in matching_items:
if item.name == self.get(fieldname) and (not self.is_new() or self.flags.is_split_asset):
return item.name
linked_asset_qty = get_linked_asset_qty(fieldname, [item.name for item in matching_items], self.name)
matching_items = [
item
for item in matching_items
if flt(item.qty) - flt(linked_asset_qty.get(item.name)) >= self.asset_quantity
]
for item in matching_items:
if item.name == self.get(fieldname):
return item.name
precision = self.precision("gross_purchase_amount")
for item in matching_items:
for rate in (
flt(item.base_net_rate),
flt(item.valuation_rate) * flt(item.conversion_factor or 1),
):
if flt(rate * self.asset_quantity, precision) == flt(self.gross_purchase_amount, precision):
return item.name
if len(matching_items) == 1:
return matching_items[0].name
def validate_asset_and_reference(self):
if self.purchase_invoice or self.purchase_receipt:
reference_doc = "Purchase Invoice" if self.purchase_invoice else "Purchase Receipt"
@@ -1262,7 +1277,7 @@ def has_active_capitalization(asset):
@frappe.whitelist()
def get_values_from_purchase_doc(purchase_doc_name, item_code, doctype):
def get_values_from_purchase_doc(purchase_doc_name, item_code, doctype, asset=None):
# `doctype` is caller-supplied and reaches frappe.get_doc(), so without this list any document
# with an `items` table could be read for its valuation rates
if doctype not in ("Purchase Receipt", "Purchase Invoice"):
@@ -1273,22 +1288,43 @@ def get_values_from_purchase_doc(purchase_doc_name, item_code, doctype):
purchase_doc = frappe.get_doc(doctype, purchase_doc_name)
purchase_doc.check_permission("read")
matching_items = [item for item in purchase_doc.items if item.item_code == item_code]
matching_items = purchase_doc.get("items", {"item_code": item_code})
if not matching_items:
frappe.throw(_(f"Selected {doctype} does not contain the Item Code {item_code}"))
frappe.throw(_("Selected {0} does not contain the Item Code {1}").format(doctype, item_code))
first_item = matching_items[0]
if asset:
asset = frappe.get_doc("Asset", asset)
asset.check_permission("write")
fieldname = frappe.scrub(doctype)
linked_asset_qty = get_linked_asset_qty(
fieldname + "_item", [item.name for item in matching_items], asset.name if asset else None
)
if asset and asset.get(fieldname) == purchase_doc_name:
matching_items.sort(key=lambda item: item.name != asset.get(fieldname + "_item"))
for item in matching_items:
asset_quantity = flt(item.qty) - flt(linked_asset_qty.get(item.name))
if asset_quantity > 0:
break
else:
frappe.throw(
_("All quantities of Item {0} in {1} are already linked to Assets.").format(
frappe.bold(item_code), get_link_to_form(doctype, purchase_doc_name)
),
title=_("No Available Purchase Row"),
)
return {
"company": purchase_doc.company,
"purchase_date": purchase_doc.get("posting_date"),
"gross_purchase_amount": flt(first_item.valuation_rate) * flt(first_item.qty),
"asset_quantity": first_item.qty,
"cost_center": first_item.cost_center or purchase_doc.get("cost_center"),
"asset_location": first_item.get("asset_location"),
"purchase_receipt_item": first_item.name if doctype == "Purchase Receipt" else None,
"purchase_invoice_item": first_item.name if doctype == "Purchase Invoice" else None,
"gross_purchase_amount": flt(item.valuation_rate) * flt(item.conversion_factor or 1) * asset_quantity,
"asset_quantity": asset_quantity,
"cost_center": item.cost_center or purchase_doc.get("cost_center"),
"asset_location": item.get("asset_location"),
"purchase_receipt_item": item.name if doctype == "Purchase Receipt" else None,
"purchase_invoice_item": item.name if doctype == "Purchase Invoice" else None,
}
@@ -1378,6 +1414,7 @@ def update_existing_asset(asset, remaining_qty, new_asset):
def create_new_asset_after_split(asset, split_qty):
new_asset = frappe.copy_doc(asset)
new_asset.flags.is_split_asset = True
new_gross_purchase_amount = flt(
(asset.gross_purchase_amount * split_qty) / asset.asset_quantity,
asset.precision("gross_purchase_amount"),
+14
View File
@@ -5,6 +5,7 @@
import frappe
from frappe import ValidationError, _, msgprint
from frappe.contacts.doctype.address.address import render_address
from frappe.query_builder.functions import Sum
from frappe.utils import cint, flt, getdate
from frappe.utils.data import nowtime
@@ -1101,3 +1102,16 @@ def update_regional_item_valuation_rate(doc):
def get_stock_qty(row, qty) -> float:
"""Qty of a row in the stock UOM, rounded as the stock ledger posts it."""
return flt(flt(qty) * flt(row.conversion_factor), row.precision("stock_qty"))
def get_linked_asset_qty(detail_field, details, exclude_asset=None):
asset = frappe.qb.DocType("Asset")
query = (
frappe.qb.from_(asset)
.select(asset[detail_field], Sum(asset.asset_quantity))
.where((asset[detail_field].isin(details)) & (asset.docstatus < 2))
.groupby(asset[detail_field])
)
if exclude_asset:
query = query.where(asset.name != exclude_asset)
return dict(query.run())