diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 18a86434ef9..13413ad2ab7 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -33,6 +33,7 @@ from erpnext.accounts.utils import (
get_account_currency,
update_voucher_outstanding,
)
+from erpnext.assets.doctype.asset.asset import split_asset
from erpnext.assets.doctype.asset.depreciation import (
depreciate_asset,
get_gl_entries_on_asset_disposal,
@@ -468,6 +469,8 @@ class SalesInvoice(SellingController):
self.update_stock_reservation_entries()
self.update_stock_ledger()
+ self.split_asset_based_on_sale_qty()
+
self.process_asset_depreciation()
# 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))
+ 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):
if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1):
self.depreciate_asset_on_sale()
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index e0b4593416a..ec167fafb3b 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -111,7 +111,7 @@ frappe.ui.form.on("Asset", {
frm.add_custom_button(
__("Sell Asset"),
function () {
- frm.trigger("make_sales_invoice");
+ frm.trigger("sell_asset");
},
__("Manage")
);
@@ -484,22 +484,6 @@ frappe.ui.form.on("Asset", {
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) {
frappe.call({
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.
Do you want to continue?"
+ ),
+ () => make_sales_invoice(sell_qty)
+ );
+ } else {
+ make_sales_invoice(sell_qty);
+ }
+
+ dialog.hide();
+ });
+
+ dialog.show();
+ },
+
split_asset: function (frm) {
const title = __("Split Asset");
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 701be98a0d6..1e64f74b372 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -1083,7 +1083,7 @@ def get_asset_naming_series():
@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)
si = frappe.new_doc("Sales Invoice")
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,
"serial_no": serial_no,
"cost_center": depreciation_cost_center,
- "qty": 1,
+ "qty": sell_qty,
},
)