diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index 231e9cdf522..f7451830e1d 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -5,8 +5,10 @@
import frappe
from frappe import _, msgprint
from frappe.model.document import Document
+from frappe.query_builder import Case
from frappe.query_builder.custom import ConstantColumn
-from frappe.utils import cint, flt, fmt_money, get_link_to_form, getdate
+from frappe.query_builder.functions import Coalesce, Sum
+from frappe.utils import cint, flt, fmt_money, getdate
from pypika import Order
import erpnext
@@ -182,65 +184,162 @@ def get_payment_entries_for_bank_clearance(
):
entries = []
- condition = ""
- pe_condition = ""
+ journal_entry = frappe.qb.DocType("Journal Entry")
+ journal_entry_account = frappe.qb.DocType("Journal Entry Account")
+
+ journal_entry_query = (
+ frappe.qb.from_(journal_entry_account)
+ .inner_join(journal_entry)
+ .on(journal_entry_account.parent == journal_entry.name)
+ .select(
+ ConstantColumn("Journal Entry").as_("payment_document"),
+ journal_entry.name.as_("payment_entry"),
+ journal_entry.cheque_no.as_("cheque_number"),
+ journal_entry.cheque_date,
+ Sum(journal_entry_account.debit_in_account_currency).as_("debit"),
+ Sum(journal_entry_account.credit_in_account_currency).as_("credit"),
+ journal_entry.posting_date,
+ journal_entry_account.against_account,
+ journal_entry.clearance_date,
+ journal_entry_account.account_currency,
+ )
+ .where(
+ (journal_entry_account.account == account)
+ & (journal_entry.docstatus == 1)
+ & (journal_entry.posting_date >= from_date)
+ & (journal_entry.posting_date <= to_date)
+ & (journal_entry.is_opening == "No")
+ )
+ )
+
if not include_reconciled_entries:
- condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
- pe_condition = "and (pe.clearance_date IS NULL or pe.clearance_date='0000-00-00')"
+ journal_entry_query = journal_entry_query.where(
+ (journal_entry.clearance_date.isnull()) | (journal_entry.clearance_date == "0000-00-00")
+ )
- journal_entries = frappe.db.sql(
- f"""
- select
- "Journal Entry" as payment_document, t1.name as payment_entry,
- t1.cheque_no as cheque_number, t1.cheque_date,
- sum(t2.debit_in_account_currency) as debit, sum(t2.credit_in_account_currency) as credit,
- t1.posting_date, t2.against_account, t1.clearance_date, t2.account_currency
- from
- `tabJournal Entry` t1, `tabJournal Entry Account` t2
- where
- t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1
- and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s
- and ifnull(t1.is_opening, 'No') = 'No' {condition}
- group by t2.account, t1.name
- order by t1.posting_date ASC, t1.name DESC
- """,
- {"account": account, "from": from_date, "to": to_date},
- as_dict=1,
+ journal_entries = (
+ journal_entry_query.groupby(journal_entry_account.account, journal_entry.name)
+ .orderby(journal_entry.posting_date)
+ .orderby(journal_entry.name, order=Order.desc)
+ ).run(as_dict=True)
+
+ pe = frappe.qb.DocType("Payment Entry")
+ company = frappe.qb.DocType("Company")
+ payment_entry_query = (
+ frappe.qb.from_(pe)
+ .join(company)
+ .on(pe.company == company.name)
+ .select(
+ ConstantColumn("Payment Entry").as_("payment_document"),
+ pe.name.as_("payment_entry"),
+ pe.reference_no.as_("cheque_number"),
+ pe.reference_date.as_("cheque_date"),
+ (
+ Case()
+ .when(
+ pe.paid_from == account,
+ (
+ pe.paid_amount
+ + (
+ Case()
+ .when(
+ (pe.payment_type == "Pay")
+ & (company.default_currency == pe.paid_from_account_currency),
+ pe.base_total_taxes_and_charges,
+ )
+ .else_(pe.total_taxes_and_charges)
+ )
+ ),
+ )
+ .else_(0)
+ ).as_("credit"),
+ (
+ Case()
+ .when(pe.paid_from == account, 0)
+ .else_(
+ pe.received_amount
+ + (
+ Case()
+ .when(
+ company.default_currency == pe.paid_to_account_currency,
+ pe.base_total_taxes_and_charges,
+ )
+ .else_(pe.total_taxes_and_charges)
+ )
+ )
+ ).as_("debit"),
+ pe.posting_date,
+ Coalesce(pe.party, Case().when(pe.paid_from == account, pe.paid_to).else_(pe.paid_from)).as_(
+ "against_account"
+ ),
+ pe.clearance_date,
+ (
+ Case()
+ .when(pe.paid_to == account, pe.paid_to_account_currency)
+ .else_(pe.paid_from_account_currency)
+ ).as_("account_currency"),
+ )
+ .where(
+ ((pe.paid_from == account) | (pe.paid_to == account))
+ & (pe.docstatus == 1)
+ & (pe.posting_date >= from_date)
+ & (pe.posting_date <= to_date)
+ )
)
- payment_entries = frappe.db.sql(
- f"""
- select
- "Payment Entry" as payment_document, pe.name as payment_entry,
- pe.reference_no as cheque_number, pe.reference_date as cheque_date,
- if(pe.paid_from=%(account)s, pe.paid_amount + if(pe.payment_type = 'Pay' and c.default_currency = pe.paid_from_account_currency, pe.base_total_taxes_and_charges, pe.total_taxes_and_charges) , 0) as credit,
- if(pe.paid_from=%(account)s, 0, pe.received_amount + pe.total_taxes_and_charges) as debit,
- pe.posting_date, ifnull(pe.party,if(pe.paid_from=%(account)s,pe.paid_to,pe.paid_from)) as against_account, pe.clearance_date,
- if(pe.paid_to=%(account)s, pe.paid_to_account_currency, pe.paid_from_account_currency) as account_currency
- from `tabPayment Entry` as pe
- join `tabCompany` c on c.name = pe.company
- where
- (pe.paid_from=%(account)s or pe.paid_to=%(account)s) and pe.docstatus=1
- and pe.posting_date >= %(from)s and pe.posting_date <= %(to)s
- {pe_condition}
- order by
- pe.posting_date ASC, pe.name DESC
- """,
- {
- "account": account,
- "from": from_date,
- "to": to_date,
- },
- as_dict=1,
+ if not include_reconciled_entries:
+ payment_entry_query = payment_entry_query.where(
+ (pe.clearance_date.isnull()) | (pe.clearance_date == "0000-00-00")
+ )
+
+ payment_entries = (payment_entry_query.orderby(pe.posting_date).orderby(pe.name, order=Order.desc)).run(
+ as_dict=True
)
- pos_sales_invoices, pos_purchase_invoices = [], []
+ acc = frappe.qb.DocType("Account")
+
+ pi = frappe.qb.DocType("Purchase Invoice")
+
+ paid_purchase_invoices_query = (
+ frappe.qb.from_(pi)
+ .inner_join(acc)
+ .on(pi.cash_bank_account == acc.name)
+ .select(
+ ConstantColumn("Purchase Invoice").as_("payment_document"),
+ pi.name.as_("payment_entry"),
+ pi.paid_amount.as_("credit"),
+ pi.posting_date,
+ pi.supplier.as_("against_account"),
+ pi.bill_no.as_("cheque_number"),
+ pi.clearance_date,
+ acc.account_currency,
+ ConstantColumn(0).as_("debit"),
+ )
+ .where(
+ (pi.docstatus == 1)
+ & (pi.is_paid == 1)
+ & (pi.cash_bank_account == account)
+ & (pi.posting_date >= from_date)
+ & (pi.posting_date <= to_date)
+ )
+ )
+
+ if not include_reconciled_entries:
+ paid_purchase_invoices_query = paid_purchase_invoices_query.where(
+ (pi.clearance_date.isnull()) | (pi.clearance_date == "0000-00-00")
+ )
+
+ paid_purchase_invoices = (
+ paid_purchase_invoices_query.orderby(pi.posting_date).orderby(pi.name, order=Order.desc)
+ ).run(as_dict=True)
+
+ pos_sales_invoices = []
+
if include_pos_transactions:
si_payment = frappe.qb.DocType("Sales Invoice Payment")
si = frappe.qb.DocType("Sales Invoice")
- acc = frappe.qb.DocType("Account")
- pos_sales_invoices = (
+ pos_sales_invoices_query = (
frappe.qb.from_(si_payment)
.inner_join(si)
.on(si_payment.parent == si.name)
@@ -263,38 +362,22 @@ def get_payment_entries_for_bank_clearance(
& (si.posting_date >= from_date)
& (si.posting_date <= to_date)
)
- .orderby(si.posting_date)
- .orderby(si.name, order=Order.desc)
- ).run(as_dict=True)
+ )
- pi = frappe.qb.DocType("Purchase Invoice")
+ if not include_reconciled_entries:
+ pos_sales_invoices_query = pos_sales_invoices_query.where(
+ (si_payment.clearance_date.isnull()) | (si_payment.clearance_date == "0000-00-00")
+ )
- pos_purchase_invoices = (
- frappe.qb.from_(pi)
- .inner_join(acc)
- .on(pi.cash_bank_account == acc.name)
- .select(
- ConstantColumn("Purchase Invoice").as_("payment_document"),
- pi.name.as_("payment_entry"),
- pi.paid_amount.as_("credit"),
- pi.posting_date,
- pi.supplier.as_("against_account"),
- pi.clearance_date,
- acc.account_currency,
- ConstantColumn(0).as_("debit"),
- )
- .where(
- (pi.docstatus == 1)
- & (pi.cash_bank_account == account)
- & (pi.posting_date >= from_date)
- & (pi.posting_date <= to_date)
- )
- .orderby(pi.posting_date)
- .orderby(pi.name, order=Order.desc)
+ pos_sales_invoices = (
+ pos_sales_invoices_query.orderby(si.posting_date).orderby(si.name, order=Order.desc)
).run(as_dict=True)
entries = (
- list(payment_entries) + list(journal_entries) + list(pos_sales_invoices) + list(pos_purchase_invoices)
+ list(payment_entries)
+ + list(journal_entries)
+ + list(pos_sales_invoices)
+ + list(paid_purchase_invoices)
)
return entries
diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
index ae3dbd59d69..9f0852bb686 100644
--- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
+++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
@@ -101,11 +101,11 @@
"label": "Use HTTP Protocol"
}
],
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:19:02.873815",
+ "modified": "2026-03-16 13:28:21.075743",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Currency Exchange Settings",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 6428896acae..23fb4fd0825 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -293,6 +293,8 @@ class JournalEntry(AccountsController):
# References for this Journal are removed on the `on_cancel` event in accounts_controller
super().on_cancel()
+
+ from_doc_events = getattr(self, "ignore_linked_doctypes", ())
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
@@ -306,6 +308,10 @@ class JournalEntry(AccountsController):
"Advance Payment Ledger Entry",
"Tax Withholding Entry",
)
+
+ if from_doc_events and from_doc_events != self.ignore_linked_doctypes:
+ self.ignore_linked_doctypes = self.ignore_linked_doctypes + from_doc_events
+
self.make_gl_entries(1)
JournalTaxWithholding(self).on_cancel()
self.unlink_advance_entry_reference()
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index c68672466c2..289c19a5885 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -2556,14 +2556,9 @@ def get_orders_to_be_billed(
if not voucher_type:
return []
- # Add cost center condition
- doc = frappe.get_doc({"doctype": voucher_type})
- condition = ""
- if doc and hasattr(doc, "cost_center") and doc.cost_center:
- condition = " and cost_center='%s'" % cost_center
-
# dynamic dimension filters
- active_dimensions = get_dimensions()[0]
+ condition = ""
+ active_dimensions = get_dimensions(True)[0]
for dim in active_dimensions:
if filters.get(dim.fieldname):
condition += f" and {dim.fieldname}='{filters.get(dim.fieldname)}'"
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
index 59a01bc84ef..8d4e55c970a 100644
--- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
@@ -22,6 +22,7 @@
"reqd": 1
},
{
+ "allow_on_submit": 1,
"fieldname": "cost_center",
"fieldtype": "Link",
"in_list_view": 1,
@@ -59,7 +60,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2025-08-13 06:52:46.130142",
+ "modified": "2026-03-11 14:26:11.312950",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Deduction",
diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.json b/erpnext/accounts/doctype/pos_settings/pos_settings.json
index 7afc19423d8..8e9adc56f14 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.json
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.json
@@ -50,10 +50,10 @@
"options": "1"
}
],
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"issingle": 1,
"links": [],
- "modified": "2026-01-09 17:30:41.476806",
+ "modified": "2026-03-16 13:28:19.677217",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Settings",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 24c32fca451..d2021e0f9a4 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -321,7 +321,7 @@
"fieldtype": "Date",
"in_list_view": 1,
"in_standard_filter": 1,
- "label": "Date",
+ "label": "Posting Date",
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"print_hide": 1,
@@ -391,7 +391,7 @@
},
{
"collapsible": 1,
- "collapsible_depends_on": "bill_no",
+ "collapsible_depends_on": "posting_date",
"fieldname": "supplier_invoice_details",
"fieldtype": "Section Break",
"label": "Supplier Invoice"
@@ -1693,7 +1693,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2026-03-09 17:15:27.014131",
+ "modified": "2026-03-17 20:44:00.221219",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json
index ba8188c1440..808986bba23 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json
+++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json
@@ -15,11 +15,11 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"in_create": 1,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:19:08.888368",
+ "modified": "2026-03-16 13:28:21.312607",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Accounting Ledger Settings",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index b67552788fa..50734582dad 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -383,7 +383,7 @@
"hide_seconds": 1,
"in_list_view": 1,
"in_standard_filter": 1,
- "label": "Date",
+ "label": "Posting Date",
"no_copy": 1,
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index fb32b4db364..75a9fae92e9 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -919,11 +919,9 @@ class SalesInvoice(SellingController):
if self.pos_profile:
pos = frappe.get_doc("POS Profile", self.pos_profile)
- if not self.get("payments") and not for_validate:
- update_multi_mode_option(self, pos)
-
if pos:
if not for_validate:
+ update_multi_mode_option(self, pos)
self.tax_category = pos.get("tax_category")
if not for_validate and not self.customer:
@@ -3008,6 +3006,8 @@ def update_multi_mode_option(doc, pos_profile):
payment.account = payment_mode.default_account
payment.type = payment_mode.type
+ mop_refetched = bool(doc.payments) and not doc.is_created_using_pos
+
doc.set("payments", [])
invalid_modes = []
mode_of_payments = [d.mode_of_payment for d in pos_profile.get("payments")]
@@ -3029,6 +3029,12 @@ def update_multi_mode_option(doc, pos_profile):
msg = _("Please set default Cash or Bank account in Mode of Payments {}")
frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
+ if mop_refetched:
+ frappe.toast(
+ _("Payment methods refreshed. Please review before proceeding."),
+ indicator="orange",
+ )
+
def get_all_mode_of_payments(doc):
return frappe.db.sql(
diff --git a/erpnext/accounts/doctype/subscription_settings/subscription_settings.json b/erpnext/accounts/doctype/subscription_settings/subscription_settings.json
index 326fee345dc..06112d51fd1 100644
--- a/erpnext/accounts/doctype/subscription_settings/subscription_settings.json
+++ b/erpnext/accounts/doctype/subscription_settings/subscription_settings.json
@@ -31,10 +31,10 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:34.671062",
+ "modified": "2026-03-16 13:28:20.485964",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription Settings",
diff --git a/erpnext/accounts/print_format/point_of_sale/__init__.py b/erpnext/accounts/print_format/point_of_sale/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json
deleted file mode 100644
index c0c50cb4e26..00000000000
--- a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "align_labels_right": 0,
- "creation": "2016-05-05 17:16:18.564460",
- "custom_format": 1,
- "disabled": 0,
- "doc_type": "Sales Invoice",
- "docstatus": 0,
- "doctype": "Print Format",
- "font": "Default",
- "html": "\n\n
\n\t{{ company }}
\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}
\n
\n\n\t{{ __(\"Customer\") }}: {{ customer }}
\n
\n\n\n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
\n
\n\n
\n\n\t\n\t\t\n\t\t\t| {{ __(\"Item\") }} | \n\t\t\t{{ __(\"Qty\") }} | \n\t\t\t{{ __(\"Amount\") }} | \n\t\t
\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t| \n\t\t\t\t{{ item.item_name }}\n\t\t\t | \n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }} @ {{ format_currency(item.rate, currency) }} | \n\t\t\t{{ format_currency(item.amount, currency) }} | \n\t\t
\n\t\t{% endfor %}\n\t\n
\n\n\n\t\n\t\t\n\t\t\t| \n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t | \n\t\t
\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t| \n\t\t\t\t{{ row.description }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t | \n\t\t
\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t| \n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t | \n\t\t
\n\t\t{% endif %}\n\t\t\n\t\t\t| \n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t | \n\t\t
\n\t\t\n\t\t\t| \n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t | \n\t\t
\n\t\t\n\t\t\t| \n\t\t\t\t{{ __(\"Qty Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ qty_total }}\n\t\t\t | \n\t\t
\n\t\n
\n\n\n
\n{{ terms }}
\n{{ __(\"Thank you, please visit again.\") }}
",
- "idx": 0,
- "line_breaks": 0,
- "modified": "2019-09-05 17:20:30.726659",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Point of Sale",
- "owner": "Administrator",
- "print_format_builder": 0,
- "print_format_type": "JS",
- "raw_printing": 0,
- "show_section_headings": 0,
- "standard": "Yes"
-}
\ No newline at end of file
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index ae675670446..5320de2b66c 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -4,7 +4,10 @@
import frappe
from frappe import _
-from frappe.utils import getdate, nowdate
+from frappe.query_builder import Case
+from frappe.query_builder.custom import ConstantColumn
+from frappe.utils import getdate
+from pypika import Order
def execute(filters=None):
@@ -48,17 +51,6 @@ def get_columns():
return columns
-def get_conditions(filters):
- conditions = ""
-
- if filters.get("from_date"):
- conditions += " and posting_date>=%(from_date)s"
- if filters.get("to_date"):
- conditions += " and posting_date<=%(to_date)s"
-
- return conditions
-
-
def get_entries(filters):
entries = []
@@ -73,41 +65,90 @@ def get_entries(filters):
return sorted(
entries,
- key=lambda k: k[2].strftime("%H%M%S") or getdate(nowdate()),
+ key=lambda k: getdate(k[2]),
)
def get_entries_for_bank_clearance_summary(filters):
entries = []
- conditions = get_conditions(filters)
+ je = frappe.qb.DocType("Journal Entry")
+ jea = frappe.qb.DocType("Journal Entry Account")
- journal_entries = frappe.db.sql(
- f"""SELECT
- "Journal Entry", jv.name, jv.posting_date, jv.cheque_no,
- jv.clearance_date, jvd.against_account, jvd.debit - jvd.credit
- FROM
- `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- WHERE
- jvd.parent = jv.name and jv.docstatus=1 and jvd.account = %(account)s {conditions}
- order by posting_date DESC, jv.name DESC""",
- filters,
- as_list=1,
- )
+ journal_entries = (
+ frappe.qb.from_(jea)
+ .inner_join(je)
+ .on(jea.parent == je.name)
+ .select(
+ ConstantColumn("Journal Entry").as_("payment_document"),
+ je.name.as_("payment_entry"),
+ je.posting_date,
+ je.cheque_no,
+ je.clearance_date,
+ jea.against_account,
+ jea.debit_in_account_currency - jea.credit_in_account_currency,
+ )
+ .where(
+ (jea.account == filters.account)
+ & (je.docstatus == 1)
+ & (je.posting_date >= filters.from_date)
+ & (je.posting_date <= filters.to_date)
+ & ((je.is_opening == "No") | (je.is_opening.isnull()))
+ )
+ .orderby(je.posting_date, order=Order.desc)
+ .orderby(je.name, order=Order.desc)
+ ).run(as_list=True)
- payment_entries = frappe.db.sql(
- f"""SELECT
- "Payment Entry", name, posting_date, reference_no, clearance_date, party,
- if(paid_from=%(account)s, ((paid_amount * -1) - total_taxes_and_charges) , received_amount)
- FROM
- `tabPayment Entry`
- WHERE
- docstatus=1 and (paid_from = %(account)s or paid_to = %(account)s) {conditions}
- order by posting_date DESC, name DESC""",
- filters,
- as_list=1,
- )
+ pe = frappe.qb.DocType("Payment Entry")
+ payment_entries = (
+ frappe.qb.from_(pe)
+ .select(
+ ConstantColumn("Payment Entry").as_("payment_document"),
+ pe.name.as_("payment_entry"),
+ pe.posting_date,
+ pe.reference_no.as_("cheque_no"),
+ pe.clearance_date,
+ pe.party.as_("against_account"),
+ Case()
+ .when(
+ (pe.paid_from == filters.account),
+ ((pe.paid_amount * -1) - pe.total_taxes_and_charges),
+ )
+ .else_(pe.received_amount),
+ )
+ .where((pe.paid_from == filters.account) | (pe.paid_to == filters.account))
+ .where(
+ (pe.docstatus == 1)
+ & (pe.posting_date >= filters.from_date)
+ & (pe.posting_date <= filters.to_date)
+ )
+ .orderby(pe.posting_date, order=Order.desc)
+ .orderby(pe.name, order=Order.desc)
+ ).run(as_list=True)
- entries = journal_entries + payment_entries
+ pi = frappe.qb.DocType("Purchase Invoice")
+ purchase_invoices = (
+ frappe.qb.from_(pi)
+ .select(
+ ConstantColumn("Purchase Invoice").as_("payment_document"),
+ pi.name.as_("payment_entry"),
+ pi.posting_date,
+ pi.bill_no.as_("cheque_no"),
+ pi.clearance_date,
+ pi.supplier.as_("against_account"),
+ (pi.paid_amount * -1).as_("amount"),
+ )
+ .where(
+ (pi.docstatus == 1)
+ & (pi.is_paid == 1)
+ & (pi.cash_bank_account == filters.account)
+ & (pi.posting_date >= filters.from_date)
+ & (pi.posting_date <= filters.to_date)
+ )
+ .orderby(pi.posting_date, order=Order.desc)
+ .orderby(pi.name, order=Order.desc)
+ ).run(as_list=True)
+
+ entries = journal_entries + payment_entries + purchase_invoices
return entries
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index bfc2f2d56ff..474e25c5474 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -4,7 +4,11 @@
import frappe
from frappe import _
+from frappe.query_builder import Case
+from frappe.query_builder.custom import ConstantColumn
+from frappe.query_builder.functions import Coalesce, Sum
from frappe.utils import flt, getdate
+from pypika import Order
from erpnext.accounts.utils import get_balance_on
@@ -123,73 +127,143 @@ def get_entries_for_bank_reconciliation_statement(filters):
payment_entries = get_payment_entries(filters)
+ purchase_invoices = get_purchase_invoices(filters)
+
pos_entries = []
if filters.include_pos_transactions:
pos_entries = get_pos_entries(filters)
- return list(journal_entries) + list(payment_entries) + list(pos_entries)
+ return list(journal_entries) + list(payment_entries) + list(pos_entries) + list(purchase_invoices)
def get_journal_entries(filters):
- return frappe.db.sql(
- """
- select "Journal Entry" as payment_document, jv.posting_date,
- jv.name as payment_entry, jvd.debit_in_account_currency as debit,
- jvd.credit_in_account_currency as credit, jvd.against_account,
- jv.cheque_no as reference_no, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
- from
- `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1
- and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
- and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
- and ifnull(jv.is_opening, 'No') = 'No'
- and jv.company = %(company)s """,
- filters,
- as_dict=1,
- )
+ je = frappe.qb.DocType("Journal Entry")
+ jea = frappe.qb.DocType("Journal Entry Account")
+ return (
+ frappe.qb.from_(jea)
+ .join(je)
+ .on(jea.parent == je.name)
+ .select(
+ ConstantColumn("Journal Entry").as_("payment_document"),
+ je.name.as_("payment_entry"),
+ je.posting_date,
+ jea.debit_in_account_currency.as_("debit"),
+ jea.credit_in_account_currency.as_("credit"),
+ jea.against_account,
+ je.cheque_no.as_("reference_no"),
+ je.cheque_date.as_("ref_date"),
+ je.clearance_date,
+ jea.account_currency,
+ )
+ .where(
+ (je.docstatus == 1)
+ & (jea.account == filters.account)
+ & (je.posting_date <= filters.report_date)
+ & (je.clearance_date.isnull() | (je.clearance_date > filters.report_date))
+ & (je.company == filters.company)
+ & ((je.is_opening.isnull()) | (je.is_opening == "No"))
+ )
+ .orderby(je.posting_date)
+ .orderby(je.name, order=Order.desc)
+ ).run(as_dict=True)
def get_payment_entries(filters):
- return frappe.db.sql(
- """
- select
- "Payment Entry" as payment_document, name as payment_entry,
- reference_no, reference_date as ref_date,
- if(paid_to=%(account)s, received_amount_after_tax, 0) as debit,
- if(paid_from=%(account)s, paid_amount_after_tax, 0) as credit,
- posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
- if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
- from `tabPayment Entry`
- where
- (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
- and posting_date <= %(report_date)s
- and ifnull(clearance_date, '4000-01-01') > %(report_date)s
- and company = %(company)s
- """,
- filters,
- as_dict=1,
- )
+ pe = frappe.qb.DocType("Payment Entry")
+ return (
+ frappe.qb.from_(pe)
+ .select(
+ ConstantColumn("Payment Entry").as_("payment_document"),
+ pe.name.as_("payment_entry"),
+ pe.reference_no.as_("reference_no"),
+ pe.reference_date.as_("ref_date"),
+ Case().when(pe.paid_to == filters.account, pe.received_amount_after_tax).else_(0).as_("debit"),
+ Case().when(pe.paid_from == filters.account, pe.paid_amount_after_tax).else_(0).as_("credit"),
+ pe.posting_date,
+ Coalesce(
+ pe.party, Case().when(pe.paid_from == filters.account, pe.paid_to).else_(pe.paid_from)
+ ).as_("against_account"),
+ pe.clearance_date,
+ (
+ Case()
+ .when(pe.paid_to == filters.account, pe.paid_to_account_currency)
+ .else_(pe.paid_from_account_currency)
+ ).as_("account_currency"),
+ )
+ .where(
+ (pe.docstatus == 1)
+ & ((pe.paid_from == filters.account) | (pe.paid_to == filters.account))
+ & (pe.posting_date <= filters.report_date)
+ & (pe.clearance_date.isnull() | (pe.clearance_date > filters.report_date))
+ & (pe.company == filters.company)
+ )
+ .orderby(pe.posting_date)
+ .orderby(pe.name, order=Order.desc)
+ ).run(as_dict=True)
+
+
+def get_purchase_invoices(filters):
+ pi = frappe.qb.DocType("Purchase Invoice")
+ acc = frappe.qb.DocType("Account")
+ return (
+ frappe.qb.from_(pi)
+ .inner_join(acc)
+ .on(pi.cash_bank_account == acc.name)
+ .select(
+ ConstantColumn("Purchase Invoice").as_("payment_document"),
+ pi.name.as_("payment_entry"),
+ pi.bill_no.as_("reference_no"),
+ pi.posting_date.as_("ref_date"),
+ Case().when(pi.paid_amount < 0, pi.paid_amount * -1).else_(0).as_("debit"),
+ Case().when(pi.paid_amount > 0, pi.paid_amount).else_(0).as_("credit"),
+ pi.posting_date,
+ pi.supplier.as_("against_account"),
+ pi.clearance_date,
+ acc.account_currency,
+ )
+ .where(
+ (pi.docstatus == 1)
+ & (pi.is_paid == 1)
+ & (pi.cash_bank_account == filters.account)
+ & (pi.posting_date <= filters.report_date)
+ & (pi.clearance_date.isnull() | (pi.clearance_date > filters.report_date))
+ & (pi.company == filters.company)
+ )
+ .orderby(pi.posting_date)
+ .orderby(pi.name, order=Order.desc)
+ ).run(as_dict=True)
def get_pos_entries(filters):
- return frappe.db.sql(
- """
- select
- "Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
- si.posting_date, si.debit_to as against_account, sip.clearance_date,
- account.account_currency, 0 as credit
- from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
- where
- sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
- and account.name = sip.account and si.posting_date <= %(report_date)s and
- ifnull(sip.clearance_date, '4000-01-01') > %(report_date)s
- and si.company = %(company)s
- order by
- si.posting_date ASC, si.name DESC
- """,
- filters,
- as_dict=1,
- )
+ si = frappe.qb.DocType("Sales Invoice")
+ si_payment = frappe.qb.DocType("Sales Invoice Payment")
+ acc = frappe.qb.DocType("Account")
+ return (
+ frappe.qb.from_(si_payment)
+ .join(si)
+ .on(si_payment.parent == si.name)
+ .join(acc)
+ .on(si_payment.account == acc.name)
+ .select(
+ ConstantColumn("Sales Invoice").as_("payment_document"),
+ si.name.as_("payment_entry"),
+ si_payment.amount.as_("debit"),
+ si.posting_date,
+ si.debit_to.as_("against_account"),
+ si_payment.clearance_date,
+ acc.account_currency,
+ ConstantColumn(0).as_("credit"),
+ )
+ .where(
+ (si_payment.account == filters.account)
+ & (si.docstatus == 1)
+ & (si.posting_date <= filters.report_date)
+ & (si_payment.clearance_date.isnull() | (si_payment.clearance_date > filters.report_date))
+ & (si.company == filters.company)
+ )
+ .orderby(si.posting_date)
+ .orderby(si_payment.name, order=Order.desc)
+ ).run(as_dict=True)
def get_amounts_not_reflected_in_system(filters):
@@ -205,30 +279,66 @@ def get_amounts_not_reflected_in_system(filters):
def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters):
- je_amount = frappe.db.sql(
- """
- select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
- from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%(account)s
- and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
- and ifnull(jv.is_opening, 'No') = 'No' """,
- filters,
+ je = frappe.qb.DocType("Journal Entry")
+ jea = frappe.qb.DocType("Journal Entry Account")
+
+ je_amount = (
+ frappe.qb.from_(jea)
+ .inner_join(je)
+ .on(jea.parent == je.name)
+ .select(
+ Sum(jea.debit_in_account_currency - jea.credit_in_account_currency).as_("amount"),
+ )
+ .where(
+ (je.docstatus == 1)
+ & (jea.account == filters.account)
+ & (je.posting_date > filters.report_date)
+ & (je.clearance_date <= filters.report_date)
+ & (je.company == filters.company)
+ & ((je.is_opening.isnull()) | (je.is_opening == "No"))
+ )
+ .run(as_dict=True)
)
+ je_amount = flt(je_amount[0].amount) if je_amount else 0.0
- je_amount = flt(je_amount[0][0]) if je_amount else 0.0
-
- pe_amount = frappe.db.sql(
- """
- select sum(if(paid_from=%(account)s, paid_amount, received_amount))
- from `tabPayment Entry`
- where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
- and posting_date > %(report_date)s and clearance_date <= %(report_date)s""",
- filters,
+ pe = frappe.qb.DocType("Payment Entry")
+ pe_amount = (
+ frappe.qb.from_(pe)
+ .select(
+ Sum(Case().when(pe.paid_from == filters.account, pe.paid_amount).else_(pe.received_amount)).as_(
+ "amount"
+ ),
+ )
+ .where(
+ ((pe.paid_from == filters.account) | (pe.paid_to == filters.account))
+ & (pe.docstatus == 1)
+ & (pe.posting_date > filters.report_date)
+ & (pe.clearance_date <= filters.report_date)
+ & (pe.company == filters.company)
+ )
+ .run(as_dict=True)
)
+ pe_amount = flt(pe_amount[0].amount) if pe_amount else 0.0
- pe_amount = flt(pe_amount[0][0]) if pe_amount else 0.0
+ pi = frappe.qb.DocType("Purchase Invoice")
+ pi_amount = (
+ frappe.qb.from_(pi)
+ .select(
+ Sum(pi.paid_amount).as_("amount"),
+ )
+ .where(
+ (pi.docstatus == 1)
+ & (pi.is_paid == 1)
+ & (pi.cash_bank_account == filters.account)
+ & (pi.posting_date > filters.report_date)
+ & (pi.clearance_date <= filters.report_date)
+ & (pi.company == filters.company)
+ )
+ ).run(as_dict=True)
- return je_amount + pe_amount
+ pi_amount = flt(pi_amount[0].amount) if pi_amount else 0.0
+
+ return je_amount + pe_amount + pi_amount
def get_balance_row(label, amount, account_currency):
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index 7166e5da691..0f0121f4e6c 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -32,6 +32,7 @@ def _execute(filters=None, additional_table_columns=None):
item_list = get_items(filters, additional_table_columns)
aii_account_map = get_aii_accounts()
+ default_taxes = {}
if item_list:
itemised_tax, tax_columns = get_tax_accounts(
item_list,
@@ -40,6 +41,9 @@ def _execute(filters=None, additional_table_columns=None):
doctype="Purchase Invoice",
tax_doctype="Purchase Taxes and Charges",
)
+ for tax in tax_columns:
+ default_taxes[f"{tax}_rate"] = 0
+ default_taxes[f"{tax}_amount"] = 0
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
@@ -87,6 +91,7 @@ def _execute(filters=None, additional_table_columns=None):
total_tax = 0
total_other_charges = 0
+ row.update(default_taxes.copy())
for tax, details in itemised_tax.get(d.name, {}).items():
row.update(
{
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index d77eb56525f..d849aa7e285 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -33,6 +33,10 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
return columns, [], None, None, None, 0
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
+ default_taxes = {}
+ for tax in tax_columns:
+ default_taxes[f"{tax}_rate"] = 0
+ default_taxes[f"{tax}_amount"] = 0
mode_of_payments = get_mode_of_payments(set(d.parent for d in item_list))
so_dn_map = get_delivery_notes_against_sales_order(item_list)
@@ -90,6 +94,9 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
total_tax = 0
total_other_charges = 0
+
+ row.update(default_taxes.copy())
+
for tax, details in itemised_tax.get(d.name, {}).items():
row.update(
{
diff --git a/erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py
index 34ec398e7f3..09a2b4a46d6 100644
--- a/erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py
@@ -16,9 +16,11 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase):
def tearDown(self):
frappe.db.rollback()
- def create_sales_invoice(self, do_not_submit=False):
+ def create_sales_invoice(self, item=None, taxes=None, do_not_submit=False):
si = create_sales_invoice(
- item=self.item,
+ item=item or self.item,
+ item_name=item or self.item,
+ description=item or self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
@@ -29,6 +31,19 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase):
price_list_rate=100,
do_not_save=1,
)
+
+ for tax in taxes or []:
+ si.append(
+ "taxes",
+ {
+ "charge_type": "On Net Total",
+ "account_head": tax["account_head"],
+ "cost_center": self.cost_center,
+ "description": tax["description"],
+ "rate": tax["rate"],
+ },
+ )
+
si = si.save()
if not do_not_submit:
si = si.submit()
@@ -62,3 +77,50 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase):
report_output = {k: v for k, v in report[1][0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)
+
+ def test_grouped_report_handles_different_tax_descriptions(self):
+ self.create_item(item_name="_Test Item Tax Description A")
+ first_item = self.item
+ self.create_item(item_name="_Test Item Tax Description B")
+ second_item = self.item
+
+ first_tax_description = "Tax Description A"
+ second_tax_description = "Tax Description B"
+ first_tax_amount_field = f"{frappe.scrub(first_tax_description)}_amount"
+ second_tax_amount_field = f"{frappe.scrub(second_tax_description)}_amount"
+
+ self.create_sales_invoice(
+ item=first_item,
+ taxes=[
+ {
+ "account_head": "_Test Account VAT - _TC",
+ "description": first_tax_description,
+ "rate": 5,
+ }
+ ],
+ )
+ self.create_sales_invoice(
+ item=second_item,
+ taxes=[
+ {
+ "account_head": "_Test Account Service Tax - _TC",
+ "description": second_tax_description,
+ "rate": 2,
+ }
+ ],
+ )
+
+ filters = frappe._dict(
+ {
+ "from_date": today(),
+ "to_date": today(),
+ "company": self.company,
+ "group_by": "Customer",
+ }
+ )
+ _, data, _, _, _, _ = execute(filters)
+
+ grand_total_row = next(row for row in data if row.get("bold") and row.get("item_code") == "Total")
+
+ self.assertEqual(grand_total_row[first_tax_amount_field], 5.0)
+ self.assertEqual(grand_total_row[second_tax_amount_field], 2.0)
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
index e679f1f5728..5c9aaa62c94 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
@@ -39,7 +39,7 @@ frappe.query_reports[PL_REPORT_NAME]["filters"].push(
fieldname: "accumulated_values",
label: __("Accumulated Values"),
fieldtype: "Check",
- default: 1,
+ default: 0,
},
{
fieldname: "include_default_book_entries",
diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json
index 2080f51933a..37556b6b4c2 100644
--- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json
+++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json
@@ -7,10 +7,10 @@
"docstatus": 0,
"doctype": "Report",
"filters": [],
- "idx": 3,
+ "idx": 4,
"is_standard": "Yes",
- "letterhead": null,
- "modified": "2025-11-05 11:55:49.950442",
+ "letter_head": null,
+ "modified": "2026-03-13 17:35:39.703838",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Trends",
diff --git a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json
index 1ed34ff4c36..93aa6567f0c 100644
--- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json
+++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json
@@ -9,8 +9,8 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
- "letterhead": null,
- "modified": "2025-11-05 11:55:50.070651",
+ "letter_head": null,
+ "modified": "2026-03-13 17:36:13.725601",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Trends",
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index cf6a7c12326..7a23a39497b 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -1536,7 +1536,7 @@ def parse_naming_series_variable(doc, variable):
getdate(doc.get("posting_date") or doc.get("transaction_date") or doc.get("posting_datetime"))
or now_datetime()
)
- if frappe.get_single_value("Global Defaults", "use_posting_datetime_for_naming_documents")
+ if doc and frappe.get_single_value("Global Defaults", "use_posting_datetime_for_naming_documents")
else now_datetime()
)
return date.strftime(data[variable]) if variable in data else determine_consecutive_week_number(date)
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 2a7703495bf..5a5f22b5788 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -36,6 +36,7 @@ frappe.ui.form.on("Asset", {
},
company: function (frm) {
+ frm.trigger("set_dynamic_labels");
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
},
@@ -87,6 +88,8 @@ frappe.ui.form.on("Asset", {
},
refresh: async function (frm) {
+ frm.trigger("set_dynamic_labels");
+
frappe.ui.form.trigger("Asset", "asset_type");
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
@@ -227,6 +230,10 @@ frappe.ui.form.on("Asset", {
}
},
+ set_dynamic_labels: function (frm) {
+ frm.set_currency_labels(["net_purchase_amount"], erpnext.get_currency(frm.doc.company));
+ },
+
should_show_accounting_ledger: async function (frm) {
if (["Capitalized"].includes(frm.doc.status)) {
return false;
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 9fe15940970..c048e972882 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -9,13 +9,13 @@
"engine": "InnoDB",
"field_order": [
"naming_series",
- "company",
"item_code",
"item_name",
"asset_name",
+ "location",
"image",
"column_break_3",
- "location",
+ "company",
"asset_category",
"asset_type",
"maintenance_required",
@@ -533,7 +533,7 @@
"fieldtype": "Currency",
"label": "Net Purchase Amount",
"mandatory_depends_on": "eval:(doc.asset_type != \"Composite Asset\" || doc.docstatus==1)",
- "options": "Company:company:default_currency",
+ "options": "currency",
"read_only_depends_on": "eval: doc.asset_type == \"Composite Asset\""
},
{
@@ -626,7 +626,7 @@
"link_fieldname": "target_asset"
}
],
- "modified": "2026-03-09 17:15:32.819896",
+ "modified": "2026-03-12 16:07:39.543227",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js
index bdbf60f653e..4d9ef28ceae 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.js
@@ -86,6 +86,26 @@ frappe.ui.form.on("Asset Repair", {
}
},
+ show_general_ledger: function (frm) {
+ if (frm.doc.docstatus > 0) {
+ frm.add_custom_button(
+ __("Accounting Ledger"),
+ function () {
+ frappe.route_options = {
+ voucher_no: frm.doc.name,
+ from_date: frm.doc.completion_date,
+ to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
+ company: frm.doc.company,
+ categorize_by: "",
+ show_cancelled_entries: frm.doc.docstatus === 2,
+ };
+ frappe.set_route("query-report", "General Ledger");
+ },
+ __("View")
+ );
+ }
+ },
+
repair_status: (frm) => {
if (frm.doc.completion_date && frm.doc.repair_status == "Completed") {
frappe.call({
@@ -164,26 +184,6 @@ frappe.ui.form.on("Asset Repair Purchase Invoice", {
},
});
},
-
- show_general_ledger: (frm) => {
- if (frm.doc.docstatus > 0) {
- frm.add_custom_button(
- __("Accounting Ledger"),
- function () {
- frappe.route_options = {
- voucher_no: frm.doc.name,
- from_date: frm.doc.posting_date,
- to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
- company: frm.doc.company,
- categorize_by: "",
- show_cancelled_entries: frm.doc.docstatus === 2,
- };
- frappe.set_route("query-report", "General Ledger");
- },
- __("View")
- );
- }
- },
});
frappe.ui.form.on("Asset Repair Consumed Item", {
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index fbe17f3dbbc..5a0edbdf5d1 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -282,13 +282,13 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:16:35.885540",
+ "modified": "2026-03-16 13:28:19.432589",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 87435f19393..bda926b4040 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -67,7 +67,7 @@ frappe.ui.form.on("Purchase Order", {
},
transaction_date(frm) {
- prevent_past_schedule_dates(frm);
+ erpnext.buying.prevent_past_schedule_dates(frm);
frm.set_value("schedule_date", "");
},
@@ -87,7 +87,7 @@ frappe.ui.form.on("Purchase Order", {
if (frm.doc.docstatus == 0) {
erpnext.set_unit_price_items_note(frm);
}
- prevent_past_schedule_dates(frm);
+ erpnext.buying.prevent_past_schedule_dates(frm);
},
get_materials_from_supplier: function (frm) {
@@ -779,11 +779,3 @@ frappe.ui.form.on("Purchase Order", "is_subcontracted", function (frm) {
erpnext.buying.get_default_bom(frm);
}
});
-
-function prevent_past_schedule_dates(frm) {
- if (frm.doc.transaction_date) {
- frm.fields_dict["schedule_date"].datepicker?.update({
- minDate: new Date(frm.doc.transaction_date),
- });
- }
-}
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 3171ad595b1..b71d0dd3006 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -165,14 +165,10 @@ frappe.ui.form.on("Request for Quotation", {
},
show_supplier_quotation_comparison(frm) {
- const today = new Date();
- const oneMonthAgo = new Date(today);
- oneMonthAgo.setMonth(today.getMonth() - 1);
-
frappe.route_options = {
company: frm.doc.company,
- from_date: moment(oneMonthAgo).format("YYYY-MM-DD"),
- to_date: moment(today).format("YYYY-MM-DD"),
+ from_date: moment(frm.doc.transaction_date).format("YYYY-MM-DD"),
+ to_date: moment(new Date()).format("YYYY-MM-DD"),
request_for_quotation: frm.doc.name,
};
frappe.set_route("query-report", "Supplier Quotation Comparison");
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
index 23a188057d9..b66c1c429d0 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.js
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
@@ -65,6 +65,11 @@ frappe.query_reports["Purchase Analytics"] = {
default: "Monthly",
reqd: 1,
},
+ {
+ fieldname: "show_aggregate_value_from_subsidiary_companies",
+ label: __("Show Aggregate Value from Subsidiary Companies"),
+ fieldtype: "Check",
+ },
],
get_datatable_options(options) {
return Object.assign(options, {
diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json
index 0047d6ecbe5..e53b8e6d669 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json
@@ -9,8 +9,8 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
- "letterhead": null,
- "modified": "2025-11-05 11:55:50.058154",
+ "letter_head": null,
+ "modified": "2026-03-13 17:36:05.561765",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Trends",
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 86cfbc01172..07349a3363f 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -1092,12 +1092,9 @@ class BuyingController(SubcontractingController):
}
)
for dimension in accounting_dimensions[0]:
- asset.update(
- {
- dimension["fieldname"]: self.get(dimension["fieldname"])
- or dimension.get("default_dimension")
- }
- )
+ fieldname = dimension["fieldname"]
+ default_dimension = accounting_dimensions[1].get(self.company, {}).get(fieldname)
+ asset.update({fieldname: row.get(fieldname) or self.get(fieldname) or default_dimension})
asset.flags.ignore_validate = True
asset.flags.ignore_mandatory = True
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 77599e3a009..1e2ef75de93 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -360,13 +360,13 @@ def copy_attributes_to_variant(item, variant):
else:
if item.variant_based_on == "Item Attribute":
if variant.attributes:
- attributes_description = item.description + " "
+ attributes_description = item.description or ""
for d in variant.attributes:
attributes_description += (
"" + d.attribute + ": " + cstr(d.attribute_value) + "
"
)
- if attributes_description not in variant.description:
+ if attributes_description not in (variant.description or ""):
variant.description = attributes_description
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index e96d155701e..b7845bc5698 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -15,6 +15,7 @@ from frappe.utils import cint, nowdate, today, unique
from pypika import Order
import erpnext
+from erpnext.accounts.utils import build_qb_match_conditions
from erpnext.stock.get_item_details import ItemDetailsCtx, _get_item_tax_template
@@ -616,34 +617,37 @@ def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_income_account(doctype, txt, searchfield, start, page_len, filters):
- from erpnext.controllers.queries import get_match_cond
-
# income account can be any Credit account,
# but can also be a Asset account with account_type='Income Account' in special circumstances.
# Hence the first condition is an "OR"
+
if not filters:
filters = {}
- doctype = "Account"
- condition = ""
+ dt = "Account"
+
+ acc = qb.DocType(dt)
+ condition = [
+ (acc.report_type.eq("Profit and Loss") | acc.account_type.isin(["Income Account", "Temporary"])),
+ acc.is_group.eq(0),
+ acc.disabled.eq(0),
+ ]
+ if txt:
+ condition.append(acc.name.like(f"%{txt}%"))
+
if filters.get("company"):
- condition += "and tabAccount.company = %(company)s"
+ condition.append(acc.company.eq(filters.get("company")))
- condition += " and tabAccount.disabled = %(disabled)s"
+ user_perms = build_qb_match_conditions(dt)
+ condition.extend(user_perms)
- return frappe.db.sql(
- f"""select tabAccount.name from `tabAccount`
- where (tabAccount.report_type = "Profit and Loss"
- or tabAccount.account_type in ("Income Account", "Temporary"))
- and tabAccount.is_group=0
- and tabAccount.`{searchfield}` LIKE %(txt)s
- {condition} {get_match_cond(doctype)}
- order by idx desc, name""",
- {
- "txt": "%" + txt + "%",
- "company": filters.get("company", ""),
- "disabled": cint(filters.get("disabled", 0)),
- },
+ return (
+ qb.from_(acc)
+ .select(acc.name)
+ .where(Criterion.all(condition))
+ .orderby(acc.idx, order=Order.desc)
+ .orderby(acc.name)
+ .run()
)
@@ -704,26 +708,38 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters,
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
- from erpnext.controllers.queries import get_match_cond
-
if not filters:
filters = {}
- doctype = "Account"
- condition = ""
- if filters.get("company"):
- condition += "and tabAccount.company = %(company)s"
+ dt = "Account"
- return frappe.db.sql(
- f"""select tabAccount.name from `tabAccount`
- where (tabAccount.report_type = "Profit and Loss"
- or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
- and tabAccount.is_group=0
- and tabAccount.disabled = 0
- and tabAccount.{searchfield} LIKE %(txt)s
- {condition} {get_match_cond(doctype)}""",
- {"company": filters.get("company", ""), "txt": "%" + txt + "%"},
- )
+ acc = qb.DocType(dt)
+ condition = [
+ (
+ acc.report_type.eq("Profit and Loss")
+ | acc.account_type.isin(
+ [
+ "Expense Account",
+ "Fixed Asset",
+ "Temporary",
+ "Asset Received But Not Billed",
+ "Capital Work in Progress",
+ ]
+ )
+ ),
+ acc.is_group.eq(0),
+ acc.disabled.eq(0),
+ ]
+ if txt:
+ condition.append(acc.name.like(f"%{txt}%"))
+
+ if filters.get("company"):
+ condition.append(acc.company.eq(filters.get("company")))
+
+ user_perms = build_qb_match_conditions(dt)
+ condition.extend(user_perms)
+
+ return qb.from_(acc).select(acc.name).where(Criterion.all(condition)).run()
@frappe.whitelist()
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index e3c571e71d8..62eabb6f45a 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -596,7 +596,6 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
target_doc.against_sales_order = source_doc.against_sales_order
target_doc.against_sales_invoice = source_doc.against_sales_invoice
target_doc.so_detail = source_doc.so_detail
- target_doc.si_detail = source_doc.si_detail
target_doc.expense_account = source_doc.expense_account
target_doc.dn_detail = source_doc.name
if default_warehouse_for_sales_return:
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 742d8627fdf..8138a94693c 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -526,6 +526,9 @@ class SellingController(StockController):
if self.doctype not in ("Delivery Note", "Sales Invoice"):
return
+ if self.doctype == "Sales Invoice" and not self.update_stock and not self.is_internal_transfer():
+ return
+
from erpnext.stock.serial_batch_bundle import get_batch_nos, get_serial_nos
allow_at_arms_length_price = frappe.get_cached_value(
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 4ec5e739143..fe0f8a831aa 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -444,7 +444,10 @@ class StatusUpdater(Document):
):
return
- if args["source_dt"] != "Pick List Item" and args["target_dt"] != "Quotation Item":
+ if args["source_dt"] != "Pick List Item" and args["target_dt"] not in [
+ "Quotation Item",
+ "Packed Item",
+ ]:
if qty_or_amount == "qty":
action_msg = _(
'To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.'
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
index d1f70af86f1..b79e974e301 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
@@ -102,10 +102,10 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:46.617101",
+ "modified": "2026-03-16 13:28:21.198138",
"modified_by": "Administrator",
"module": "CRM",
"name": "Appointment Booking Settings",
diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.json b/erpnext/crm/doctype/crm_settings/crm_settings.json
index 3f231d460ab..4760d504a57 100644
--- a/erpnext/crm/doctype/crm_settings/crm_settings.json
+++ b/erpnext/crm/doctype/crm_settings/crm_settings.json
@@ -101,12 +101,12 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"icon": "fa fa-cog",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:52.204988",
+ "modified": "2026-03-16 13:28:19.573964",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM Settings",
diff --git a/erpnext/desktop_icon/banking.json b/erpnext/desktop_icon/banking.json
index 66ee8c7b318..6e55cfa0d02 100644
--- a/erpnext/desktop_icon/banking.json
+++ b/erpnext/desktop_icon/banking.json
@@ -10,7 +10,7 @@
"label": "Banking",
"link_to": "Banking",
"link_type": "Workspace Sidebar",
- "modified": "2026-01-12 12:29:48.687545",
+ "modified": "2026-02-12 12:29:48.687545",
"modified_by": "Administrator",
"name": "Banking",
"owner": "Administrator",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 0780822b191..962fe3e98aa 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -221,11 +221,12 @@ website_route_rules = [
standard_navbar_items = [
{
- "item_label": "Clear Demo Data",
+ "item_label": "Delete Demo Data",
"item_type": "Action",
"action": "erpnext.demo.clear_demo();",
"is_standard": 1,
- "condition": "eval: frappe.boot.sysdefaults.demo_company",
+ "condition": "eval: frappe.boot.sysdefaults.demo_company && frappe.boot.sysdefaults.demo_company.length > 0",
+ "icon": "trash",
},
]
diff --git a/erpnext/locale/main.pot b/erpnext/locale/main.pot
index 2016b23d789..1361fc78c39 100644
--- a/erpnext/locale/main.pot
+++ b/erpnext/locale/main.pot
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: ERPNext VERSION\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n"
-"POT-Creation-Date: 2026-03-08 09:42+0000\n"
-"PO-Revision-Date: 2026-03-08 09:42+0000\n"
+"POT-Creation-Date: 2026-03-15 09:45+0000\n"
+"PO-Revision-Date: 2026-03-15 09:45+0000\n"
"Last-Translator: hello@frappe.io\n"
"Language-Team: hello@frappe.io\n"
"MIME-Version: 1.0\n"
@@ -157,7 +157,7 @@ msgstr ""
msgid "% Delivered"
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:992
+#: erpnext/manufacturing/doctype/bom/bom.js:999
#, python-format
msgid "% Finished Item Quantity"
msgstr ""
@@ -282,7 +282,7 @@ msgstr ""
msgid "'Default {0} Account' in Company {1}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1220
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1226
msgid "'Entries' cannot be empty"
msgstr ""
@@ -1004,7 +1004,7 @@ msgstr ""
msgid "A Reconciliation Job {0} is running for the same filters. Cannot reconcile now"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1754
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1760
msgid "A Reverse Journal Entry {0} already exists for this Journal Entry."
msgstr ""
@@ -1536,7 +1536,7 @@ msgstr ""
msgid "Account {0} is invalid. Account Currency must be {1}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:347
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:353
msgid "Account {0} should be of type Expense"
msgstr ""
@@ -1560,7 +1560,7 @@ msgstr ""
msgid "Account: {0} is capital Work in progress and can not be updated by Journal Entry"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:362
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:368
msgid "Account: {0} can only be updated via Stock Transactions"
msgstr ""
@@ -1866,15 +1866,15 @@ msgstr ""
msgid "Accounting Entry for Service"
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1015
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1036
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1054
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1075
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1096
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1124
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1231
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1467
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1489
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1016
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1037
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1055
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1076
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1097
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1125
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1232
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1468
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1490
#: erpnext/controllers/stock_controller.py:727
#: erpnext/controllers/stock_controller.py:744
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:930
@@ -1893,8 +1893,8 @@ msgid "Accounting Entry for {0}: {1} can only be made in currency: {2}"
msgstr ""
#: erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js:193
-#: erpnext/assets/doctype/asset/asset.js:182
-#: erpnext/assets/doctype/asset_repair/asset_repair.js:171
+#: erpnext/assets/doctype/asset/asset.js:185
+#: erpnext/assets/doctype/asset_repair/asset_repair.js:92
#: erpnext/buying/doctype/supplier/supplier.js:98
#: erpnext/public/js/controllers/stock_controller.js:88
#: erpnext/public/js/utils/ledger_preview.js:8
@@ -2083,7 +2083,7 @@ msgstr ""
msgid "Accounts Setup"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1319
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1325
msgid "Accounts table cannot be blank."
msgstr ""
@@ -2117,13 +2117,13 @@ msgstr ""
#. Label of the accumulated_depreciation_amount (Currency) field in DocType
#. 'Depreciation Schedule'
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:178
-#: erpnext/assets/doctype/asset/asset.js:373
+#: erpnext/assets/doctype/asset/asset.js:380
#: erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json
msgid "Accumulated Depreciation Amount"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:635
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:653
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:876
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:894
msgid "Accumulated Depreciation as on"
msgstr ""
@@ -2609,7 +2609,7 @@ msgid "Add Quote"
msgstr ""
#. Label of the add_raw_materials (Button) field in DocType 'BOM Operation'
-#: erpnext/manufacturing/doctype/bom/bom.js:1020
+#: erpnext/manufacturing/doctype/bom/bom.js:1027
#: erpnext/manufacturing/doctype/bom_operation/bom_operation.json
msgid "Add Raw Materials"
msgstr ""
@@ -2670,7 +2670,7 @@ msgstr ""
msgid "Add Sub Assembly"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:520
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:516
#: erpnext/public/js/event.js:32
msgid "Add Suppliers"
msgstr ""
@@ -2965,7 +2965,7 @@ msgstr ""
msgid "Additional Information updated successfully."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:784
+#: erpnext/manufacturing/doctype/work_order/work_order.js:811
msgid "Additional Material Transfer"
msgstr ""
@@ -3270,7 +3270,7 @@ msgstr ""
msgid "Advance amount cannot be greater than {0} {1}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:867
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:873
msgid "Advance paid against {0} {1} cannot be greater than Grand Total {2}"
msgstr ""
@@ -3398,7 +3398,7 @@ msgstr ""
msgid "Against Income Account"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:729
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:735
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:777
msgid "Against Journal Entry {0} does not have any unmatched {1} entry"
msgstr ""
@@ -3749,7 +3749,7 @@ msgstr ""
msgid "All items have already been Invoiced/Returned"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:1216
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:1230
msgid "All items have already been received"
msgstr ""
@@ -3779,11 +3779,11 @@ msgstr ""
msgid "All the items have been already returned."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1168
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1195
msgid "All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table."
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:866
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:870
msgid "All these items have already been Invoiced/Returned"
msgstr ""
@@ -4335,7 +4335,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/bom/bom.js:250
#: erpnext/manufacturing/doctype/work_order/work_order.js:165
#: erpnext/manufacturing/doctype/work_order/work_order.js:180
-#: erpnext/public/js/utils.js:561
+#: erpnext/public/js/utils.js:567
#: erpnext/stock/doctype/stock_entry/stock_entry.js:288
msgid "Alternate Item"
msgstr ""
@@ -4764,7 +4764,7 @@ msgstr ""
msgid "Analytical Accounting"
msgstr ""
-#: erpnext/public/js/utils.js:158
+#: erpnext/public/js/utils.js:164
msgid "Annual Billing: {0}"
msgstr ""
@@ -5286,7 +5286,7 @@ msgstr ""
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js:30
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:141
#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js:44
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:581
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:822
#: erpnext/assets/doctype/asset/asset.json
#: erpnext/assets/doctype/asset_activity/asset_activity.json
#: erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json
@@ -5358,7 +5358,7 @@ msgstr ""
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js:36
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:197
#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js:37
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:571
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:812
#: erpnext/assets/doctype/asset/asset.json
#: erpnext/assets/doctype/asset_category/asset_category.json
#: erpnext/assets/doctype/asset_maintenance/asset_maintenance.json
@@ -5528,7 +5528,7 @@ msgstr ""
#. Label of the asset_name (Data) field in DocType 'Asset Movement Item'
#. Label of the asset_name (Read Only) field in DocType 'Asset Repair'
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:148
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:590
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:831
#: erpnext/assets/doctype/asset/asset.json
#: erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
#: erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json
@@ -5576,7 +5576,7 @@ msgstr ""
#. Batch Bundle'
#. Label of the asset_repair (Link) field in DocType 'Stock Entry'
#. Label of a Workspace Sidebar Item
-#: erpnext/assets/doctype/asset/asset.js:105
+#: erpnext/assets/doctype/asset/asset.js:108
#: erpnext/assets/doctype/asset_repair/asset_repair.json
#: erpnext/assets/workspace/assets/assets.json
#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
@@ -5628,7 +5628,7 @@ msgstr ""
#. Label of the asset_value (Currency) field in DocType 'Asset Capitalization
#. Asset Item'
#: erpnext/assets/dashboard_fixtures.py:180
-#: erpnext/assets/doctype/asset/asset.js:505
+#: erpnext/assets/doctype/asset/asset.js:512
#: erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json
#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:209
#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:460
@@ -5639,7 +5639,7 @@ msgstr ""
#. Name of a DocType
#. Label of a Link in the Assets Workspace
#. Label of a Workspace Sidebar Item
-#: erpnext/assets/doctype/asset/asset.js:97
+#: erpnext/assets/doctype/asset/asset.js:100
#: erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json
#: erpnext/assets/workspace/assets/assets.json
#: erpnext/workspace_sidebar/assets.json
@@ -6313,7 +6313,7 @@ msgstr ""
#. 'Pick List Item'
#: erpnext/manufacturing/doctype/workstation/workstation.js:505
#: erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py:88
-#: erpnext/public/js/utils.js:621
+#: erpnext/public/js/utils.js:627
#: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
#: erpnext/stock/doctype/pick_list_item/pick_list_item.json
#: erpnext/stock/report/stock_ageing/stock_ageing.py:169
@@ -7593,7 +7593,7 @@ msgstr ""
msgid "Batch No is mandatory"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3351
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3368
msgid "Batch No {0} does not exists"
msgstr ""
@@ -7616,11 +7616,11 @@ msgstr ""
msgid "Batch Nos"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1916
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1933
msgid "Batch Nos are created successfully"
msgstr ""
-#: erpnext/controllers/sales_and_purchase_return.py:1194
+#: erpnext/controllers/sales_and_purchase_return.py:1193
msgid "Batch Not Available for Return"
msgstr ""
@@ -7678,7 +7678,7 @@ msgstr ""
msgid "Batch {0} and Warehouse"
msgstr ""
-#: erpnext/controllers/sales_and_purchase_return.py:1193
+#: erpnext/controllers/sales_and_purchase_return.py:1192
msgid "Batch {0} is not available in warehouse {1}"
msgstr ""
@@ -8928,7 +8928,7 @@ msgstr ""
msgid "Can not filter based on Voucher No, if grouped by Voucher"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1378
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1384
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:2879
msgid "Can only make payment against unbilled {0}"
msgstr ""
@@ -9051,7 +9051,7 @@ msgstr ""
msgid "Cannot cancel this Manufacturing Stock Entry as quantity of Finished Good produced cannot be less than quantity delivered in the linked Subcontracting Inward Order."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:569
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:575
msgid "Cannot cancel this document as it is linked with the submitted Asset Value Adjustment {0}. Please cancel the Asset Value Adjustment to continue."
msgstr ""
@@ -9343,7 +9343,7 @@ msgstr ""
msgid "Capital Work in Progress"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:220
+#: erpnext/assets/doctype/asset/asset.js:223
msgid "Capitalize Asset"
msgstr ""
@@ -9352,7 +9352,7 @@ msgstr ""
msgid "Capitalize Repair Cost"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:218
+#: erpnext/assets/doctype/asset/asset.js:221
msgid "Capitalize this asset before submitting."
msgstr ""
@@ -11246,7 +11246,7 @@ msgstr ""
msgid "Consider Minimum Order Qty"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:990
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1017
msgid "Consider Process Loss"
msgstr ""
@@ -11747,7 +11747,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json
#: erpnext/manufacturing/doctype/bom_item/bom_item.json
#: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
-#: erpnext/public/js/utils.js:876
+#: erpnext/public/js/utils.js:882
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
#: erpnext/stock/doctype/packed_item/packed_item.json
#: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -12097,7 +12097,7 @@ msgstr ""
msgid "Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group"
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1432
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1433
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:896
msgid "Cost Center is required in row {0} in Taxes table for type {1}"
msgstr ""
@@ -12247,7 +12247,7 @@ msgstr ""
msgid "Could not auto create Customer due to the following missing mandatory field(s):"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:688
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:692
msgid "Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again"
msgstr ""
@@ -12527,7 +12527,7 @@ msgstr ""
msgid "Create Payment Request"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:766
+#: erpnext/manufacturing/doctype/work_order/work_order.js:793
msgid "Create Pick List"
msgstr ""
@@ -12658,7 +12658,7 @@ msgstr ""
msgid "Create Supplier"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:184
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:180
msgid "Create Supplier Quotation"
msgstr ""
@@ -13047,7 +13047,7 @@ msgstr ""
msgid "Credit Note will update it's own outstanding amount, even if 'Return Against' is specified."
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:685
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:689
msgid "Credit Note {0} has been created automatically"
msgstr ""
@@ -13325,11 +13325,6 @@ msgstr ""
msgid "Current Exchange Rate"
msgstr ""
-#. Label of the current_index (Int) field in DocType 'Repost Item Valuation'
-#: erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
-msgid "Current Index"
-msgstr ""
-
#. Label of the current_invoice_end (Date) field in DocType 'Subscription'
#: erpnext/accounts/doctype/subscription/subscription.json
msgid "Current Invoice End Date"
@@ -14086,7 +14081,7 @@ msgstr ""
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1145
#: erpnext/selling/doctype/sales_order/sales_order.py:434
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:432
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:436
msgid "Customer {0} does not belong to project {1}"
msgstr ""
@@ -15427,7 +15422,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/master_production_schedule_item/master_production_schedule_item.json
#: erpnext/manufacturing/doctype/sales_forecast_item/sales_forecast_item.json
#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1067
-#: erpnext/public/js/utils.js:869
+#: erpnext/public/js/utils.js:875
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
#: erpnext/selling/doctype/sales_order/sales_order.js:624
#: erpnext/selling/doctype/sales_order/sales_order.js:1486
@@ -15735,12 +15730,12 @@ msgstr ""
#. Label of the depreciation_amount (Currency) field in DocType 'Depreciation
#. Schedule'
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:172
-#: erpnext/assets/doctype/asset/asset.js:372
+#: erpnext/assets/doctype/asset/asset.js:379
#: erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json
msgid "Depreciation Amount"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:641
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:882
msgid "Depreciation Amount during the period"
msgstr ""
@@ -15756,7 +15751,7 @@ msgstr ""
msgid "Depreciation Details"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:647
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:888
msgid "Depreciation Eliminated due to disposal of assets"
msgstr ""
@@ -15766,7 +15761,7 @@ msgstr ""
#: erpnext/accounts/doctype/journal_entry/journal_entry.json
#: erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:190
-#: erpnext/assets/doctype/asset/asset.js:119
+#: erpnext/assets/doctype/asset/asset.js:122
msgid "Depreciation Entry"
msgstr ""
@@ -15818,7 +15813,7 @@ msgstr ""
msgid "Depreciation Posting Date"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:909
+#: erpnext/assets/doctype/asset/asset.js:916
msgid "Depreciation Posting Date cannot be before Available-for-use Date"
msgstr ""
@@ -15859,7 +15854,7 @@ msgstr ""
msgid "Depreciation cannot be calculated for fully depreciated assets"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:659
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:900
msgid "Depreciation eliminated via reversal"
msgstr ""
@@ -16730,7 +16725,7 @@ msgstr ""
msgid "Do not update variants on save"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:947
+#: erpnext/assets/doctype/asset/asset.js:954
msgid "Do you really want to restore this scrapped asset?"
msgstr ""
@@ -17630,11 +17625,11 @@ msgstr ""
msgid "Employee User Id"
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:213
+#: erpnext/setup/doctype/employee/employee.py:211
msgid "Employee cannot report to himself."
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:445
+#: erpnext/setup/doctype/employee/employee.py:443
msgid "Employee is required"
msgstr ""
@@ -17651,7 +17646,7 @@ msgstr ""
msgid "Employee {0} is currently working on another workstation. Please assign another employee."
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:470
+#: erpnext/setup/doctype/employee/employee.py:468
msgid "Employee {0} not found"
msgstr ""
@@ -18054,7 +18049,7 @@ msgstr ""
msgid "Enter customer's phone number"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:918
+#: erpnext/assets/doctype/asset/asset.js:925
msgid "Enter date to scrap asset"
msgstr ""
@@ -18093,11 +18088,11 @@ msgstr ""
msgid "Enter the opening stock units."
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:965
+#: erpnext/manufacturing/doctype/bom/bom.js:972
msgid "Enter the quantity of the Item that will be manufactured from this Bill of Materials."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1130
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1157
msgid "Enter the quantity to manufacture. Raw material Items will be fetched only when this is set."
msgstr ""
@@ -18876,7 +18871,7 @@ msgstr ""
msgid "Failed to parse MT940 format. Error: {0}"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:253
+#: erpnext/assets/doctype/asset/asset.js:260
msgid "Failed to post depreciation entries"
msgstr ""
@@ -19254,9 +19249,9 @@ msgstr ""
msgid "Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing) "
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:850
-#: erpnext/manufacturing/doctype/work_order/work_order.js:865
-#: erpnext/manufacturing/doctype/work_order/work_order.js:874
+#: erpnext/manufacturing/doctype/work_order/work_order.js:877
+#: erpnext/manufacturing/doctype/work_order/work_order.js:892
+#: erpnext/manufacturing/doctype/work_order/work_order.js:901
msgid "Finish"
msgstr ""
@@ -19287,7 +19282,7 @@ msgstr ""
#. Service Item'
#. Label of the fg_item (Link) field in DocType 'Subcontracting Order Service
#. Item'
-#: erpnext/public/js/utils.js:895
+#: erpnext/public/js/utils.js:901
#: erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.json
#: erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json
msgid "Finished Good Item"
@@ -19300,7 +19295,7 @@ msgstr ""
msgid "Finished Good Item Code"
msgstr ""
-#: erpnext/public/js/utils.js:913
+#: erpnext/public/js/utils.js:919
msgid "Finished Good Item Qty"
msgstr ""
@@ -19890,7 +19885,7 @@ msgstr ""
msgid "For the {0}, no stock is available for the return in the warehouse {1}."
msgstr ""
-#: erpnext/controllers/sales_and_purchase_return.py:1245
+#: erpnext/controllers/sales_and_purchase_return.py:1244
msgid "For the {0}, the quantity is required to make the return entry"
msgstr ""
@@ -20772,9 +20767,9 @@ msgstr ""
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1125
#: erpnext/buying/doctype/purchase_order/purchase_order.js:540
#: erpnext/buying/doctype/purchase_order/purchase_order.js:563
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:383
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:405
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:450
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:379
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:401
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:446
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.js:75
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.js:108
#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js:80
@@ -20784,7 +20779,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/production_plan/production_plan.json
#: erpnext/public/js/controllers/buying.js:327
#: erpnext/selling/doctype/quotation/quotation.js:183
-#: erpnext/selling/doctype/sales_order/sales_order.js:196
+#: erpnext/selling/doctype/sales_order/sales_order.js:203
#: erpnext/selling/doctype/sales_order/sales_order.js:1200
#: erpnext/stock/doctype/delivery_note/delivery_note.js:187
#: erpnext/stock/doctype/delivery_note/delivery_note.js:239
@@ -20817,7 +20812,7 @@ msgstr ""
msgid "Get Items from BOM"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:422
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:418
msgid "Get Items from Material Requests against this Supplier"
msgstr ""
@@ -20907,12 +20902,12 @@ msgstr ""
msgid "Get Sub Assembly Items"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:464
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:484
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:460
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:480
msgid "Get Suppliers"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:488
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:484
msgid "Get Suppliers By"
msgstr ""
@@ -22195,7 +22190,7 @@ msgstr ""
msgid "If set, the system does not use the user's Email or the standard outgoing Email account for sending request for quotations."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1163
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1190
msgid "If the BOM results in Scrap material, the Scrap Warehouse needs to be selected."
msgstr ""
@@ -22214,7 +22209,7 @@ msgstr ""
msgid "If the reorder check is set at the Group warehouse level, the available quantity becomes the sum of the projected quantities of all its child warehouses."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1182
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1209
msgid "If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed."
msgstr ""
@@ -23315,7 +23310,7 @@ msgstr ""
msgid "Installation Note Item"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:639
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:643
msgid "Installation Note {0} has already been submitted"
msgstr ""
@@ -24820,7 +24815,7 @@ msgstr ""
#: erpnext/buying/workspace/buying/buying.json
#: erpnext/controllers/taxes_and_totals.py:1212
#: erpnext/manufacturing/doctype/blanket_order/blanket_order.json
-#: erpnext/manufacturing/doctype/bom/bom.js:1058
+#: erpnext/manufacturing/doctype/bom/bom.js:1065
#: erpnext/manufacturing/doctype/bom/bom.json
#: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:109
#: erpnext/manufacturing/doctype/workstation/workstation_job_card.html:25
@@ -25125,8 +25120,8 @@ msgstr ""
#: erpnext/projects/doctype/timesheet/timesheet.js:214
#: erpnext/public/js/controllers/transaction.js:2904
#: erpnext/public/js/stock_reservation.js:112
-#: erpnext/public/js/stock_reservation.js:318 erpnext/public/js/utils.js:553
-#: erpnext/public/js/utils.js:710
+#: erpnext/public/js/stock_reservation.js:318 erpnext/public/js/utils.js:559
+#: erpnext/public/js/utils.js:716
#: erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
#: erpnext/selling/doctype/installation_note_item/installation_note_item.json
@@ -25606,7 +25601,7 @@ msgstr ""
#: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py:92
#: erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py:138
#: erpnext/public/js/controllers/transaction.js:2910
-#: erpnext/public/js/utils.js:805
+#: erpnext/public/js/utils.js:811
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order/sales_order.js:1252
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -26543,8 +26538,8 @@ msgstr ""
#: erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
#: erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html:10
#: erpnext/accounts/workspace/invoicing/invoicing.json
-#: erpnext/assets/doctype/asset/asset.js:378
-#: erpnext/assets/doctype/asset/asset.js:387
+#: erpnext/assets/doctype/asset/asset.js:385
+#: erpnext/assets/doctype/asset/asset.js:394
#: erpnext/assets/doctype/asset/asset.json
#: erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json
#: erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json
@@ -26578,7 +26573,7 @@ msgstr ""
msgid "Journal Entry Type"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:547
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:553
msgid "Journal Entry for Asset scrapping cannot be cancelled. Please restore the Asset."
msgstr ""
@@ -26587,11 +26582,11 @@ msgstr ""
msgid "Journal Entry for Scrap"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:343
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:349
msgid "Journal Entry type should be set as Depreciation Entry for asset depreciation"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:717
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:723
msgid "Journal Entry {0} does not have account {1} or already matched against other voucher"
msgstr ""
@@ -27282,7 +27277,7 @@ msgstr ""
msgid "Link to Material Request"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:455
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:451
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.js:80
msgid "Link to Material Requests"
msgstr ""
@@ -27579,7 +27574,7 @@ msgstr ""
msgid "Loyalty Points will be calculated from the spent done (via the Sales Invoice), based on collection factor mentioned."
msgstr ""
-#: erpnext/public/js/utils.js:174
+#: erpnext/public/js/utils.js:180
msgid "Loyalty Points: {0}"
msgstr ""
@@ -27697,7 +27692,7 @@ msgstr ""
msgid "Main Item Code"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:135
+#: erpnext/assets/doctype/asset/asset.js:138
msgid "Maintain Asset"
msgstr ""
@@ -27921,8 +27916,8 @@ msgstr ""
#. Label of the make (Data) field in DocType 'Vehicle'
#: erpnext/accounts/doctype/journal_entry/journal_entry.js:123
#: erpnext/manufacturing/doctype/job_card/job_card.js:536
-#: erpnext/manufacturing/doctype/work_order/work_order.js:805
-#: erpnext/manufacturing/doctype/work_order/work_order.js:839
+#: erpnext/manufacturing/doctype/work_order/work_order.js:832
+#: erpnext/manufacturing/doctype/work_order/work_order.js:866
#: erpnext/setup/doctype/vehicle/vehicle.json
msgid "Make"
msgstr ""
@@ -28371,7 +28366,7 @@ msgstr ""
msgid "Mapping Subcontracting Order ..."
msgstr ""
-#: erpnext/public/js/utils.js:1040
+#: erpnext/public/js/utils.js:1046
msgid "Mapping {0} ..."
msgstr ""
@@ -28517,7 +28512,7 @@ msgstr ""
msgid "Material"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:830
+#: erpnext/manufacturing/doctype/work_order/work_order.js:857
msgid "Material Consumption"
msgstr ""
@@ -28598,7 +28593,7 @@ msgstr ""
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#: erpnext/buying/doctype/purchase_order/purchase_order.js:519
#: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:364
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:360
#: erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.js:56
#: erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
@@ -28909,7 +28904,7 @@ msgstr ""
msgid "Max discount allowed for item: {0} is {1}%"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:982
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1009
#: erpnext/stock/doctype/pick_list/pick_list.js:200
msgid "Max: {0}"
msgstr ""
@@ -29035,7 +29030,7 @@ msgstr ""
msgid "Merge Similar Account Heads"
msgstr ""
-#: erpnext/public/js/utils.js:1072
+#: erpnext/public/js/utils.js:1078
msgid "Merge taxes from multiple documents"
msgstr ""
@@ -29403,7 +29398,7 @@ msgstr ""
msgid "Missing Item"
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:445
+#: erpnext/setup/doctype/employee/employee.py:443
msgid "Missing Parameter"
msgstr ""
@@ -29517,10 +29512,6 @@ msgstr ""
msgid "Modes of Payment"
msgstr ""
-#: erpnext/templates/pages/projects.html:69
-msgid "Modified By"
-msgstr ""
-
#: erpnext/templates/pages/projects.html:49
#: erpnext/templates/pages/projects.html:70
msgid "Modified On"
@@ -29917,8 +29908,8 @@ msgstr ""
msgid "Net Amount (Company Currency)"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:665
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:671
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:906
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:912
msgid "Net Asset value as on"
msgstr ""
@@ -30442,9 +30433,9 @@ msgstr ""
msgid "No POS Profile found. Please create a New POS Profile first"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1564
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1624
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1638
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1570
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1630
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1644
#: erpnext/stock/doctype/item/item.py:1388
msgid "No Permission"
msgstr ""
@@ -30467,7 +30458,7 @@ msgstr ""
msgid "No Selection"
msgstr ""
-#: erpnext/controllers/sales_and_purchase_return.py:973
+#: erpnext/controllers/sales_and_purchase_return.py:972
msgid "No Serial / Batches are available for return"
msgstr ""
@@ -30491,7 +30482,7 @@ msgstr ""
msgid "No Tax withholding account set for Company {0} in Tax Withholding Category {1}."
msgstr ""
-#: erpnext/accounts/report/gross_profit/gross_profit.py:965
+#: erpnext/accounts/report/gross_profit/gross_profit.py:971
msgid "No Terms"
msgstr ""
@@ -30757,7 +30748,7 @@ msgstr ""
msgid "No {0} found for Inter Company Transactions."
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:370
+#: erpnext/assets/doctype/asset/asset.js:377
msgid "No."
msgstr ""
@@ -30943,7 +30934,7 @@ msgstr ""
msgid "Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1021
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1027
msgid "Note: {0}"
msgstr ""
@@ -31305,7 +31296,7 @@ msgstr ""
msgid "Once set, this invoice will be on hold till the set date"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:717
+#: erpnext/manufacturing/doctype/work_order/work_order.js:744
msgid "Once the Work Order is Closed. It can't be resumed."
msgstr ""
@@ -31666,7 +31657,7 @@ msgstr ""
msgid "Opening Invoice Tool"
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1646
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1647
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1993
msgid "Opening Invoice has rounding adjustment of {0}.
'{1}' account is required to post these values. Please set it in Company: {2}.
Or, '{3}' can be enabled to not post any rounding adjustment."
msgstr ""
@@ -31929,7 +31920,7 @@ msgstr ""
#. Label of the opportunity_name (Link) field in DocType 'Customer'
#. Label of the opportunity (Link) field in DocType 'Quotation'
#. Label of a Workspace Sidebar Item
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:388
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:384
#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
#: erpnext/crm/doctype/crm_settings/crm_settings.json
@@ -32995,7 +32986,7 @@ msgstr ""
msgid "Packing Slip Item"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:655
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:659
msgid "Packing Slip(s) cancelled"
msgstr ""
@@ -34407,7 +34398,7 @@ msgstr ""
msgid "Payment Unlink Error"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:889
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:895
msgid "Payment against {0} {1} cannot be greater than Outstanding Amount {2}"
msgstr ""
@@ -35335,12 +35326,12 @@ msgstr ""
msgid "Please cancel related transaction."
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:85
+#: erpnext/assets/doctype/asset/asset.js:86
#: erpnext/assets/doctype/asset/asset.py:249
msgid "Please capitalize this asset before submitting."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:963
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:969
msgid "Please check Multi Currency option to allow accounts with other currency"
msgstr ""
@@ -35543,7 +35534,7 @@ msgstr ""
msgid "Please enter Receipt Document"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1027
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1033
msgid "Please enter Reference date"
msgstr ""
@@ -35636,7 +35627,7 @@ msgstr ""
msgid "Please enter valid Financial Year Start and End Dates"
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:221
+#: erpnext/setup/doctype/employee/employee.py:219
msgid "Please enter {0}"
msgstr ""
@@ -35802,8 +35793,8 @@ msgstr ""
msgid "Please select Finished Good Item for Service Item {0}"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:744
-#: erpnext/assets/doctype/asset/asset.js:759
+#: erpnext/assets/doctype/asset/asset.js:751
+#: erpnext/assets/doctype/asset/asset.js:766
msgid "Please select Item Code first"
msgstr ""
@@ -35873,7 +35864,7 @@ msgid "Please select a Company"
msgstr ""
#: erpnext/accounts/doctype/payment_entry/payment_entry.js:268
-#: erpnext/manufacturing/doctype/bom/bom.js:680
+#: erpnext/manufacturing/doctype/bom/bom.js:687
#: erpnext/manufacturing/doctype/bom/bom.py:276
#: erpnext/public/js/controllers/accounts.js:277
#: erpnext/public/js/controllers/transaction.js:3365
@@ -35981,7 +35972,7 @@ msgstr ""
msgid "Please select atleast one operation to create Job Card"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1703
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1709
msgid "Please select correct account"
msgstr ""
@@ -36177,11 +36168,11 @@ msgstr ""
msgid "Please set a default Holiday List for Company {0}"
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:272
+#: erpnext/setup/doctype/employee/employee.py:270
msgid "Please set a default Holiday List for Employee {0} or Company {1}"
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1115
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1116
msgid "Please set account in Warehouse {0}"
msgstr ""
@@ -36275,7 +36266,7 @@ msgstr ""
msgid "Please set the Default Cost Center in {0} company."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:634
+#: erpnext/manufacturing/doctype/work_order/work_order.js:661
msgid "Please set the Item Code first"
msgstr ""
@@ -36420,7 +36411,7 @@ msgstr ""
msgid "Portal Users"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:410
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:406
msgid "Possible Supplier"
msgstr ""
@@ -36794,7 +36785,7 @@ msgid "Preventive Maintenance"
msgstr ""
#. Label of the preview (Button) field in DocType 'Request for Quotation'
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:270
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:266
#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
msgid "Preview Email"
msgstr ""
@@ -38471,7 +38462,7 @@ msgstr ""
msgid "Purchase Invoice {0} is already submitted"
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1931
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1932
msgid "Purchase Invoices"
msgstr ""
@@ -38514,7 +38505,7 @@ msgstr ""
#: erpnext/crm/doctype/contract/contract.json
#: erpnext/manufacturing/doctype/blanket_order/blanket_order.js:54
#: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
-#: erpnext/selling/doctype/sales_order/sales_order.js:174
+#: erpnext/selling/doctype/sales_order/sales_order.js:181
#: erpnext/selling/doctype/sales_order/sales_order.js:1097
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/setup/doctype/authorization_rule/authorization_rule.json
@@ -38967,7 +38958,7 @@ msgstr ""
#: erpnext/controllers/trends.py:268 erpnext/controllers/trends.py:280
#: erpnext/controllers/trends.py:285
#: erpnext/crm/doctype/opportunity_item/opportunity_item.json
-#: erpnext/manufacturing/doctype/bom/bom.js:1078
+#: erpnext/manufacturing/doctype/bom/bom.js:1085
#: erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json
#: erpnext/manufacturing/doctype/bom_item/bom_item.json
#: erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
@@ -38983,7 +38974,7 @@ msgstr ""
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:398
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:499
#: erpnext/public/js/stock_reservation.js:134
-#: erpnext/public/js/stock_reservation.js:336 erpnext/public/js/utils.js:843
+#: erpnext/public/js/stock_reservation.js:336 erpnext/public/js/utils.js:849
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
#: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
#: erpnext/selling/doctype/sales_order/sales_order.js:390
@@ -39149,7 +39140,7 @@ msgstr ""
msgid "Qty for which recursion isn't applicable."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:980
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1007
msgid "Qty for {0}"
msgstr ""
@@ -39714,7 +39705,7 @@ msgstr ""
msgid "Quantity must be greater than zero, and less or equal to {0}"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1010
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1037
#: erpnext/stock/doctype/pick_list/pick_list.js:206
msgid "Quantity must not be more than {0}"
msgstr ""
@@ -40028,7 +40019,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/bom_item/bom_item.json
#: erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
#: erpnext/manufacturing/doctype/work_order_item/work_order_item.json
-#: erpnext/public/js/utils.js:853
+#: erpnext/public/js/utils.js:859
#: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -40351,7 +40342,7 @@ msgstr ""
#. Label of the section_break_8 (Section Break) field in DocType 'Job Card'
#. Label of the mr_items (Table) field in DocType 'Production Plan'
#: erpnext/manufacturing/doctype/bom/bom.js:406
-#: erpnext/manufacturing/doctype/bom/bom.js:1051
+#: erpnext/manufacturing/doctype/bom/bom.js:1058
#: erpnext/manufacturing/doctype/bom/bom.json
#: erpnext/manufacturing/doctype/job_card/job_card.json
#: erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -40437,7 +40428,7 @@ msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order.js:369
#: erpnext/manufacturing/doctype/production_plan/production_plan.js:124
-#: erpnext/manufacturing/doctype/work_order/work_order.js:733
+#: erpnext/manufacturing/doctype/work_order/work_order.js:760
#: erpnext/selling/doctype/sales_order/sales_order.js:968
#: erpnext/selling/doctype/sales_order/sales_order_list.js:70
#: erpnext/stock/doctype/material_request/material_request.js:243
@@ -40983,7 +40974,7 @@ msgstr ""
msgid "Ref Date"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1025
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1031
msgid "Reference #{0} dated {1}"
msgstr ""
@@ -41021,7 +41012,7 @@ msgstr ""
msgid "Reference No"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:639
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:645
msgid "Reference No & Reference Date is required for {0}"
msgstr ""
@@ -41029,7 +41020,7 @@ msgstr ""
msgid "Reference No and Reference Date is mandatory for Bank transaction"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:644
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:650
msgid "Reference No is mandatory if you entered Reference Date"
msgstr ""
@@ -41123,11 +41114,11 @@ msgstr ""
msgid "References"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:399
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:403
msgid "References to Sales Invoices are Incomplete"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:394
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:395
msgid "References to Sales Orders are Incomplete"
msgstr ""
@@ -41722,7 +41713,7 @@ msgstr ""
msgid "Reqd Qty (BOM)"
msgstr ""
-#: erpnext/public/js/utils.js:869
+#: erpnext/public/js/utils.js:875
msgid "Reqd by date"
msgstr ""
@@ -41995,8 +41986,8 @@ msgstr ""
msgid "Reservation Based On"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:891
-#: erpnext/selling/doctype/sales_order/sales_order.js:92
+#: erpnext/manufacturing/doctype/work_order/work_order.js:918
+#: erpnext/selling/doctype/sales_order/sales_order.js:99
#: erpnext/stock/doctype/pick_list/pick_list.js:150
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:180
msgid "Reserve"
@@ -42117,9 +42108,9 @@ msgstr ""
#. Label of the reserved_stock (Float) field in DocType 'Bin'
#. Name of a report
#: erpnext/manufacturing/doctype/plant_floor/stock_summary_template.html:24
-#: erpnext/manufacturing/doctype/work_order/work_order.js:907
+#: erpnext/manufacturing/doctype/work_order/work_order.js:934
#: erpnext/public/js/stock_reservation.js:236
-#: erpnext/selling/doctype/sales_order/sales_order.js:120
+#: erpnext/selling/doctype/sales_order/sales_order.js:127
#: erpnext/selling/doctype/sales_order/sales_order.js:457
#: erpnext/stock/dashboard/item_dashboard_list.html:15
#: erpnext/stock/doctype/bin/bin.json
@@ -42345,7 +42336,7 @@ msgstr ""
msgid "Restart Subscription"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:175
+#: erpnext/assets/doctype/asset/asset.js:178
msgid "Restore Asset"
msgstr ""
@@ -43311,11 +43302,11 @@ msgstr ""
msgid "Row #{0}: For Customer Provided Item {1}, Source Warehouse must be {2}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:687
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:693
msgid "Row #{0}: For {1}, you can select reference document only if account gets credited"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:697
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:703
msgid "Row #{0}: For {1}, you can select reference document only if account gets debited"
msgstr ""
@@ -43609,7 +43600,7 @@ msgstr ""
msgid "Row #{0}: Status is mandatory"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:449
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:455
msgid "Row #{0}: Status must be {1} for Invoice Discounting {2}"
msgstr ""
@@ -43629,7 +43620,7 @@ msgstr ""
msgid "Row #{0}: Stock is already reserved for the Item {1}."
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:553
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:557
msgid "Row #{0}: Stock is reserved for item {1} in warehouse {2}."
msgstr ""
@@ -43823,7 +43814,7 @@ msgstr ""
msgid "Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:602
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:608
msgid "Row {0}: Account {1} and Party Type {2} have different account types"
msgstr ""
@@ -43831,11 +43822,11 @@ msgstr ""
msgid "Row {0}: Activity Type is mandatory."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:668
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:674
msgid "Row {0}: Advance against Customer must be credit"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:670
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:676
msgid "Row {0}: Advance against Supplier must be debit"
msgstr ""
@@ -43855,7 +43846,7 @@ msgstr ""
msgid "Row {0}: Bill of Materials not found for the Item {1}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:921
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:927
msgid "Row {0}: Both Debit and Credit values cannot be zero"
msgstr ""
@@ -43877,7 +43868,7 @@ msgstr ""
msgid "Row {0}: Cost center is required for an item {1}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:767
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:773
msgid "Row {0}: Credit entry can not be linked with a {1}"
msgstr ""
@@ -43885,7 +43876,7 @@ msgstr ""
msgid "Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:762
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:768
msgid "Row {0}: Debit entry can not be linked with a {1}"
msgstr ""
@@ -43905,7 +43896,7 @@ msgstr ""
msgid "Row {0}: Either Delivery Note Item or Packed Item reference is mandatory."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1012
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1018
#: erpnext/controllers/taxes_and_totals.py:1340
msgid "Row {0}: Exchange Rate is mandatory"
msgstr ""
@@ -43959,7 +43950,7 @@ msgstr ""
msgid "Row {0}: Hours value must be greater than zero."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:787
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:793
msgid "Row {0}: Invalid reference {1}"
msgstr ""
@@ -43987,7 +43978,7 @@ msgstr ""
msgid "Row {0}: Item {1}'s quantity cannot be higher than the available quantity."
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:610
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:614
msgid "Row {0}: Packed Qty must be equal to {1} Qty."
msgstr ""
@@ -43995,11 +43986,11 @@ msgstr ""
msgid "Row {0}: Packing Slip is already created for Item {1}."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:813
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:819
msgid "Row {0}: Party / Account does not match with {1} / {2} in {3} {4}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:591
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:597
msgid "Row {0}: Party Type and Party is required for Receivable / Payable account {1}"
msgstr ""
@@ -44007,11 +43998,11 @@ msgstr ""
msgid "Row {0}: Payment Term is mandatory"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:661
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:667
msgid "Row {0}: Payment against Sales/Purchase Order should always be marked as advance"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:654
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:660
msgid "Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry."
msgstr ""
@@ -44091,7 +44082,7 @@ msgstr ""
msgid "Row {0}: Task {1} does not belong to Project {2}"
msgstr ""
-#: erpnext/assets/doctype/asset_repair/asset_repair.js:158
+#: erpnext/assets/doctype/asset_repair/asset_repair.js:178
msgid "Row {0}: The entire expense amount for account {1} in {2} has already been allocated."
msgstr ""
@@ -44136,7 +44127,7 @@ msgstr ""
msgid "Row {0}: {1} {2} cannot be same as {3} (Party Account) {4}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:827
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:833
msgid "Row {0}: {1} {2} does not match with {3}"
msgstr ""
@@ -44261,7 +44252,7 @@ msgstr ""
msgid "SLA Paused On"
msgstr ""
-#: erpnext/public/js/utils.js:1233
+#: erpnext/public/js/utils.js:1239
msgid "SLA is on hold since {0}"
msgstr ""
@@ -44560,7 +44551,7 @@ msgstr ""
msgid "Sales Invoice mode is activated in POS. Please create Sales Invoice instead."
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:630
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:634
msgid "Sales Invoice {0} has already been submitted"
msgstr ""
@@ -44758,7 +44749,7 @@ msgstr ""
msgid "Sales Order Trends"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:282
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:283
msgid "Sales Order required for Item {0}"
msgstr ""
@@ -45031,7 +45022,7 @@ msgstr ""
msgid "Sales Representative"
msgstr ""
-#: erpnext/accounts/report/gross_profit/gross_profit.py:964
+#: erpnext/accounts/report/gross_profit/gross_profit.py:970
#: erpnext/stock/doctype/delivery_note/delivery_note.js:270
msgid "Sales Return"
msgstr ""
@@ -45308,7 +45299,7 @@ msgstr ""
#. Label of the schedule_date (Date) field in DocType 'Depreciation Schedule'
#. Label of the schedule_date (Datetime) field in DocType 'Production Plan Sub
#. Assembly Item'
-#: erpnext/assets/doctype/asset/asset.js:371
+#: erpnext/assets/doctype/asset/asset.js:378
#: erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json
#: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
msgid "Schedule Date"
@@ -45421,7 +45412,7 @@ msgstr ""
msgid "Scrap & Process Loss"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:160
+#: erpnext/assets/doctype/asset/asset.js:163
msgid "Scrap Asset"
msgstr ""
@@ -45572,7 +45563,7 @@ msgstr ""
msgid "Select Accounting Dimension."
msgstr ""
-#: erpnext/public/js/utils.js:529
+#: erpnext/public/js/utils.js:535
msgid "Select Alternate Item"
msgstr ""
@@ -45721,11 +45712,11 @@ msgstr ""
msgid "Select Payment Schedule"
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:414
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:410
msgid "Select Possible Supplier"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1016
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1043
#: erpnext/stock/doctype/pick_list/pick_list.js:216
msgid "Select Quantity"
msgstr ""
@@ -45863,11 +45854,11 @@ msgstr ""
msgid "Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1118
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1145
msgid "Select the Item to be manufactured."
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:958
+#: erpnext/manufacturing/doctype/bom/bom.js:965
msgid "Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically."
msgstr ""
@@ -45880,7 +45871,7 @@ msgstr ""
msgid "Select the customer or supplier."
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:921
+#: erpnext/assets/doctype/asset/asset.js:928
msgid "Select the date"
msgstr ""
@@ -45888,7 +45879,7 @@ msgstr ""
msgid "Select the date and your timezone"
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:977
+#: erpnext/manufacturing/doctype/bom/bom.js:984
msgid "Select the raw materials (Items) required to manufacture the Item"
msgstr ""
@@ -45947,22 +45938,22 @@ msgstr ""
msgid "Self delivery"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:632
+#: erpnext/assets/doctype/asset/asset.js:639
#: erpnext/stock/doctype/batch/batch_dashboard.py:9
#: erpnext/stock/doctype/item/item_dashboard.py:20
msgid "Sell"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:168
-#: erpnext/assets/doctype/asset/asset.js:621
+#: erpnext/assets/doctype/asset/asset.js:171
+#: erpnext/assets/doctype/asset/asset.js:628
msgid "Sell Asset"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:626
+#: erpnext/assets/doctype/asset/asset.js:633
msgid "Sell Qty"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:642
+#: erpnext/assets/doctype/asset/asset.js:649
msgid "Sell quantity cannot exceed the asset quantity"
msgstr ""
@@ -45970,7 +45961,7 @@ msgstr ""
msgid "Sell quantity cannot exceed the asset quantity. Asset {0} has only {1} item(s)."
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:638
+#: erpnext/assets/doctype/asset/asset.js:645
msgid "Sell quantity must be greater than zero"
msgstr ""
@@ -46165,7 +46156,7 @@ msgstr ""
msgid "Serial / Batch No"
msgstr ""
-#: erpnext/public/js/utils.js:191
+#: erpnext/public/js/utils.js:197
msgid "Serial / Batch Nos"
msgstr ""
@@ -46278,7 +46269,7 @@ msgstr ""
msgid "Serial No Range"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2579
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2596
msgid "Serial No Reserved"
msgstr ""
@@ -46364,7 +46355,7 @@ msgstr ""
msgid "Serial No {0} does not exist"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3345
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3362
msgid "Serial No {0} does not exists"
msgstr ""
@@ -46418,7 +46409,7 @@ msgstr ""
msgid "Serial Nos and Batches"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1865
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1882
msgid "Serial Nos are created successfully"
msgstr ""
@@ -46499,11 +46490,11 @@ msgstr ""
msgid "Serial and Batch Bundle"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2087
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2104
msgid "Serial and Batch Bundle created"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2159
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2176
msgid "Serial and Batch Bundle updated"
msgstr ""
@@ -46985,7 +46976,7 @@ msgstr ""
msgid "Set Posting Date"
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:1004
+#: erpnext/manufacturing/doctype/bom/bom.js:1011
msgid "Set Process Loss Item Quantity"
msgstr ""
@@ -47108,7 +47099,7 @@ msgstr ""
msgid "Set fieldname from which you want to fetch the data from the parent form."
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:994
+#: erpnext/manufacturing/doctype/bom/bom.js:1001
msgid "Set quantity of process loss item:"
msgstr ""
@@ -47124,7 +47115,7 @@ msgstr ""
msgid "Set targets Item Group-wise for this Sales Person."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1175
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1202
msgid "Set the Planned Start Date (an Estimated Date at which you want the Production to begin)"
msgstr ""
@@ -47354,7 +47345,7 @@ msgid "Shelf Life in Days"
msgstr ""
#. Label of the shift (Link) field in DocType 'Depreciation Schedule'
-#: erpnext/assets/doctype/asset/asset.js:384
+#: erpnext/assets/doctype/asset/asset.js:391
#: erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json
msgid "Shift"
msgstr ""
@@ -47426,7 +47417,7 @@ msgstr ""
msgid "Shipment details"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:801
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:805
msgid "Shipments"
msgstr ""
@@ -47613,6 +47604,7 @@ msgstr ""
msgid "Shortage Qty"
msgstr ""
+#: erpnext/buying/report/purchase_analytics/purchase_analytics.js:70
#: erpnext/selling/report/sales_analytics/sales_analytics.js:103
msgid "Show Aggregate Value from Subsidiary Companies"
msgstr ""
@@ -48206,15 +48198,15 @@ msgstr ""
msgid "Spending for Account {0} ({1}) between {2} and {3} has already exceeded the new allocated budget. Spent: {4}, Budget: {5}"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:682
+#: erpnext/assets/doctype/asset/asset.js:689
#: erpnext/stock/doctype/batch/batch.js:91
#: erpnext/stock/doctype/batch/batch.js:183
#: erpnext/support/doctype/issue/issue.js:114
msgid "Split"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:144
-#: erpnext/assets/doctype/asset/asset.js:666
+#: erpnext/assets/doctype/asset/asset.js:147
+#: erpnext/assets/doctype/asset/asset.js:673
msgid "Split Asset"
msgstr ""
@@ -48237,7 +48229,7 @@ msgstr ""
msgid "Split Issue"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:672
+#: erpnext/assets/doctype/asset/asset.js:679
msgid "Split Qty"
msgstr ""
@@ -48507,8 +48499,8 @@ msgstr ""
#: erpnext/accounts/doctype/account/account.json
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:96
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:158
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1357
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1383
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1358
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1384
#: erpnext/accounts/report/account_balance/account_balance.js:58
msgid "Stock Adjustment"
msgstr ""
@@ -48686,7 +48678,7 @@ msgstr ""
msgid "Stock Entry {0} has created"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1306
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1312
msgid "Stock Entry {0} is not submitted"
msgstr ""
@@ -48915,14 +48907,14 @@ msgstr ""
#: erpnext/manufacturing/doctype/production_plan/production_plan.js:289
#: erpnext/manufacturing/doctype/production_plan/production_plan.js:297
#: erpnext/manufacturing/doctype/production_plan/production_plan.js:303
-#: erpnext/manufacturing/doctype/work_order/work_order.js:893
-#: erpnext/manufacturing/doctype/work_order/work_order.js:902
-#: erpnext/manufacturing/doctype/work_order/work_order.js:909
+#: erpnext/manufacturing/doctype/work_order/work_order.js:920
+#: erpnext/manufacturing/doctype/work_order/work_order.js:929
+#: erpnext/manufacturing/doctype/work_order/work_order.js:936
#: erpnext/manufacturing/doctype/work_order/work_order_dashboard.py:14
#: erpnext/public/js/stock_reservation.js:12
-#: erpnext/selling/doctype/sales_order/sales_order.js:94
-#: erpnext/selling/doctype/sales_order/sales_order.js:109
-#: erpnext/selling/doctype/sales_order/sales_order.js:122
+#: erpnext/selling/doctype/sales_order/sales_order.js:101
+#: erpnext/selling/doctype/sales_order/sales_order.js:116
+#: erpnext/selling/doctype/sales_order/sales_order.js:129
#: erpnext/selling/doctype/sales_order/sales_order.js:250
#: erpnext/stock/doctype/pick_list/pick_list.js:152
#: erpnext/stock/doctype/pick_list/pick_list.js:167
@@ -48954,7 +48946,7 @@ msgid "Stock Reservation Entries Cancelled"
msgstr ""
#: erpnext/controllers/subcontracting_inward_controller.py:1003
-#: erpnext/manufacturing/doctype/production_plan/production_plan.py:2234
+#: erpnext/manufacturing/doctype/production_plan/production_plan.py:2236
#: erpnext/manufacturing/doctype/work_order/work_order.py:2124
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1777
msgid "Stock Reservation Entries Created"
@@ -48983,7 +48975,7 @@ msgstr ""
msgid "Stock Reservation Entry created against a Pick List cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one."
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:563
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:567
msgid "Stock Reservation Warehouse Mismatch"
msgstr ""
@@ -49169,6 +49161,10 @@ msgstr ""
msgid "Stock Uom"
msgstr ""
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:739
+msgid "Stock Update Not Allowed"
+msgstr ""
+
#. Name of a role
#: erpnext/accounts/doctype/fiscal_year/fiscal_year.json
#: erpnext/assets/doctype/location/location.json
@@ -49262,10 +49258,6 @@ msgstr ""
msgid "Stock cannot be reserved in the group warehouse {0}."
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:736
-msgid "Stock cannot be updated against Purchase Receipt {0}"
-msgstr ""
-
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1228
msgid "Stock cannot be updated against the following Delivery Notes: {0}"
msgstr ""
@@ -49274,6 +49266,10 @@ msgstr ""
msgid "Stock cannot be updated because the invoice contains a drop shipping item. Please disable 'Update Stock' or remove the drop shipping item."
msgstr ""
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:736
+msgid "Stock cannot be updated for Purchase Invoice {0} because a Purchase Receipt {1} has already been created for this transaction. Please disable the 'Update Stock' checkbox in the Purchase Invoice and save the invoice."
+msgstr ""
+
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1131
msgid "Stock has been unreserved for work order {0}."
msgstr ""
@@ -50081,8 +50077,8 @@ msgstr ""
#: erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js:37
#: erpnext/assets/doctype/asset/asset.json
#: erpnext/buying/doctype/purchase_order/purchase_order.json
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:188
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:273
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:184
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:269
#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
#: erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
#: erpnext/buying/doctype/supplier/supplier.json
@@ -50109,7 +50105,7 @@ msgstr ""
#: erpnext/regional/report/irs_1099/irs_1099.py:77
#: erpnext/selling/doctype/customer/customer.js:253
#: erpnext/selling/doctype/party_specific_item/party_specific_item.json
-#: erpnext/selling/doctype/sales_order/sales_order.js:182
+#: erpnext/selling/doctype/sales_order/sales_order.js:189
#: erpnext/selling/doctype/sales_order/sales_order.js:1660
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/selling/doctype/sms_center/sms_center.json
@@ -50215,7 +50211,7 @@ msgstr ""
#: erpnext/accounts/report/purchase_register/purchase_register.py:186
#: erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js:55
#: erpnext/buying/doctype/purchase_order/purchase_order.json
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:506
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:502
#: erpnext/buying/doctype/supplier/supplier.json
#: erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py:105
#: erpnext/buying/workspace/buying/buying.json
@@ -50269,7 +50265,7 @@ msgstr ""
msgid "Supplier Invoice No"
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1773
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1774
msgid "Supplier Invoice No exists in Purchase Invoice {0}"
msgstr ""
@@ -50699,7 +50695,7 @@ msgstr ""
msgid "TDS Computation Summary"
msgstr ""
-#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1534
+#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1535
msgid "TDS Deducted"
msgstr ""
@@ -51944,7 +51940,7 @@ msgstr ""
msgid "The Serial No at Row #{0}: {1} is not available in warehouse {2}."
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2576
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2593
msgid "The Serial No {0} is reserved against the {1} {2} and cannot be used for any other transaction."
msgstr ""
@@ -51986,7 +51982,7 @@ msgstr ""
msgid "The current POS opening entry is outdated. Please close it and create a new one."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1123
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1150
msgid "The default BOM for that item will be fetched by the system. You can also change the BOM."
msgstr ""
@@ -52011,7 +52007,7 @@ msgstr ""
msgid "The field To Shareholder cannot be blank"
msgstr ""
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:413
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:417
msgid "The field {0} in row {1} is not set"
msgstr ""
@@ -52172,7 +52168,7 @@ msgstr ""
msgid "The percentage you are allowed to transfer more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed transfer 110 units."
msgstr ""
-#: erpnext/public/js/utils.js:941
+#: erpnext/public/js/utils.js:947
msgid "The reserved stock will be released when you update items. Are you certain you wish to proceed?"
msgstr ""
@@ -52196,7 +52192,7 @@ msgstr ""
msgid "The selected item cannot have Batch"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:647
+#: erpnext/assets/doctype/asset/asset.js:654
msgid "The sell quantity is less than the total asset quantity. The remaining quantity will be split into a new asset. This action cannot be undone.
Do you want to continue?"
msgstr ""
@@ -52291,15 +52287,15 @@ msgstr ""
msgid "The value {0} is already assigned to an existing Item {1}."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1151
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1178
msgid "The warehouse where you store finished Items before they are shipped."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1144
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1171
msgid "The warehouse where you store your raw materials. Each required item can have a separate source warehouse. Group warehouse also can be selected as source warehouse. On submission of the Work Order, the raw materials will be reserved in these warehouses for production usage."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1156
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1183
msgid "The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse."
msgstr ""
@@ -52501,7 +52497,7 @@ msgstr ""
msgid "This is a location where scraped materials are stored."
msgstr ""
-#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:322
+#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:318
msgid "This is a preview of the email to be sent. A PDF of the document will automatically be attached with the email."
msgstr ""
@@ -52553,7 +52549,7 @@ msgstr ""
msgid "This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1137
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1164
msgid "This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox."
msgstr ""
@@ -53098,7 +53094,7 @@ msgstr ""
msgid "To Warehouse (Optional)"
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:972
+#: erpnext/manufacturing/doctype/bom/bom.js:979
msgid "To add Operations tick the 'With Operations' checkbox."
msgstr ""
@@ -53494,7 +53490,7 @@ msgstr ""
msgid "Total Credit"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:336
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:342
msgid "Total Credit/ Debit Amount should be same as linked Journal Entry"
msgstr ""
@@ -53503,7 +53499,7 @@ msgstr ""
msgid "Total Debit"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:927
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:933
msgid "Total Debit must be equal to Total Credit. The difference is {0}"
msgstr ""
@@ -53937,7 +53933,7 @@ msgstr ""
msgid "Total Time in Mins"
msgstr ""
-#: erpnext/public/js/utils.js:167
+#: erpnext/public/js/utils.js:173
msgid "Total Unpaid: {0}"
msgstr ""
@@ -54332,7 +54328,7 @@ msgstr ""
msgid "Transfer"
msgstr ""
-#: erpnext/assets/doctype/asset/asset.js:152
+#: erpnext/assets/doctype/asset/asset.js:155
msgid "Transfer Asset"
msgstr ""
@@ -54697,7 +54693,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/workstation/workstation.js:480
#: erpnext/manufacturing/report/bom_explorer/bom_explorer.py:70
#: erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py:110
-#: erpnext/public/js/stock_analytics.js:94 erpnext/public/js/utils.js:814
+#: erpnext/public/js/stock_analytics.js:94 erpnext/public/js/utils.js:820
#: erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.json
#: erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
@@ -55107,8 +55103,8 @@ msgstr ""
msgid "Unreconciled Entries"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:900
-#: erpnext/selling/doctype/sales_order/sales_order.js:107
+#: erpnext/manufacturing/doctype/work_order/work_order.js:927
+#: erpnext/selling/doctype/sales_order/sales_order.js:114
#: erpnext/stock/doctype/pick_list/pick_list.js:158
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:193
msgid "Unreserve"
@@ -55315,9 +55311,9 @@ msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order.js:324
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.js:43
-#: erpnext/public/js/utils.js:920
+#: erpnext/public/js/utils.js:926
#: erpnext/selling/doctype/quotation/quotation.js:135
-#: erpnext/selling/doctype/sales_order/sales_order.js:75
+#: erpnext/selling/doctype/sales_order/sales_order.js:82
#: erpnext/selling/doctype/sales_order/sales_order.js:940
msgid "Update Items"
msgstr ""
@@ -55417,7 +55413,7 @@ msgstr ""
msgid "Updating Variants..."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:1099
+#: erpnext/manufacturing/doctype/work_order/work_order.js:1126
msgid "Updating Work Order status"
msgstr ""
@@ -55660,7 +55656,7 @@ msgstr ""
msgid "User has not applied rule on the invoice {0}"
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:190
+#: erpnext/setup/doctype/employee/employee.py:187
msgid "User {0} does not exist"
msgstr ""
@@ -55668,15 +55664,15 @@ msgstr ""
msgid "User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User."
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:207
+#: erpnext/setup/doctype/employee/employee.py:205
msgid "User {0} is already assigned to Employee {1}"
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:245
+#: erpnext/setup/doctype/employee/employee.py:243
msgid "User {0}: Removed Employee Self Service role as there is no mapped employee."
msgstr ""
-#: erpnext/setup/doctype/employee/employee.py:240
+#: erpnext/setup/doctype/employee/employee.py:238
msgid "User {0}: Removed Employee role as there is no mapped employee."
msgstr ""
@@ -56061,8 +56057,8 @@ msgstr ""
msgid "Value Type"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:599
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:629
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:840
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:870
msgid "Value as on"
msgstr ""
@@ -56075,19 +56071,19 @@ msgstr ""
msgid "Value of Goods"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:623
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:864
msgid "Value of New Capitalized Asset"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:605
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:846
msgid "Value of New Purchase"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:617
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:858
msgid "Value of Scrapped Asset"
msgstr ""
-#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:611
+#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:852
msgid "Value of Sold Asset"
msgstr ""
@@ -56784,7 +56780,7 @@ msgid "Warehouse not found against the account {0}"
msgstr ""
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1218
-#: erpnext/stock/doctype/delivery_note/delivery_note.py:440
+#: erpnext/stock/doctype/delivery_note/delivery_note.py:444
msgid "Warehouse required for stock Item {0}"
msgstr ""
@@ -56926,7 +56922,7 @@ msgstr ""
msgid "Warning!"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1312
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1318
msgid "Warning: Another {0} # {1} exists against stock entry {2}"
msgstr ""
@@ -57093,13 +57089,6 @@ msgstr ""
msgid "Weekly Time to send"
msgstr ""
-#. Label of the task_weight (Float) field in DocType 'Task'
-#. Label of the weight (Float) field in DocType 'Task Type'
-#: erpnext/projects/doctype/task/task.json
-#: erpnext/projects/doctype/task_type/task_type.json
-msgid "Weight"
-msgstr ""
-
#. Label of the weight (Float) field in DocType 'Shipment Parcel'
#. Label of the weight (Float) field in DocType 'Shipment Parcel Template'
#: erpnext/stock/doctype/shipment_parcel/shipment_parcel.json
@@ -57821,7 +57810,7 @@ msgstr ""
msgid "You can change the parent account to a Balance Sheet account or select a different account."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:703
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:709
msgid "You can not enter current voucher in 'Against Journal Entry' column"
msgstr ""
@@ -57862,7 +57851,7 @@ msgstr ""
msgid "You can't redeem Loyalty Points having more value than the Total Amount."
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:728
+#: erpnext/manufacturing/doctype/bom/bom.js:735
msgid "You cannot change the rate if BOM is mentioned against any Item."
msgstr ""
@@ -57878,7 +57867,7 @@ msgstr ""
msgid "You cannot create/amend any accounting entries till this date."
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:936
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:942
msgid "You cannot credit and debit same account at the same time"
msgstr ""
@@ -57922,6 +57911,10 @@ msgstr ""
msgid "You cannot {0} this document because another Period Closing Entry {1} exists after {2}"
msgstr ""
+#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:566
+msgid "You do not have permission to edit this document"
+msgstr ""
+
#: erpnext/controllers/accounts_controller.py:3837
msgid "You do not have permissions to {} items in a {}."
msgstr ""
@@ -57938,7 +57931,7 @@ msgstr ""
msgid "You had {} errors while creating opening invoices. Check {} for more details"
msgstr ""
-#: erpnext/public/js/utils.js:1020
+#: erpnext/public/js/utils.js:1026
msgid "You have already selected items from {0} {1}"
msgstr ""
@@ -58058,7 +58051,7 @@ msgstr ""
msgid "as Title"
msgstr ""
-#: erpnext/manufacturing/doctype/bom/bom.js:996
+#: erpnext/manufacturing/doctype/bom/bom.js:1003
msgid "as a percentage of finished item quantity"
msgstr ""
@@ -58211,7 +58204,7 @@ msgstr ""
msgid "paid to"
msgstr ""
-#: erpnext/public/js/utils.js:437
+#: erpnext/public/js/utils.js:443
msgid "payments app is not installed. Please install it from {0} or {1}"
msgstr ""
@@ -58398,7 +58391,7 @@ msgstr ""
msgid "{0} Operating Cost for operation {1}"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:528
+#: erpnext/manufacturing/doctype/work_order/work_order.js:555
msgid "{0} Operations: {1}"
msgstr ""
@@ -58426,19 +58419,19 @@ msgstr ""
msgid "{0} account not found while submitting purchase receipt"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1056
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1062
msgid "{0} against Bill {1} dated {2}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1065
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1071
msgid "{0} against Purchase Order {1}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1032
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1038
msgid "{0} against Sales Invoice {1}"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1039
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1045
msgid "{0} against Sales Order {1}"
msgstr ""
@@ -58579,6 +58572,10 @@ msgstr ""
msgid "{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}."
msgstr ""
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1737
+msgid "{0} is not a CSV file."
+msgstr ""
+
#: erpnext/selling/doctype/customer/customer.py:234
msgid "{0} is not a company bank account"
msgstr ""
@@ -58623,18 +58620,30 @@ msgstr ""
msgid "{0} is open. Close the POS or cancel the existing POS Opening Entry to create a new POS Opening Entry."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:483
+#: erpnext/manufacturing/doctype/work_order/work_order.js:520
+msgid "{0} items disassembled"
+msgstr ""
+
+#: erpnext/manufacturing/doctype/work_order/work_order.js:484
msgid "{0} items in progress"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:494
+#: erpnext/manufacturing/doctype/work_order/work_order.js:508
msgid "{0} items lost during process."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/work_order.js:464
+#: erpnext/manufacturing/doctype/work_order/work_order.js:465
msgid "{0} items produced"
msgstr ""
+#: erpnext/manufacturing/doctype/work_order/work_order.js:488
+msgid "{0} items returned"
+msgstr ""
+
+#: erpnext/manufacturing/doctype/work_order/work_order.js:491
+msgid "{0} items to return"
+msgstr ""
+
#: erpnext/controllers/sales_and_purchase_return.py:218
msgid "{0} must be negative in return document"
msgstr ""
@@ -58785,7 +58794,7 @@ msgstr ""
msgid "{0} {1} is cancelled so the action cannot be completed"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:851
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:857
msgid "{0} {1} is closed"
msgstr ""
@@ -58797,7 +58806,7 @@ msgstr ""
msgid "{0} {1} is frozen"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:848
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:854
msgid "{0} {1} is fully billed"
msgstr ""
@@ -58813,8 +58822,8 @@ msgstr ""
msgid "{0} {1} is not in any active Fiscal Year"
msgstr ""
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:845
-#: erpnext/accounts/doctype/journal_entry/journal_entry.py:884
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:851
+#: erpnext/accounts/doctype/journal_entry/journal_entry.py:890
msgid "{0} {1} is not submitted"
msgstr ""
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index d283bc8dadb..0f4c9d569fa 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -383,9 +383,8 @@ class JobCard(Document):
# if key number reaches/crosses to production_capacity means capacity is full and overlap error generated
# this will store last to_time of sequential job cards
alloted_capacity = {1: time_logs[0]["to_time"]}
- # flag for sequential Job card found
- sequential_job_card_found = False
for i in range(1, len(time_logs)):
+ sequential_job_card_found = False
# scanning for all Existing keys
for key in alloted_capacity.keys():
# if current Job Card from time is greater than last to_time in that key means these job card are sequential
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
index f9eb0e40b20..1a150dc864f 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -239,12 +239,12 @@
"label": "Allow Editing of Items and Quantities in Work Order"
}
],
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"icon": "icon-wrench",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:05.759229",
+ "modified": "2026-03-16 13:28:20.714576",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 947eb45787e..30b3968fc80 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -731,6 +731,7 @@ class ProductionPlan(Document):
"description": d.description,
"stock_uom": d.stock_uom,
"company": self.company,
+ "source_warehouse": frappe.get_value("BOM", d.bom_no, "default_source_warehouse"),
"fg_warehouse": d.warehouse,
"production_plan": self.name,
"production_plan_item": d.name,
@@ -807,6 +808,7 @@ class ProductionPlan(Document):
continue
work_order_data = {
+ "source_warehouse": frappe.get_value("BOM", row.bom_no, "default_source_warehouse"),
"wip_warehouse": default_warehouses.get("wip_warehouse"),
"fg_warehouse": default_warehouses.get("fg_warehouse"),
"scrap_warehouse": default_warehouses.get("scrap_warehouse"),
@@ -1896,7 +1898,7 @@ def get_item_data(item_code):
return {
"bom_no": item_details.get("bom_no"),
"stock_uom": item_details.get("stock_uom"),
- # "description": item_details.get("description")
+ "description": item_details.get("description"),
}
@@ -1912,6 +1914,7 @@ def get_sub_assembly_items(
skip_available_sub_assembly_item=False,
):
data = get_bom_children(parent=bom_no)
+ precision = frappe.get_precision("Production Plan Sub Assembly Item", "qty")
for d in data:
if d.expandable:
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
@@ -1951,8 +1954,8 @@ def get_sub_assembly_items(
"is_sub_contracted_item": d.is_sub_contracted_item,
"bom_level": indent,
"indent": indent,
- "stock_qty": stock_qty,
- "required_qty": required_qty,
+ "stock_qty": flt(stock_qty, precision),
+ "required_qty": flt(required_qty, precision),
"projected_qty": bin_details[d.item_code][0].get("projected_qty", 0)
if bin_details.get(d.item_code)
else 0,
@@ -2098,16 +2101,16 @@ def get_raw_materials_of_sub_assembly_items(
for item in query.run(as_dict=True):
key = (item.item_code, item.bom_no)
- if item.is_phantom_item:
- sub_assembly_items[key] += item.get("qty")
+ existing_key = (item.item_code, item.bom_no or item.main_bom)
- if (item.bom_no and key not in sub_assembly_items) or (
- (item.item_code, item.bom_no or item.main_bom) in existing_sub_assembly_items
- ):
+ if item.bom_no and not item.is_phantom_item and key not in sub_assembly_items:
+ continue
+
+ if not item.is_phantom_item and existing_key in existing_sub_assembly_items:
continue
if item.bom_no:
- planned_qty = flt(sub_assembly_items[key])
+ recursion_qty = flt(item.get("qty")) if item.is_phantom_item else flt(sub_assembly_items[key])
get_raw_materials_of_sub_assembly_items(
existing_sub_assembly_items,
item_details,
@@ -2115,9 +2118,10 @@ def get_raw_materials_of_sub_assembly_items(
item.bom_no,
include_non_stock_items,
sub_assembly_items,
- planned_qty=planned_qty,
+ planned_qty=recursion_qty,
)
- existing_sub_assembly_items.add((item.item_code, item.bom_no or item.main_bom))
+ if not item.is_phantom_item:
+ existing_sub_assembly_items.add(existing_key)
else:
if not item.conversion_factor and item.purchase_uom:
item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index b7787d6489b..08249de49fb 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -2713,6 +2713,92 @@ class TestProductionPlan(IntegrationTestCase):
[item.item_code for item in plan.mr_items], ["Item Level 1-3", "Item Level 2-3", "Item Level 3-1"]
)
+ def test_phantom_bom_explosion_across_multiple_po_items(self):
+ """
+ Regression: when the same phantom item (BOM) is referenced inside sub-assemblies
+ of two different production plan items, its raw materials must be fully exploded
+ for *both* plan items.
+ """
+ # Setup items
+ fg_a = make_item("FG for Cross-PO Phantom Test A")
+ fg_b = make_item("FG for Cross-PO Phantom Test B")
+ sa_a = make_item("SA for Cross-PO Phantom Test A")
+ sa_b = make_item("SA for Cross-PO Phantom Test B")
+ phantom = make_item("Phantom for Cross-PO Test")
+ rm = make_item("RM for Cross-PO Phantom Test")
+
+ # Create the shared phantom BOM
+ phantom_bom = make_bom(item=phantom.name, raw_materials=[rm.name], do_not_save=True)
+ phantom_bom.is_phantom_bom = 1
+ phantom_bom.save()
+ phantom_bom.submit()
+
+ # Create SA-A BOM with phantom
+ sa_a_bom = make_bom(item=sa_a.name, raw_materials=[phantom.name], do_not_save=True)
+ sa_a_bom.items[0].bom_no = phantom_bom.name
+ sa_a_bom.save()
+ sa_a_bom.submit()
+
+ # Create SA-B BOM with the SAME phantom
+ sa_b_bom = make_bom(item=sa_b.name, raw_materials=[phantom.name], do_not_save=True)
+ sa_b_bom.items[0].bom_no = phantom_bom.name
+ sa_b_bom.save()
+ sa_b_bom.submit()
+
+ # Create FG-A BOM with SA-A
+ fg_a_bom = make_bom(item=fg_a.name, raw_materials=[sa_a.name], do_not_save=True)
+ fg_a_bom.items[0].bom_no = sa_a_bom.name
+ fg_a_bom.save()
+ fg_a_bom.submit()
+
+ # Create FG-B BOM with SA-B
+ fg_b_bom = make_bom(item=fg_b.name, raw_materials=[sa_b.name], do_not_save=True)
+ fg_b_bom.items[0].bom_no = sa_b_bom.name
+ fg_b_bom.save()
+ fg_b_bom.submit()
+
+ # Build Production Plan with both FGs
+ plan = frappe.new_doc("Production Plan")
+ plan.company = "_Test Company"
+ plan.posting_date = nowdate()
+ plan.ignore_existing_ordered_qty = 1
+ plan.skip_available_sub_assembly_item = 1
+ plan.sub_assembly_warehouse = "_Test Warehouse - _TC"
+
+ for fg_item, bom in [(fg_a.name, fg_a_bom.name), (fg_b.name, fg_b_bom.name)]:
+ plan.append(
+ "po_items",
+ {
+ "use_multi_level_bom": 1,
+ "item_code": fg_item,
+ "bom_no": bom,
+ "planned_qty": 1,
+ "planned_start_date": now_datetime(),
+ "stock_uom": "Nos",
+ },
+ )
+
+ plan.insert()
+ plan.get_sub_assembly_items()
+
+ # Verify both sub-assemblies are present
+ sa_items = {row.production_item for row in plan.sub_assembly_items}
+ self.assertIn(sa_a.name, sa_items)
+ self.assertIn(sa_b.name, sa_items)
+
+ plan.submit()
+
+ mr_items = get_items_for_material_requests(plan.as_dict())
+
+ # Phantom raw material should be counted twice (once per FG → SA → shared phantom)
+ rm_total_qty = sum(flt(d["quantity"]) for d in mr_items if d["item_code"] == rm.name)
+ self.assertEqual(
+ rm_total_qty,
+ 2.0,
+ f"Expected RM qty=2 (1 per FG via shared phantom BOM), got {rm_total_qty}. "
+ "The phantom BOM was not re-exploded for the second po_item.",
+ )
+
def create_production_plan(**args):
"""
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
index 26d65116396..35f7bc39799 100644
--- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
@@ -268,7 +268,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2026-02-11 13:00:09.092676",
+ "modified": "2026-03-16 10:28:41.879801",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan Sub Assembly Item",
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 4365a501ae3..6e5295fbfa0 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -570,7 +570,8 @@
{
"fieldname": "production_plan_sub_assembly_item",
"fieldtype": "Data",
- "label": "Production Plan Sub-assembly Item",
+ "hidden": 1,
+ "label": "Production Plan Sub Assembly Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
@@ -704,7 +705,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
- "modified": "2026-02-06 17:53:11.295600",
+ "modified": "2026-03-16 10:15:28.708688",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index f9427049f15..1867b567da0 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -101,9 +101,6 @@ class Workstation(Document):
self.total_working_hours += row.hours
def validate_working_hours(self, row):
- if not (row.start_time and row.end_time):
- frappe.throw(_("Row #{0}: Start Time and End Time are required").format(row.idx))
-
if get_time(row.start_time) >= get_time(row.end_time):
frappe.throw(_("Row #{0}: Start Time must be before End Time").format(row.idx))
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index c80d2a2969b..4b1fc449473 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -470,3 +470,5 @@ erpnext.patches.v16_0.complete_onboarding_steps_for_older_sites #2
erpnext.patches.v16_0.migrate_asset_type_checkboxes_to_select
erpnext.patches.v16_0.update_order_qty_and_requested_qty_based_on_mr_and_po
erpnext.patches.v16_0.enable_serial_batch_setting
+erpnext.patches.v16_0.update_requested_qty_packed_item
+erpnext.patches.v16_0.remove_payables_receivables_workspace
diff --git a/erpnext/patches/v16_0/remove_payables_receivables_workspace.py b/erpnext/patches/v16_0/remove_payables_receivables_workspace.py
new file mode 100644
index 00000000000..bd1bd96fe77
--- /dev/null
+++ b/erpnext/patches/v16_0/remove_payables_receivables_workspace.py
@@ -0,0 +1,7 @@
+import frappe
+
+
+def execute():
+ for ws in ["Receivables", "Payables"]:
+ frappe.delete_doc_if_exists("Workspace Sidebar", ws)
+ frappe.delete_doc_if_exists("Workspace", ws)
diff --git a/erpnext/patches/v16_0/update_requested_qty_packed_item.py b/erpnext/patches/v16_0/update_requested_qty_packed_item.py
new file mode 100644
index 00000000000..82a636d79bf
--- /dev/null
+++ b/erpnext/patches/v16_0/update_requested_qty_packed_item.py
@@ -0,0 +1,24 @@
+import frappe
+from frappe.query_builder.functions import Sum
+
+
+def execute():
+ MaterialRequestItem = frappe.qb.DocType("Material Request Item")
+
+ mri_query = (
+ frappe.qb.from_(MaterialRequestItem)
+ .select(MaterialRequestItem.packed_item, Sum(MaterialRequestItem.qty))
+ .where((MaterialRequestItem.packed_item.isnotnull()) & (MaterialRequestItem.docstatus == 1))
+ .groupby(MaterialRequestItem.packed_item)
+ )
+
+ mri_data = mri_query.run()
+
+ if not mri_data:
+ return
+
+ updates_against_mr = {data[0]: {"requested_qty": data[1]} for data in mri_data}
+
+ frappe.db.auto_commit_on_many_writes = True
+ frappe.db.bulk_update("Packed Item", updates_against_mr)
+ frappe.db.auto_commit_on_many_writes = False
diff --git a/erpnext/projects/doctype/projects_settings/projects_settings.json b/erpnext/projects/doctype/projects_settings/projects_settings.json
index 08ec43fe391..5cf29d6e208 100644
--- a/erpnext/projects/doctype/projects_settings/projects_settings.json
+++ b/erpnext/projects/doctype/projects_settings/projects_settings.json
@@ -43,10 +43,10 @@
"label": "Fetch Timesheet in Sales Invoice"
}
],
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:28.414222",
+ "modified": "2026-03-16 13:28:20.265634",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects Settings",
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 873edfb7165..5c3581ac2e4 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -682,3 +682,10 @@ erpnext.buying.get_items_from_product_bundle = function (frm) {
dialog.show();
};
+erpnext.buying.prevent_past_schedule_dates = function (frm) {
+ if (frm.doc.transaction_date) {
+ frm.fields_dict["schedule_date"].datepicker?.update({
+ minDate: new Date(frm.doc.transaction_date),
+ });
+ }
+};
diff --git a/erpnext/public/js/utils/demo.js b/erpnext/public/js/utils/demo.js
index db15834ae95..980ac530baf 100644
--- a/erpnext/public/js/utils/demo.js
+++ b/erpnext/public/js/utils/demo.js
@@ -2,7 +2,7 @@ frappe.provide("erpnext.demo");
$(document).on("desktop_screen", function (event, data) {
data.desktop.add_menu_item({
- label: __("Clear Demo Data"),
+ label: __("Delete Demo Data"),
icon: "trash",
condition: function () {
return frappe.boot.sysdefaults.demo_company;
diff --git a/erpnext/public/js/utils/landed_taxes_and_charges_common.js b/erpnext/public/js/utils/landed_taxes_and_charges_common.js
index 7d801ca91e6..751d831a6f7 100644
--- a/erpnext/public/js/utils/landed_taxes_and_charges_common.js
+++ b/erpnext/public/js/utils/landed_taxes_and_charges_common.js
@@ -42,9 +42,9 @@ erpnext.landed_cost_taxes_and_charges = {
if (row.account_currency == company_currency) {
row.exchange_rate = 1;
- frm.set_df_property("taxes", "hidden", 1, row.name, "exchange_rate");
+ frm.set_df_property("taxes", "hidden", 1, frm.docname, "exchange_rate", cdn);
} else if (!row.exchange_rate || row.exchange_rate == 1) {
- frm.set_df_property("taxes", "hidden", 0, row.name, "exchange_rate");
+ frm.set_df_property("taxes", "hidden", 0, frm.docname, "exchange_rate", cdn);
frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
args: {
diff --git a/erpnext/regional/italy/e-invoice.xml b/erpnext/regional/italy/e-invoice.xml
index 7c436a2b449..ef1e94ff27b 100644
--- a/erpnext/regional/italy/e-invoice.xml
+++ b/erpnext/regional/italy/e-invoice.xml
@@ -18,25 +18,27 @@
{{ address.country_code }}
{%- endmacro %}
-{%- macro render_discount_or_margin(item) -%}
-{%- if (item.discount_percentage and item.discount_percentage > 0.0) or item.margin_type %}
+{%- macro render_discount_or_margin(item, tax_divisor) -%}
+{%- if item.discount_percentage and item.discount_percentage > 0.0 %}
- {%- if item.discount_percentage > 0.0 %}
SC
{{ format_float(item.discount_percentage) }}
- {%- endif %}
- {%- if item.margin_rate_or_amount > 0.0 -%}
- MG
- {%- if item.margin_type == "Percentage" -%}
- {{ format_float(item.margin_rate_or_amount) }}
- {%- elif item.margin_type == "Amount" -%}
- {{ format_float(item.margin_rate_or_amount) }}
- {%- endif -%}
- {%- endif %}
-{%- endif -%}
+{%- endif %}
+{%- if item.margin_rate_or_amount and item.margin_rate_or_amount > 0.0 %}
+
+ MG
+ {%- if item.margin_type == "Percentage" -%}
+ {{ format_float(item.margin_rate_or_amount) }}
+ {%- elif item.margin_type == "Amount" -%}
+ {{ format_float(item.margin_rate_or_amount / tax_divisor) }}
+ {%- endif -%}
+
+{%- endif %}
{%- endmacro -%}
+{%- set has_inclusive_tax = doc.taxes | selectattr("included_in_print_rate") | list | length > 0 -%}
+
{%- for item in doc.e_invoice_items %}
+ {%- set tax_divisor = (1 + item.tax_rate / 100) if has_inclusive_tax and item.tax_rate else 1 %}
{{ item.idx }}
@@ -188,8 +191,9 @@
{{ html2text(item.description or '') or item.item_name }}
{{ format_float(item.qty) }}
{{ item.stock_uom }}
- {{ format_float(item.net_rate or item.price_list_rate or item.rate, item_meta.get_field("rate").precision) }}
- {{ render_discount_or_margin(item) }}
+ {%- set item_unit_net_price = (item.price_list_rate / tax_divisor) or (item.net_rate) or (item.rate / tax_divisor) %}
+ {{ format_float(item_unit_net_price, item_meta.get_field("rate").precision) }}
+ {{ render_discount_or_margin(item, tax_divisor) }}
{{ format_float(item.net_amount, item_meta.get_field("amount").precision) }}
{{ format_float(item.tax_rate, item_meta.get_field("tax_rate").precision) }}
{%- if item.tax_exemption_reason %}
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 5ec2418df1f..ff22b0e4c2e 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -26,7 +26,7 @@ frappe.ui.form.on("Sales Order", {
let color;
if (!doc.qty && frm.doc.has_unit_price_items) {
color = "yellow";
- } else if (doc.stock_qty <= doc.delivered_qty) {
+ } else if (doc.stock_qty <= doc.actual_qty) {
color = "green";
} else {
color = "orange";
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 5c6f64278af..7918dced389 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1003,20 +1003,19 @@ def close_or_unclose_sales_orders(names, status):
def get_requested_item_qty(sales_order):
result = {}
- for d in frappe.db.get_all(
- "Material Request Item",
- filters={"docstatus": 1, "sales_order": sales_order},
- fields=[
- "sales_order_item",
- "packed_item",
- {"SUM": "qty", "as": "qty"},
- {"SUM": "received_qty", "as": "received_qty"},
- ],
- group_by="sales_order_item, packed_item",
- ):
- result[d.sales_order_item or d.packed_item] = frappe._dict(
- {"qty": d.qty, "received_qty": d.received_qty}
- )
+
+ so = frappe.get_doc("Sales Order", sales_order)
+
+ for item in so.items:
+ if is_product_bundle(item.item_code):
+ for packed_item in so.get("packed_items"):
+ if (
+ packed_item.parent_item == item.item_code
+ and packed_item.parent_detail_docname == item.name
+ ):
+ result[packed_item.name] = frappe._dict({"qty": packed_item.requested_qty})
+ else:
+ result[item.name] = frappe._dict({"qty": item.requested_qty})
return result
@@ -1035,8 +1034,7 @@ def make_material_request(source_name, target_doc=None):
flt(so_item.qty)
- flt(requested_item_qty.get(so_item.name, {}).get("qty"))
- max(
- flt(so_item.get("delivered_qty"))
- - flt(requested_item_qty.get(so_item.name, {}).get("received_qty")),
+ flt(so_item.get("delivered_qty")),
0,
)
)
@@ -1051,16 +1049,12 @@ def make_material_request(source_name, target_doc=None):
)
return flt(
- (
- flt(so_item.qty)
- - flt(requested_item_qty.get(so_item.name, {}).get("qty"))
- - max(
- flt(delivered_qty) * flt(bundle_item_qty)
- - flt(requested_item_qty.get(so_item.name, {}).get("received_qty")),
- 0,
- )
+ flt(so_item.qty)
+ - flt(requested_item_qty.get(so_item.name, {}).get("qty"))
+ - max(
+ flt(delivered_qty) * flt(bundle_item_qty),
+ 0,
)
- * bundle_item_qty
)
def update_item(source, target, source_parent):
@@ -1122,8 +1116,10 @@ def make_material_request(source_name, target_doc=None):
target_doc,
postprocess,
)
-
- return doc
+ if doc and doc.items:
+ return doc
+ else:
+ frappe.throw(_("Material Request already created for the ordered quantity"))
@frappe.whitelist()
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 6c7ae9c0660..e4a372454be 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -57,6 +57,71 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase):
frappe.db.rollback()
frappe.set_user("Administrator")
+ @IntegrationTestCase.change_settings(
+ "Stock Settings",
+ {
+ "auto_insert_price_list_rate_if_missing": 1,
+ "update_existing_price_list_rate": 1,
+ "update_price_list_based_on": "Rate",
+ },
+ )
+ def test_sales_order_expired_item_price(self):
+ price_list = "_Test Price List"
+
+ item_1 = make_item("_Test Expired Item 1", {"is_stock_item": 1})
+
+ frappe.db.delete("Item Price", {"item_code": item_1.item_code})
+
+ item_price = frappe.new_doc("Item Price")
+ item_price.item_code = item_1.item_code
+ item_price.price_list = price_list
+ item_price.price_list_rate = 100
+ item_price.valid_from = add_days(today(), -10)
+ item_price.valid_upto = add_days(today(), -5)
+ item_price.save()
+
+ so = make_sales_order(
+ item_code=item_1.item_code, qty=1, rate=1000, selling_price_list=price_list, do_not_save=True
+ )
+ so.save()
+ so.reload()
+
+ self.assertEqual(frappe.db.get_value("Item Price", item_price.name, "price_list_rate"), 100)
+ self.assertEqual(
+ frappe.db.count("Item Price", {"item_code": item_1.item_code, "price_list": price_list}),
+ 2,
+ )
+ all_item_prices = frappe.get_all(
+ "Item Price", filters={"item_code": item_1.item_code}, order_by="valid_from desc"
+ )
+ self.assertEqual(frappe.db.get_value("Item Price", all_item_prices[0].name, "price_list_rate"), 1000)
+
+ def test_sales_order_with_product_bundle_for_partial_material_request(self):
+ product_bundle = make_product_bundle(
+ "_Test Product Bundle Item", ["_Test Item", "_Test Item Home Desktop 100"]
+ )
+ so = make_sales_order(item_code=product_bundle.name, qty=2)
+ mr = make_material_request(so.name)
+ mr.items[0].qty = 4
+ mr.items[1].qty = 2
+ mr.items[0].schedule_date = today()
+ mr.items[1].schedule_date = today()
+ mr.save()
+ mr.submit()
+ mr.reload()
+ self.assertEqual(mr.items[0].qty, 4)
+ mr = make_material_request(so.name)
+ self.assertEqual(mr.items[0].qty, 6)
+
+ def test_sales_order_with_full_material_request(self):
+ so = make_sales_order(item_code="_Test Item", qty=5, do_not_submit=True)
+ so.submit()
+ mr = make_material_request(so.name)
+ mr.save()
+ mr.submit()
+ mr.reload()
+ self.assertRaises(frappe.ValidationError, make_material_request, so.name)
+
def test_sales_order_skip_delivery_note(self):
so = make_sales_order(do_not_submit=True)
so.order_type = "Maintenance"
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index 6304a55c7d8..b7896b58dff 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -323,13 +323,13 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2026-02-27 00:47:46.003305",
+ "modified": "2026-03-16 13:28:18.988883",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling Settings",
diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.json b/erpnext/selling/report/quotation_trends/quotation_trends.json
index a4011db4041..8722bf61fd7 100644
--- a/erpnext/selling/report/quotation_trends/quotation_trends.json
+++ b/erpnext/selling/report/quotation_trends/quotation_trends.json
@@ -9,8 +9,8 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
- "letterhead": null,
- "modified": "2025-11-05 11:55:50.127020",
+ "letter_head": null,
+ "modified": "2026-03-13 17:36:37.619715",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Trends",
diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.json b/erpnext/selling/report/sales_order_trends/sales_order_trends.json
index dedec06bcf9..26758f5ab3f 100644
--- a/erpnext/selling/report/sales_order_trends/sales_order_trends.json
+++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.json
@@ -9,8 +9,8 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
- "letterhead": null,
- "modified": "2025-11-05 11:55:50.096303",
+ "letter_head": null,
+ "modified": "2026-03-13 17:36:21.440118",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Trends",
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.json b/erpnext/setup/doctype/global_defaults/global_defaults.json
index 92bff6d4fbe..9178bb64768 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.json
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.json
@@ -91,13 +91,13 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 1,
"in_create": 1,
"issingle": 1,
"links": [],
- "modified": "2026-01-12 09:45:59.819161",
+ "modified": "2026-03-16 13:28:20.155574",
"modified_by": "Administrator",
"module": "Setup",
"name": "Global Defaults",
diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py
index f383562e4e9..210898e2df3 100644
--- a/erpnext/stock/deprecated_serial_batch.py
+++ b/erpnext/stock/deprecated_serial_batch.py
@@ -251,6 +251,7 @@ class DeprecatedBatchNoValuation:
.select(
sle.batch_no,
Sum(sle.actual_qty).as_("batch_qty"),
+ Sum(sle.stock_value_difference).as_("batch_value"),
)
.where(
(sle.item_code == self.sle.item_code)
@@ -267,9 +268,24 @@ class DeprecatedBatchNoValuation:
if self.sle.name:
query = query.where(sle.name != self.sle.name)
+ # Moving Average items with no Use Batch wise Valuation but want to use batch wise valuation
+ moving_avg_item_non_batch_value = False
+ if valuation_method := self.get_valuation_method(self.sle.item_code):
+ if valuation_method == "Moving Average" and not frappe.db.get_single_value(
+ "Stock Settings", "do_not_use_batchwise_valuation"
+ ):
+ query = query.where(batch.use_batchwise_valuation == 0)
+ moving_avg_item_non_batch_value = True
+
batch_data = query.run(as_dict=True)
for d in batch_data:
self.available_qty[d.batch_no] += flt(d.batch_qty)
+ if moving_avg_item_non_batch_value:
+ self.non_batchwise_balance_qty[d.batch_no] += flt(d.batch_qty)
+ self.non_batchwise_balance_value[d.batch_no] += flt(d.batch_value)
+
+ if moving_avg_item_non_batch_value:
+ return
for d in batch_data:
if self.available_qty.get(d.batch_no):
@@ -378,9 +394,24 @@ class DeprecatedBatchNoValuation:
query = query.where(bundle.voucher_type != "Pick List")
+ # Moving Average items with no Use Batch wise Valuation but want to use batch wise valuation
+ moving_avg_item_non_batch_value = False
+ if valuation_method := self.get_valuation_method(self.sle.item_code):
+ if valuation_method == "Moving Average" and not frappe.db.get_single_value(
+ "Stock Settings", "do_not_use_batchwise_valuation"
+ ):
+ query = query.where(batch.use_batchwise_valuation == 0)
+ moving_avg_item_non_batch_value = True
+
batch_data = query.run(as_dict=True)
for d in batch_data:
self.available_qty[d.batch_no] += flt(d.batch_qty)
+ if moving_avg_item_non_batch_value:
+ self.non_batchwise_balance_qty[d.batch_no] += flt(d.batch_qty)
+ self.non_batchwise_balance_value[d.batch_no] += flt(d.batch_value)
+
+ if moving_avg_item_non_batch_value:
+ return
if not self.last_sle:
return
@@ -388,3 +419,8 @@ class DeprecatedBatchNoValuation:
for batch_no in self.available_qty:
self.non_batchwise_balance_value[batch_no] = flt(self.last_sle.stock_value)
self.non_batchwise_balance_qty[batch_no] = flt(self.last_sle.qty_after_transaction)
+
+ def get_valuation_method(self, item_code):
+ from erpnext.stock.utils import get_valuation_method
+
+ return get_valuation_method(item_code, self.sle.company)
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 07c1623a182..e0154bf13cc 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -7,6 +7,7 @@ import json
import frappe
from frappe import _
from frappe.contacts.doctype.address.address import get_company_address
+from frappe.contacts.doctype.contact.contact import get_default_contact
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
@@ -395,6 +396,9 @@ class DeliveryNote(SellingController):
)
def validate_sales_invoice_references(self):
+ if self.is_return:
+ return
+
self._validate_dependent_item_fields(
"against_sales_invoice", "si_detail", _("References to Sales Invoices are Incomplete")
)
@@ -978,6 +982,11 @@ def make_sales_invoice(source_name, target_doc=None, args=None):
def make_delivery_trip(source_name, target_doc=None, kwargs=None):
if not target_doc:
target_doc = frappe.new_doc("Delivery Trip")
+
+ def update_address(source_doc, target_doc, source_parent):
+ target_doc.address = source_doc.shipping_address_name or source_doc.customer_address
+ target_doc.customer_address = source_doc.shipping_address or source_doc.address_display
+
doclist = get_mapped_doc(
"Delivery Note",
source_name,
@@ -987,11 +996,10 @@ def make_delivery_trip(source_name, target_doc=None, kwargs=None):
"on_parent": target_doc,
"field_map": {
"name": "delivery_note",
- "shipping_address_name": "address",
- "shipping_address": "customer_address",
"contact_person": "contact",
"contact_display": "customer_contact",
},
+ "postprocess": update_address,
},
},
ignore_child_tables=True,
@@ -1103,18 +1111,24 @@ def make_shipment(source_name, target_doc=None):
# As we are using session user details in the pickup_contact then pickup_contact_person will be session user
target.pickup_contact_person = frappe.session.user
- if source.contact_person:
+ contact_person = source.contact_person or get_default_contact("Customer", source.customer)
+ if contact_person:
contact = frappe.db.get_value(
- "Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1
+ "Contact", contact_person, ["email_id", "phone", "mobile_no"], as_dict=1
)
- delivery_contact_display = f"{source.contact_display}"
- if contact:
+
+ delivery_contact_display = source.contact_display or contact_person or ""
+ if contact and not source.contact_display:
if contact.email_id:
delivery_contact_display += "
" + contact.email_id
if contact.phone:
delivery_contact_display += "
" + contact.phone
if contact.mobile_no and not contact.phone:
delivery_contact_display += "
" + contact.mobile_no
+
+ target.delivery_contact_name = contact_person
+ if contact and contact.email_id and not target.delivery_contact_email:
+ target.delivery_contact_email = contact.email_id
target.delivery_contact = delivery_contact_display
if source.shipping_address_name:
diff --git a/erpnext/stock/doctype/delivery_settings/delivery_settings.json b/erpnext/stock/doctype/delivery_settings/delivery_settings.json
index dbcc4fcc3ed..66320e0a214 100644
--- a/erpnext/stock/doctype/delivery_settings/delivery_settings.json
+++ b/erpnext/stock/doctype/delivery_settings/delivery_settings.json
@@ -50,10 +50,10 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:39.931428",
+ "modified": "2026-03-16 13:28:20.371015",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Settings",
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 444c2ec0e66..b957b905258 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -167,6 +167,7 @@
"set_only_once": 1
},
{
+ "allow_in_quick_entry": 1,
"bold": 1,
"fieldname": "item_name",
"fieldtype": "Data",
@@ -357,6 +358,7 @@
},
{
"depends_on": "is_stock_item",
+ "documentation_url": "https://docs.frappe.io/erpnext/change-valuation-method",
"fieldname": "valuation_method",
"fieldtype": "Select",
"label": "Valuation Method",
@@ -987,7 +989,7 @@
"image_field": "image",
"links": [],
"make_attachments_public": 1,
- "modified": "2026-03-05 16:29:31.653447",
+ "modified": "2026-03-17 20:39:05.218344",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
index 393a0b52e75..bea8feb554a 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
@@ -49,10 +49,10 @@
"label": "Allow Variant UOM to be different from Template UOM"
}
],
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:23.005859",
+ "modified": "2026-03-16 13:28:20.597912",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Variant Settings",
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index b730080ea35..e0bbff4cbda 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -116,12 +116,12 @@ frappe.ui.form.on("Material Request", {
refresh: function (frm) {
frm.events.make_custom_buttons(frm);
frm.toggle_reqd("customer", frm.doc.material_request_type == "Customer Provided");
- prevent_past_schedule_dates(frm);
+ erpnext.buying.prevent_past_schedule_dates(frm);
frm.trigger("set_warehouse_label");
},
transaction_date(frm) {
- prevent_past_schedule_dates(frm);
+ erpnext.buying.prevent_past_schedule_dates(frm);
frm.set_value("schedule_date", "");
},
@@ -410,33 +410,11 @@ frappe.ui.form.on("Material Request", {
},
make_purchase_order: function (frm) {
- frappe.prompt(
- {
- label: __("For Default Supplier (Optional)"),
- fieldname: "default_supplier",
- fieldtype: "Link",
- options: "Supplier",
- description: __(
- "Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only."
- ),
- get_query: () => {
- return {
- query: "erpnext.stock.doctype.material_request.material_request.get_default_supplier_query",
- filters: { doc: frm.doc.name },
- };
- },
- },
- (values) => {
- frappe.model.open_mapped_doc({
- method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
- frm: frm,
- args: { default_supplier: values.default_supplier },
- run_link_triggers: true,
- });
- },
- __("Enter Supplier"),
- __("Create")
- );
+ frappe.model.open_mapped_doc({
+ method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
+ frm: frm,
+ run_link_triggers: true,
+ });
},
make_request_for_quotation: function (frm) {
@@ -681,11 +659,3 @@ function set_schedule_date(frm) {
);
}
}
-
-function prevent_past_schedule_dates(frm) {
- if (frm.doc.transaction_date) {
- frm.fields_dict["schedule_date"].datepicker.update({
- minDate: new Date(frm.doc.transaction_date),
- });
- }
-}
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 15e13561afc..24e8ac4b1a9 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -18,7 +18,6 @@ from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, new_line_se
from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items
from erpnext.controllers.buying_controller import BuyingController
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
-from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.stock.stock_balance import get_indented_qty, update_bin_qty
from erpnext.subcontracting.doctype.subcontracting_bom.subcontracting_bom import (
get_subcontracting_boms_for_finished_goods,
@@ -96,7 +95,16 @@ class MaterialRequest(BuyingController):
"join_field": "sales_order_item",
"target_ref_field": "stock_qty",
"source_field": "stock_qty",
- }
+ },
+ {
+ "source_dt": "Material Request Item",
+ "target_dt": "Packed Item",
+ "target_field": "requested_qty",
+ "target_parent_dt": "Sales Order",
+ "join_field": "packed_item",
+ "target_ref_field": "qty",
+ "source_field": "qty",
+ },
]
def check_if_already_pulled(self):
@@ -501,17 +509,6 @@ def make_purchase_order(source_name, target_doc=None, args=None):
def postprocess(source, target_doc):
target_doc.is_subcontracted = is_subcontracted
- if frappe.flags.args and frappe.flags.args.default_supplier:
- # items only for given default supplier
- supplier_items = []
- for d in target_doc.items:
- if is_subcontracted and not d.item_code:
- continue
- default_supplier = get_item_defaults(d.item_code, target_doc.company).get("default_supplier")
- if frappe.flags.args.default_supplier == default_supplier:
- supplier_items.append(d)
- target_doc.items = supplier_items
-
set_missing_values(source, target_doc)
def select_item(d):
@@ -689,37 +686,6 @@ def get_material_requests_based_on_supplier(doctype, txt, searchfield, start, pa
return material_requests
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters):
- doc = frappe.get_doc("Material Request", filters.get("doc"))
- item_list = []
- for d in doc.items:
- item_list.append(d.item_code)
-
- supplier = frappe.qb.DocType("Supplier")
- item_default = frappe.qb.DocType("Item Default")
- query = (
- frappe.qb.from_(supplier)
- .left_join(item_default)
- .on(supplier.name == item_default.default_supplier)
- .select(item_default.default_supplier)
- .where(
- (item_default.parent.isin(item_list))
- & (item_default.default_supplier.notnull())
- & (supplier[searchfield].like(f"%{txt}%"))
- )
- .offset(start)
- .limit(page_len)
- )
-
- meta = frappe.get_meta("Supplier")
- if meta.show_title_field_in_link and meta.title_field:
- query = query.select(supplier[meta.title_field])
-
- return query.run(as_dict=False)
-
-
@frappe.whitelist()
def make_supplier_quotation(source_name, target_doc=None):
def postprocess(source, target_doc):
diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json
index 8938d503356..1614bfe2ab7 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -34,6 +34,7 @@
"projected_qty",
"ordered_qty",
"packed_qty",
+ "requested_qty",
"column_break_16",
"incoming_rate",
"picked_qty",
@@ -298,13 +299,22 @@
"fieldtype": "Check",
"label": "Supplier delivers to Customer",
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "requested_qty",
+ "fieldtype": "Float",
+ "label": "Requested Qty",
+ "non_negative": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2025-07-09 19:12:45.850219",
+ "modified": "2026-03-16 18:10:47.511381",
"modified_by": "Administrator",
"module": "Stock",
"name": "Packed Item",
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index 9477b85131d..5a6531f1526 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -45,6 +45,7 @@ class PackedItem(Document):
projected_qty: DF.Float
qty: DF.Float
rate: DF.Currency
+ requested_qty: DF.Float
serial_and_batch_bundle: DF.Link | None
serial_no: DF.Text | None
target_warehouse: DF.Link | None
@@ -354,11 +355,19 @@ def update_product_bundle_rate(parent_items_price, pi_row, item_row):
def set_product_bundle_rate_amount(doc, parent_items_price):
"Set cumulative rate and amount in bundle item."
+ rate_updated = False
for item in doc.get("items"):
bundle_rate = parent_items_price.get((item.item_code, item.name))
if bundle_rate and bundle_rate != item.rate:
item.rate = bundle_rate
item.amount = flt(bundle_rate * item.qty)
+ item.margin_rate_or_amount = 0
+ item.discount_percentage = 0
+ item.discount_amount = 0
+ rate_updated = True
+ if rate_updated:
+ doc.calculate_taxes_and_totals()
+ doc.set_total_in_words()
def on_doctype_update():
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 933cd0051e7..65a8717047a 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -19,7 +19,7 @@ frappe.ui.form.on("Purchase Receipt", {
frm.set_query("wip_composite_asset", "items", function () {
return {
- filters: { is_composite_asset: 1, docstatus: 0 },
+ filters: { asset_type: "Composite Asset", docstatus: 0 },
};
});
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index c987a364ade..76031397b01 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -1714,17 +1714,34 @@ def upload_csv_file(item_code, file_path):
def get_serial_batch_from_csv(item_code, file_path):
- if "private" in file_path:
- file_path = frappe.get_site_path() + file_path
- else:
- file_path = frappe.get_site_path() + "/public" + file_path
+ from frappe.utils.csvutils import read_csv_content
serial_nos = []
batch_nos = []
- with open(file_path) as f:
- reader = csv.reader(f)
- serial_nos, batch_nos = parse_csv_file_to_get_serial_batch(reader)
+ if not file_path:
+ return serial_nos, batch_nos
+
+ try:
+ file = frappe.get_doc("File", {"file_url": file_path})
+ except frappe.DoesNotExistError:
+ frappe.msgprint(
+ _("File '{0}' not found").format(frappe.bold(file_path)),
+ alert=True,
+ indicator="red",
+ raise_exception=FileNotFoundError,
+ )
+
+ if file.file_type != "CSV":
+ frappe.msgprint(
+ _("{0} is not a CSV file.").format(frappe.bold(file.file_name)),
+ alert=True,
+ indicator="red",
+ raise_exception=frappe.ValidationError,
+ )
+
+ csv_data = read_csv_content(file.get_content())
+ serial_nos, batch_nos = parse_csv_file_to_get_serial_batch(csv_data)
if serial_nos:
make_serial_nos(item_code, serial_nos)
@@ -2808,7 +2825,7 @@ def get_auto_batch_nos(kwargs):
)
if kwargs.based_on == "Expiry":
- available_batches = sorted(available_batches, key=lambda x: (x.expiry_date or getdate("9999-12-31")))
+ available_batches = sorted(available_batches, key=lambda x: x.expiry_date or getdate("9999-12-31"))
if not kwargs.get("do_not_check_future_batches") and available_batches and kwargs.get("posting_datetime"):
filter_zero_near_batches(available_batches, kwargs)
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
index 8cbb59599b7..d02affd68f0 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
@@ -359,6 +359,136 @@ class TestSerialandBatchBundle(IntegrationTestCase):
self.assertFalse(json.loads(sle.stock_queue or "[]"))
self.assertEqual(flt(sle.stock_value), 0.0)
+ def test_old_moving_avg_item_with_without_batchwise_valuation(self):
+ frappe.flags.ignore_serial_batch_bundle_validation = True
+ frappe.flags.use_serial_and_batch_fields = True
+ batch_item_code = "Old Batch Item Valuation 2"
+ make_item(
+ batch_item_code,
+ {
+ "has_batch_no": 1,
+ "batch_number_series": "TEST-OLD2-BAT-VAL-.#####",
+ "create_new_batch": 1,
+ "is_stock_item": 1,
+ "valuation_method": "Moving Average",
+ },
+ )
+
+ non_batchwise_val_batches = [
+ "TEST-OLD2-BAT-VAL-00001",
+ "TEST-OLD2-BAT-VAL-00002",
+ "TEST-OLD2-BAT-VAL-00003",
+ "TEST-OLD2-BAT-VAL-00004",
+ ]
+
+ for batch_id in non_batchwise_val_batches:
+ if not frappe.db.exists("Batch", batch_id):
+ batch_doc = frappe.get_doc(
+ {
+ "doctype": "Batch",
+ "batch_id": batch_id,
+ "item": batch_item_code,
+ "use_batchwise_valuation": 0,
+ }
+ ).insert(ignore_permissions=True)
+
+ self.assertTrue(batch_doc.use_batchwise_valuation)
+ batch_doc.db_set(
+ {
+ "use_batchwise_valuation": 0,
+ "batch_qty": 20,
+ }
+ )
+
+ qty_after_transaction = 0
+ balance_value = 0
+ i = 0
+ for batch_id in non_batchwise_val_batches:
+ i += 1
+ qty = 20
+ valuation = 100 * i
+ qty_after_transaction += qty
+ balance_value += qty * valuation
+
+ doc = frappe.get_doc(
+ {
+ "doctype": "Stock Ledger Entry",
+ "posting_date": today(),
+ "posting_time": nowtime(),
+ "batch_no": batch_id,
+ "incoming_rate": valuation,
+ "qty_after_transaction": qty_after_transaction,
+ "stock_value_difference": valuation * qty,
+ "stock_value": balance_value,
+ "balance_value": balance_value,
+ "valuation_rate": balance_value / qty_after_transaction,
+ "actual_qty": qty,
+ "item_code": batch_item_code,
+ "warehouse": "_Test Warehouse - _TC",
+ }
+ )
+
+ doc.set_posting_datetime()
+ doc.flags.ignore_permissions = True
+ doc.flags.ignore_mandatory = True
+ doc.flags.ignore_links = True
+ doc.flags.ignore_validate = True
+ doc.submit()
+ doc.reload()
+
+ frappe.flags.ignore_serial_batch_bundle_validation = False
+ frappe.flags.use_serial_and_batch_fields = False
+
+ se = make_stock_entry(
+ item_code=batch_item_code,
+ target="_Test Warehouse - _TC",
+ qty=30,
+ rate=355,
+ use_serial_batch_fields=True,
+ )
+
+ se = make_stock_entry(
+ item_code=batch_item_code,
+ source="_Test Warehouse - _TC",
+ qty=70,
+ use_serial_batch_fields=True,
+ )
+
+ sle = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"item_code": batch_item_code, "is_cancelled": 0, "voucher_no": se.name},
+ ["qty_after_transaction", "stock_value"],
+ as_dict=True,
+ )
+
+ self.assertEqual(flt(sle.stock_value), 14000.0)
+ self.assertEqual(flt(sle.qty_after_transaction), 40.0)
+
+ se = make_stock_entry(
+ item_code=batch_item_code,
+ target="_Test Warehouse - _TC",
+ qty=10,
+ rate=200,
+ use_serial_batch_fields=True,
+ )
+
+ se = make_stock_entry(
+ item_code=batch_item_code,
+ source="_Test Warehouse - _TC",
+ qty=50,
+ use_serial_batch_fields=True,
+ )
+
+ sle = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"item_code": batch_item_code, "is_cancelled": 0, "voucher_no": se.name},
+ ["qty_after_transaction", "stock_value"],
+ as_dict=True,
+ )
+
+ self.assertEqual(flt(sle.stock_value), 0.0)
+ self.assertEqual(flt(sle.qty_after_transaction), 0.0)
+
def test_old_serial_no_valuation(self):
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 277ab61865b..113d211320c 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -524,11 +524,11 @@ class StockReconciliation(StockController):
return True
rate_precision = item.precision("valuation_rate")
- item_dict["rate"] = flt(item_dict.get("rate"), rate_precision)
- item.valuation_rate = flt(item.valuation_rate, rate_precision) if item.valuation_rate else None
+ rate = flt(item_dict.get("rate"), rate_precision)
+ valuation_rate = flt(item.valuation_rate, rate_precision) if item.valuation_rate else None
if (
(item.qty is None or item.qty == item_dict.get("qty"))
- and (item.valuation_rate is None or item.valuation_rate == item_dict.get("rate"))
+ and (valuation_rate is None or valuation_rate == rate)
and (not item.serial_no or (item.serial_no == item_dict.get("serial_nos")))
):
return False
@@ -1008,9 +1008,9 @@ class StockReconciliation(StockController):
def set_total_qty_and_amount(self):
for d in self.get("items"):
- d.amount = flt(d.qty, d.precision("qty")) * flt(d.valuation_rate, d.precision("valuation_rate"))
- d.current_amount = flt(d.current_qty, d.precision("current_qty")) * flt(
- d.current_valuation_rate, d.precision("current_valuation_rate")
+ d.amount = flt(flt(d.qty) * flt(d.valuation_rate), d.precision("amount"))
+ d.current_amount = flt(
+ flt(d.current_qty) * flt(d.current_valuation_rate), d.precision("current_amount")
)
d.quantity_difference = flt(d.qty) - flt(d.current_qty)
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
index f72b50dc131..ccca2fc9819 100644
--- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -101,11 +101,11 @@
"label": "Enable Separate Reposting for GL"
}
],
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2026-02-25 14:11:33.461173",
+ "modified": "2026-03-16 13:28:20.978007",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reposting Settings",
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index ba195104564..cd9ced97baf 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -558,13 +558,13 @@
"label": "Enable Serial / Batch No for Item"
}
],
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"icon": "icon-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2026-02-25 10:56:34.105949",
+ "modified": "2026-03-16 13:28:19.254641",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 8ec3e9865d9..dc92bfc8289 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -114,6 +114,22 @@ class StockSettings(Document):
self.validate_auto_insert_price_list_rate_if_missing()
self.change_precision_for_for_sales()
self.change_precision_for_purchase()
+ self.validate_do_not_use_batchwise_valuation()
+
+ def validate_do_not_use_batchwise_valuation(self):
+ doc_before_save = self.get_doc_before_save()
+ if not doc_before_save:
+ return
+
+ if not frappe.get_all("Serial and Batch Bundle", filters={"docstatus": 1}, limit=1, pluck="name"):
+ return
+
+ if doc_before_save.do_not_use_batchwise_valuation and not self.do_not_use_batchwise_valuation:
+ frappe.throw(
+ _("Cannot disable {0} as it may lead to incorrect stock valuation.").format(
+ frappe.bold(_("Do Not Use Batchwise Valuation"))
+ )
+ )
def validate_serial_and_batch_no_settings(self):
doc_before_save = self.get_doc_before_save()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index f2ac54898a7..2b9fd98dd53 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -13,7 +13,7 @@ from frappe.model.document import Document
from frappe.model.meta import get_field_precision
from frappe.model.utils import get_fetch_values
from frappe.query_builder.functions import IfNull, Sum
-from frappe.utils import add_days, add_months, cint, cstr, flt, getdate, parse_json
+from frappe.utils import add_days, add_months, cint, cstr, flt, get_link_to_form, getdate, parse_json
import erpnext
from erpnext import get_company_currency
@@ -1055,16 +1055,30 @@ def insert_item_price(ctx: ItemDetailsCtx):
):
return
- item_price = frappe.db.get_value(
+ transaction_date = (
+ getdate(ctx.get("posting_date") or ctx.get("transaction_date") or ctx.get("posting_datetime"))
+ or getdate()
+ )
+
+ item_prices = frappe.get_all(
"Item Price",
- {
+ filters={
"item_code": ctx.item_code,
"price_list": ctx.price_list,
"currency": ctx.currency,
"uom": ctx.stock_uom,
},
- ["name", "price_list_rate"],
- as_dict=1,
+ fields=["name", "price_list_rate", "valid_from", "valid_upto"],
+ order_by="valid_from desc, creation desc",
+ )
+ item_price = next(
+ (
+ row
+ for row in item_prices
+ if (not row.valid_from or getdate(row.valid_from) <= transaction_date)
+ and (not row.valid_upto or getdate(row.valid_upto) >= transaction_date)
+ ),
+ item_prices[0] if item_prices else None,
)
update_based_on_price_list_rate = stock_settings.update_price_list_based_on == "Price List Rate"
@@ -1079,11 +1093,33 @@ def insert_item_price(ctx: ItemDetailsCtx):
if not price_list_rate or item_price.price_list_rate == price_list_rate:
return
- frappe.db.set_value("Item Price", item_price.name, "price_list_rate", price_list_rate)
- frappe.msgprint(
- _("Item Price updated for {0} in Price List {1}").format(ctx.item_code, ctx.price_list),
- alert=True,
- )
+ is_price_valid_for_transaction = (
+ not item_price.valid_from or getdate(item_price.valid_from) <= transaction_date
+ ) and (not item_price.valid_upto or getdate(item_price.valid_upto) >= transaction_date)
+ if is_price_valid_for_transaction:
+ frappe.db.set_value("Item Price", item_price.name, "price_list_rate", price_list_rate)
+ frappe.msgprint(
+ _("Item Price updated for {0} in Price List {1}").format(ctx.item_code, ctx.price_list),
+ alert=True,
+ )
+ else:
+ # if price is not valid for the transaction date, insert a new price list rate with updated price and future validity
+
+ item_price = frappe.new_doc(
+ "Item Price",
+ item_code=ctx.item_code,
+ price_list_rate=price_list_rate,
+ currency=ctx.currency,
+ uom=ctx.stock_uom,
+ price_list=ctx.price_list,
+ )
+ item_price.insert()
+ frappe.msgprint(
+ _("Item Price Added for {0} in Price List {1}").format(
+ get_link_to_form("Item", ctx.item_code), ctx.price_list
+ ),
+ alert=True,
+ )
else:
rate_to_consider = (
(flt(ctx.price_list_rate) or flt(ctx.rate)) if update_based_on_price_list_rate else flt(ctx.rate)
@@ -1102,8 +1138,9 @@ def insert_item_price(ctx: ItemDetailsCtx):
)
item_price.insert()
frappe.msgprint(
- _("Item Price added for {0} in Price List {1}").format(ctx.item_code, ctx.price_list),
- alert=True,
+ _("Item Price added for {0} in Price List {1}").format(
+ get_link_to_form("Item", ctx.item_code), ctx.price_list
+ )
)
diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json
index cef82c5912d..b4da5b466c3 100644
--- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json
+++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json
@@ -9,8 +9,8 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
- "letterhead": null,
- "modified": "2025-11-05 11:55:50.114173",
+ "letter_head": null,
+ "modified": "2026-03-13 17:36:31.552712",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Trends",
diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json
index 03c2a09f3bb..6743b359e30 100644
--- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json
+++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json
@@ -9,8 +9,8 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
- "letterhead": null,
- "modified": "2025-11-05 11:55:49.983683",
+ "letter_head": null,
+ "modified": "2026-03-13 17:35:57.060786",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Trends",
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 43ac974a88b..dadc586edb4 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -925,7 +925,10 @@ class update_entries_after:
if (
sle.is_adjustment_entry
and flt(sle.qty_after_transaction, self.flt_precision) == 0
- and flt(sle.stock_value, self.currency_precision) != 0
+ and (
+ flt(sle.stock_value, self.currency_precision) != 0
+ or flt(sle.stock_value_difference, self.currency_precision) == 0
+ )
):
sle.stock_value_difference = (
get_stock_value_difference(
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 22db45e36ac..70ced430257 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -227,8 +227,14 @@ def set_status(name, status):
def auto_close_tickets():
- """Auto-close replied support tickets after 7 days"""
- auto_close_after_days = frappe.db.get_single_value("Support Settings", "close_issue_after_days") or 7
+ """
+ Auto-close replied support tickets as defined on `close_issue_after_days` in Support Settings.
+ Disables the feature if `close_issue_after_days` is set to 0.
+ """
+ auto_close_after_days = frappe.db.get_single_value("Support Settings", "close_issue_after_days")
+
+ if not auto_close_after_days:
+ return
table = frappe.qb.DocType("Issue")
issues = (
diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json
index a47f97b3889..eb17f97c5e7 100644
--- a/erpnext/support/doctype/support_settings/support_settings.json
+++ b/erpnext/support/doctype/support_settings/support_settings.json
@@ -37,9 +37,11 @@
},
{
"default": "7",
+ "description": "Set this value to 0 to disable the feature.",
"fieldname": "close_issue_after_days",
"fieldtype": "Int",
- "label": "Close Issue After Days"
+ "label": "Close Issue After Days",
+ "non_negative": 1
},
{
"fieldname": "portal_sb",
@@ -154,10 +156,10 @@
}
],
"grid_page_length": 50,
- "hide_toolbar": 1,
+ "hide_toolbar": 0,
"issingle": 1,
"links": [],
- "modified": "2026-01-02 18:18:11.475222",
+ "modified": "2026-03-16 16:33:45.859541",
"modified_by": "Administrator",
"module": "Support",
"name": "Support Settings",
diff --git a/pyproject.toml b/pyproject.toml
index d8acb83723b..960efd87a63 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -81,4 +81,5 @@ docstring-code-format = true
[project.urls]
Homepage = "https://frappe.io/erpnext"
Repository = "https://github.com/frappe/erpnext.git"
+Documentation = "https://docs.frappe.io/erpnext"
"Bug Reports" = "https://github.com/frappe/erpnext/issues"