mirror of
https://github.com/frappe/erpnext.git
synced 2026-10-29 22:57:42 +00:00
fix(assets): preserve asset purchase row links
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user