mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-01 11:19:09 +00:00
fix(asset): handle partial asset sales by splitting remaining quantity
This commit is contained in:
@@ -33,6 +33,7 @@ from erpnext.accounts.utils import (
|
|||||||
get_account_currency,
|
get_account_currency,
|
||||||
update_voucher_outstanding,
|
update_voucher_outstanding,
|
||||||
)
|
)
|
||||||
|
from erpnext.assets.doctype.asset.asset import split_asset
|
||||||
from erpnext.assets.doctype.asset.depreciation import (
|
from erpnext.assets.doctype.asset.depreciation import (
|
||||||
depreciate_asset,
|
depreciate_asset,
|
||||||
get_gl_entries_on_asset_disposal,
|
get_gl_entries_on_asset_disposal,
|
||||||
@@ -468,6 +469,8 @@ class SalesInvoice(SellingController):
|
|||||||
self.update_stock_reservation_entries()
|
self.update_stock_reservation_entries()
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
|
self.split_asset_based_on_sale_qty()
|
||||||
|
|
||||||
self.process_asset_depreciation()
|
self.process_asset_depreciation()
|
||||||
|
|
||||||
# this sequence because outstanding may get -ve
|
# this sequence because outstanding may get -ve
|
||||||
@@ -1358,6 +1361,41 @@ class SalesInvoice(SellingController):
|
|||||||
):
|
):
|
||||||
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
|
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
|
||||||
|
|
||||||
|
def split_asset_based_on_sale_qty(self):
|
||||||
|
asset_qty_map = self.get_asset_qty()
|
||||||
|
for asset, qty in asset_qty_map.items():
|
||||||
|
remaining_qty = qty["actual_qty"] - qty["sale_qty"]
|
||||||
|
if remaining_qty > 0:
|
||||||
|
split_asset(asset, remaining_qty)
|
||||||
|
|
||||||
|
def get_asset_qty(self):
|
||||||
|
asset_qty_map = {}
|
||||||
|
|
||||||
|
assets = {row.asset for row in self.items if row.is_fixed_asset and row.asset}
|
||||||
|
if not assets or self.is_return:
|
||||||
|
return asset_qty_map
|
||||||
|
|
||||||
|
asset_actual_qty = dict(
|
||||||
|
frappe.db.get_all(
|
||||||
|
"Asset",
|
||||||
|
{"name": ["in", list(assets)]},
|
||||||
|
["name", "asset_quantity"],
|
||||||
|
as_list=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for row in self.items:
|
||||||
|
if row.is_fixed_asset and row.asset:
|
||||||
|
actual_qty = asset_actual_qty.get(row.asset)
|
||||||
|
asset_qty_map.setdefault(
|
||||||
|
row.asset,
|
||||||
|
{
|
||||||
|
"sale_qty": flt(row.qty),
|
||||||
|
"actual_qty": flt(actual_qty),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return asset_qty_map
|
||||||
|
|
||||||
def process_asset_depreciation(self):
|
def process_asset_depreciation(self):
|
||||||
if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1):
|
if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1):
|
||||||
self.depreciate_asset_on_sale()
|
self.depreciate_asset_on_sale()
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ frappe.ui.form.on("Asset", {
|
|||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
__("Sell Asset"),
|
__("Sell Asset"),
|
||||||
function () {
|
function () {
|
||||||
frm.trigger("make_sales_invoice");
|
frm.trigger("sell_asset");
|
||||||
},
|
},
|
||||||
__("Manage")
|
__("Manage")
|
||||||
);
|
);
|
||||||
@@ -484,22 +484,6 @@ frappe.ui.form.on("Asset", {
|
|||||||
frm.trigger("toggle_reference_doc");
|
frm.trigger("toggle_reference_doc");
|
||||||
},
|
},
|
||||||
|
|
||||||
make_sales_invoice: function (frm) {
|
|
||||||
frappe.call({
|
|
||||||
args: {
|
|
||||||
asset: frm.doc.name,
|
|
||||||
item_code: frm.doc.item_code,
|
|
||||||
company: frm.doc.company,
|
|
||||||
serial_no: frm.doc.serial_no,
|
|
||||||
},
|
|
||||||
method: "erpnext.assets.doctype.asset.asset.make_sales_invoice",
|
|
||||||
callback: function (r) {
|
|
||||||
var doclist = frappe.model.sync(r.message);
|
|
||||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
create_asset_maintenance: function (frm) {
|
create_asset_maintenance: function (frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
args: {
|
args: {
|
||||||
@@ -548,6 +532,57 @@ frappe.ui.form.on("Asset", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sell_asset: function (frm) {
|
||||||
|
const make_sales_invoice = (sell_qty) => {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.assets.doctype.asset.asset.make_sales_invoice",
|
||||||
|
args: {
|
||||||
|
asset: frm.doc.name,
|
||||||
|
item_code: frm.doc.item_code,
|
||||||
|
company: frm.doc.company,
|
||||||
|
serial_no: frm.doc.serial_no,
|
||||||
|
sell_qty: sell_qty,
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const dialog = new frappe.ui.Dialog({
|
||||||
|
title: __("Sell Asset"),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldname: "sell_qty",
|
||||||
|
fieldtype: "Int",
|
||||||
|
label: __("Sell Qty"),
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.set_primary_action(__("Sell"), function () {
|
||||||
|
const dialog_data = dialog.get_values();
|
||||||
|
const sell_qty = cint(dialog_data.sell_qty);
|
||||||
|
|
||||||
|
if (sell_qty < cint(frm.doc.asset_quantity)) {
|
||||||
|
frappe.confirm(
|
||||||
|
__(
|
||||||
|
"The sell quantity is less than the total asset quantity. The remaining quantity will be split into a new asset. This action cannot be undone. <br><b>Do you want to continue?<b>"
|
||||||
|
),
|
||||||
|
() => make_sales_invoice(sell_qty)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
make_sales_invoice(sell_qty);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
},
|
||||||
|
|
||||||
split_asset: function (frm) {
|
split_asset: function (frm) {
|
||||||
const title = __("Split Asset");
|
const title = __("Split Asset");
|
||||||
|
|
||||||
|
|||||||
@@ -1083,7 +1083,7 @@ def get_asset_naming_series():
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_invoice(asset, item_code, company, serial_no=None, posting_date=None):
|
def make_sales_invoice(asset, item_code, company, sell_qty, serial_no=None):
|
||||||
asset_doc = frappe.get_doc("Asset", asset)
|
asset_doc = frappe.get_doc("Asset", asset)
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
si.company = company
|
si.company = company
|
||||||
@@ -1098,7 +1098,7 @@ def make_sales_invoice(asset, item_code, company, serial_no=None, posting_date=N
|
|||||||
"income_account": disposal_account,
|
"income_account": disposal_account,
|
||||||
"serial_no": serial_no,
|
"serial_no": serial_no,
|
||||||
"cost_center": depreciation_cost_center,
|
"cost_center": depreciation_cost_center,
|
||||||
"qty": 1,
|
"qty": sell_qty,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user