mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-03 13:38:27 +00:00
Merge pull request #51538 from frappe/version-15-hotfix
chore: release v15
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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"]],
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user