fix(accounts): stop coalescing a DATE with an int in Asset Depreciations report

The Asset Depreciations and Balances report tested disposal status with
IfNull(asset.disposal_date, 0) != 0 / == 0 — coalescing the DATE column
disposal_date with the integer 0. frappe.qb renders this as
COALESCE("disposal_date", 0); PostgreSQL requires COALESCE arguments to
share a type and raises:

    psycopg2.errors.DatatypeMismatch: COALESCE types date and integer
    cannot be matched

The predicate is in the WHERE/CASE of every query the report runs (both
group_by=Asset Category and group_by=Asset), so the whole report errored
on PostgreSQL. MariaDB's IFNULL(date, 0) is permissive and worked.

Replace each comparison with the null-test form already used elsewhere in
this same file: IfNull(disposal_date, 0) != 0 -> disposal_date.isnotnull(),
== 0 -> disposal_date.isnull(). Semantically identical (a stored date is
never 0), valid on both engines, MariaDB output unchanged.
This commit is contained in:
Mihir Kandoi
2026-06-23 15:46:26 +05:30
parent c79febf403
commit 27ec5eabc6
2 changed files with 40 additions and 26 deletions

View File

@@ -82,7 +82,7 @@ def get_asset_categories_for_grouped_by_category(filters):
asset_capitalization = frappe.qb.DocType("Asset Capitalization")
disposal_in_period = (
(IfNull(asset.disposal_date, 0) != 0)
(asset.disposal_date.isnotnull())
& (asset.disposal_date >= filters.from_date)
& (asset.disposal_date <= filters.to_date)
)
@@ -92,7 +92,7 @@ def get_asset_categories_for_grouped_by_category(filters):
frappe.qb.terms.Case()
.when(
(asset.purchase_date < filters.from_date)
& ((IfNull(asset.disposal_date, 0) == 0) | (asset.disposal_date >= filters.from_date)),
& ((asset.disposal_date.isnull()) | (asset.disposal_date >= filters.from_date)),
asset.net_purchase_amount,
)
.else_(0)
@@ -209,9 +209,7 @@ def get_assets_for_grouped_by_category(filters):
frappe.qb.terms.Case()
.when(
(gl_entry.posting_date < filters.from_date)
& (
(IfNull(asset.disposal_date, 0) == 0) | (asset.disposal_date >= filters.from_date)
),
& ((asset.disposal_date.isnull()) | (asset.disposal_date >= filters.from_date)),
gl_entry.debit,
)
.else_(0)
@@ -222,7 +220,7 @@ def get_assets_for_grouped_by_category(filters):
Sum(
frappe.qb.terms.Case()
.when(
(gl_entry.posting_date <= filters.to_date) & (IfNull(asset.disposal_date, 0) == 0),
(gl_entry.posting_date <= filters.to_date) & (asset.disposal_date.isnull()),
gl_entry.credit,
)
.else_(0)
@@ -233,7 +231,7 @@ def get_assets_for_grouped_by_category(filters):
Sum(
frappe.qb.terms.Case()
.when(
(IfNull(asset.disposal_date, 0) != 0)
(asset.disposal_date.isnotnull())
& (asset.disposal_date >= filters.from_date)
& (asset.disposal_date <= filters.to_date)
& (gl_entry.posting_date <= asset.disposal_date),
@@ -249,10 +247,7 @@ def get_assets_for_grouped_by_category(filters):
.when(
(gl_entry.posting_date >= filters.from_date)
& (gl_entry.posting_date <= filters.to_date)
& (
(IfNull(asset.disposal_date, 0) == 0)
| (gl_entry.posting_date <= asset.disposal_date)
),
& ((asset.disposal_date.isnull()) | (gl_entry.posting_date <= asset.disposal_date)),
gl_entry.debit,
)
.else_(0)
@@ -282,7 +277,7 @@ def get_assets_for_grouped_by_category(filters):
Sum(
frappe.qb.terms.Case()
.when(
(IfNull(asset.disposal_date, 0) != 0) & (asset.disposal_date < filters.from_date),
(asset.disposal_date.isnotnull()) & (asset.disposal_date < filters.from_date),
0,
)
.else_(asset.opening_accumulated_depreciation)
@@ -476,7 +471,7 @@ def get_asset_details_for_grouped_by_category(filters):
asset_capitalization = frappe.qb.DocType("Asset Capitalization")
disposal_in_period = (
(IfNull(asset.disposal_date, 0) != 0)
(asset.disposal_date.isnotnull())
& (asset.disposal_date >= filters.from_date)
& (asset.disposal_date <= filters.to_date)
)
@@ -500,9 +495,7 @@ def get_asset_details_for_grouped_by_category(filters):
frappe.qb.terms.Case()
.when(
(asset.purchase_date < filters.from_date)
& (
(IfNull(asset.disposal_date, 0) == 0) | (asset.disposal_date >= filters.from_date)
),
& ((asset.disposal_date.isnull()) | (asset.disposal_date >= filters.from_date)),
asset.net_purchase_amount,
)
.else_(0)
@@ -599,9 +592,7 @@ def get_assets_for_grouped_by_asset(filters):
frappe.qb.terms.Case()
.when(
(gl_entry.posting_date < filters.from_date)
& (
(IfNull(asset.disposal_date, 0) == 0) | (asset.disposal_date >= filters.from_date)
),
& ((asset.disposal_date.isnull()) | (asset.disposal_date >= filters.from_date)),
gl_entry.debit,
)
.else_(0)
@@ -612,7 +603,7 @@ def get_assets_for_grouped_by_asset(filters):
Sum(
frappe.qb.terms.Case()
.when(
(gl_entry.posting_date <= filters.to_date) & (IfNull(asset.disposal_date, 0) == 0),
(gl_entry.posting_date <= filters.to_date) & (asset.disposal_date.isnull()),
gl_entry.credit,
)
.else_(0)
@@ -623,7 +614,7 @@ def get_assets_for_grouped_by_asset(filters):
Sum(
frappe.qb.terms.Case()
.when(
(IfNull(asset.disposal_date, 0) != 0)
(asset.disposal_date.isnotnull())
& (asset.disposal_date >= filters.from_date)
& (asset.disposal_date <= filters.to_date)
& (gl_entry.posting_date <= asset.disposal_date),
@@ -639,10 +630,7 @@ def get_assets_for_grouped_by_asset(filters):
.when(
(gl_entry.posting_date >= filters.from_date)
& (gl_entry.posting_date <= filters.to_date)
& (
(IfNull(asset.disposal_date, 0) == 0)
| (gl_entry.posting_date <= asset.disposal_date)
),
& ((asset.disposal_date.isnull()) | (gl_entry.posting_date <= asset.disposal_date)),
gl_entry.debit,
)
.else_(0)
@@ -672,7 +660,7 @@ def get_assets_for_grouped_by_asset(filters):
Sum(
frappe.qb.terms.Case()
.when(
(IfNull(asset.disposal_date, 0) != 0) & (asset.disposal_date < filters.from_date),
(asset.disposal_date.isnotnull()) & (asset.disposal_date < filters.from_date),
0,
)
.else_(asset.opening_accumulated_depreciation)

View File

@@ -0,0 +1,26 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from erpnext.accounts.report.asset_depreciations_and_balances.asset_depreciations_and_balances import (
execute,
)
from erpnext.tests.utils import ERPNextTestSuite
class TestAssetDepreciationsAndBalancesReport(ERPNextTestSuite):
def test_report_runs_on_both_engines(self):
"""The report compared IfNull(asset.disposal_date, 0) against 0 -- coalescing a DATE
column with integer 0. Postgres rejects that (COALESCE types date and integer cannot be
matched) at plan time, so the whole report errored there regardless of data. It must run
on both engines."""
for group_by in ("Asset Category", "Asset"):
filters = frappe._dict(
company="_Test Company",
from_date="2020-01-01",
to_date="2030-12-31",
group_by=group_by,
)
result = execute(filters)
self.assertIsInstance(result[1], list)