Compare commits

...

29 Commits

Author SHA1 Message Date
Khushi Rawat
d3cd887f5e Merge pull request #51666 from aerele/fix-asset-value-adjustment-cancel
fix(asset value adjustment): skip cancelling revaluation journal entry if already cancelled
2026-01-13 12:38:44 +05:30
Navin-S-R
d65cd605a1 fix: move validation to before_cancel 2026-01-13 12:16:52 +05:30
Ankush Menat
6ec41fa47e build: Bump dev version 2026-01-13 12:15:21 +05:30
Khushi Rawat
d879a91165 Merge pull request #51509 from khushi8112/fix-test-cases
fix: use system configured float precision for depreciation rate
2026-01-13 11:58:58 +05:30
Khushi Rawat
d21cfae095 Merge pull request #51363 from aerele/asset-partial-sales
fix(asset): handle partial asset sales by splitting remaining quantity
2026-01-13 11:54:20 +05:30
Mihir Kandoi
be5f2b6cf0 Merge pull request #51650 from mihir-kandoi/v16-prep 2026-01-13 11:06:02 +05:30
ruthra kumar
37b3a22825 Merge pull request #51412 from ljain112/fix-tds-customer
fix(tds): correct tax logic for customer
2026-01-13 11:05:31 +05:30
Mihir Kandoi
bb307dec0a chore: add v14
remove when EOL reached
2026-01-13 11:04:17 +05:30
Nabin Hait
3bc58fb46f fix: Redirect to Desktop after signup (#51696) 2026-01-12 19:20:27 +05:30
Navin-S-R
73b038084b fix: prevent manual cancellation of the linked Revaluation Journal Entry 2026-01-12 18:18:22 +05:30
Navin-S-R
eeb6d0e9bf fix: remove the redundant purchase receipt submit 2026-01-12 16:45:04 +05:30
Navin-S-R
ca97f34092 fix: use new_asset instead of asset_doc when checking values after splitting 2026-01-12 16:36:34 +05:30
Navin-S-R
500c44e3f5 fix: ignore permissions when cancelling revaluation journal entry 2026-01-11 21:30:09 +05:30
Navin-S-R
5f00239bba refactor(journal entry): replace raw SQL with query builder to unlink asset value adjustment 2026-01-11 19:25:22 +05:30
Navin-S-R
b1704ccef1 fix(asset value adjustment): skip cancelling revaluation journal entry if already cancelled 2026-01-11 19:20:01 +05:30
Mihir Kandoi
4987b2fe26 ci: ignore ci folder for tests 2026-01-10 17:01:04 +05:30
Mihir Kandoi
7e7e83440f ci: version 16 related changes 2026-01-10 16:43:39 +05:30
khushi8112
c0a85faa68 test: set up float precision 2026-01-06 23:49:21 +05:30
khushi8112
825e3717ca fix: do not update float precision on setup 2026-01-06 14:41:36 +05:30
khushi8112
007258d657 refactor: modify test cases to handle float precision rounded to 2 decimals 2026-01-06 14:39:31 +05:30
khushi8112
8d186d6b3f fix: use correct test class 2026-01-05 16:47:32 +05:30
khushi8112
1296829b9c fix(test): Use the system-configured float precision 2026-01-05 16:44:06 +05:30
ljain112
86b0f67dbc fix(tds): correct tax logic for customer 2025-12-31 14:26:22 +05:30
Navin-S-R
4adeaedfde test: validate asset split for auto created asset from purchase voucher 2025-12-30 16:29:46 +05:30
Navin-S-R
23b094f151 fix(asset): handle same asset being sold in multiple line items in sales invoice 2025-12-30 14:47:28 +05:30
Navin-S-R
e7e6567792 fix(asset): skip purchase document validation while splitting existing asset 2025-12-30 12:09:37 +05:30
Navin-S-R
9eeccb765d test: validate asset partial sales 2025-12-29 22:14:26 +05:30
Navin-S-R
a88fe2ecab fix: refactor older testcases 2025-12-29 15:53:31 +05:30
Navin-S-R
9a2710b9d7 fix(asset): handle partial asset sales by splitting remaining quantity 2025-12-29 15:40:46 +05:30
15 changed files with 325 additions and 56 deletions

View File

@@ -19,7 +19,7 @@ jobs:
strategy:
fail-fast: false
matrix:
version: ["14", "15"]
version: ["14", "15", "16"]
steps:
- uses: octokit/request-action@v2.x

View File

@@ -11,6 +11,7 @@ on:
- 'crowdin.yml'
- '.coderabbit.yml'
- '.mergify.yml'
- '.github/**'
workflow_dispatch:
permissions:

View File

@@ -13,6 +13,7 @@ on:
- 'crowdin.yml'
- '.coderabbit.yml'
- '.mergify.yml'
- '.github/**'
schedule:
# Run everday at midnight UTC / 5:30 IST
- cron: "0 0 * * *"

View File

@@ -50,13 +50,13 @@ pull_request_rules:
- version-15-hotfix
assignees:
- "{{ author }}"
- name: backport to version-16-beta
- name: backport to version-16-hotfix
conditions:
- label="backport version-16-beta"
- label="backport version-16-hotfix"
actions:
backport:
branches:
- version-16-beta
- version-16-hotfix
assignees:
- "{{ author }}"
- name: Automatic merge on CI success and review

View File

@@ -6,7 +6,7 @@ import frappe
from frappe.model.document import Document
from frappe.utils.user import is_website_user
__version__ = "16.0.0-dev"
__version__ = "17.0.0-dev"
def get_default_company(user=None):

View File

@@ -184,6 +184,9 @@ class JournalEntry(AccountsController):
else:
return self._submit()
def before_cancel(self):
self.has_asset_adjustment_entry()
def cancel(self):
if len(self.accounts) > 100:
queue_submission(self, "_cancel")
@@ -554,12 +557,27 @@ class JournalEntry(AccountsController):
)
frappe.db.set_value("Journal Entry", self.name, "inter_company_journal_entry_reference", "")
def unlink_asset_adjustment_entry(self):
frappe.db.sql(
""" update `tabAsset Value Adjustment`
set journal_entry = null where journal_entry = %s""",
self.name,
def has_asset_adjustment_entry(self):
if self.flags.get("via_asset_value_adjustment"):
return
asset_value_adjustment = frappe.db.get_value(
"Asset Value Adjustment", {"docstatus": 1, "journal_entry": self.name}, "name"
)
if asset_value_adjustment:
frappe.throw(
_(
"Cannot cancel this document as it is linked with the submitted Asset Value Adjustment <b>{0}</b>. Please cancel the Asset Value Adjustment to continue."
).format(frappe.utils.get_link_to_form("Asset Value Adjustment", asset_value_adjustment))
)
def unlink_asset_adjustment_entry(self):
AssetValueAdjustment = frappe.qb.DocType("Asset Value Adjustment")
(
frappe.qb.update(AssetValueAdjustment)
.set(AssetValueAdjustment.journal_entry, None)
.where(AssetValueAdjustment.journal_entry == self.name)
).run()
def validate_party(self):
for d in self.get("accounts"):

View File

@@ -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,51 @@ 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():
if qty["actual_qty"] < qty["sale_qty"]:
frappe.throw(
_(
"Sell quantity cannot exceed the asset quantity. Asset {0} has only {1} item(s)."
).format(asset, qty["actual_qty"])
)
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)
if row.asset in asset_qty_map.keys():
asset_qty_map[row.asset]["sale_qty"] += flt(row.qty)
else:
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()

View File

@@ -415,7 +415,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase):
"cost_center": "Main - _TC",
"tax_amount": 500,
"description": "Test",
"add_deduct_tax": "Add",
},
)
pi.save()
@@ -506,7 +505,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase):
"cost_center": "Main - _TC",
"tax_amount": 200,
"description": "Test Gross Tax",
"add_deduct_tax": "Add",
},
)
si.save()
@@ -541,10 +539,10 @@ class TestTaxWithholdingCategory(IntegrationTestCase):
"cost_center": "Main - _TC",
"tax_amount": 400,
"description": "Test Gross Tax",
"add_deduct_tax": "Add",
},
)
si.save()
si.reload()
si.submit()
invoices.append(si)
# For amount before threshold (first 8000 + VAT): TCS entry with amount zero
@@ -594,7 +592,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase):
"cost_center": "Main - _TC",
"tax_amount": 500,
"description": "VAT added to test TDS calculation on gross amount",
"add_deduct_tax": "Add",
},
)
si.save()
@@ -1024,7 +1021,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase):
"cost_center": "Main - _TC",
"tax_amount": 1000,
"description": "VAT added to test TDS calculation on gross amount",
"add_deduct_tax": "Add",
},
)
pi.save()
@@ -1162,7 +1158,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase):
"cost_center": "Main - _TC",
"tax_amount": 8000,
"description": "Test",
"add_deduct_tax": "Add",
},
)

View File

@@ -708,6 +708,10 @@ class TaxWithholdingController:
existing_taxes = {row.account_head: row for row in self.doc.taxes if row.is_tax_withholding_account}
precision = self.doc.precision("tax_amount", "taxes")
conversion_rate = self.get_conversion_rate()
add_deduct_tax = "Deduct"
if self.party_type == "Customer":
add_deduct_tax = "Add"
for account_head, base_amount in account_amount_map.items():
tax_amount = flt(base_amount / conversion_rate, precision)
@@ -724,6 +728,7 @@ class TaxWithholdingController:
tax_row = self._create_tax_row(account_head, tax_amount)
for_update = False
tax_row.add_deduct_tax = add_deduct_tax
# Set item-wise tax breakup for this tax row
self._set_item_wise_tax_for_tds(
tax_row, account_head, category_withholding_map, for_update=for_update
@@ -743,7 +748,6 @@ class TaxWithholdingController:
"account_head": account_head,
"description": account_head,
"cost_center": cost_center,
"add_deduct_tax": "Deduct",
"tax_amount": tax_amount,
"dont_recompute_tax": 1,
},
@@ -807,12 +811,14 @@ class TaxWithholdingController:
else:
item_tax_amount = 0
multiplier = -1 if tax_row.add_deduct_tax == "Deduct" else 1
self.doc._item_wise_tax_details.append(
frappe._dict(
item=item,
tax=tax_row,
rate=category.tax_rate,
amount=item_tax_amount * -1, # Negative because it's a deduction
amount=item_tax_amount * multiplier,
taxable_amount=item_base_taxable,
)
)

View File

@@ -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")
);
@@ -521,22 +521,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: {
@@ -585,6 +569,69 @@ 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);
},
});
};
let 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);
const asset_qty = cint(frm.doc.asset_quantity);
if (sell_qty <= 0) {
frappe.throw(__("Sell quantity must be greater than zero"));
}
if (sell_qty > asset_qty) {
frappe.throw(__("Sell quantity cannot exceed the asset quantity"));
}
if (sell_qty < asset_qty) {
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><br><b>Do you want to continue?</b>"
),
() => {
make_sales_invoice(sell_qty);
dialog.hide();
}
);
return;
}
make_sales_invoice(sell_qty);
dialog.hide();
});
dialog.show();
},
split_asset: function (frm) {
const title = __("Split Asset");

View File

@@ -482,6 +482,9 @@ class Asset(AccountsController):
frappe.throw(_("Available-for-use Date should be after purchase date"))
def validate_linked_purchase_documents(self):
if self.flags.is_split_asset:
return
for fieldname, doctype in [
("purchase_receipt", "Purchase Receipt"),
("purchase_invoice", "Purchase Invoice"),
@@ -589,9 +592,7 @@ class Asset(AccountsController):
def set_depreciation_rate(self):
for d in self.get("finance_books"):
self.validate_asset_finance_books(d)
d.rate_of_depreciation = flt(
self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation")
)
d.rate_of_depreciation = self.get_depreciation_rate(d, on_validate=True)
def validate_asset_finance_books(self, row):
row.expected_value_after_useful_life = flt(
@@ -981,7 +982,7 @@ class Asset(AccountsController):
if isinstance(args, str):
args = json.loads(args)
rate_field_precision = frappe.get_precision(args.doctype, "rate_of_depreciation") or 2
rate_field_precision = frappe.get_single_value("System Settings", "float_precision") or 2
if args.get("depreciation_method") == "Double Declining Balance":
return self.get_double_declining_balance_rate(args, rate_field_precision)
@@ -1083,7 +1084,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 +1099,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,
},
)
@@ -1378,6 +1379,7 @@ def process_asset_split(existing_asset, split_qty, splitted_asset=None, is_new_a
scaling_factor = flt(split_qty) / flt(existing_asset.asset_quantity)
new_asset = frappe.copy_doc(existing_asset) if is_new_asset else splitted_asset
asset_doc = new_asset if is_new_asset else existing_asset
asset_doc.flags.is_split_asset = True
set_split_asset_values(asset_doc, scaling_factor, split_qty, existing_asset, is_new_asset)
log_asset_activity(existing_asset, asset_doc, splitted_asset, is_new_asset)

View File

@@ -330,7 +330,9 @@ class TestAsset(AssetSetup):
post_depreciation_entries(date=add_months(purchase_date, 2))
si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
si = make_sales_invoice(
asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=asset.asset_quantity
)
si.customer = "_Test Customer"
si.due_date = date
si.get("items")[0].rate = 25000
@@ -458,7 +460,9 @@ class TestAsset(AssetSetup):
post_depreciation_entries(date="2021-01-01")
si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
si = make_sales_invoice(
asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=asset.asset_quantity
)
si.customer = "_Test Customer"
si.due_date = nowdate()
si.get("items")[0].rate = 25000
@@ -698,8 +702,142 @@ class TestAsset(AssetSetup):
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
def test_partial_asset_sale(self):
date = nowdate()
purchase_date = add_months(get_first_day(date), -2)
depreciation_start_date = add_months(get_last_day(date), -2)
# create an asset
asset = create_asset(
item_code="Macbook Pro",
is_existing_asset=1,
calculate_depreciation=1,
available_for_use_date=purchase_date,
purchase_date=purchase_date,
depreciation_start_date=depreciation_start_date,
net_purchase_amount=1000000.0,
purchase_amount=1000000.0,
asset_quantity=10,
total_number_of_depreciations=12,
frequency_of_depreciation=1,
submit=1,
)
asset_depr_schedule_before_sale = get_asset_depr_schedule_doc(asset.name, "Active")
post_depreciation_entries(date)
asset.reload()
# check asset values before sale
self.assertEqual(asset.asset_quantity, 10)
self.assertEqual(asset.net_purchase_amount, 1000000)
self.assertEqual(asset.status, "Partially Depreciated")
self.assertEqual(
asset_depr_schedule_before_sale.depreciation_schedule[0].get("depreciation_amount"), 83333.33
)
# make a partial sales against the asset
si = make_sales_invoice(
asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=5
)
si.customer = "_Test Customer"
si.due_date = date
si.get("items")[0].rate = 25000
si.insert()
si.submit()
asset.reload()
asset_depr_schedule_after_sale = get_asset_depr_schedule_doc(asset.name, "Active")
# check asset values after sales
self.assertEqual(asset.asset_quantity, 5)
self.assertEqual(asset.net_purchase_amount, 500000)
self.assertEqual(asset.status, "Sold")
self.assertEqual(
asset_depr_schedule_after_sale.depreciation_schedule[0].get("depreciation_amount"), 41666.66
)
def test_asset_splitting_for_non_existing_asset(self):
date = nowdate()
purchase_date = add_months(get_first_day(date), -2)
depreciation_start_date = add_months(get_last_day(date), -2)
asset_qty = 10
asset_rate = 100000.0
asset_item = "Macbook Pro"
asset_location = "Test Location"
frappe.db.set_value("Item", asset_item, "is_grouped_asset", 1)
# Inward asset via Purchase Receipt
pr = make_purchase_receipt(
item_code="Macbook Pro",
posting_date=purchase_date,
qty=asset_qty,
rate=asset_rate,
location=asset_location,
supplier="_Test Supplier",
)
asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name")
asset_doc = frappe.get_doc("Asset", asset)
asset_doc.calculate_depreciation = 1
asset_doc.available_for_use_date = purchase_date
asset_doc.location = asset_location
asset_doc.append(
"finance_books",
{
"expected_value_after_useful_life": 0,
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 12,
"frequency_of_depreciation": 1,
"depreciation_start_date": depreciation_start_date,
},
)
asset_doc.submit()
# check asset values before splitting
asset_depr_schedule_before_splitting = get_asset_depr_schedule_doc(asset_doc.name, "Active")
self.assertEqual(asset_doc.asset_quantity, 10)
self.assertEqual(asset_doc.net_purchase_amount, 1000000)
self.assertEqual(
asset_depr_schedule_before_splitting.depreciation_schedule[0].get("depreciation_amount"), 83333.33
)
# initate asset split
new_asset = split_asset(asset_doc.name, 5)
asset_doc.reload()
asset_depr_schedule_after_sale = get_asset_depr_schedule_doc(asset_doc.name, "Active")
new_asset_depr_schedule = get_asset_depr_schedule_doc(new_asset.name, "Active")
# check asset values after splitting
self.assertEqual(asset_doc.asset_quantity, 5)
self.assertEqual(asset_doc.net_purchase_amount, 500000)
self.assertEqual(
asset_depr_schedule_after_sale.depreciation_schedule[0].get("depreciation_amount"), 41666.66
)
# check new asset values after splitting
self.assertEqual(new_asset.asset_quantity, 5)
self.assertEqual(new_asset.net_purchase_amount, 500000)
self.assertEqual(
new_asset_depr_schedule.depreciation_schedule[0].get("depreciation_amount"), 41666.66
)
frappe.db.set_value("Item", asset_item, "is_grouped_asset", 0)
class TestDepreciationMethods(AssetSetup):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls._old_float_precision = frappe.db.get_single_value("System Settings", "float_precision")
frappe.db.set_single_value("System Settings", "float_precision", 2)
@classmethod
def tearDownClass(cls):
frappe.db.set_single_value("System Settings", "float_precision", cls._old_float_precision)
super().tearDownClass()
def test_schedule_for_straight_line_method(self):
asset = create_asset(
calculate_depreciation=1,
@@ -797,9 +935,9 @@ class TestDepreciationMethods(AssetSetup):
self.assertEqual(asset.status, "Draft")
expected_schedules = [
["2030-12-31", 66667.00, 66667.00],
["2031-12-31", 22222.11, 88889.11],
["2032-12-31", 1110.89, 90000.0],
["2030-12-31", 66670.0, 66670.0],
["2031-12-31", 22221.11, 88891.11],
["2032-12-31", 1108.89, 90000.0],
]
schedules = [
@@ -825,7 +963,7 @@ class TestDepreciationMethods(AssetSetup):
self.assertEqual(asset.status, "Draft")
expected_schedules = [["2031-12-31", 33333.50, 83333.50], ["2032-12-31", 6666.50, 90000.0]]
expected_schedules = [["2031-12-31", 33335.0, 83335.0], ["2032-12-31", 6665.0, 90000.0]]
schedules = [
[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
@@ -943,12 +1081,12 @@ class TestDepreciationMethods(AssetSetup):
)
expected_schedules = [
["2022-02-28", 337.72, 337.72],
["2022-03-31", 675.45, 1013.17],
["2022-04-30", 675.45, 1688.62],
["2022-05-31", 675.45, 2364.07],
["2022-06-30", 675.45, 3039.52],
["2022-07-15", 1960.48, 5000.0],
["2022-02-28", 337.71, 337.71],
["2022-03-31", 675.42, 1013.13],
["2022-04-30", 675.42, 1688.55],
["2022-05-31", 675.42, 2363.97],
["2022-06-30", 675.42, 3039.39],
["2022-07-15", 1960.61, 5000.0],
]
schedules = [

View File

@@ -51,7 +51,9 @@ class TestAssetRepair(IntegrationTestCase):
submit=1,
)
si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
si = make_sales_invoice(
asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=asset.asset_quantity
)
si.customer = "_Test Customer"
si.due_date = date
si.get("items")[0].rate = 25000

View File

@@ -74,7 +74,7 @@ class AssetValueAdjustment(Document):
)
def on_cancel(self):
frappe.get_doc("Journal Entry", self.journal_entry).cancel()
self.cancel_asset_revaluation_entry()
self.update_asset()
add_asset_activity(
self.asset,
@@ -167,6 +167,17 @@ class AssetValueAdjustment(Document):
if dimension.get("mandatory_for_pl"):
debit_entry.update({dimension["fieldname"]: dimension_value})
def cancel_asset_revaluation_entry(self):
if not self.journal_entry:
return
revaluation_entry = frappe.get_doc("Journal Entry", self.journal_entry)
if revaluation_entry.docstatus == 1:
# Ignore permissions to match Journal Entry submission behavior
revaluation_entry.flags.ignore_permissions = True
revaluation_entry.flags.via_asset_value_adjustment = True
revaluation_entry.cancel()
def update_asset(self):
asset = self.update_asset_value_after_depreciation()
note = self.get_adjustment_note()

View File

@@ -8,7 +8,7 @@ app_email = "hello@frappe.io"
app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
app_home = "/app/home"
app_home = "/desk"
add_to_apps_screen = [
{