Merge pull request #48461 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
ruthra kumar
2025-07-08 18:22:45 +05:30
committed by GitHub
17 changed files with 44 additions and 105 deletions

View File

@@ -6,7 +6,11 @@ import unittest
import frappe
from frappe.utils import now_datetime, nowdate
from erpnext.accounts.doctype.budget.budget import BudgetError, get_actual_expense
from erpnext.accounts.doctype.budget.budget import (
BudgetError,
get_accumulated_monthly_budget,
get_actual_expense,
)
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -96,6 +100,10 @@ class TestBudget(unittest.TestCase):
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
accumulated_limit = get_accumulated_monthly_budget(
budget.monthly_distribution, nowdate(), budget.fiscal_year, budget.accounts[0].budget_amount
)
mr = frappe.get_doc(
{
"doctype": "Material Request",
@@ -109,7 +117,7 @@ class TestBudget(unittest.TestCase):
"uom": "_Test UOM",
"warehouse": "_Test Warehouse - _TC",
"schedule_date": nowdate(),
"rate": 100000,
"rate": accumulated_limit + 1,
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
}

View File

@@ -1126,9 +1126,6 @@ class PurchaseInvoice(BuyingController):
elif self.is_return and self.update_stock and (self.is_internal_supplier or not self.return_against):
net_rate = item.base_net_amount
if item.sales_incoming_rate: # for internal transfer
net_rate = item.qty * item.sales_incoming_rate
stock_amount = net_rate + item.item_tax_amount + flt(item.landed_cost_voucher_amount)
if flt(stock_amount, net_amt_precision) != flt(warehouse_debit_amount, net_amt_precision):

View File

@@ -121,7 +121,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
)
out.append(row)
out.sort(key=lambda x: x["section_code"])
out.sort(key=lambda x: (x["section_code"], x["transaction_date"]))
return out

View File

@@ -67,11 +67,12 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase):
mid_year = add_to_date(fiscal_year[1], months=6)
tds_doc = frappe.get_doc("Tax Withholding Category", "TDS - 3")
tds_doc.rates[0].to_date = mid_year
from_date = add_to_date(mid_year, days=1)
tds_doc.append(
"rates",
{
"tax_withholding_rate": 20,
"from_date": add_to_date(mid_year, days=1),
"from_date": from_date,
"to_date": fiscal_year[2],
"single_threshold": 1,
"cumulative_threshold": 1,
@@ -80,18 +81,19 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase):
tds_doc.save()
inv_1 = make_purchase_invoice(rate=1000, do_not_submit=True)
inv_1 = make_purchase_invoice(
rate=1000, posting_date=add_to_date(fiscal_year[1], days=1), do_not_save=True, do_not_submit=True
)
inv_1.set_posting_time = 1
inv_1.apply_tds = 1
inv_1.tax_withholding_category = "TDS - 3"
inv_1.tax_withholding_category = tds_doc.name
inv_1.save()
inv_1.submit()
inv_2 = make_purchase_invoice(
rate=1000, do_not_submit=True, posting_date=add_to_date(mid_year, days=1), do_not_save=True
)
inv_2 = make_purchase_invoice(rate=1000, posting_date=from_date, do_not_save=True, do_not_submit=True)
inv_2.set_posting_time = 1
inv_1.apply_tds = 1
inv_2.tax_withholding_category = "TDS - 3"
inv_2.apply_tds = 1
inv_2.tax_withholding_category = tds_doc.name
inv_2.save()
inv_2.submit()

View File

@@ -79,8 +79,14 @@ frappe.require("assets/erpnext/js/financial_statements.js", function () {
options: erpnext.get_presentation_currency_list(),
},
{
fieldname: "with_period_closing_entry",
label: __("Period Closing Entry"),
fieldname: "with_period_closing_entry_for_opening",
label: __("With Period Closing Entry For Opening Balances"),
fieldtype: "Check",
default: 1,
},
{
fieldname: "with_period_closing_entry_for_current_period",
label: __("Period Closing Entry For Current Period"),
fieldtype: "Check",
default: 1,
},

View File

@@ -120,7 +120,7 @@ def get_data(filters):
max_rgt,
filters,
gl_entries_by_account,
ignore_closing_entries=not flt(filters.with_period_closing_entry),
ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period),
ignore_opening_entries=True,
)
@@ -274,7 +274,7 @@ def get_opening_balance(
):
opening_balance = opening_balance.where(closing_balance.posting_date >= filters.year_start_date)
if not flt(filters.with_period_closing_entry):
if not flt(filters.with_period_closing_entry_for_opening):
if doctype == "Account Closing Balance":
opening_balance = opening_balance.where(closing_balance.is_period_closing_voucher_entry == 0)
else:

View File

@@ -107,11 +107,7 @@ def convert_to_presentation_currency(gl_entries, currency_info):
credit_in_account_currency = flt(entry["credit_in_account_currency"])
account_currency = entry["account_currency"]
if (
len(account_currencies) == 1
and account_currency == presentation_currency
and (debit_in_account_currency or credit_in_account_currency)
):
if len(account_currencies) == 1 and account_currency == presentation_currency:
entry["debit"] = debit_in_account_currency
entry["credit"] = credit_in_account_currency
else:

View File

@@ -1027,12 +1027,7 @@ def is_reposting_pending():
)
def future_sle_exists(args, sl_entries=None, allow_force_reposting=True):
if allow_force_reposting and frappe.db.get_single_value(
"Stock Reposting Settings", "do_reposting_for_each_stock_transaction"
):
return True
def future_sle_exists(args, sl_entries=None):
key = (args.voucher_type, args.voucher_no)
if not hasattr(frappe.local, "future_sle"):
frappe.local.future_sle = {}

View File

@@ -202,7 +202,7 @@ def update_qty(bin_name, args):
sle = frappe.qb.DocType("Stock Ledger Entry")
# actual qty is not up to date in case of backdated transaction
if future_sle_exists(args, allow_force_reposting=False):
if future_sle_exists(args):
last_sle_qty = (
frappe.qb.from_(sle)
.select(sle.qty_after_transaction)

View File

@@ -75,7 +75,9 @@ frappe.ui.form.on("Inventory Dimension", {
set_parent_fields(frm) {
if (frm.doc.apply_to_all_doctypes) {
frm.set_df_property("fetch_from_parent", "options", frm.doc.reference_document);
let options = ["\n", frm.doc.reference_document];
frm.set_df_property("fetch_from_parent", "options", options);
} else if (frm.doc.document_type && frm.doc.istable) {
frappe.call({
method: "erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_parent_fields",
@@ -85,7 +87,7 @@ frappe.ui.form.on("Inventory Dimension", {
},
callback: (r) => {
if (r.message && r.message.length) {
frm.set_df_property("fetch_from_parent", "options", [""].concat(r.message));
frm.set_df_property("fetch_from_parent", "options", ["\n"].concat(r.message));
} else {
frm.set_df_property("fetch_from_parent", "hidden", 1);
}

View File

@@ -143,7 +143,6 @@
"fieldtype": "Column Break"
},
{
"depends_on": "eval:!doc.apply_to_all_doctypes",
"description": "Set fieldname from which you want to fetch the data from the parent form.",
"fieldname": "fetch_from_parent",
"fieldtype": "Select",
@@ -189,7 +188,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-10-05 12:52:18.705431",
"modified": "2025-07-07 15:51:29.329064",
"modified_by": "Administrator",
"module": "Stock",
"name": "Inventory Dimension",
@@ -236,4 +235,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -40,16 +40,11 @@ class InventoryDimension(Document):
self.reset_value()
self.set_source_and_target_fieldname()
self.set_type_of_transaction()
self.set_fetch_value_from()
def set_type_of_transaction(self):
if self.apply_to_all_doctypes:
self.type_of_transaction = "Both"
def set_fetch_value_from(self):
if self.apply_to_all_doctypes:
self.fetch_from_parent = self.reference_document
def do_not_update_document(self):
if self.is_new() or not self.has_stock_ledger():
return

View File

@@ -154,6 +154,8 @@ class TestInventoryDimension(FrappeTestCase):
reference_document="Rack", dimension_name="Rack", apply_to_all_doctypes=1
)
inv_dimension.db_set("fetch_from_parent", "Rack")
self.assertEqual(inv_dimension.type_of_transaction, "Both")
self.assertEqual(inv_dimension.fetch_from_parent, "Rack")

View File

@@ -489,10 +489,6 @@ class StockReconciliation(StockController):
self.update_inventory_dimensions(row, data)
if self.docstatus == 1 and has_dimensions and not row.batch_no:
data.qty_after_transaction = data.actual_qty
data.actual_qty = 0.0
return data
def make_sle_on_cancel(self):

View File

@@ -13,7 +13,6 @@
"end_time",
"limits_dont_apply_on",
"item_based_reposting",
"do_reposting_for_each_stock_transaction",
"errors_notification_section",
"notify_reposting_error_to_role"
],
@@ -66,18 +65,12 @@
"fieldname": "errors_notification_section",
"fieldtype": "Section Break",
"label": "Errors Notification"
},
{
"default": "0",
"fieldname": "do_reposting_for_each_stock_transaction",
"fieldtype": "Check",
"label": "Do reposting for each Stock Transaction"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-04-24 12:19:40.204888",
"modified": "2025-07-08 11:27:46.659056",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reposting Settings",

View File

@@ -11,10 +11,6 @@ class StockRepostingSettings(Document):
def validate(self):
self.set_minimum_reposting_time_slot()
def before_save(self):
if self.do_reposting_for_each_stock_transaction:
self.item_based_reposting = 1
def set_minimum_reposting_time_slot(self):
"""Ensure that timeslot for reposting is at least 12 hours."""
if not self.limit_reposting_timeslot:

View File

@@ -38,51 +38,3 @@ class TestStockRepostingSettings(unittest.TestCase):
users = get_recipients()
self.assertTrue(user in users)
def test_do_reposting_for_each_stock_transaction(self):
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
frappe.db.set_single_value("Stock Reposting Settings", "do_reposting_for_each_stock_transaction", 1)
if frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting"):
frappe.db.set_single_value("Stock Reposting Settings", "item_based_reposting", 0)
item = make_item(
"_Test item for reposting check for each transaction", properties={"is_stock_item": 1}
).name
stock_entry = make_stock_entry(
item_code=item,
qty=1,
rate=100,
stock_entry_type="Material Receipt",
target="_Test Warehouse - _TC",
)
riv = frappe.get_all("Repost Item Valuation", filters={"voucher_no": stock_entry.name}, pluck="name")
self.assertTrue(riv)
frappe.db.set_single_value("Stock Reposting Settings", "do_reposting_for_each_stock_transaction", 0)
def test_do_not_reposting_for_each_stock_transaction(self):
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
frappe.db.set_single_value("Stock Reposting Settings", "do_reposting_for_each_stock_transaction", 0)
if frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting"):
frappe.db.set_single_value("Stock Reposting Settings", "item_based_reposting", 0)
item = make_item(
"_Test item for do not reposting check for each transaction", properties={"is_stock_item": 1}
).name
stock_entry = make_stock_entry(
item_code=item,
qty=1,
rate=100,
stock_entry_type="Material Receipt",
target="_Test Warehouse - _TC",
)
riv = frappe.get_all("Repost Item Valuation", filters={"voucher_no": stock_entry.name}, pluck="name")
self.assertFalse(riv)