Merge pull request #51538 from frappe/version-15-hotfix

chore: release v15
This commit is contained in:
ruthra kumar
2026-01-07 10:29:48 +05:30
committed by GitHub
17 changed files with 145 additions and 17 deletions

View File

@@ -93,6 +93,7 @@
"receivable_payable_remarks_length",
"accounts_receivable_payable_tuning_section",
"receivable_payable_fetch_method",
"default_ageing_range",
"column_break_ntmi",
"drop_ar_procedures",
"legacy_section",
@@ -657,6 +658,12 @@
"fieldname": "show_party_balance",
"fieldtype": "Check",
"label": "Show Party Balance"
},
{
"default": "30, 60, 90, 120",
"fieldname": "default_ageing_range",
"fieldtype": "Data",
"label": "Default Ageing Range"
}
],
"icon": "icon-cog",
@@ -664,7 +671,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-11-06 17:48:07.682837",
"modified": "2025-12-26 19:46:55.093717",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@@ -694,4 +701,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -41,6 +41,7 @@ class AccountsSettings(Document):
check_supplier_invoice_uniqueness: DF.Check
create_pr_in_draft_status: DF.Check
credit_controller: DF.Link | None
default_ageing_range: DF.Data | None
delete_linked_ledger_entries: DF.Check
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
enable_common_party_accounting: DF.Check

View File

@@ -20,6 +20,23 @@ frappe.ui.form.on("Journal Entry", {
"Unreconcile Payment Entries",
"Bank Transaction",
];
frm.trigger("set_queries");
},
set_queries(frm) {
frm.set_query("project", "accounts", function (doc, cdt, cdn) {
let row = frappe.get_doc(cdt, cdn);
let filters = {
company: doc.company,
};
if (row.party_type == "Customer") {
filters.customer = row.party;
}
return {
query: "erpnext.controllers.queries.get_project_name",
filters,
};
});
},
refresh: function (frm) {

View File

@@ -6,6 +6,7 @@ import json
import frappe
from frappe import _, msgprint, scrub
from frappe.core.doctype.submission_queue.submission_queue import queue_submission
from frappe.utils import comma_and, cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
import erpnext
@@ -172,15 +173,13 @@ class JournalEntry(AccountsController):
def submit(self):
if len(self.accounts) > 100:
msgprint(_("The task has been enqueued as a background job."), alert=True)
self.queue_action("submit", timeout=4600)
queue_submission(self, "_submit")
else:
return self._submit()
def cancel(self):
if len(self.accounts) > 100:
msgprint(_("The task has been enqueued as a background job."), alert=True)
self.queue_action("cancel", timeout=4600)
queue_submission(self, "_cancel")
else:
return self._cancel()

View File

@@ -13,9 +13,9 @@ frappe.ui.form.on("Period Closing Voucher", {
return {
filters: [
["Account", "company", "=", frm.doc.company],
["Account", "is_group", "=", "0"],
["Account", "is_group", "=", 0],
["Account", "freeze_account", "=", "No"],
["Account", "root_type", "in", "Liability, Equity"],
["Account", "root_type", "in", ["Liability", "Equity"]],
],
};
});

View File

@@ -165,6 +165,10 @@ frappe.query_reports["Accounts Payable"] = {
var filters = report.get_values();
frappe.set_route("query-report", "Accounts Payable Summary", { company: filters.company });
});
if (frappe.boot.sysdefaults.default_ageing_range) {
report.set_filter_value("range", frappe.boot.sysdefaults.default_ageing_range);
}
},
};

View File

@@ -114,6 +114,10 @@ frappe.query_reports["Accounts Payable Summary"] = {
var filters = report.get_values();
frappe.set_route("query-report", "Accounts Payable", { company: filters.company });
});
if (frappe.boot.sysdefaults.default_ageing_range) {
report.set_filter_value("range", frappe.boot.sysdefaults.default_ageing_range);
}
},
};

View File

@@ -192,6 +192,10 @@ frappe.query_reports["Accounts Receivable"] = {
var filters = report.get_values();
frappe.set_route("query-report", "Accounts Receivable Summary", { company: filters.company });
});
if (frappe.boot.sysdefaults.default_ageing_range) {
report.set_filter_value("range", frappe.boot.sysdefaults.default_ageing_range);
}
},
};

View File

@@ -137,6 +137,10 @@ frappe.query_reports["Accounts Receivable Summary"] = {
var filters = report.get_values();
frappe.set_route("query-report", "Accounts Receivable", { company: filters.company });
});
if (frappe.boot.sysdefaults.default_ageing_range) {
report.set_filter_value("range", frappe.boot.sysdefaults.default_ageing_range);
}
},
};

View File

@@ -81,5 +81,11 @@ frappe.query_reports["Trial Balance for Party"] = {
label: __("Show zero values"),
fieldtype: "Check",
},
{
fieldname: "exclude_zero_balance_parties",
label: __("Exclude Zero Balance Parties"),
fieldtype: "Check",
default: 1,
},
],
};

View File

@@ -75,20 +75,20 @@ def get_data(filters, show_party_name):
closing_debit, closing_credit = toggle_debit_credit(opening_debit + debit, opening_credit + credit)
row.update({"closing_debit": closing_debit, "closing_credit": closing_credit})
# totals
for col in total_row:
total_row[col] += row.get(col)
row.update({"currency": company_currency})
has_value = False
if opening_debit or opening_credit or debit or credit or closing_debit or closing_credit:
has_value = True
# Exclude zero balance parties if filter is set
if filters.get("exclude_zero_balance_parties") and not closing_debit and not closing_credit:
continue
if cint(filters.show_zero_values) or has_value:
data.append(row)
# Add total row
# totals
for col in total_row:
total_row[col] += row.get(col)
total_row.update({"party": "'" + _("Totals") + "'", "currency": company_currency})
data.append(total_row)

View File

@@ -1,5 +1,6 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2017-10-23 11:38:54.004355",
"doctype": "DocType",
@@ -250,7 +251,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2025-11-17 18:35:54.575265",
"modified": "2026-01-06 15:48:13.862505",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Repair",
@@ -264,6 +265,7 @@
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"print": 1,
"read": 1,
"report": 1,
@@ -279,6 +281,7 @@
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"print": 1,
"read": 1,
"report": 1,
@@ -295,4 +298,4 @@
"title_field": "asset_name",
"track_changes": 1,
"track_seen": 1
}
}

View File

@@ -64,6 +64,9 @@ def boot_session(bootinfo):
bootinfo.party_account_types = frappe._dict(party_account_types)
bootinfo.sysdefaults.demo_company = frappe.db.get_single_value("Global Defaults", "demo_company")
bootinfo.sysdefaults.default_ageing_range = frappe.db.get_single_value(
"Accounts Settings", "default_ageing_range"
)
def update_page_info(bootinfo):

View File

@@ -140,6 +140,24 @@ frappe.ui.form.on("Stock Entry", {
};
});
frm.set_query("project", "items", function (doc) {
return {
query: "erpnext.controllers.queries.get_project_name",
filters: {
company: doc.company,
},
};
});
frm.set_query("project", function (doc) {
return {
query: "erpnext.controllers.queries.get_project_name",
filters: {
company: doc.company,
},
};
});
frm.add_fetch("bom_no", "inspection_required", "inspection_required");
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
@@ -914,6 +932,9 @@ frappe.ui.form.on("Stock Entry Detail", {
item_code(frm, cdt, cdn) {
var d = locals[cdt][cdn];
// since some items may not have image, so empty the image field to avoid setting the image of previous item
d.image = "";
if (d.item_code) {
var args = {
item_code: d.item_code,

View File

@@ -879,7 +879,7 @@ class StockReconciliation(StockController):
if row.get(dimension.get("fieldname")):
has_dimensions = True
if self.docstatus == 2 and (not row.batch_no or not row.serial_and_batch_bundle):
if self.docstatus == 2:
if row.current_qty and current_bundle:
data.actual_qty = -1 * row.current_qty
data.qty_after_transaction = flt(row.current_qty)

View File

@@ -1645,6 +1645,59 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
batch_qty = get_batch_qty(batch_no, warehouse, item_code)
self.assertEqual(batch_qty, 4)
def test_sabb_cancel_on_stock_reco_cancellation(self):
item_code = self.make_item(
"Test Item for SABB Cancel on Stock Reco Cancellation",
{
"is_stock_item": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "TEST-BATCH-SABBCANC-.###",
},
).name
warehouse = "_Test Warehouse - _TC"
sr = create_stock_reconciliation(
item_code=item_code,
warehouse=warehouse,
qty=10,
rate=100,
use_serial_batch_fields=1,
)
sr.reload()
batch_no = get_batch_from_bundle(sr.items[0].serial_and_batch_bundle)
sr1 = create_stock_reconciliation(
item_code=item_code,
warehouse=warehouse,
qty=20,
rate=100,
use_serial_batch_fields=1,
batch_no=batch_no,
)
sr1.reload()
current_serial_and_batch_bundle = sr1.items[0].current_serial_and_batch_bundle
serial_and_batch_bundle = sr1.items[0].serial_and_batch_bundle
self.assertTrue(current_serial_and_batch_bundle)
self.assertTrue(serial_and_batch_bundle)
sr1.cancel()
for sabb in [serial_and_batch_bundle, current_serial_and_batch_bundle]:
docstatus = frappe.db.get_value(
"Serial and Batch Bundle",
sabb,
"docstatus",
)
self.assertEqual(docstatus, 2)
def create_batch_item_with_batch(item_name, batch_id):
batch_item_doc = create_item(item_name, is_stock_item=1)

View File

@@ -445,6 +445,7 @@ class StockReservationEntry(Document):
voucher_delivered_qty = flt(delivered_qty) * flt(conversion_factor)
allowed_qty = min(self.available_qty, (self.voucher_qty - voucher_delivered_qty - total_reserved_qty))
allowed_qty = flt(allowed_qty, self.precision("reserved_qty"))
qty_to_be_reserved = flt(qty_to_be_reserved, self.precision("reserved_qty"))
if self.get("_action") != "submit" and self.voucher_type == "Sales Order" and allowed_qty <= 0:
@@ -537,6 +538,7 @@ def get_available_qty_to_reserve(
& (sre.reserved_qty >= sre.delivered_qty)
& (sre.status.notin(["Delivered", "Cancelled"]))
)
.for_update()
)
if ignore_sre: