Compare commits

..

31 Commits

Author SHA1 Message Date
Frappe PR Bot
48c3c80383 chore(release): Bumped to Version 14.91.2
## [14.91.2](https://github.com/frappe/erpnext/compare/v14.91.1...v14.91.2) (2025-09-25)

### Performance Improvements

* reposting for backdated transactions ([f3da431](f3da431f1f))
2025-09-25 14:45:52 +00:00
rohitwaghchaure
a9a68f2523 Merge pull request #49726 from frappe/mergify/bp/version-14/pr-49723
perf: reposting for backdated transactions (backport #49720) (backport #49723)
2025-09-25 20:14:23 +05:30
Rohit Waghchaure
f3da431f1f perf: reposting for backdated transactions
(cherry picked from commit 1b0fc0541b)
(cherry picked from commit 3d51c1b363)
2025-09-25 13:22:15 +00:00
Frappe PR Bot
825ef415ef chore(release): Bumped to Version 14.91.1
## [14.91.1](https://github.com/frappe/erpnext/compare/v14.91.0...v14.91.1) (2025-09-23)

### Bug Fixes

* add condition for name ([0c7911d](0c7911d0fe))
2025-09-23 13:35:10 +00:00
ruthra kumar
f6148c4352 chore: release v14 (#49696)
* fix: add condition for name

(cherry picked from commit cf5a2d6351)

* test: add test to validate user permission in qb

(cherry picked from commit a5b881ea74)

# Conflicts:
#	erpnext/setup/doctype/employee/test_employee.py

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2025-09-23 19:03:40 +05:30
ruthra kumar
3c408ff66b Merge pull request #49616 from frappe/mergify/bp/version-14-hotfix/pr-49467
fix: add condition for name (backport #49467)
2025-09-22 10:49:01 +05:30
venkat102
f6bc9fc505 test: add test to validate user permission in qb
(cherry picked from commit a5b881ea74)

# Conflicts:
#	erpnext/setup/doctype/employee/test_employee.py
2025-09-18 16:47:10 +05:30
venkat102
0c7911d0fe fix: add condition for name
(cherry picked from commit cf5a2d6351)
2025-09-18 11:12:13 +00:00
Frappe PR Bot
b8ecc4274f chore(release): Bumped to Version 14.91.0
# [14.91.0](https://github.com/frappe/erpnext/compare/v14.90.1...v14.91.0) (2025-09-16)

### Bug Fixes

* duplicate items being created when fetching items from warehouse in stock reco ([646de0b](646de0bec1))
* remove ignore_permissions ([180f406](180f406917))

### Features

* add permission check for custom button ([42b38b7](42b38b7998))
2025-09-16 14:41:37 +00:00
ruthra kumar
a9b7bc1385 Merge pull request #49565 from frappe/version-14-hotfix
chore: release v14
2025-09-16 20:08:58 +05:30
Mihir Kandoi
d062e50f35 Merge pull request #49515 from frappe/mergify/bp/version-14-hotfix/pr-48456 2025-09-10 10:06:48 +05:30
Mihir Kandoi
646de0bec1 fix: duplicate items being created when fetching items from warehouse in stock reco
(cherry picked from commit 73f6c29559)
2025-09-10 03:01:06 +00:00
Raffael Meyer
02de47c673 Merge pull request #49434 from frappe/mergify/bp/version-14-hotfix/pr-49374
fix: improve permission handling for party link creation (backport #49374)
2025-09-03 17:12:27 +02:00
Marc-Constantin Enke
42b38b7998 feat: add permission check for custom button
(cherry picked from commit 00fd1d2f26)
2025-09-02 17:20:31 +00:00
Marc-Constantin Enke
180f406917 fix: remove ignore_permissions
(cherry picked from commit 7f55f421ab)
2025-09-02 17:20:31 +00:00
Frappe PR Bot
90803b852e chore(release): Bumped to Version 14.90.1
## [14.90.1](https://github.com/frappe/erpnext/compare/v14.90.0...v14.90.1) (2025-09-02)

### Bug Fixes

* add is_cancelled in condition ([b9f9be3](b9f9be3d88))
* ignore cancelled gl and add company filter ([6b29c06](6b29c06511))
* set missing due date in Purchase Invoice and POS Invoice ([#49232](https://github.com/frappe/erpnext/issues/49232)) ([2b6b0b3](2b6b0b32a7))
2025-09-02 13:48:57 +00:00
ruthra kumar
4cbde47ac4 Merge pull request #49424 from frappe/version-14-hotfix
chore: release v14
2025-09-02 19:17:38 +05:30
ruthra kumar
0b3215cd42 Merge pull request #49416 from frappe/mergify/bp/version-14-hotfix/pr-49379
fix: add is_cancelled in condition (backport #49379)
2025-09-02 11:16:44 +05:30
l0gesh29
b9f9be3d88 fix: add is_cancelled in condition
(cherry picked from commit 77a9cf6398)
2025-09-02 05:27:23 +00:00
ruthra kumar
faeedb01a8 Merge pull request #49411 from frappe/mergify/bp/version-14-hotfix/pr-49366
fix(trial-balance-simple): ignore cancelled gl and add company filter (backport #49366)
2025-09-02 10:42:36 +05:30
Raffael Meyer
a2402ddf52 Merge pull request #49233 from frappe/mergify/bp/version-14-hotfix/pr-49232
fix: set missing due date in Purchase Invoice and POS Invoice (backport #49232)
2025-09-02 00:49:37 +02:00
l0gesh29
6b29c06511 fix: ignore cancelled gl and add company filter
(cherry picked from commit afb067ce50)
2025-09-01 11:16:22 +00:00
Frappe PR Bot
2cedd455d9 chore(release): Bumped to Version 14.90.0
# [14.90.0](https://github.com/frappe/erpnext/compare/v14.89.2...v14.90.0) (2025-08-26)

### Bug Fixes

* **dunning:** include accounting dimension upon gl creation ([4fccef0](4fccef0636))
* **payment_entry:** clear party_type when switching to Internal Transfer to avoid 'Supplier None not found' ([e27dd64](e27dd64044))
* **Supplier:** add make_method for Pricing Rule (backport [#49282](https://github.com/frappe/erpnext/issues/49282)) ([#49283](https://github.com/frappe/erpnext/issues/49283)) ([7986e69](7986e69839))

### Features

* add make methods for Bank Account (backport [#49000](https://github.com/frappe/erpnext/issues/49000)) ([#49274](https://github.com/frappe/erpnext/issues/49274)) ([836545b](836545bdb4))
2025-08-26 11:35:26 +00:00
ruthra kumar
ba31f685f8 Merge pull request #49327 from frappe/version-14-hotfix
chore: release v14
2025-08-26 17:01:27 +05:30
ruthra kumar
507d27d509 Merge pull request #49309 from niyati34/fix/payment-entry-clear-party-type-v14hotfix
Fix/payment entry clear party type v14hotfix
2025-08-26 13:27:38 +05:30
niyati34
e27dd64044 fix(payment_entry): clear party_type when switching to Internal Transfer to avoid 'Supplier None not found' 2025-08-25 19:49:05 +05:30
ruthra kumar
7f617a54d1 Merge pull request #49278 from aerele/dunning-accouting-dimension
fix(dunning): include accounting dimension upon gl creation
2025-08-25 16:56:35 +05:30
mergify[bot]
7986e69839 fix(Supplier): add make_method for Pricing Rule (backport #49282) (#49283)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Supplier): add make_method for Pricing Rule (#49282)
2025-08-22 19:26:10 +02:00
mergify[bot]
836545bdb4 feat: add make methods for Bank Account (backport #49000) (#49274)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-08-22 19:17:44 +02:00
ravibharathi656
4fccef0636 fix(dunning): include accounting dimension upon gl creation 2025-08-22 12:34:39 +05:30
Raffael Meyer
2b6b0b32a7 fix: set missing due date in Purchase Invoice and POS Invoice (#49232)
(cherry picked from commit 77478303fe)
2025-08-19 12:30:46 +00:00
16 changed files with 137 additions and 80 deletions

View File

@@ -3,7 +3,7 @@ import inspect
import frappe
__version__ = "14.89.2"
__version__ = "14.91.2"
def get_default_company(user=None):

View File

@@ -55,46 +55,46 @@ class Dunning(AccountsController):
"conversion_rate",
"cost_center",
]
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
accounting_dimensions = get_accounting_dimensions()
invoice_fields.extend(accounting_dimensions)
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
gl_entries.append(
self.get_gl_dict(
{
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
},
inv.party_account_currency,
item=inv,
)
)
gl_entries.append(
self.get_gl_dict(
{
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"cost_center": inv.cost_center or default_cost_center,
"credit_in_account_currency": self.dunning_amount,
"project": inv.project,
},
item=inv,
)
)
debit = {
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
}
credit = {
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"credit_in_account_currency": self.dunning_amount,
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
}
for dimension in accounting_dimensions:
if val := inv.get(dimension):
debit[dimension] = credit[dimension] = val
gl_entries = [
self.get_gl_dict(debit, inv.party_account_currency, item=inv),
self.get_gl_dict(credit, item=inv),
]
make_gl_entries(
gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
)

View File

@@ -261,7 +261,7 @@ def validate_balance_type(account, adv_adj=False):
if balance_must_be:
balance = frappe.db.sql(
"""select sum(debit) - sum(credit)
from `tabGL Entry` where account = %s""",
from `tabGL Entry` where is_cancelled = 0 and account = %s""",
account,
)[0][0]

View File

@@ -60,6 +60,6 @@ def create_party_link(primary_role, primary_party, secondary_party):
party_link.secondary_role = "Customer" if primary_role == "Supplier" else "Supplier"
party_link.secondary_party = secondary_party
party_link.save(ignore_permissions=True)
party_link.save()
return party_link

View File

@@ -330,7 +330,7 @@ frappe.ui.form.on('Payment Entry', {
payment_type: function(frm) {
set_default_party_type(frm);
if(frm.doc.payment_type == "Internal Transfer") {
$.each(["party", "party_balance", "paid_from", "paid_to",
$.each(["party", "party_type", "party_balance", "paid_from", "paid_to",
"references", "total_allocated_amount"], function(i, field) {
frm.set_value(field, null);
});

View File

@@ -560,7 +560,13 @@ class POSInvoice(SalesInvoice):
"Account", self.debit_to, "account_currency", cache=True
)
if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
self.due_date = get_due_date(
self.posting_date,
"Customer",
self.customer,
self.company,
template_name=self.payment_terms_template,
)
super(SalesInvoice, self).set_missing_values(for_validate)

View File

@@ -173,7 +173,12 @@ class PurchaseInvoice(BuyingController):
)
if not self.due_date:
self.due_date = get_due_date(
self.posting_date, "Supplier", self.supplier, self.company, self.bill_date
self.posting_date,
"Supplier",
self.supplier,
self.company,
self.bill_date,
template_name=self.payment_terms_template,
)
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")

View File

@@ -4,15 +4,25 @@
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"mandatory": 1,
"options": "Company",
"wildcard_filter": 0
}
],
"idx": 0,
"is_standard": "Yes",
"modified": "2019-01-17 17:20:42.374958",
"modified": "2025-08-28 19:06:54.273322",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Trial Balance (Simple)",
"owner": "Administrator",
"prepared_report": 0,
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\nwhere is_cancelled = 0 and company = %(company)s\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
"ref_doctype": "GL Entry",
"report_name": "Trial Balance (Simple)",
"report_type": "Query Report",

View File

@@ -2219,6 +2219,10 @@ def build_qb_match_conditions(doctype, user=None) -> list:
for filter in match_filters:
for link_option, allowed_values in filter.items():
fieldnames = link_fields_map.get(link_option, [])
cond = None
if link_option == doctype:
cond = _dt["name"].isin(allowed_values)
for fieldname in fieldnames:
field = _dt[fieldname]
@@ -2227,6 +2231,7 @@ def build_qb_match_conditions(doctype, user=None) -> list:
if not apply_strict_user_permissions:
cond = (Coalesce(field, "") == "") | cond
if cond:
criterion.append(cond)
return criterion

View File

@@ -42,6 +42,11 @@ frappe.ui.form.on("Supplier", {
},
};
});
frm.make_methods = {
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
"Pricing Rule": () => erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name),
};
},
refresh: function (frm) {
@@ -108,7 +113,10 @@ frappe.ui.form.on("Supplier", {
__("Actions")
);
if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
if (
cint(frappe.defaults.get_default("enable_common_party_accounting")) &&
frappe.model.can_create("Party Link")
) {
frm.add_custom_button(
__("Link with Customer"),
function () {

View File

@@ -215,17 +215,9 @@ $.extend(erpnext.utils, {
},
make_bank_account: function (doctype, docname) {
frappe.call({
method: "erpnext.accounts.doctype.bank_account.bank_account.make_bank_account",
args: {
doctype: doctype,
docname: docname,
},
freeze: true,
callback: function (r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
},
frappe.new_doc("Bank Account", {
party_type: doctype,
party: docname,
});
},

View File

@@ -14,6 +14,7 @@ frappe.ui.form.on("Customer", {
method: "erpnext.selling.doctype.customer.customer.make_opportunity",
frm: cur_frm,
}),
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
};
frm.add_fetch("lead_name", "company_name", "customer_name");
@@ -155,7 +156,10 @@ frappe.ui.form.on("Customer", {
__("Actions")
);
if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
if (
cint(frappe.defaults.get_default("enable_common_party_accounting")) &&
frappe.model.can_create("Party Link")
) {
frm.add_custom_button(
__("Link with Supplier"),
function () {

View File

@@ -21,6 +21,12 @@ erpnext.setup.EmployeeController = class EmployeeController extends frappe.ui.fo
};
frappe.ui.form.on("Employee", {
setup: function (frm) {
frm.make_methods = {
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
};
},
onload: function (frm) {
frm.set_query("department", function () {
return {

View File

@@ -5,8 +5,10 @@ import unittest
import frappe
import frappe.utils
from frappe.query_builder import Criterion
import erpnext
from erpnext.accounts.utils import build_qb_match_conditions
from erpnext.setup.doctype.employee.employee import InactiveEmployeeStatusError
test_records = frappe.get_test_records("Employee")
@@ -34,6 +36,32 @@ class TestEmployee(unittest.TestCase):
employee_doc.save()
self.assertTrue("Employee" not in frappe.get_roles(user))
def test_employee_user_permission(self):
employee1 = make_employee("employee_1_test@company.com", create_user_permission=1)
employee2 = make_employee("employee_2_test@company.com", create_user_permission=1)
make_employee("employee_3_test@company.com", create_user_permission=1)
employee1_doc = frappe.get_doc("Employee", employee1)
employee2_doc = frappe.get_doc("Employee", employee2)
employee2_doc.reload()
employee2_doc.reports_to = employee1_doc.name
employee2_doc.save()
frappe.set_user(employee1_doc.user_id)
Employee = frappe.qb.DocType("Employee")
qb_employee_list = (
frappe.qb.from_(Employee)
.select(Employee.name)
.where(Criterion.all(build_qb_match_conditions("Employee")))
.orderby(Employee.Name)
).run(pluck=Employee.name)
employee_list = frappe.db.get_list("Employee", pluck="name", order_by="name")
self.assertEqual(qb_employee_list, employee_list)
frappe.set_user("Administrator")
def tearDown(self):
frappe.db.rollback()

View File

@@ -748,12 +748,12 @@ def get_items(warehouse, posting_date, posting_time, company, item_code=None, ig
itemwise_batch_data = get_itemwise_batch(warehouse, posting_date, company, item_code)
for d in items:
if d.item_code in itemwise_batch_data:
if (d.item_code, d.warehouse) in itemwise_batch_data:
valuation_rate = get_stock_balance(
d.item_code, d.warehouse, posting_date, posting_time, with_valuation_rate=True
)[1]
for row in itemwise_batch_data.get(d.item_code):
for row in itemwise_batch_data.get((d.item_code, d.warehouse)):
if ignore_empty_stock and not row.qty:
continue
@@ -885,7 +885,7 @@ def get_itemwise_batch(warehouse, posting_date, company, item_code=None):
columns, data = execute(filters)
for row in data:
itemwise_batch_data.setdefault(row[0], []).append(
itemwise_batch_data.setdefault((row[0], row[3]), []).append(
frappe._dict(
{
"item_code": row[0],

View File

@@ -489,32 +489,16 @@ class update_entries_after:
self.distinct_item_warehouses[key] = val
self.new_items_found = True
else:
# Check if the dependent voucher is reposted
# If not, then do not add it to the list
if not self.is_dependent_voucher_reposted(dependant_sle):
return
existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key)
if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
if dependent_voucher_detail_nos and dependant_sle.voucher_detail_no in set(
dependent_voucher_detail_nos
):
return
val.sle_changed = True
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
existing_sle = self.distinct_item_warehouses[key].get("sle", {})
if getdate(existing_sle.get("posting_date")) > getdate(dependant_sle.posting_date):
self.distinct_item_warehouses[key] = val
self.new_items_found = True
elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos):
# Future dependent voucher needs to be repost to get the correct stock value
# If dependent voucher has not reposted, then add it to the list
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
self.new_items_found = True
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
elif dependant_sle.voucher_type == "Stock Entry" and is_transfer_stock_entry(
dependant_sle.voucher_no
):
print(dependant_sle.voucher_no)
self.distinct_item_warehouses[key] = val
self.new_items_found = True
def is_dependent_voucher_reposted(self, dependant_sle) -> bool:
# Return False if the dependent voucher is not reposted
@@ -1403,6 +1387,8 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
"posting_time",
"voucher_detail_no",
"posting_datetime as timestamp",
"voucher_type",
"voucher_no",
],
as_dict=1,
)
@@ -1830,3 +1816,10 @@ def get_stock_value_difference(item_code, warehouse, posting_date, posting_time,
difference_amount = query.run()
return flt(difference_amount[0][0]) if difference_amount else 0
@frappe.request_cache
def is_transfer_stock_entry(voucher_no):
purpose = frappe.get_cached_value("Stock Entry", voucher_no, "purpose")
return purpose in ["Material Transfer", "Material Transfer for Manufacture", "Send to Subcontractor"]