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\n\t\t\t\n\t\t\t\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\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
{{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
\n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }}
@ {{ format_currency(item.rate, currency) }}
{{ format_currency(item.amount, currency) }}
\n\n\n\t\n\t\t\n\t\t\t\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\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\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
\n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Qty Total\") }}\n\t\t\t\n\t\t\t\t{{ qty_total }}\n\t\t\t
\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"