Skip to content

Commit f8f61f0

Browse files
committed
Merge branch 'develop' into feat/employee-creation-and-lifecycle
2 parents 053242d + bf38dea commit f8f61f0

137 files changed

Lines changed: 5587 additions & 3454 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

SECURITY.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Security Policy
22

3-
The ERPNext team and community take security issues seriously. To report a security issue, fill out the form at [https://erpnext.com/security/report](https://erpnext.com/security/report).
3+
The ERPNext team and community take security issues seriously. To report a security issue, please go through the information mentioned [here](https://frappe.io/security).
44

5-
You can help us make ERPNext and all it's users more secure by following the [Reporting guidelines](https://erpnext.com/security).
5+
You can help us make ERPNext and all its users more secure by following the [Reporting guidelines](https://frappe.io/security).
66

7-
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.
7+
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.

erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,37 +69,34 @@ def validate_applicable_accounts(self):
6969

7070

7171
def get_dimension_filter_map():
72-
if not frappe.flags.get("dimension_filter_map"):
73-
filters = frappe.db.sql(
74-
"""
75-
SELECT
76-
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
77-
p.allow_or_restrict, p.fieldname, a.is_mandatory
78-
FROM
79-
`tabApplicable On Account` a,
80-
`tabAccounting Dimension Filter` p
81-
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
82-
WHERE
83-
p.name = a.parent
84-
AND p.disabled = 0
85-
""",
86-
as_dict=1,
87-
)
88-
89-
dimension_filter_map = {}
72+
filters = frappe.db.sql(
73+
"""
74+
SELECT
75+
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
76+
p.allow_or_restrict, p.fieldname, a.is_mandatory
77+
FROM
78+
`tabApplicable On Account` a,
79+
`tabAccounting Dimension Filter` p
80+
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
81+
WHERE
82+
p.name = a.parent
83+
AND p.disabled = 0
84+
""",
85+
as_dict=1,
86+
)
9087

91-
for f in filters:
92-
build_map(
93-
dimension_filter_map,
94-
f.fieldname,
95-
f.applicable_on_account,
96-
f.dimension_value,
97-
f.allow_or_restrict,
98-
f.is_mandatory,
99-
)
100-
frappe.flags.dimension_filter_map = dimension_filter_map
88+
dimension_filter_map = {}
10189

102-
return frappe.flags.dimension_filter_map
90+
for f in filters:
91+
build_map(
92+
dimension_filter_map,
93+
f.fieldname,
94+
f.applicable_on_account,
95+
f.dimension_value,
96+
f.allow_or_restrict,
97+
f.is_mandatory,
98+
)
99+
return dimension_filter_map
103100

104101

105102
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):

erpnext/accounts/doctype/accounts_settings/accounts_settings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@
205205
"description": "Payment Terms from orders will be fetched into the invoices as is",
206206
"fieldname": "automatically_fetch_payment_terms",
207207
"fieldtype": "Check",
208-
"label": "Automatically Fetch Payment Terms from Order"
208+
"label": "Automatically Fetch Payment Terms from Order/Quotation"
209209
},
210210
{
211211
"description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
@@ -697,7 +697,7 @@
697697
"index_web_pages_for_search": 1,
698698
"issingle": 1,
699699
"links": [],
700-
"modified": "2026-02-04 17:15:38.609327",
700+
"modified": "2026-02-27 01:04:09.415288",
701701
"modified_by": "Administrator",
702702
"module": "Accounts",
703703
"name": "Accounts Settings",

erpnext/accounts/doctype/payment_request/payment_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,7 @@ def get_irequests_of_payment_request(doc: str | None = None) -> list:
11541154

11551155

11561156
@frappe.whitelist()
1157-
def get_available_payment_schedules(reference_doctype, reference_name):
1157+
def get_available_payment_schedules(reference_doctype: str, reference_name: str):
11581158
ref_doc = frappe.get_doc(reference_doctype, reference_name)
11591159

11601160
if not hasattr(ref_doc, "payment_schedule") or not ref_doc.payment_schedule:

erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,6 @@
44
frappe.ui.form.on("POS Closing Entry", {
55
onload: async function (frm) {
66
frm.ignore_doctypes_on_cancel_all = ["POS Invoice Merge Log", "Sales Invoice"];
7-
frm.set_query("pos_profile", function (doc) {
8-
return {
9-
filters: { user: doc.user },
10-
};
11-
});
12-
13-
frm.set_query("user", function (doc) {
14-
return {
15-
query: "erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_cashiers",
16-
filters: { parent: doc.pos_profile },
17-
};
18-
});
19-
207
frm.set_query("pos_opening_entry", function (doc) {
218
return { filters: { status: "Open", docstatus: 1 } };
229
});

erpnext/accounts/doctype/pricing_rule/pricing_rule.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,7 @@ def apply_pricing_rule(args: str | dict, doc: str | dict | Document | None = Non
346346

347347
args = frappe._dict(args)
348348

349-
if not args.transaction_type:
350-
set_transaction_type(args)
349+
set_transaction_type(args)
351350

352351
# list of dictionaries
353352
out = []
@@ -688,23 +687,23 @@ def remove_pricing_rules(item_list: str | list):
688687
return out
689688

690689

691-
def set_transaction_type(args):
692-
if args.transaction_type:
690+
def set_transaction_type(pricing_ctx: frappe._dict) -> None:
691+
if pricing_ctx.transaction_type in ["buying", "selling"]:
693692
return
694-
if args.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
695-
args.transaction_type = "selling"
696-
elif args.doctype in (
693+
if pricing_ctx.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
694+
pricing_ctx.transaction_type = "selling"
695+
elif pricing_ctx.doctype in (
697696
"Material Request",
698697
"Supplier Quotation",
699698
"Purchase Order",
700699
"Purchase Receipt",
701700
"Purchase Invoice",
702701
):
703-
args.transaction_type = "buying"
704-
elif args.customer:
705-
args.transaction_type = "selling"
702+
pricing_ctx.transaction_type = "buying"
703+
elif pricing_ctx.customer:
704+
pricing_ctx.transaction_type = "selling"
706705
else:
707-
args.transaction_type = "buying"
706+
pricing_ctx.transaction_type = "buying"
708707

709708

710709
@frappe.whitelist()

erpnext/accounts/doctype/sales_invoice/sales_invoice.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -800,8 +800,7 @@
800800
"hide_seconds": 1,
801801
"label": "Time Sheets",
802802
"options": "Sales Invoice Timesheet",
803-
"print_hide": 1,
804-
"read_only": 1
803+
"print_hide": 1
805804
},
806805
{
807806
"default": "0",
@@ -2331,7 +2330,7 @@
23312330
"link_fieldname": "consolidated_invoice"
23322331
}
23332332
],
2334-
"modified": "2026-02-25 12:41:57.043459",
2333+
"modified": "2026-02-28 17:58:56.453076",
23352334
"modified_by": "Administrator",
23362335
"module": "Accounts",
23372336
"name": "Sales Invoice",

erpnext/accounts/doctype/sales_invoice/sales_invoice.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,9 @@ def get_asset_qty(self):
14521452
return asset_qty_map
14531453

14541454
def process_asset_depreciation(self):
1455+
if self.is_internal_transfer():
1456+
return
1457+
14551458
if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1):
14561459
self.depreciate_asset_on_sale()
14571460
else:

erpnext/accounts/doctype/tax_rule/tax_rule.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from frappe import _
99
from frappe.contacts.doctype.address.address import get_default_address
1010
from frappe.model.document import Document
11+
from frappe.query_builder import DocType
12+
from frappe.query_builder.functions import IfNull
1113
from frappe.utils import cstr
1214
from frappe.utils.nestedset import get_root_of
1315

@@ -83,6 +85,8 @@ def validate_tax_template(self):
8385
frappe.throw(_("Tax Template is mandatory."))
8486

8587
def validate_filters(self):
88+
TaxRule = DocType("Tax Rule")
89+
8690
filters = {
8791
"tax_type": self.tax_type,
8892
"customer": self.customer,
@@ -105,33 +109,34 @@ def validate_filters(self):
105109
"company": self.company,
106110
}
107111

108-
conds = ""
109-
for d in filters:
110-
if conds:
111-
conds += " and "
112-
conds += f"""ifnull({d}, '') = {frappe.db.escape(cstr(filters[d]))}"""
113-
114-
if self.from_date and self.to_date:
115-
conds += f""" and ((from_date > '{self.from_date}' and from_date < '{self.to_date}') or
116-
(to_date > '{self.from_date}' and to_date < '{self.to_date}') or
117-
('{self.from_date}' > from_date and '{self.from_date}' < to_date) or
118-
('{self.from_date}' = from_date and '{self.to_date}' = to_date))"""
119-
120-
elif self.from_date and not self.to_date:
121-
conds += f""" and to_date > '{self.from_date}'"""
122-
123-
elif self.to_date and not self.from_date:
124-
conds += f""" and from_date < '{self.to_date}'"""
125-
126-
tax_rule = frappe.db.sql(
127-
f"select name, priority \
128-
from `tabTax Rule` where {conds} and name != '{self.name}'",
129-
as_dict=1,
112+
query = (
113+
frappe.qb.from_(TaxRule).select(TaxRule.name, TaxRule.priority).where(TaxRule.name != self.name)
130114
)
131115

132-
if tax_rule:
133-
if tax_rule[0].priority == self.priority:
134-
frappe.throw(_("Tax Rule Conflicts with {0}").format(tax_rule[0].name), ConflictingTaxRule)
116+
for field, value in filters.items():
117+
query = query.where(IfNull(TaxRule[field], "") == cstr(value))
118+
119+
if self.from_date and self.to_date:
120+
query = query.where(
121+
((TaxRule.from_date > self.from_date) & (TaxRule.from_date < self.to_date))
122+
| ((TaxRule.to_date > self.from_date) & (TaxRule.to_date < self.to_date))
123+
| ((self.from_date > TaxRule.from_date) & (self.from_date < TaxRule.to_date))
124+
| ((TaxRule.from_date == self.from_date) & (TaxRule.to_date == self.to_date))
125+
)
126+
127+
elif self.from_date:
128+
query = query.where(TaxRule.to_date > self.from_date)
129+
130+
elif self.to_date:
131+
query = query.where(TaxRule.from_date < self.to_date)
132+
133+
tax_rule = query.run(as_dict=True)
134+
135+
if tax_rule and tax_rule[0].priority == self.priority:
136+
frappe.throw(
137+
_("Tax Rule Conflicts with {0}").format(tax_rule[0].name),
138+
ConflictingTaxRule,
139+
)
135140

136141

137142
@frappe.whitelist()

erpnext/accounts/report/accounts_payable/test_accounts_payable.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import frappe
22
from frappe.tests import IntegrationTestCase
3-
from frappe.utils import today
3+
from frappe.utils import add_days, today
44

55
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
66
from erpnext.accounts.report.accounts_payable.accounts_payable import execute
@@ -57,3 +57,66 @@ def create_purchase_invoice(self, do_not_submit=False):
5757
if not do_not_submit:
5858
pi = pi.submit()
5959
return pi
60+
61+
def test_payment_terms_template_filters(self):
62+
from erpnext.controllers.accounts_controller import get_payment_terms
63+
64+
payment_term1 = frappe.get_doc(
65+
{"doctype": "Payment Term", "payment_term_name": "_Test 50% on 15 Days"}
66+
).insert()
67+
payment_term2 = frappe.get_doc(
68+
{"doctype": "Payment Term", "payment_term_name": "_Test 50% on 30 Days"}
69+
).insert()
70+
71+
template = frappe.get_doc(
72+
{
73+
"doctype": "Payment Terms Template",
74+
"template_name": "_Test 50-50",
75+
"terms": [
76+
{
77+
"doctype": "Payment Terms Template Detail",
78+
"due_date_based_on": "Day(s) after invoice date",
79+
"payment_term": payment_term1.name,
80+
"description": "_Test 50-50",
81+
"invoice_portion": 50,
82+
"credit_days": 15,
83+
},
84+
{
85+
"doctype": "Payment Terms Template Detail",
86+
"due_date_based_on": "Day(s) after invoice date",
87+
"payment_term": payment_term2.name,
88+
"description": "_Test 50-50",
89+
"invoice_portion": 50,
90+
"credit_days": 30,
91+
},
92+
],
93+
}
94+
)
95+
template.insert()
96+
97+
filters = {
98+
"company": self.company,
99+
"report_date": today(),
100+
"range": "30, 60, 90, 120",
101+
"based_on_payment_terms": 1,
102+
"payment_terms_template": template.name,
103+
"ageing_based_on": "Posting Date",
104+
}
105+
106+
pi = self.create_purchase_invoice(do_not_submit=True)
107+
pi.payment_terms_template = template.name
108+
schedule = get_payment_terms(template.name)
109+
pi.set("payment_schedule", [])
110+
111+
for row in schedule:
112+
row["due_date"] = add_days(pi.posting_date, row.get("credit_days", 0))
113+
pi.append("payment_schedule", row)
114+
115+
pi.save()
116+
pi.submit()
117+
118+
report = execute(filters)
119+
row = report[1][0]
120+
121+
self.assertEqual(len(report[1]), 2)
122+
self.assertEqual([pi.name, payment_term1.payment_term_name], [row.voucher_no, row.payment_term])

0 commit comments

Comments
 (0)