Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
1ff297e
fix: seed cancelled voucher replay from before its posting datetime
mihir-kandoi Jul 22, 2026
27a48a7
Merge pull request #57347 from mihir-kandoi/fix-neg-stock-cancel-repl…
mihir-kandoi Jul 22, 2026
d937385
fix: restore Save button on reverse journal entry
krishna-254 Jul 23, 2026
c1afc55
Merge pull request #57379 from krishna-254/fix/reverse-journal-entry-…
krishna-254 Jul 23, 2026
c5235af
fix: respect selected BOM when creating work order for variant item (…
Shllokkk Jul 23, 2026
c740d7d
Merge pull request #57394 from frappe/mergify/bp/version-15-hotfix/pr…
Shllokkk Jul 23, 2026
745513d
fix: check if item is variant when creating WO from MR
mihir-kandoi Dec 10, 2025
bb1320f
fix: add is_active filter
mihir-kandoi Dec 10, 2025
4808202
fix: rebalance batch slot values at the pooled rate when driven negative
mihir-kandoi Jul 23, 2026
3ad971d
Merge pull request #57401 from mihir-kandoi/backport-51019-v15
mihir-kandoi Jul 23, 2026
6fa522d
fix: guard against missing is_your_company_address custom field on ad…
Pandiyan5273 Jul 23, 2026
ce3bd02
Merge pull request #57405 from mihir-kandoi/fix-stock-ageing-batch-po…
mihir-kandoi Jul 23, 2026
df93600
Merge pull request #57407 from frappe/mergify/bp/version-15-hotfix/pr…
mihir-kandoi Jul 23, 2026
56bd024
fix: typeerror in get_batches_by_oldest for mixed batch expiry
Pandiyan5273 Jul 23, 2026
0752fcf
Merge pull request #57414 from frappe/mergify/bp/version-15-hotfix/pr…
mihir-kandoi Jul 23, 2026
0dc5894
fix: map pick list customer to delivery note when no sales order
Pandiyan5273 Jul 23, 2026
04a281e
Merge pull request #57422 from aerele/backport-57412-customer-pick-li…
mihir-kandoi Jul 24, 2026
348555b
fix: respect user permissions in party dashboard company list
Pandiyan5273 Jul 24, 2026
b5d8c75
Merge pull request #57439 from frappe/mergify/bp/version-15-hotfix/pr…
mihir-kandoi Jul 24, 2026
4e8f5de
fix: enable the 'Include Zero Stock Items' filter by default to show …
rohitwaghchaure Jul 24, 2026
4fe91bd
fix: recalculate operating cost on hour rate change in routing
Shllokkk Jul 26, 2026
1721c40
Merge pull request #57467 from frappe/mergify/bp/version-15-hotfix/pr…
Shllokkk Jul 26, 2026
60b4e60
fix: rename misleading filter labels in AR/AP reports
Shllokkk Jul 24, 2026
e2319c3
fix: migrate stored AR/AP ageing filter to renamed field
Shllokkk Jul 24, 2026
7a858be
chore: resolve patches.txt conflict for backport
Shllokkk Jul 26, 2026
97fa543
refactor: configurable date in reverse ERR journals
ruthra-kumar Jul 27, 2026
435fe19
refactor(test): manually submit reverse err journal
ruthra-kumar Jul 27, 2026
d970627
fix(subcontracting): release raw-material reservation when closing a …
Sudharsanan11 Jul 25, 2026
92a7dca
test(subcontracting): cover reservation release on closing a subcontr…
Sudharsanan11 Jul 25, 2026
08f92ed
Merge pull request #57477 from frappe/mergify/bp/version-15-hotfix/pr…
ruthra-kumar Jul 27, 2026
f38b3b4
fix: pool batch slot values on every run, not only when negative
mihir-kandoi Jul 27, 2026
168ec66
test: assert batch pooling preserves the group total on a repeating rate
mihir-kandoi Jul 27, 2026
a2ace9d
fix: resolve backport conflict in stock ageing test imports
mihir-kandoi Jul 27, 2026
a0b26d1
Merge pull request #57486 from frappe/mergify/bp/version-15-hotfix/pr…
mihir-kandoi Jul 27, 2026
2a95bd2
Merge pull request #57480 from frappe/mergify/bp/version-15-hotfix/pr…
mihir-kandoi Jul 27, 2026
53d9d1c
fix(crm): align Opportunity status checks with Quotation statuses (ba…
mergify[bot] Jul 27, 2026
982648c
Merge pull request #57472 from frappe/mergify/bp/version-15-hotfix/pr…
Shllokkk Jul 27, 2026
98a0fd8
fix(crm): clarify the reason why an opportunity cannot be declared as…
mergify[bot] Jul 27, 2026
c70cf8e
fix(stock): narrow legacy serial ledger lookup by item (backport #574…
mergify[bot] Jul 27, 2026
ae0cd16
fix: Incorrect creation time at the time cancelling an entry causing …
mergify[bot] Jul 27, 2026
ceb6778
fix(quotation): carry forward communications from opportunity (backpo…
mergify[bot] Jul 27, 2026
8f68b7e
fix: update operating cost when propagating workstation hour rate to …
Shllokkk Jul 28, 2026
5a1a9b2
Merge pull request #57514 from frappe/mergify/bp/version-15-hotfix/pr…
Shllokkk Jul 28, 2026
d96999d
fix: stop storing "{supplier_name}" / "{customer_name}" as the docume…
mihir-kandoi Jul 28, 2026
0ad0d77
fix: use company currency instead of global default in report (backpo…
Pandiyan5273 Jul 28, 2026
8cceb6a
fix: detect the currency column by fieldtype in trends total row
Pandiyan5273 Jul 28, 2026
da698b7
Merge pull request #57525 from aerele/backport-56561-version-15-hotfix
mihir-kandoi Jul 28, 2026
43a96c3
Merge pull request #57524 from mihir-kandoi/fix-titles-v15
mihir-kandoi Jul 28, 2026
25e5b10
fix(manufacturing): update cost of BOMs created via BOM Creator
mihir-kandoi Jul 28, 2026
1ac19d7
Merge pull request #57537 from mihir-kandoi/backport-bom-creator-upda…
mihir-kandoi Jul 28, 2026
20b6dd3
fix: respect child warehouse account override in Stock and Account Va…
Shllokkk Jul 28, 2026
12b4c13
fix(manufacturing): fall back to UOM Conversion Factor in Production …
mihir-kandoi Jul 28, 2026
6939d9a
Merge pull request #57556 from frappe/mergify/bp/version-15-hotfix/pr…
Shllokkk Jul 28, 2026
41effcf
fix: add permission check for `get_item_details` (backport #57515) (#…
mergify[bot] Jul 28, 2026
1d60ab4
fix(tnc): `get_terms_and_conditions` render_template with `safe_exec`…
mergify[bot] Jul 28, 2026
abc53b0
fix(ppcv): replace incorrect usage of `frappe.in_test` with `frappe.f…
diptanilsaha Jul 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions erpnext/accounts/custom/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def validate(self):

def link_address(self):
"""Link address based on owner"""
if self.is_your_company_address:
if self.get("is_your_company_address"):
return

return super().link_address()
Expand All @@ -26,7 +26,9 @@ def update_compnay_address(self):
self.is_your_company_address = 1

def validate_reference(self):
if self.is_your_company_address and not [row for row in self.links if row.link_doctype == "Company"]:
if self.get("is_your_company_address") and not [
row for row in self.links if row.link_doctype == "Company"
]:
frappe.throw(
_(
"Address needs to be linked to a Company. Please add a row for Company in the Links table."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,15 +619,26 @@ def make_reverse_journal(self):
if journals:
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry

for x in journals:
reversal = make_reverse_journal_entry(x)
reversal.posting_date = nowdate()
reversal.submit()
frappe.msgprint(
_("Revaluation journal for {0} has been created: {1}").format(
frappe.bold(x), get_link_to_form("Journal Entry", reversal.name)
)
if drafts := frappe.db.get_all(
"Journal Entry",
filters={"docstatus": 0, "reversal_of": ["in", journals]},
pluck="name",
):
part = "journals are" if len(drafts) > 1 else "journal is"
doc_links = ", ".join(["{}".format(get_link_to_form("Journal Entry", x)) for x in drafts])
frappe.throw(
msg=_("Reverse {0} already available in draft status: {1}").format(part, doc_links),
)
else:
for x in journals:
reversal = make_reverse_journal_entry(x)
reversal.posting_date = nowdate()
reversal.save()
frappe.msgprint(
_("A draft reverse journal for {0} has been created: {1}").format(
frappe.bold(x), get_link_to_form("Journal Entry", reversal.name)
)
)


def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,14 @@ def test_05_revaluation_journal_reversal(self):
self.assertFalse(ret.get("reversals_posted"))

err.make_reverse_journal()
# submit
draft = frappe.db.get_all(
"Journal Entry",
filters={"docstatus": 0, "reversal_of": je.name, "voucher_type": "Exchange Rate Revaluation"},
pluck="name",
)
self.assertIsNotNone(draft)
frappe.get_doc("Journal Entry", draft[0]).submit()
ret = err.check_journal_and_reversal()
self.assertTrue(ret.get("journals_posted"))
self.assertTrue(ret.get("reversals_posted"))
Expand Down
10 changes: 9 additions & 1 deletion erpnext/accounts/doctype/journal_entry/journal_entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ frappe.ui.form.on("Journal Entry", {

refresh: function (frm) {
if (frm.doc.reversal_of && (frm.is_new() || frm.doc.docstatus == 0)) {
frm.set_read_only();
erpnext.journal_entry.lock_reversal_entry(frm);
}

erpnext.toggle_naming_series();
Expand Down Expand Up @@ -513,6 +513,14 @@ $.extend(erpnext.journal_entry, {
});
},

lock_reversal_entry: function (frm) {
frm.fields
.filter((field) => field.has_input)
.filter((field) => field.df.fieldname != "posting_date")
.forEach((field) => frm.set_df_property(field.df.fieldname, "read_only", 1));
frm.set_df_property("accounts", "read_only", 1);
},

set_debit_credit_in_company_currency: function (frm, cdt, cdn) {
var row = locals[cdt][cdn];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def initialize_parallel_threads(docname: str):
)
# keep transaction on PPCV and PPCVD short
# prevents concurrency errors - REPEATABLE READ
if not frappe.in_test:
if not frappe.flags.in_test:
frappe.db.commit() # nosemgrep
else:
frappe.db.set_value("Process Period Closing Voucher", docname, "status", "Completed")
Expand Down Expand Up @@ -272,7 +272,7 @@ def schedule_next_date(docname: str):
)
# keep transaction on PPCV and PPCVD short
# prevents concurrency errors - REPEATABLE READ
if not frappe.in_test:
if not frappe.flags.in_test:
frappe.db.commit() # nosemgrep

frappe.enqueue(
Expand Down Expand Up @@ -449,7 +449,7 @@ def summarize_and_post_ledger_entries(docname):

# keep transaction on PPCV and PPCVD short
# prevents concurrency errors - REPEATABLE READ
if not frappe.in_test:
if not frappe.flags.in_test:
frappe.db.commit() # nosemgrep

frappe.db.set_value("Period Closing Voucher", pcv.name, "gle_processing_status", "Completed")
Expand Down Expand Up @@ -599,7 +599,7 @@ def process_individual_date(docname: str, row_name, date, report_type, parentfie
"Completed",
)
# commit heavy computation before touching PPCV or PPCVD
if not frappe.in_test:
if not frappe.flags.in_test:
frappe.db.commit() # nosemgrep

# chain call
Expand Down
2 changes: 1 addition & 1 deletion erpnext/accounts/party.py
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ def get_dashboard_info(party_type, party, loyalty_program=None):

doctype = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"

companies = frappe.get_all(
companies = frappe.get_list(
doctype, filters={"docstatus": 1, party_type.lower(): party}, distinct=1, fields=["company"]
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ frappe.query_reports["Accounts Payable"] = {
},
{
fieldname: "report_date",
label: __("Posting Date"),
label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
Expand Down Expand Up @@ -69,10 +69,10 @@ frappe.query_reports["Accounts Payable"] = {
default: "Due Date",
},
{
fieldname: "calculate_ageing_with",
label: __("Calculate Ageing With"),
fieldname: "age_as_on",
label: __("Age as on"),
fieldtype: "Select",
options: "Report Date\nToday Date",
options: "Report Date\nToday",
default: "Report Date",
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ frappe.query_reports["Accounts Payable Summary"] = {
},
{
fieldname: "report_date",
label: __("Posting Date"),
label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
Expand All @@ -24,10 +24,10 @@ frappe.query_reports["Accounts Payable Summary"] = {
default: "Due Date",
},
{
fieldname: "calculate_ageing_with",
label: __("Calculate Ageing With"),
fieldname: "age_as_on",
label: __("Age as on"),
fieldtype: "Select",
options: "Report Date\nToday Date",
options: "Report Date\nToday",
default: "Report Date",
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ frappe.query_reports["Accounts Receivable"] = {
},
{
fieldname: "report_date",
label: __("Posting Date"),
label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
Expand Down Expand Up @@ -98,10 +98,10 @@ frappe.query_reports["Accounts Receivable"] = {
default: "Due Date",
},
{
fieldname: "calculate_ageing_with",
label: __("Calculate Ageing With"),
fieldname: "age_as_on",
label: __("Age as on"),
fieldtype: "Select",
options: "Report Date\nToday Date",
options: "Report Date\nToday",
default: "Report Date",
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ def __init__(self, filters=None):
self.filters.report_date = getdate(self.filters.report_date or nowdate())
self.age_as_on = (
getdate(nowdate())
if "calculate_ageing_with" not in self.filters
or self.filters.calculate_ageing_with == "Today Date"
if "age_as_on" not in self.filters or self.filters.age_as_on == "Today"
else self.filters.report_date
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ frappe.query_reports["Accounts Receivable Summary"] = {
},
{
fieldname: "report_date",
label: __("Posting Date"),
label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
Expand All @@ -24,10 +24,10 @@ frappe.query_reports["Accounts Receivable Summary"] = {
default: "Due Date",
},
{
fieldname: "calculate_ageing_with",
label: __("Calculate Ageing With"),
fieldname: "age_as_on",
label: __("Age as on"),
fieldtype: "Select",
options: "Report Date\nToday Date",
options: "Report Date\nToday",
default: "Report Date",
},
{
Expand Down
1 change: 1 addition & 0 deletions erpnext/accounts/report/cash_flow/cash_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def execute(filters=None):
"parent_section": None,
"indent": 0.0,
"section": cash_flow_section["section_header"],
"currency": company_currency,
}
)

Expand Down
2 changes: 2 additions & 0 deletions erpnext/accounts/report/gross_profit/gross_profit.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_
)
if total_base_amount
else 0,
"currency": filters.currency,
}
)
)
Expand Down Expand Up @@ -269,6 +270,7 @@ def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_
"buying_amount": total_buying_amount,
"gross_profit": total_gross_profit,
"gross_profit_percent": flt(gross_profit_percent, currency_precision),
"currency": filters.currency,
}

total_row = [total_row.get(col, None) for col in [*group_columns, "currency"]]
Expand Down
6 changes: 2 additions & 4 deletions erpnext/buying/doctype/purchase_order/purchase_order.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,12 @@
},
{
"allow_on_submit": 1,
"default": "{supplier_name}",
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
"label": "Title",
"no_copy": 1,
"print_hide": 1,
"reqd": 1
"print_hide": 1
},
{
"fieldname": "naming_series",
Expand Down Expand Up @@ -1309,7 +1307,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2025-07-31 17:19:40.816883",
"modified": "2026-07-28 12:20:11.284370",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
Expand Down
2 changes: 1 addition & 1 deletion erpnext/buying/doctype/purchase_order/purchase_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class PurchaseOrder(BuyingController):
taxes_and_charges_deducted: DF.Currency
tc_name: DF.Link | None
terms: DF.TextEditor | None
title: DF.Data
title: DF.Data | None
to_date: DF.Date | None
total: DF.Currency
total_net_weight: DF.Float
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def execute(filters=None):
conditions = get_columns(filters, "Purchase Order")
data = get_data(filters, conditions)
chart_data = get_chart_data(data, conditions, filters)

return conditions["columns"], data, None, chart_data


Expand All @@ -39,9 +38,15 @@ def get_chart_data(data, conditions, filters):
labels = [column.split(":")[0] for column in columns]
datapoints = [0] * len(labels)

group_by_col_idx = None
if filters.get("group_by"):
group_by_col_idx = conditions["columns"].index(conditions["grbc"][0])

for row in data:
# If group by filter, don't add first row of group (it's already summed)
if not row[start]:
# Skip the final grand-total row
if row[0] == f"'{_('Total')}'":
continue
if group_by_col_idx is not None and row[group_by_col_idx] == "":
continue
# Remove None values and compute only periodic data
row = [x if x else 0 for x in row[start:-2]]
Expand All @@ -60,4 +65,6 @@ def get_chart_data(data, conditions, filters):
"type": "line",
"lineOptions": {"regionFill": 1},
"fieldtype": "Currency",
"options": "currency",
"currency": conditions.get("company_currency"),
}
20 changes: 16 additions & 4 deletions erpnext/controllers/trends.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from frappe import _
from frappe.utils import DateTimeLikeObject, getdate, today

import erpnext
from erpnext.accounts.utils import get_fiscal_year


Expand Down Expand Up @@ -42,6 +43,9 @@ def get_columns(filters, trans):
"addl_tables": based_on_details["addl_tables"],
"addl_tables_relational_cond": based_on_details.get("addl_tables_relational_cond", ""),
}
conditions["company_currency"] = (
erpnext.get_company_currency(filters.get("company")) if filters.get("company") else None
)

return conditions

Expand Down Expand Up @@ -206,7 +210,7 @@ def get_data(filters, conditions):

data.append(des)

total_row = calculate_total_row(data1, conditions["columns"])
total_row = calculate_total_row(data1, conditions["columns"], conditions.get("company_currency"))
data.append(total_row)
else:
data = frappe.db.sql(
Expand All @@ -231,19 +235,24 @@ def get_data(filters, conditions):
as_list=1,
)

total_row = calculate_total_row(data, conditions["columns"])
total_row = calculate_total_row(data, conditions["columns"], conditions.get("company_currency"))
data.append(total_row)

return data


def calculate_total_row(data, columns):
def calculate_total_row(data, columns, company_currency=None):
def wrap_in_quotes(label):
return f"'{label}'"

total_values = {}
currency_col_idx = None
for i, col in enumerate(columns):
if "Float" in col or "Currency/currency" in col:
# based-on and group-by columns are dicts, periodic and total columns are strings
if isinstance(col, dict):
if col.get("fieldtype") == "Link" and col.get("options") == "Currency":
currency_col_idx = i
elif "Float" in col or "Currency/currency" in col:
total_values[i] = 0

for row in data:
Expand All @@ -254,6 +263,9 @@ def wrap_in_quotes(label):
for i in range(1, len(columns)):
total_row.append(total_values.get(i, None))

if currency_col_idx is not None:
total_row[currency_col_idx] = company_currency

return total_row


Expand Down
Loading
Loading