diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 2c74f812e0e..51b5e26f330 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -331,7 +331,7 @@ def add_bank_account(data, bank_account):
bank_account_loc = loc
for row in data[1:]:
- if bank_account_loc:
+ if bank_account_loc is not None:
row[bank_account_loc] = bank_account
else:
row.append(bank_account)
diff --git a/erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.py b/erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.py
index aa311483eae..ddbc39c5c9a 100644
--- a/erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.py
+++ b/erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.py
@@ -157,12 +157,9 @@ class BankTransactionRule(Document):
"""
Delete the matched rule from the bank transaction
"""
- try:
- frappe.db.set_value(
- "Bank Transaction", {"matched_transaction_rule": self.name}, "matched_transaction_rule", None
- )
- except Exception:
- pass
+ frappe.db.set_value(
+ "Bank Transaction", {"matched_transaction_rule": self.name}, "matched_transaction_rule", None
+ )
def after_delete(self):
"""
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 84ba411c97f..22c70e6d8cd 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -623,15 +623,27 @@ class ExchangeRateRevaluation(Document):
if journals:
from erpnext.accounts.doctype.journal_entry.mapper import make_reverse_journal_entry
- for x in journals:
- reversal = make_reverse_journal_entry(x)
- reversal.posting_date = nowdate()
- reversal.submit()
- frappe.msgprint(
- _("Revaluation journal for {0} has been created: {1}").format(
- frappe.bold(x), get_link_to_form("Journal Entry", reversal.name)
- )
+ if drafts := frappe.db.get_all(
+ "Journal Entry",
+ filters={"docstatus": 0, "reversal_of": ["in", journals]},
+ pluck="name",
+ as_list=1,
+ ):
+ part = "journals are" if len(drafts) > 1 else "journal is"
+ doc_links = ", ".join(["{}".format(get_link_to_form("Journal Entry", x)) for x in drafts])
+ frappe.throw(
+ msg=_("Reverse {0} already available in draft status: {1}").format(part, doc_links),
)
+ else:
+ for x in journals:
+ reversal = make_reverse_journal_entry(x)
+ reversal.posting_date = nowdate()
+ reversal.save()
+ frappe.msgprint(
+ _("A draft reverse journal for {0} has been created: {1}").format(
+ frappe.bold(x), get_link_to_form("Journal Entry", reversal.name)
+ )
+ )
def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
index 3e5b08d069d..a2b36d85b19 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
@@ -9,11 +9,10 @@ from frappe.utils import add_days, flt, today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.tests.utils import ERPNextTestSuite
-class TestExchangeRateRevaluation(ERPNextTestSuite, AccountsTestMixin):
+class TestExchangeRateRevaluation(ERPNextTestSuite):
def setUp(self):
self.company = "_Test Company"
self.item = "_Test Item"
@@ -23,14 +22,6 @@ class TestExchangeRateRevaluation(ERPNextTestSuite, AccountsTestMixin):
self.set_system_and_company_settings()
def set_system_and_company_settings(self):
- # set number and currency precision
- system_settings = frappe.get_doc("System Settings")
- system_settings.float_precision = 2
- system_settings.currency_precision = 2
- system_settings.language = "en"
- system_settings.time_zone = "Asia/Kolkata"
- system_settings.save()
-
# Using Exchange Gain/Loss account for unrealized as well.
company_doc = frappe.get_doc("Company", self.company)
company_doc.unrealized_exchange_gain_loss_account = company_doc.exchange_gain_loss_account
@@ -312,7 +303,7 @@ class TestExchangeRateRevaluation(ERPNextTestSuite, AccountsTestMixin):
si = create_sales_invoice(
item=self.item,
company=self.company,
- customer=self.customer,
+ customer="_Test Customer 1",
debit_to=self.debtors_usd,
posting_date=today(),
parent_cost_center=self.cost_center,
@@ -377,6 +368,15 @@ class TestExchangeRateRevaluation(ERPNextTestSuite, AccountsTestMixin):
self.assertFalse(ret.get("reversals_posted"))
err.make_reverse_journal()
+ # submit
+ draft = frappe.db.get_all(
+ "Journal Entry",
+ filters={"docstatus": 0, "reversal_of": je.name, "voucher_type": "Exchange Rate Revaluation"},
+ pluck="name",
+ as_list=1,
+ )
+ self.assertIsNotNone(draft)
+ frappe.get_doc("Journal Entry", draft[0]).submit()
ret = err.check_journal_and_reversal()
self.assertTrue(ret.get("journals_posted"))
self.assertTrue(ret.get("reversals_posted"))
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index ffe6630c725..d294573eca2 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -235,6 +235,7 @@ Object.assign(erpnext.journal_entry, {
lock_reversal_entry(frm) {
frm.fields
.filter((field) => field.has_input)
+ .filter((field) => field.df.fieldname != "posting_date")
.forEach((field) => frm.set_df_property(field.df.fieldname, "read_only", 1));
frm.set_df_property("accounts", "read_only", 1);
},
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index f8c3113a714..a957e246553 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -459,6 +459,11 @@ class PaymentRequest(Document):
else:
return True
except Exception:
+ frappe.log_error(
+ title=f"Payment Gateway validation failed: {self.payment_gateway}",
+ reference_doctype=self.doctype,
+ reference_name=self.name,
+ )
return False
def set_payment_request_url(self):
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 566f34551b1..9914d78aa1a 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -219,7 +219,8 @@ class POSClosingEntry(StatusUpdater):
self.update_sales_invoices_closing_entry()
def before_cancel(self):
- self.check_pce_is_cancellable()
+ if self.status != "Failed":
+ self.check_pce_is_cancellable()
def on_cancel(self):
unconsolidate_pos_invoices(closing_entry=self)
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index f67a3861826..16340362c85 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -89,7 +89,11 @@ def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()):
filtered_pricing_rules.append(pricing_rule)
except Exception:
- pass
+ frappe.log_error(
+ title=f"Pricing Rule condition failed to evaluate: {pricing_rule.name}",
+ reference_doctype="Pricing Rule",
+ reference_name=pricing_rule.name,
+ )
else:
filtered_pricing_rules.append(pricing_rule)
else:
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index dff423b36b4..510da22ada1 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -403,12 +403,7 @@ def get_recipients_and_cc(customer, doc):
if doc.primary_mandatory and clist.primary_email:
for email in clist.primary_email.split(","):
recipients.append(email.strip())
- cc = []
- if doc.cc_to != "":
- try:
- cc = [frappe.get_value("User", user.cc, "email") for user in doc.cc_to]
- except Exception:
- pass
+ cc = [email for user in doc.cc_to if (email := frappe.get_value("User", user.cc, "email"))]
return recipients, cc
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js
index c304c7f17eb..3ca9518a1e8 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js
@@ -22,27 +22,50 @@ frappe.ui.form.on("Repost Accounting Ledger", {
},
refresh: function (frm) {
- frm.add_custom_button(__("Show Preview"), () => {
- frm.call({
- method: "generate_preview",
- doc: frm.doc,
- freeze: true,
- freeze_message: __("Generating Preview"),
- callback: function (r) {
- if (r && r.message) {
- let content = r.message;
- let opts = {
- title: "Preview",
- subtitle: "preview",
- content: content,
- print_settings: { orientation: "landscape" },
- columns: [],
- data: [],
- };
- frappe.render_grid(opts);
- }
- },
+ // the server refuses only while the job is alive, so a dead one can be restarted here
+ if (frm.doc.docstatus == 1 && !["Completed", "Cancelled"].includes(frm.doc.status)) {
+ frm.add_custom_button(__("Start Reposting"), () => {
+ frm.events.start_repost(frm);
});
+ }
+
+ if (frm.doc.docstatus != 2) {
+ frm.add_custom_button(__("Show Preview"), () => {
+ frm.events.generate_preview(frm);
+ });
+ }
+ },
+
+ generate_preview: function (frm) {
+ frm.call({
+ method: "generate_preview",
+ doc: frm.doc,
+ freeze: true,
+ freeze_message: __("Generating Preview"),
+ callback: function (r) {
+ if (r && r.message) {
+ let content = r.message;
+ let opts = {
+ title: "Preview",
+ subtitle: "preview",
+ content: content,
+ print_settings: { orientation: "landscape" },
+ columns: [],
+ data: [],
+ };
+ frappe.render_grid(opts);
+ }
+ },
+ });
+ },
+
+ start_repost: function (frm) {
+ frm.call({
+ method: "start_repost",
+ doc: frm.doc,
+ callback: function (r) {
+ frm.reload_doc();
+ },
});
},
});
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json
index 818b0e38fe1..90044abf40d 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_bulk_edit": 1,
"creation": "2023-07-04 13:07:32.923675",
"default_view": "List",
"doctype": "DocType",
@@ -7,16 +8,24 @@
"engine": "InnoDB",
"field_order": [
"company",
- "column_break_vpup",
"delete_cancelled_entries",
+ "column_break_vpup",
+ "status",
"section_break_metl",
"vouchers",
- "amended_from"
+ "error_section",
+ "error_log",
+ "miscellaneous_section",
+ "amended_from",
+ "column_break_hrah",
+ "scheduled_job"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Company",
"options": "Company"
},
@@ -48,12 +57,54 @@
"fieldname": "delete_cancelled_entries",
"fieldtype": "Check",
"label": "Delete Cancelled Ledger Entries"
+ },
+ {
+ "fieldname": "error_section",
+ "fieldtype": "Section Break",
+ "label": "Error"
+ },
+ {
+ "fieldname": "error_log",
+ "fieldtype": "Code",
+ "label": "Error Log",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "miscellaneous_section",
+ "fieldtype": "Section Break",
+ "label": "Miscellaneous"
+ },
+ {
+ "fieldname": "column_break_hrah",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval:doc.docstatus >= 1;",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "\nQueued\nIn Progress\nPartially Reposted\nCompleted\nFailed\nCancelled",
+ "read_only": 1
+ },
+ {
+ "fieldname": "scheduled_job",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Scheduled Job",
+ "no_copy": 1,
+ "options": "RQ Job",
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2024-06-03 17:30:37.012593",
+ "modified": "2026-07-28 00:56:50.290314",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Accounting Ledger",
@@ -76,8 +127,9 @@
"write": 1
}
],
+ "row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
index fe0647be386..fcb3a02633d 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
@@ -7,7 +7,14 @@ import frappe
from frappe import _, qb
from frappe.desk.form.linked_with import get_child_tables_of_doctypes
from frappe.model.document import Document
+from frappe.utils.background_jobs import create_job_id, is_job_enqueued
from frappe.utils.data import comma_and
+from frappe.utils.scheduler import is_scheduler_inactive
+
+# a batch has to finish well within the timeout of the job reposting it
+MAX_VOUCHERS_PER_REPOST = 50
+
+HANDLED_VOUCHER_STATUSES = ("Reposted", "Skipped")
class RepostAccountingLedger(Document):
@@ -26,6 +33,11 @@ class RepostAccountingLedger(Document):
amended_from: DF.Link | None
company: DF.Link | None
delete_cancelled_entries: DF.Check
+ error_log: DF.Code | None
+ scheduled_job: DF.Link | None
+ status: DF.Literal[
+ "", "Queued", "In Progress", "Partially Reposted", "Completed", "Failed", "Cancelled"
+ ]
vouchers: DF.Table[RepostAccountingLedgerItems]
# end: auto-generated types
@@ -35,6 +47,11 @@ class RepostAccountingLedger(Document):
def validate(self):
self.validate_vouchers()
+ self.validate_repost_preconditions()
+
+ def validate_repost_preconditions(self):
+ """The checks a repost queued days ago could have outlived, re-run before it touches
+ the ledger. Vouchers cancelled since are skipped one by one while reposting."""
self.validate_for_closed_fiscal_year()
self.validate_for_deferred_accounting()
@@ -71,8 +88,52 @@ class RepostAccountingLedger(Document):
frappe.throw(_("Cannot Resubmit Ledger entries for vouchers in Closed fiscal year."))
def validate_vouchers(self):
- if self.vouchers:
- validate_docs_for_voucher_types([x.voucher_type for x in self.vouchers])
+ if not self.vouchers:
+ frappe.throw(_("Add atleast one voucher to repost."))
+
+ if len(self.vouchers) > MAX_VOUCHERS_PER_REPOST:
+ frappe.throw(
+ _("Cannot repost more than {0} vouchers at once. Split them into multiple documents.").format(
+ MAX_VOUCHERS_PER_REPOST
+ )
+ )
+
+ validate_docs_for_voucher_types([x.voucher_type for x in self.vouchers])
+
+ self.validate_no_duplicate_vouchers()
+ self.validate_vouchers_are_submitted()
+
+ def validate_no_duplicate_vouchers(self):
+ vouchers = [(x.voucher_type, x.voucher_no) for x in self.vouchers]
+
+ if len(vouchers) != len(set(vouchers)):
+ frappe.throw(_("Duplicate vouchers found. Remove the duplicate vouchers to continue to repost."))
+
+ def validate_vouchers_are_submitted(self):
+ voucher_type_wise_map = {}
+ for d in self.vouchers:
+ voucher_type_wise_map.setdefault(d.voucher_type, [])
+ voucher_type_wise_map[d.voucher_type].append(d.voucher_no)
+
+ non_submitted_vouchers = []
+ for key in voucher_type_wise_map.keys():
+ non_submitted_vouchers.extend(
+ frappe.get_all(
+ key,
+ filters={"name": ["in", voucher_type_wise_map[key]], "docstatus": ["!=", 1]},
+ pluck="name",
+ )
+ )
+
+ if non_submitted_vouchers:
+ frappe.throw(
+ _("The following vouchers are not submitted: {0}").format(
+ comma_and(non_submitted_vouchers, add_quotes=True)
+ )
+ )
+
+ def on_discard(self):
+ self.db_set("status", "Cancelled")
def get_existing_ledger_entries(self):
vouchers = [x.voucher_no for x in self.vouchers]
@@ -137,80 +198,245 @@ class RepostAccountingLedger(Document):
return rendered_page
def on_submit(self):
- if len(self.vouchers) > 5:
- job_name = "repost_accounting_ledger_" + self.name
- frappe.enqueue(
- method="erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.start_repost",
- account_repost_doc=self.name,
- is_async=True,
- job_name=job_name,
- enqueue_after_commit=True,
+ self.start_repost()
+
+ def before_cancel(self):
+ self._raise_error_if_reposting_in_progress()
+
+ def on_cancel(self):
+ self.db_set("status", "Cancelled")
+
+ def _raise_error_if_reposting_in_progress(self):
+ if self.scheduled_job and is_job_enqueued(_repost_job_id(self.name)):
+ frappe.throw(_("Reposting is still in progress in background."))
+
+ @frappe.whitelist()
+ def start_repost(self):
+ if self.docstatus != 1:
+ frappe.throw(_("Reposting can be started only for submitted document."))
+
+ # under a row lock, so two concurrent starts cannot both get past here
+ status = frappe.db.get_value(self.doctype, self.name, "status", for_update=True)
+ if status in ("Completed", "Cancelled"):
+ frappe.throw(_("Reposting cannot be started when status is {0}.").format(status))
+
+ # `Queued` and `In Progress` are held back by the job, not by the status: a worker that
+ # died leaves the status behind and the document has to stay restartable
+ self._raise_error_if_reposting_in_progress()
+
+ self.check_permission("write")
+
+ # workers pick up enqueued jobs whether or not the scheduler runs, so this is a warning
+ if is_scheduler_inactive():
+ frappe.msgprint(
+ _("Scheduler is inactive. Reposting will only run once background jobs are processed."),
+ alert=True,
+ indicator="orange",
)
- frappe.msgprint(_("Repost has started in the background"))
- else:
- start_repost(self.name)
+
+ self.db_set({"status": "Queued", "scheduled_job": create_job_id(_repost_job_id(self.name))})
+ _enqueue_repost(self.name)
+ frappe.msgprint(_("Repost has started in the background"), alert=True, indicator="blue")
-@frappe.whitelist()
-def start_repost(account_repost_doc: str | None = None) -> None:
- from erpnext.accounts.general_ledger import make_reverse_gl_entries
+def _repost_job_id(repost_doc_name: str) -> str:
+ """Derived from the document, so a repost can only ever have one job."""
+ return f"repost_accounting_ledger::{repost_doc_name}"
+
+
+def _enqueue_repost(repost_doc_name: str) -> None:
+ """Hand the repost to a background worker.
+
+ Tests run it in the foreground, inside their own transaction: documents edited after submit
+ repost themselves through `repost_accounting_entries`, and tests across apps assert on the
+ ledger right after doing so.
+ """
+ frappe.enqueue(
+ method="erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.repost",
+ repost_doc_name=repost_doc_name,
+ commit=not frappe.in_test,
+ queue="long",
+ timeout=1500,
+ job_id=_repost_job_id(repost_doc_name),
+ deduplicate=True,
+ enqueue_after_commit=True,
+ now=frappe.in_test,
+ )
+
+
+def _lock_vouchers(vouchers) -> dict:
+ """Lock every voucher up front so a concurrent repost cannot touch the same GL entries.
+
+ Returns them keyed by voucher, so reposting does not load them again. These are file locks
+ under the site directory: they serialise nothing across hosts that do not share it, and a
+ worker killed outright leaves them behind until they expire.
+ """
+ locked_docs = {}
+ try:
+ for x in vouchers:
+ doc = frappe.get_doc(x.voucher_type, x.voucher_no)
+ doc.lock()
+ locked_docs[(x.voucher_type, x.voucher_no)] = doc
+ except Exception:
+ for doc in locked_docs.values():
+ doc.unlock()
+ raise
+ return locked_docs
+
+
+def repost(repost_doc_name: str, commit: bool = True):
+ """Repost every voucher of the document, one transaction at a time.
+
+ `commit` says whether this call owns the transaction. The background job does, and commits
+ after every voucher so progress survives a crash; a caller inside its own passes `False`.
+ """
+ from erpnext.accounts.utils import _delete_accounting_ledger_entries, _delete_adv_pl_entries
frappe.flags.through_repost_accounting_ledger = True
- if account_repost_doc:
- repost_doc = frappe.get_doc("Repost Accounting Ledger", account_repost_doc)
- repost_doc.check_permission("write")
- if repost_doc.docstatus == 1:
- # Prevent repost on invoices with deferred accounting
- repost_doc.validate_for_deferred_accounting()
+ repost_doc = frappe.get_doc("Repost Accounting Ledger", repost_doc_name)
+ locked_docs = {}
- for x in repost_doc.vouchers:
- doc = frappe.get_doc(x.voucher_type, x.voucher_no)
+ try:
+ repost_doc.validate_repost_preconditions()
+
+ # a retry leaves the vouchers it is done with alone: they are not locked, not loaded
+ # and not reposted again
+ pending = [x for x in repost_doc.vouchers if x.status not in HANDLED_VOUCHER_STATUSES]
+ locked_docs = _lock_vouchers(pending)
+
+ repost_doc.db_set("status", "In Progress", commit=commit)
+
+ for position, x in enumerate(pending, start=1):
+ frappe.publish_progress(
+ position * 100 / len(pending),
+ doctype=repost_doc.doctype,
+ docname=repost_doc.name,
+ description=_("Reposting {0} {1}").format(x.voucher_type, x.voucher_no),
+ )
+
+ save_point = "reposting"
+ frappe.db.savepoint(save_point=save_point)
+ try:
+ doc = locked_docs[(x.voucher_type, x.voucher_no)]
+
+ if doc.docstatus == 2:
+ x.db_set({"status": "Skipped", "traceback": ""})
+ continue
if repost_doc.delete_cancelled_entries:
- frappe.db.delete(
- "GL Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
- )
- frappe.db.delete(
- "Payment Ledger Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
- )
- frappe.db.delete(
- "Advance Payment Ledger Entry",
- filters={"voucher_type": doc.doctype, "voucher_no": doc.name},
- )
+ _delete_accounting_ledger_entries(doc.doctype, doc.name)
+ _delete_adv_pl_entries(doc.doctype, doc.name)
- if doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
- if not repost_doc.delete_cancelled_entries:
- doc.docstatus = 2
- doc.make_gl_entries_on_cancel(from_repost=True)
+ _repost_vouchers(doc, repost_doc.delete_cancelled_entries)
+ except Exception:
+ frappe.db.rollback(save_point=save_point)
- doc.docstatus = 1
- if doc.doctype == "Sales Invoice":
- doc.force_set_against_income_account()
- else:
- doc.force_set_against_expense_account()
- doc.make_gl_entries()
+ x.db_set({"status": "Failed", "traceback": frappe.get_traceback()})
+ else:
+ x.db_set({"status": "Reposted", "traceback": ""})
+ finally:
+ if commit:
+ frappe.db.commit() # nosemgrep
- elif doc.doctype == "Purchase Receipt":
- if not repost_doc.delete_cancelled_entries:
- doc.docstatus = 2
- doc.make_gl_entries_on_cancel(from_repost=True)
+ except Exception:
+ if commit:
+ frappe.db.rollback()
- doc.docstatus = 1
- doc.make_gl_entries(from_repost=True)
+ _record_repost_failure(repost_doc, commit=commit)
+ raise
+ else:
+ repost_doc.db_set({"status": _derive_status(repost_doc), "error_log": ""}, notify=True)
+ finally:
+ for doc in locked_docs.values():
+ doc.unlock()
+ if commit:
+ frappe.db.commit() # nosemgrep
- elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]:
- if not repost_doc.delete_cancelled_entries:
- doc.make_gl_entries(1)
- doc.make_gl_entries()
- elif doc.doctype in frappe.get_hooks("repost_allowed_doctypes"):
- if hasattr(doc, "make_gl_entries") and callable(doc.make_gl_entries):
- if not repost_doc.delete_cancelled_entries:
- if "cancel" in inspect.getfullargspec(doc.make_gl_entries):
- doc.make_gl_entries(cancel=1)
- else:
- make_reverse_gl_entries(voucher_type=doc.doctype, voucher_no=doc.name)
- doc.make_gl_entries()
+
+def _derive_status(repost_doc) -> str:
+ """Vouchers are committed one by one, so the status follows what was actually handled."""
+ handled = sum(1 for voucher in repost_doc.vouchers if voucher.status in HANDLED_VOUCHER_STATUSES)
+
+ if handled == len(repost_doc.vouchers):
+ return "Completed"
+ elif handled == 0:
+ return "Failed"
+
+ return "Partially Reposted"
+
+
+def _record_repost_failure(repost_doc, commit=False) -> None:
+ """Persist the traceback of a run that could not finish, without discarding its progress."""
+ # the traceback with frame locals goes to the Error Log, which is permissioned separately
+ traceback = frappe.get_traceback()
+
+ frappe.log_error(
+ title=_("Unable to Repost Accounting Ledger"),
+ reference_doctype=repost_doc.doctype,
+ reference_name=repost_doc.name,
+ )
+
+ frappe.db.set_value(
+ repost_doc.doctype, repost_doc.name, {"error_log": traceback, "status": _derive_status(repost_doc)}
+ )
+
+ if commit:
+ frappe.db.commit()
+
+
+def _repost_vouchers(doc, delete_cancelled_entries: bool | int | None):
+ if doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
+ _repost_invoices(doc, delete_cancelled_entries)
+
+ elif doc.doctype == "Purchase Receipt":
+ _repost_purchase_receipt(doc, delete_cancelled_entries)
+
+ elif doc.doctype in ["Payment Entry", "Journal Entry"]:
+ _repost_pe_je(doc, delete_cancelled_entries)
+
+ elif doc.doctype in frappe.get_hooks("repost_allowed_doctypes"):
+ _repost_allowed_hook_doctypes(doc, delete_cancelled_entries)
+
+
+def _repost_invoices(invoice_doc, delete_cancelled_entries):
+ if not delete_cancelled_entries:
+ invoice_doc.docstatus = 2
+ invoice_doc.make_gl_entries_on_cancel(from_repost=True)
+
+ invoice_doc.docstatus = 1
+ if invoice_doc.doctype == "Sales Invoice":
+ invoice_doc.force_set_against_income_account()
+ else:
+ invoice_doc.force_set_against_expense_account()
+ invoice_doc.make_gl_entries()
+
+
+def _repost_purchase_receipt(receipt_doc, delete_cancelled_entries):
+ if not delete_cancelled_entries:
+ receipt_doc.docstatus = 2
+ receipt_doc.make_gl_entries_on_cancel(from_repost=True)
+
+ receipt_doc.docstatus = 1
+ receipt_doc.make_gl_entries(from_repost=True)
+
+
+def _repost_pe_je(entry_doc, delete_cancelled_entries):
+ if not delete_cancelled_entries:
+ entry_doc.make_gl_entries(cancel=1)
+ entry_doc.make_gl_entries()
+
+
+def _repost_allowed_hook_doctypes(repost_doc, delete_cancelled_entries: bool | int | None):
+ from erpnext.accounts.general_ledger import make_reverse_gl_entries
+
+ if hasattr(repost_doc, "make_gl_entries") and callable(repost_doc.make_gl_entries):
+ if not delete_cancelled_entries:
+ if "cancel" in inspect.getfullargspec(repost_doc.make_gl_entries).args:
+ repost_doc.make_gl_entries(cancel=1)
+ else:
+ make_reverse_gl_entries(voucher_type=repost_doc.doctype, voucher_no=repost_doc.name)
+ repost_doc.make_gl_entries()
def get_allowed_types_from_settings(child_doc: bool = False):
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger_list.js b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger_list.js
new file mode 100644
index 00000000000..0ecdca3843c
--- /dev/null
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger_list.js
@@ -0,0 +1,16 @@
+frappe.listview_settings["Repost Accounting Ledger"] = {
+ add_fields: ["status"],
+ // drafts and cancelled documents are coloured by the framework before it gets here
+ get_indicator: function (doc) {
+ if (!doc.status) return;
+
+ const status_color = {
+ Queued: "yellow",
+ "In Progress": "blue",
+ "Partially Reposted": "orange",
+ Completed: "green",
+ Failed: "red",
+ };
+ return [__(doc.status), status_color[doc.status] || "gray", "status,=," + doc.status];
+ },
+};
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py
index fe1f4c2379d..5c436dc360f 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py
@@ -1,27 +1,42 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
+from contextlib import contextmanager
+from unittest.mock import patch
+
import frappe
from frappe import qb
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, nowdate, today
+from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
+ _lock_vouchers,
+ _record_repost_failure,
+ _repost_allowed_hook_doctypes,
+ _repost_job_id,
+ _repost_vouchers,
+ repost,
+)
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.utils import get_fiscal_year
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries, make_purchase_receipt
from erpnext.tests.utils import ERPNextTestSuite
+REPOST_MODULE = "erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger"
+SIMULATED_FAILURE = "Simulated repost failure"
+
class TestRepostAccountingLedger(ERPNextTestSuite):
def setUp(self):
frappe.db.set_single_value("Selling Settings", "validate_selling_price", 0)
update_repost_settings()
- def test_01_basic_functions(self):
- si = create_sales_invoice(
+ def make_invoice(self, **kwargs):
+ return create_sales_invoice(
item="_Test Item",
company="_Test Company",
customer="_Test Customer",
@@ -29,8 +44,71 @@ class TestRepostAccountingLedger(ERPNextTestSuite):
parent_cost_center="Main - _TC",
cost_center="Main - _TC",
rate=100,
+ **kwargs,
)
+ def make_invoice_and_payment(self):
+ si = self.make_invoice()
+ pe = get_payment_entry(si.doctype, si.name)
+ pe.save().submit()
+ return si, pe
+
+ def create_repost_doc(self, vouchers, delete_cancelled_entries=False, submit=False):
+ ral = frappe.new_doc("Repost Accounting Ledger")
+ ral.company = "_Test Company"
+ ral.delete_cancelled_entries = delete_cancelled_entries
+ for voucher in vouchers:
+ ral.append("vouchers", {"voucher_type": voucher.doctype, "voucher_no": voucher.name})
+
+ ral.save()
+ if submit:
+ ral.submit()
+ ral.reload()
+ return ral
+
+ @contextmanager
+ def patched_repost(self, fail_for=()):
+ """Yield the vouchers handed over to `_repost_vouchers`, failing the given types."""
+ reposted = []
+
+ def repost_voucher(doc, delete_cancelled_entries):
+ reposted.append(doc.name)
+ if doc.doctype in fail_for:
+ frappe.throw(SIMULATED_FAILURE)
+ _repost_vouchers(doc, delete_cancelled_entries)
+
+ with patch(f"{REPOST_MODULE}._repost_vouchers", new=repost_voucher):
+ yield reposted
+
+ def make_period_closing_voucher(self):
+ fy = get_fiscal_year(today(), company="_Test Company")
+ pcv = frappe.get_doc(
+ {
+ "doctype": "Period Closing Voucher",
+ "transaction_date": today(),
+ "period_start_date": fy[1],
+ "period_end_date": today(),
+ "company": "_Test Company",
+ "fiscal_year": fy[0],
+ "cost_center": "Main - _TC",
+ "closing_account_head": "Retained Earnings - _TC",
+ "remarks": "test",
+ }
+ )
+ return pcv.save().submit()
+
+ def get_gl_totals(self, voucher_no, is_cancelled=0):
+ gl = qb.DocType("GL Entry")
+ return (
+ qb.from_(gl)
+ .select(Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
+ .where((gl.voucher_no == voucher_no) & (gl.is_cancelled == is_cancelled))
+ .run()
+ )[0]
+
+ def test_01_basic_functions(self):
+ si = self.make_invoice()
+
preq = frappe.get_doc(
make_payment_request(
dt=si.doctype,
@@ -64,53 +142,24 @@ class TestRepostAccountingLedger(ERPNextTestSuite):
gle = frappe.db.get_all("GL Entry", filters={"voucher_no": si.name, "account": "Debtors - _TC"})
frappe.db.set_value("GL Entry", gle[0], "debit", 90)
- gl = qb.DocType("GL Entry")
- res = (
- qb.from_(gl)
- .select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
- .where((gl.voucher_no == si.name) & (gl.is_cancelled == 0))
- .groupby(gl.voucher_no)
- .run()
- )
-
# Assert incorrect ledger balance
- self.assertNotEqual(res[0], (si.name, 100, 100))
+ self.assertNotEqual(self.get_gl_totals(si.name), (100, 100))
# Submit repost document
ral.save().submit()
- res = (
- qb.from_(gl)
- .select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
- .where((gl.voucher_no == si.name) & (gl.is_cancelled == 0))
- .groupby(gl.voucher_no)
- .run()
- )
-
# Ledger should reflect correct amount post repost
- self.assertEqual(res[0], (si.name, 100, 100))
+ self.assertEqual(self.get_gl_totals(si.name), (100, 100))
def test_02_deferred_accounting_valiations(self):
- si = create_sales_invoice(
- item="_Test Item",
- company="_Test Company",
- customer="_Test Customer",
- debit_to="Debtors - _TC",
- parent_cost_center="Main - _TC",
- cost_center="Main - _TC",
- rate=100,
- do_not_submit=True,
- )
+ si = self.make_invoice(do_not_submit=True)
si.items[0].enable_deferred_revenue = True
si.items[0].deferred_revenue_account = "Deferred Revenue - _TC"
si.items[0].service_start_date = nowdate()
si.items[0].service_end_date = add_days(nowdate(), 90)
si.save().submit()
- ral = frappe.new_doc("Repost Accounting Ledger")
- ral.company = "_Test Company"
- ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
- self.assertRaises(frappe.ValidationError, ral.save)
+ self.assertRaises(frappe.ValidationError, self.create_repost_doc, [si])
@ERPNextTestSuite.change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1})
def test_04_pcv_validation(self):
@@ -118,86 +167,29 @@ class TestRepostAccountingLedger(ERPNextTestSuite):
gl = frappe.qb.DocType("GL Entry")
qb.from_(gl).delete().where(gl.company == "_Test Company").run()
- si = create_sales_invoice(
- item="_Test Item",
- company="_Test Company",
- customer="_Test Customer",
- debit_to="Debtors - _TC",
- parent_cost_center="Main - _TC",
- cost_center="Main - _TC",
- rate=100,
- )
- fy = get_fiscal_year(today(), company="_Test Company")
- pcv = frappe.get_doc(
- {
- "doctype": "Period Closing Voucher",
- "transaction_date": today(),
- "period_start_date": fy[1],
- "period_end_date": today(),
- "company": "_Test Company",
- "fiscal_year": fy[0],
- "cost_center": "Main - _TC",
- "closing_account_head": "Retained Earnings - _TC",
- "remarks": "test",
- }
- )
- pcv.save().submit()
+ si = self.make_invoice()
+ pcv = self.make_period_closing_voucher()
- ral = frappe.new_doc("Repost Accounting Ledger")
- ral.company = "_Test Company"
- ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
- self.assertRaises(frappe.ValidationError, ral.save)
+ self.assertRaises(frappe.ValidationError, self.create_repost_doc, [si])
pcv.reload()
pcv.cancel()
pcv.delete()
def test_03_deletion_flag_and_preview_function(self):
- si = create_sales_invoice(
- item="_Test Item",
- company="_Test Company",
- customer="_Test Customer",
- debit_to="Debtors - _TC",
- parent_cost_center="Main - _TC",
- cost_center="Main - _TC",
- rate=100,
- )
-
- pe = get_payment_entry(si.doctype, si.name)
- pe.save().submit()
+ si, pe = self.make_invoice_and_payment()
# with deletion flag set
- ral = frappe.new_doc("Repost Accounting Ledger")
- ral.company = "_Test Company"
- ral.delete_cancelled_entries = True
- ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
- ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
- ral.save().submit()
+ self.create_repost_doc([si, pe], delete_cancelled_entries=True, submit=True)
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
def test_05_without_deletion_flag(self):
- si = create_sales_invoice(
- item="_Test Item",
- company="_Test Company",
- customer="_Test Customer",
- debit_to="Debtors - _TC",
- parent_cost_center="Main - _TC",
- cost_center="Main - _TC",
- rate=100,
- )
-
- pe = get_payment_entry(si.doctype, si.name)
- pe.save().submit()
+ si, pe = self.make_invoice_and_payment()
# without deletion flag set
- ral = frappe.new_doc("Repost Accounting Ledger")
- ral.company = "_Test Company"
- ral.delete_cancelled_entries = False
- ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
- ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
- ral.save().submit()
+ self.create_repost_doc([si, pe], submit=True)
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
@@ -248,11 +240,7 @@ class TestRepostAccountingLedger(ERPNextTestSuite):
another_provisional_account,
)
- repost_doc = frappe.new_doc("Repost Accounting Ledger")
- repost_doc.company = "_Test Company"
- repost_doc.delete_cancelled_entries = True
- repost_doc.append("vouchers", {"voucher_type": pr.doctype, "voucher_no": pr.name})
- repost_doc.save().submit()
+ repost_doc = self.create_repost_doc([pr], delete_cancelled_entries=True, submit=True)
pr_gles_after_repost = get_gl_entries(pr.doctype, pr.name, skip_cancelled=True)
expected_pr_gles_after_repost = [
@@ -273,6 +261,281 @@ class TestRepostAccountingLedger(ERPNextTestSuite):
company.default_provisional_account = None
company.save()
+ def test_07_voucher_validations(self):
+ submitted_si = self.make_invoice()
+ draft_si = self.make_invoice(do_not_submit=True)
+ cancelled_si = self.make_invoice()
+ cancelled_si.cancel()
+
+ for vouchers, exception, message in (
+ ([], frappe.ValidationError, "Add atleast one voucher"),
+ ([submitted_si, submitted_si], frappe.ValidationError, "Duplicate vouchers found"),
+ ([draft_si], frappe.ValidationError, f"not submitted.*{draft_si.name}"),
+ # cancelled vouchers don't make it past link validation
+ ([cancelled_si], frappe.CancelledLinkError, "Cannot link cancelled document"),
+ ):
+ with self.subTest(vouchers=[x.name for x in vouchers]):
+ self.assertRaisesRegex(exception, message, self.create_repost_doc, vouchers)
+
+ self.create_repost_doc([submitted_si])
+
+ def test_08_voucher_count_limit(self):
+ si, pe = self.make_invoice_and_payment()
+ another_si = self.make_invoice()
+
+ with patch(f"{REPOST_MODULE}.MAX_VOUCHERS_PER_REPOST", 2):
+ self.create_repost_doc([si, pe])
+ self.assertRaisesRegex(
+ frappe.ValidationError,
+ "Cannot repost more than 2 vouchers",
+ self.create_repost_doc,
+ [si, pe, another_si],
+ )
+
+ def test_09_status_lifecycle(self):
+ si, pe = self.make_invoice_and_payment()
+
+ ral = self.create_repost_doc([si, pe])
+ self.assertEqual(ral.status, "")
+
+ ral.submit()
+ ral.reload()
+
+ self.assertEqual(ral.status, "Completed")
+ self.assertFalse(ral.error_log)
+ for voucher in ral.vouchers:
+ self.assertEqual(voucher.status, "Reposted")
+ self.assertFalse(voucher.traceback)
+
+ ral.cancel()
+ ral.reload()
+ self.assertEqual(ral.status, "Cancelled")
+
+ discarded = self.create_repost_doc([si])
+ discarded.discard()
+ discarded.reload()
+ self.assertEqual(discarded.status, "Cancelled")
+
+ def test_10_start_repost_guards(self):
+ si = self.make_invoice()
+ ral = self.create_repost_doc([si])
+
+ self.assertRaisesRegex(frappe.ValidationError, "only for submitted document", ral.start_repost)
+
+ ral.submit()
+ ral.reload()
+ self.assertRaisesRegex(
+ frappe.ValidationError, "cannot be started when status is Completed", ral.start_repost
+ )
+
+ # a document left behind by a worker that died mid-repost
+ ral.db_set("status", "In Progress")
+
+ with patch(f"{REPOST_MODULE}.is_job_enqueued", return_value=True):
+ self.assertRaisesRegex(
+ frappe.ValidationError, "still in progress in background", ral.start_repost
+ )
+ self.assertRaisesRegex(frappe.ValidationError, "still in progress in background", ral.cancel)
+
+ # `cancel` flips docstatus in memory before running `before_cancel`
+ ral.reload()
+
+ with patch(f"{REPOST_MODULE}.is_job_enqueued", return_value=False):
+ # the job is gone, so `In Progress` must not keep the document stuck
+ ral.start_repost()
+
+ ral.reload()
+ self.assertEqual(ral.status, "Completed")
+
+ def test_11_repost_job_is_tied_to_the_document(self):
+ si = self.make_invoice()
+ ral = self.create_repost_doc([si], submit=True)
+ ral.db_set("status", "Failed")
+
+ with patch(f"{REPOST_MODULE}.frappe.enqueue") as enqueue:
+ ral.start_repost()
+
+ kwargs = enqueue.call_args.kwargs
+ self.assertEqual(kwargs["repost_doc_name"], ral.name)
+ self.assertEqual(kwargs["job_id"], _repost_job_id(ral.name))
+ # a second start cannot queue a second job for the same document
+ self.assertTrue(kwargs["deduplicate"])
+
+ def test_12_voucher_failures_are_isolated_and_retried(self):
+ si, pe = self.make_invoice_and_payment()
+ pe_gl_entries = frappe.db.count("GL Entry", {"voucher_no": pe.name})
+
+ # the deletion flag drops the existing entries before reposting them
+ ral = self.create_repost_doc([si, pe], delete_cancelled_entries=True)
+ with self.patched_repost(fail_for=["Payment Entry"]):
+ ral.submit()
+
+ ral.reload()
+ self.assertEqual(ral.status, "Partially Reposted")
+
+ si_row, pe_row = ral.vouchers
+ self.assertEqual((si_row.status, pe_row.status), ("Reposted", "Failed"))
+ self.assertFalse(si_row.traceback)
+ self.assertIn(SIMULATED_FAILURE, pe_row.traceback)
+
+ # the failed voucher is rolled back to its savepoint, so its entries are back
+ self.assertEqual(frappe.db.count("GL Entry", {"voucher_no": pe.name}), pe_gl_entries)
+
+ # a retry only picks up the vouchers that are not reposted yet, and leaves the rest
+ # alone entirely: they are not locked or loaded either
+ with (
+ patch(f"{REPOST_MODULE}._lock_vouchers", side_effect=_lock_vouchers) as lock_vouchers,
+ self.patched_repost() as retried,
+ ):
+ ral.start_repost()
+
+ self.assertEqual(retried, [pe.name])
+ self.assertEqual([x.voucher_no for x in lock_vouchers.call_args.args[0]], [pe.name])
+
+ ral.reload()
+ self.assertEqual(ral.status, "Completed")
+ for voucher in ral.vouchers:
+ self.assertEqual(voucher.status, "Reposted")
+ self.assertFalse(voucher.traceback)
+
+ def test_13_status_of_a_run_that_could_not_finish(self):
+ si, pe = self.make_invoice_and_payment()
+
+ ral = self.create_repost_doc([si, pe])
+ with self.patched_repost(fail_for=["Payment Entry"]):
+ ral.submit()
+
+ ral.reload()
+
+ # the job dies after the loop committed the invoice, e.g. killed or timed out
+ try:
+ frappe.throw(SIMULATED_FAILURE)
+ except frappe.ValidationError:
+ _record_repost_failure(ral)
+
+ ral.reload()
+
+ # progress already committed must not be reported as a total failure
+ self.assertEqual(ral.status, "Partially Reposted")
+ self.assertIn(SIMULATED_FAILURE, ral.error_log)
+ self.assertTrue(
+ frappe.db.exists("Error Log", {"reference_doctype": ral.doctype, "reference_name": ral.name})
+ )
+
+ @ERPNextTestSuite.change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1})
+ def test_14_period_closed_after_the_repost_was_started(self):
+ gl = qb.DocType("GL Entry")
+ qb.from_(gl).delete().where(gl.company == "_Test Company").run()
+
+ si = self.make_invoice()
+ ral = self.create_repost_doc([si], submit=True)
+ ral.db_set("status", "Failed")
+ ral.vouchers[0].db_set("status", "Pending")
+
+ # the period is closed between the repost being started and the job running
+ self.make_period_closing_voucher()
+
+ gl_entries = frappe.db.count("GL Entry", {"voucher_no": si.name})
+ self.assertRaisesRegex(frappe.ValidationError, "Closed fiscal year", repost, ral.name, commit=False)
+
+ ral.reload()
+ self.assertEqual(ral.status, "Failed")
+ self.assertIn("Closed fiscal year", ral.error_log)
+
+ # the ledger is left exactly as it was
+ self.assertEqual(frappe.db.count("GL Entry", {"voucher_no": si.name}), gl_entries)
+ self.assertEqual(ral.vouchers[0].status, "Pending")
+
+ def test_15_failed_repost_skips_cancelled_voucher(self):
+ si = self.make_invoice()
+
+ ral = self.create_repost_doc([si])
+ with self.patched_repost(fail_for=["Sales Invoice"]):
+ ral.submit()
+
+ ral.reload()
+ self.assertEqual(ral.status, "Failed")
+
+ si.reload()
+ si.cancel()
+
+ ral.start_repost()
+ ral.reload()
+
+ # nothing was reposted, but there is nothing left to repost either
+ self.assertEqual(ral.status, "Completed")
+ self.assertEqual(ral.vouchers[0].status, "Skipped")
+ self.assertFalse(ral.vouchers[0].traceback)
+
+ def test_16_concurrent_repost_is_blocked_by_voucher_lock(self):
+ si, pe = self.make_invoice_and_payment()
+ ral = self.create_repost_doc([si, pe])
+
+ # a concurrent repost holding the lock on the second voucher
+ locked_pe = frappe.get_doc(pe.doctype, pe.name)
+ locked_pe.lock()
+ try:
+ self.assertRaises(frappe.DocumentLockedError, ral.submit)
+
+ # vouchers locked before the failure are released again
+ self.assertFalse(frappe.get_doc(si.doctype, si.name).is_locked)
+ finally:
+ locked_pe.unlock()
+
+ def test_17_journal_entry_repost(self):
+ je = make_journal_entry("_Test Bank - _TC", "_Test Cash - _TC", 500, submit=True)
+ je = frappe.get_doc("Journal Entry", je.name)
+
+ self.assertEqual(self.get_gl_totals(je.name), (500.0, 500.0))
+
+ # without the deletion flag the 2 original entries are marked as cancelled,
+ # along with the 2 reverse entries booked against them
+ for delete_cancelled_entries, cancelled_entries in ((False, 4), (True, 0)):
+ with self.subTest(delete_cancelled_entries=delete_cancelled_entries):
+ ral = self.create_repost_doc(
+ [je], delete_cancelled_entries=delete_cancelled_entries, submit=True
+ )
+
+ self.assertEqual(ral.status, "Completed")
+ self.assertEqual(self.get_gl_totals(je.name), (500.0, 500.0))
+ self.assertEqual(
+ frappe.db.count("GL Entry", {"voucher_no": je.name, "is_cancelled": 1}),
+ cancelled_entries,
+ )
+
+ def test_18_hook_allowed_doctype_repost(self):
+ class VoucherWithCancelArg:
+ doctype = "Test Repost Voucher"
+ name = "TRV-00001"
+
+ def __init__(self):
+ self.calls = []
+
+ def make_gl_entries(self, cancel=0):
+ self.calls.append(cancel)
+
+ class VoucherWithoutCancelArg(VoucherWithCancelArg):
+ def make_gl_entries(self):
+ self.calls.append("repost")
+
+ # vouchers that can reverse their own entries are asked to do so first
+ doc = VoucherWithCancelArg()
+ _repost_allowed_hook_doctypes(doc, delete_cancelled_entries=False)
+ self.assertEqual(doc.calls, [1, 0])
+
+ # nothing to reverse when the old entries are deleted
+ doc = VoucherWithCancelArg()
+ _repost_allowed_hook_doctypes(doc, delete_cancelled_entries=True)
+ self.assertEqual(doc.calls, [0])
+
+ # the rest fall back to the generic reversal
+ doc = VoucherWithoutCancelArg()
+ with patch("erpnext.accounts.general_ledger.make_reverse_gl_entries") as make_reverse_gl_entries:
+ _repost_allowed_hook_doctypes(doc, delete_cancelled_entries=False)
+
+ make_reverse_gl_entries.assert_called_once_with(voucher_type=doc.doctype, voucher_no=doc.name)
+ self.assertEqual(doc.calls, ["repost"])
+
def update_repost_settings():
allowed_types = [
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json b/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
index fd5bb92959d..c6d9468e36f 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
+++ b/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_bulk_edit": 1,
"allow_rename": 1,
"creation": "2023-07-04 14:14:01.243848",
"doctype": "DocType",
@@ -7,34 +8,70 @@
"engine": "InnoDB",
"field_order": [
"voucher_type",
- "voucher_no"
+ "column_break_ndex",
+ "voucher_no",
+ "reposting_status_section",
+ "status",
+ "traceback"
],
"fields": [
{
+ "columns": 5,
"fieldname": "voucher_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Voucher Type",
- "options": "DocType"
+ "options": "DocType",
+ "reqd": 1
},
{
+ "fieldname": "column_break_ndex",
+ "fieldtype": "Column Break"
+ },
+ {
+ "columns": 5,
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Voucher No",
- "options": "voucher_type"
+ "options": "voucher_type",
+ "reqd": 1
+ },
+ {
+ "fieldname": "reposting_status_section",
+ "fieldtype": "Section Break",
+ "label": "Reposting Status"
+ },
+ {
+ "columns": 2,
+ "default": "Pending",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Pending\nReposted\nSkipped\nFailed",
+ "read_only": 1
+ },
+ {
+ "fieldname": "traceback",
+ "fieldtype": "Code",
+ "label": "Traceback",
+ "no_copy": 1,
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2024-03-27 13:10:32.170897",
+ "modified": "2026-07-29 02:41:00.000000",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Accounting Ledger Items",
"owner": "Administrator",
"permissions": [],
+ "row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.py b/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.py
index 6e02e3a6b98..a895e218e4d 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.py
+++ b/erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.py
@@ -17,8 +17,10 @@ class RepostAccountingLedgerItems(Document):
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
- voucher_no: DF.DynamicLink | None
- voucher_type: DF.Link | None
+ status: DF.Literal["Pending", "Reposted", "Skipped", "Failed"]
+ traceback: DF.Code | None
+ voucher_no: DF.DynamicLink
+ voucher_type: DF.Link
# end: auto-generated types
pass
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 770b24fe4ca..5033fc25cc0 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -3215,6 +3215,10 @@ class TestSalesInvoice(ERPNextTestSuite):
"Stock Received But Not Billed - _TC1",
)
+ # companies are created with their Stores warehouse as Default Warehouse; clear it so the
+ # item genuinely maps without one
+ frappe.db.set_value("Company", "_Test Company 1", "default_warehouse", None)
+
# begin test
si = create_sales_invoice(
company="Wind Power LLC",
diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js
index 9e12afcddd0..4a1fb8ec0bb 100644
--- a/erpnext/accounts/doctype/subscription/subscription.js
+++ b/erpnext/accounts/doctype/subscription/subscription.js
@@ -110,6 +110,32 @@ frappe.ui.form.on("Subscription", {
},
});
+frappe.ui.form.on("Subscription Plan Detail", {
+ plan: function (frm, cdt, cdn) {
+ const row = locals[cdt][cdn];
+ if (!row.plan) return;
+ const requested_plan = row.plan;
+
+ frappe.call({
+ method: "erpnext.accounts.doctype.subscription.subscription.get_plan_dimensions",
+ args: {
+ plan: requested_plan,
+ company: frm.doc.company,
+ party_type: frm.doc.party_type,
+ },
+ callback: function (r) {
+ if (!r.message || locals[cdt]?.[cdn]?.plan !== requested_plan) return;
+ // Only fill dimensions left empty, so a manual entry or an earlier plan is never overwritten.
+ for (const [dimension, value] of Object.entries(r.message)) {
+ if (frm.fields_dict[dimension] && !frm.doc[dimension]) {
+ frm.set_value(dimension, value);
+ }
+ }
+ },
+ });
+ },
+});
+
// Status -> colour and label for the calendar heatmap. Keys are Title-case to
// match the value frappe-charts shows in its hover tooltip.
const HEATMAP_COLORS = {
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 21cd276c508..5e4c32d82a4 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -26,6 +26,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
+from erpnext.stock.doctype.item.item import get_item_defaults
class InvoiceCancelled(frappe.ValidationError):
@@ -981,6 +982,39 @@ def get_prorata_factor(
return diff / plan_days
+@frappe.whitelist()
+def get_plan_dimensions(
+ plan: str, company: str | None = None, party_type: str | None = None
+) -> dict[str, str]:
+ """Resolve a plan's accounting dimensions, falling back to the plan item's company defaults."""
+ plan_doc = frappe.get_cached_doc("Subscription Plan", plan)
+
+ dimensions = {}
+ for dimension in ["cost_center", *get_accounting_dimensions()]:
+ value = plan_doc.get(dimension) or get_item_dimension(plan_doc.item, dimension, company, party_type)
+ if value:
+ dimensions[dimension] = value
+
+ return dimensions
+
+
+def get_item_dimension(
+ item_code: str, dimension: str, company: str | None, party_type: str | None
+) -> str | None:
+ if not company:
+ return None
+
+ item_defaults = get_item_defaults(item_code, company)
+ if dimension != "cost_center":
+ return item_defaults.get(dimension)
+
+ selling = item_defaults.get("selling_cost_center")
+ buying = item_defaults.get("buying_cost_center")
+ if party_type == PARTY_SUPPLIER:
+ return buying or selling
+ return selling or buying
+
+
def process_all(subscription: list, posting_date: DateTimeLikeObject | None = None) -> None:
"""
Task to updates the status of all `Subscription` apart from those that are cancelled
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 02e8fec22b6..551bdb69166 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -18,7 +18,12 @@ from frappe.utils.data import (
)
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
-from erpnext.accounts.doctype.subscription.subscription import Subscription, get_prorata_factor, process_all
+from erpnext.accounts.doctype.subscription.subscription import (
+ Subscription,
+ get_plan_dimensions,
+ get_prorata_factor,
+ process_all,
+)
from erpnext.accounts.utils import update_subscription_on_invoice_update
from erpnext.tests.utils import ERPNextTestSuite
@@ -951,6 +956,47 @@ class TestSubscription(ERPNextTestSuite):
cells = {cell["date"]: cell for cell in subscription.get_billing_heatmap()}
self.assertEqual(cells[str(getdate(invoice.from_date))]["status"], "refunded")
+ def test_plan_dimensions_resolve_from_plan_then_item(self):
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ # Plan-level cost center takes precedence.
+ create_plan(plan_name="_Test Sub Plan CC", cost=100, currency="INR")
+ frappe.db.set_value(
+ "Subscription Plan", "_Test Sub Plan CC", "cost_center", "_Test Cost Center - _TC"
+ )
+ self.assertEqual(
+ get_plan_dimensions("_Test Sub Plan CC", "_Test Company", "Customer").get("cost_center"),
+ "_Test Cost Center - _TC",
+ )
+
+ # No plan cost center: fall back to the item's company default (selling vs buying by party type).
+ item = make_item(
+ "_Test Sub Dimension Item",
+ {
+ "is_stock_item": 0,
+ "item_defaults": [
+ {
+ "company": "_Test Company",
+ "selling_cost_center": "_Test Cost Center - _TC",
+ "buying_cost_center": "_Test Cost Center 2 - _TC",
+ }
+ ],
+ },
+ )
+ create_plan(plan_name="_Test Sub Plan No CC", cost=100, currency="INR", item=item.name)
+
+ self.assertEqual(
+ get_plan_dimensions("_Test Sub Plan No CC", "_Test Company", "Customer").get("cost_center"),
+ "_Test Cost Center - _TC",
+ )
+ self.assertEqual(
+ get_plan_dimensions("_Test Sub Plan No CC", "_Test Company", "Supplier").get("cost_center"),
+ "_Test Cost Center 2 - _TC",
+ )
+
+ # Without a company the item fallback is skipped.
+ self.assertNotIn("cost_center", get_plan_dimensions("_Test Sub Plan No CC"))
+
def make_full_credit_note(invoice_name):
from erpnext.accounts.doctype.sales_invoice.mapper import make_sales_return
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 6244dbc954b..a962dcff459 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -869,7 +869,7 @@ def get_dashboard_info(party_type, party, loyalty_program=None):
doctype = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
- companies = frappe.get_all(
+ companies = frappe.get_list(
doctype, filters={"docstatus": 1, party_type.lower(): party}, distinct=1, fields=["company"]
)
diff --git a/erpnext/accounts/print_format/pos_invoice_bordered/__init__.py b/erpnext/accounts/print_format/pos_invoice_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/pos_invoice_bordered/pos_invoice_bordered.json b/erpnext/accounts/print_format/pos_invoice_bordered/pos_invoice_bordered.json
new file mode 100644
index 00000000000..17870e8474c
--- /dev/null
+++ b/erpnext/accounts/print_format/pos_invoice_bordered/pos_invoice_bordered.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:48:19.769473",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "POS Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Credit Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6,\"visible_if\":\"doc.is_return\"},{\"label\":\"POS Invoice\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6,\"visible_if\":\"doc.is_return == 0\"},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"
\\n
Bill From:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"company_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu_zBGrfDlm\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill To:
\\n
{{ doc.customer }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"POS Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":9},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":11}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[]},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":65,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"f\",\"v\":\"tax_amount\"}],\"align\":\"right\",\"width\":35,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1.5px solid #e5e7eb;margin-top:6px;padding-top:10px;font-weight:700;\",\"label_color\":\"#1f2328\"}]}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":40,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0},\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":5,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:53:40.378190",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "POS Invoice Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/pos_invoice_classic/__init__.py b/erpnext/accounts/print_format/pos_invoice_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/pos_invoice_classic/pos_invoice_classic.json b/erpnext/accounts/print_format/pos_invoice_classic/pos_invoice_classic.json
new file mode 100644
index 00000000000..47e5699636b
--- /dev/null
+++ b/erpnext/accounts/print_format/pos_invoice_classic/pos_invoice_classic.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:48:19.754273",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "POS Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom\":1},{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"POS Invoice\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"visible_if\":\"doc.is_return == 0\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Credit Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"visible_if\":\"doc.is_return\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"POS Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":47},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":50}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":24,\"margin\":{\"top\":10,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\"}]}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-24 16:53:40.517086",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "POS Invoice Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/pos_invoice_modern/__init__.py b/erpnext/accounts/print_format/pos_invoice_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/pos_invoice_modern/pos_invoice_modern.json b/erpnext/accounts/print_format/pos_invoice_modern/pos_invoice_modern.json
new file mode 100644
index 00000000000..c278b993790
--- /dev/null
+++ b/erpnext/accounts/print_format/pos_invoice_modern/pos_invoice_modern.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:48:19.782866",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "POS Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n {{ \\\"Credit Note\\\" if doc.is_return else \\\"POS Invoice\\\" }}\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Billed To\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nReturn\\nCredit Note Issued\\nConsolidated\\nSubmitted\\nPaid\\nPartly Paid\\nUnpaid\\nPartly Paid and Discounted\\nUnpaid and Discounted\\nOverdue and Discounted\\nOverdue\\nCancelled\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"POS Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":14},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":45},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\",\"custom_style\":\"\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":49}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:53:40.542432",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "POS Invoice Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/pos_invoice_modern_with_images/__init__.py b/erpnext/accounts/print_format/pos_invoice_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/pos_invoice_modern_with_images/pos_invoice_modern_with_images.json b/erpnext/accounts/print_format/pos_invoice_modern_with_images/pos_invoice_modern_with_images.json
new file mode 100644
index 00000000000..71eedf9ef80
--- /dev/null
+++ b/erpnext/accounts/print_format/pos_invoice_modern_with_images/pos_invoice_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:48:19.620343",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "POS Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n {{ \\\"Credit Note\\\" if doc.is_return else \\\"POS Invoice\\\" }}\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Customer\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"},{\"label\":\"Bill To\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\",\"align\":\"left\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nReturn\\nCredit Note Issued\\nConsolidated\\nSubmitted\\nPaid\\nPartly Paid\\nUnpaid\\nPartly Paid and Discounted\\nUnpaid and Discounted\\nOverdue and Discounted\\nOverdue\\nCancelled\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"POS Invoice Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":51},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":0,\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":70,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"color\":\"#1f2328\",\"width\":30}],\"custom_style\":\"\",\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"border-top: 1px solid #e5e7eb;\\nmargin-top:5px;\\nfont-weight: bold;\\npadding-top:9px\\n\"}],\"width\":49}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words:\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"label_gap\":2,\"custom_style\":\"\"}]}],\"field_orientation\":\"left-right\",\"background\":\"#f8f8f8\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:53:40.566570",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "POS Invoice Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/purchase_invoice_bordered/__init__.py b/erpnext/accounts/print_format/purchase_invoice_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/purchase_invoice_bordered/purchase_invoice_bordered.json b/erpnext/accounts/print_format/purchase_invoice_bordered/purchase_invoice_bordered.json
new file mode 100644
index 00000000000..47218cd74ab
--- /dev/null
+++ b/erpnext/accounts/print_format/purchase_invoice_bordered/purchase_invoice_bordered.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:38:15.502360",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Supplier Name\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Debit Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6,\"visible_if\":\"doc.is_return\"},{\"label\":\"Purchase Invoice\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6,\"visible_if\":\"doc.is_return == 0\"},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Company:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"billing_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu_zBGrfDlm\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Supplier:
\\n
{{ doc.supplier }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":9},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":11}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[]},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":65,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"f\",\"v\":\"tax_amount\"}],\"align\":\"right\",\"width\":35,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1.5px solid #e5e7eb;margin-top:6px;padding-top:10px;font-weight:700;\",\"label_color\":\"#1f2328\"}]}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":40,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0},\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":5,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:41:02.765422",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/purchase_invoice_classic/__init__.py b/erpnext/accounts/print_format/purchase_invoice_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/purchase_invoice_classic/purchase_invoice_classic.json b/erpnext/accounts/print_format/purchase_invoice_classic/purchase_invoice_classic.json
new file mode 100644
index 00000000000..3cd50c029b4
--- /dev/null
+++ b/erpnext/accounts/print_format/purchase_invoice_classic/purchase_invoice_classic.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:38:15.617373",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom\":1},{\"label\":\"Supplier Name\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"Purchase Invoice\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"visible_if\":\"doc.is_return == 0\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Debit Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"visible_if\":\"doc.is_return\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":47},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":50}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":24,\"margin\":{\"top\":10,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\"}]}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-24 16:41:02.910454",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/purchase_invoice_modern/__init__.py b/erpnext/accounts/print_format/purchase_invoice_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/purchase_invoice_modern/purchase_invoice_modern.json b/erpnext/accounts/print_format/purchase_invoice_modern/purchase_invoice_modern.json
new file mode 100644
index 00000000000..a6d48e4d5cc
--- /dev/null
+++ b/erpnext/accounts/print_format/purchase_invoice_modern/purchase_invoice_modern.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:38:15.630335",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n {{ \\\"Debit Note\\\" if doc.is_return else \\\"Purchase Invoice\\\" }}\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Supplier\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nReturn\\nDebit Note Issued\\nSubmitted\\nPaid\\nPartly Paid\\nUnpaid\\nOverdue\\nCancelled\\nInternal Transfer\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":14},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":45},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\",\"custom_style\":\"\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":49}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:41:02.936246",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/purchase_invoice_modern_with_images/__init__.py b/erpnext/accounts/print_format/purchase_invoice_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/purchase_invoice_modern_with_images/purchase_invoice_modern_with_images.json b/erpnext/accounts/print_format/purchase_invoice_modern_with_images/purchase_invoice_modern_with_images.json
new file mode 100644
index 00000000000..b61bce4c74a
--- /dev/null
+++ b/erpnext/accounts/print_format/purchase_invoice_modern_with_images/purchase_invoice_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:38:15.604751",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n {{ \\\"Debit Note\\\" if doc.is_return else \\\"Purchase Invoice\\\" }}\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Supplier Name\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"},{\"label\":\"Supplier\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\",\"align\":\"left\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nReturn\\nDebit Note Issued\\nSubmitted\\nPaid\\nPartly Paid\\nUnpaid\\nOverdue\\nCancelled\\nInternal Transfer\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Invoice Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":51},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":0,\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":70,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"color\":\"#1f2328\",\"width\":30}],\"custom_style\":\"\",\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"border-top: 1px solid #e5e7eb;\\nmargin-top:5px;\\nfont-weight: bold;\\npadding-top:9px\\n\"}],\"width\":49}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words:\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"label_gap\":2,\"custom_style\":\"\"}]}],\"field_orientation\":\"left-right\",\"background\":\"#f8f8f8\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:41:02.974372",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/sales_invoice_bordered/__init__.py b/erpnext/accounts/print_format/sales_invoice_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/sales_invoice_bordered/sales_invoice_bordered.json b/erpnext/accounts/print_format/sales_invoice_bordered/sales_invoice_bordered.json
new file mode 100644
index 00000000000..d4644537a54
--- /dev/null
+++ b/erpnext/accounts/print_format/sales_invoice_bordered/sales_invoice_bordered.json
@@ -0,0 +1,37 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-13 00:29:39.065778",
+ "css": "",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Small Text\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Credit Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6,\"visible_if\":\"doc.is_return\"},{\"label\":\"Sales Invoice\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6,\"visible_if\":\"doc.is_return == 0\"},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill From:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"company_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu_zBGrfDlm\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill To:
\\n
{{ doc.customer }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":9},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":11}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[]},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":65,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"f\",\"v\":\"tax_amount\"}],\"align\":\"right\",\"width\":35,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1.5px solid #e5e7eb;margin-top:6px;padding-top:10px;font-weight:700;\",\"label_color\":\"#1f2328\"}]}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":40,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0},\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Small Text\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":5,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-23 17:31:13.130963",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/sales_invoice_classic/__init__.py b/erpnext/accounts/print_format/sales_invoice_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/sales_invoice_classic/sales_invoice_classic.json b/erpnext/accounts/print_format/sales_invoice_classic/sales_invoice_classic.json
new file mode 100644
index 00000000000..87fe6936aad
--- /dev/null
+++ b/erpnext/accounts/print_format/sales_invoice_classic/sales_invoice_classic.json
@@ -0,0 +1,37 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-14 12:45:49.467585",
+ "css": "",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom\":1},{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Small Text\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"Sales Invoice\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"visible_if\":\"doc.is_return == 0\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Credit Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"visible_if\":\"doc.is_return\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":47},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":50}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":24,\"margin\":{\"top\":10,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Small Text\"}]}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-23 17:39:50.717285",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/sales_invoice_modern/__init__.py b/erpnext/accounts/print_format/sales_invoice_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/sales_invoice_modern/sales_invoice_modern.json b/erpnext/accounts/print_format/sales_invoice_modern/sales_invoice_modern.json
new file mode 100644
index 00000000000..75f50ecd957
--- /dev/null
+++ b/erpnext/accounts/print_format/sales_invoice_modern/sales_invoice_modern.json
@@ -0,0 +1,37 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-14 12:45:49.415037",
+ "css": "",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n {{ \\\"Credit Note\\\" if doc.is_return else \\\"Sales Invoice\\\" }}\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Billed To\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Small Text\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nReturn\\nCredit Note Issued\\nSubmitted\\nPaid\\nPartly Paid\\nUnpaid\\nUnpaid and Discounted\\nPartly Paid and Discounted\\nOverdue and Discounted\\nOverdue\\nCancelled\\nInternal Transfer\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Invoice Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":14},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":45},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\",\"custom_style\":\"\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":49}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Small Text\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-23 17:28:35.317242",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/print_format/sales_invoice_modern_with_images/__init__.py b/erpnext/accounts/print_format/sales_invoice_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/print_format/sales_invoice_modern_with_images/sales_invoice_modern_with_images.json b/erpnext/accounts/print_format/sales_invoice_modern_with_images/sales_invoice_modern_with_images.json
new file mode 100644
index 00000000000..7414e96e496
--- /dev/null
+++ b/erpnext/accounts/print_format/sales_invoice_modern_with_images/sales_invoice_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-06-23 14:56:06.351208",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n {{ \\\"Credit Note\\\" if doc.is_return else \\\"Sales Invoice\\\" }}\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Customer\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Small Text\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"},{\"label\":\"Bill To\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\",\"align\":\"left\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Payment Due Date\",\"fieldname\":\"due_date\",\"fieldtype\":\"Date\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nReturn\\nCredit Note Issued\\nSubmitted\\nPaid\\nPartly Paid\\nUnpaid\\nUnpaid and Discounted\\nPartly Paid and Discounted\\nOverdue and Discounted\\nOverdue\\nCancelled\\nInternal Transfer\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Invoice Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":51},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":0,\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":70,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"color\":\"#1f2328\",\"width\":30}],\"custom_style\":\"\",\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"border-top: 1px solid #e5e7eb;\\nmargin-top:5px;\\nfont-weight: bold;\\npadding-top:9px\\n\"}],\"width\":49}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words:\",\"fieldname\":\"in_words\",\"fieldtype\":\"Small Text\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"label_gap\":2,\"custom_style\":\"\"}]}],\"field_orientation\":\"left-right\",\"background\":\"#f8f8f8\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-23 17:30:42.583874",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js
index f0bca38d443..f148f3fa585 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.js
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js
@@ -13,7 +13,7 @@ frappe.query_reports["Accounts Payable"] = {
},
{
fieldname: "report_date",
- label: __("Posting Date"),
+ label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
@@ -69,10 +69,10 @@ frappe.query_reports["Accounts Payable"] = {
default: "Due Date",
},
{
- fieldname: "calculate_ageing_with",
- label: __("Calculate Ageing With"),
+ fieldname: "age_as_on",
+ label: __("Age as on"),
fieldtype: "Select",
- options: "Report Date\nToday Date",
+ options: "Report Date\nToday",
default: "Report Date",
},
{
@@ -180,17 +180,25 @@ frappe.query_reports["Accounts Payable"] = {
return Object.assign(options, {
checkboxColumn: true,
events: {
- onCheckRow: () => erpnext.accounts.toggle_create_pe_primary_action(frappe.query_report),
+ onCheckRow: () => toggle_create_pe_button(frappe.query_report),
},
});
},
after_refresh: function (report) {
report.datatable?.rowmanager?.checkAll(false);
- report.page.clear_primary_action();
+ toggle_create_pe_button(report);
},
onload: function (report) {
+ if (frappe.model.can_create("Payment Entry")) {
+ report.create_pe_btn = report.page
+ .add_inner_button(__("Create Payment Entries"), function () {
+ create_payment_entries_from_payable_report(report);
+ })
+ .toggle(false);
+ }
+
report.page.add_inner_button(__("Accounts Payable Summary"), function () {
var filters = report.get_values();
frappe.set_route("query-report", "Accounts Payable Summary", { company: filters.company });
@@ -202,25 +210,17 @@ frappe.query_reports["Accounts Payable"] = {
},
};
-frappe.provide("erpnext.accounts");
-
-erpnext.accounts.toggle_create_pe_primary_action = function (report) {
- if (!report || !report.datatable || !frappe.model.can_create("Payment Entry")) return;
+function toggle_create_pe_button(report) {
+ if (!report || !report.create_pe_btn || !report.datatable) return;
const has_purchase_invoice = report.datatable.rowmanager
.getCheckedRows()
.some((i) => report.datatable.datamanager.data[i]?.voucher_type === "Purchase Invoice");
- if (has_purchase_invoice) {
- report.page.set_primary_action(__("Create Payment Entries"), () =>
- erpnext.accounts.create_payment_entries_from_payable_report(report)
- );
- } else {
- report.page.clear_primary_action();
- }
-};
+ report.create_pe_btn.toggle(has_purchase_invoice);
+}
-erpnext.accounts.create_payment_entries_from_payable_report = function (report) {
+function create_payment_entries_from_payable_report(report) {
const datatable = report.datatable;
if (!datatable) return;
@@ -343,7 +343,7 @@ erpnext.accounts.create_payment_entries_from_payable_report = function (report)
},
});
dialog.show();
-};
+}
erpnext.utils.add_dimensions("Accounts Payable", 10);
diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
index 72fb564cf9e..db30b68b9a6 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
@@ -12,7 +12,7 @@ frappe.query_reports["Accounts Payable Summary"] = {
},
{
fieldname: "report_date",
- label: __("Posting Date"),
+ label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
@@ -24,10 +24,10 @@ frappe.query_reports["Accounts Payable Summary"] = {
default: "Due Date",
},
{
- fieldname: "calculate_ageing_with",
- label: __("Calculate Ageing With"),
+ fieldname: "age_as_on",
+ label: __("Age as on"),
fieldtype: "Select",
- options: "Report Date\nToday Date",
+ options: "Report Date\nToday",
default: "Report Date",
},
{
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 4a6ef4dd86a..7e4fbdaded0 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -15,7 +15,7 @@ frappe.query_reports["Accounts Receivable"] = {
},
{
fieldname: "report_date",
- label: __("Posting Date"),
+ label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
@@ -98,10 +98,10 @@ frappe.query_reports["Accounts Receivable"] = {
default: "Due Date",
},
{
- fieldname: "calculate_ageing_with",
- label: __("Calculate Ageing With"),
+ fieldname: "age_as_on",
+ label: __("Age as on"),
fieldtype: "Select",
- options: "Report Date\nToday Date",
+ options: "Report Date\nToday",
default: "Report Date",
},
{
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index bb07fee6c66..a6cdb823cac 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -54,8 +54,7 @@ class ReceivablePayableReport:
self.filters.report_date = getdate(self.filters.report_date or nowdate())
self.age_as_on = (
getdate(nowdate())
- if "calculate_ageing_with" not in self.filters
- or self.filters.calculate_ageing_with == "Today Date"
+ if "age_as_on" not in self.filters or self.filters.age_as_on == "Today"
else self.filters.report_date
)
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
index e71638a59e4..22a5182a41c 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
@@ -12,7 +12,7 @@ frappe.query_reports["Accounts Receivable Summary"] = {
},
{
fieldname: "report_date",
- label: __("Posting Date"),
+ label: __("Report Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
},
@@ -24,10 +24,10 @@ frappe.query_reports["Accounts Receivable Summary"] = {
default: "Due Date",
},
{
- fieldname: "calculate_ageing_with",
- label: __("Calculate Ageing With"),
+ fieldname: "age_as_on",
+ label: __("Age as on"),
fieldtype: "Select",
- options: "Report Date\nToday Date",
+ options: "Report Date\nToday",
default: "Report Date",
},
{
diff --git a/erpnext/accounts/services/child_item_update.py b/erpnext/accounts/services/child_item_update.py
index 99b6b186116..ae7f8109f74 100644
--- a/erpnext/accounts/services/child_item_update.py
+++ b/erpnext/accounts/services/child_item_update.py
@@ -314,7 +314,7 @@ class ChildItemUpdater:
@frappe.whitelist()
def update_child_qty_rate(
- parent_doctype: str, trans_items: str, parent_doctype_name: str, child_docname: str = "items"
+ parent_doctype: str, trans_items: str | list, parent_doctype_name: str, child_docname: str = "items"
) -> None:
ChildItemUpdater(parent_doctype, parent_doctype_name, child_docname).update(trans_items)
@@ -432,6 +432,7 @@ def validate_and_delete_children(parent, data, ordered_item=None) -> bool:
for d in deleted_children:
validate_child_on_delete(d, parent, ordered_item)
+ d.flags.ignore_permissions = True
d.cancel()
d.delete()
diff --git a/erpnext/accounts/services/deferred_accounting.py b/erpnext/accounts/services/deferred_accounting.py
index 8465d079955..55a9ac44f47 100644
--- a/erpnext/accounts/services/deferred_accounting.py
+++ b/erpnext/accounts/services/deferred_accounting.py
@@ -55,3 +55,16 @@ class DeferredAccountingService:
def _is_deferred(self, item) -> bool:
return bool(item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"))
+
+ def clear_stale_deferred_fields(self) -> None:
+ account_field = DEFERRED_ACCOUNT_FIELD.get(self.doc.doctype)
+
+ for item in self.doc.get("items"):
+ if self._is_deferred(item):
+ continue
+
+ item.service_start_date = None
+ item.service_end_date = None
+ item.service_stop_date = None
+ if account_field:
+ item.set(account_field, None)
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 5df9f368c2a..0f8566e3a28 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -1203,7 +1203,7 @@ def get_values_from_purchase_doc(
return {
"company": purchase_doc.company,
"purchase_date": purchase_doc.get("posting_date"),
- "net_purchase_amount": flt(first_item.base_net_amount),
+ "net_purchase_amount": flt(first_item.valuation_rate) * flt(first_item.qty),
"asset_quantity": first_item.qty,
"cost_center": first_item.cost_center or purchase_doc.get("cost_center"),
"asset_location": first_item.get("asset_location"),
diff --git a/erpnext/buying/doctype/request_for_quotation/mapper.py b/erpnext/buying/doctype/request_for_quotation/mapper.py
index 71015e16058..643f4824fe7 100644
--- a/erpnext/buying/doctype/request_for_quotation/mapper.py
+++ b/erpnext/buying/doctype/request_for_quotation/mapper.py
@@ -64,27 +64,24 @@ def create_supplier_quotation(doc: str | Document | dict):
):
frappe.throw(_("Not Permitted"), frappe.PermissionError)
- try:
- sq_doc = frappe.get_doc(
- {
- "doctype": "Supplier Quotation",
- "supplier": doc.get("supplier"),
- "terms": doc.get("terms"),
- "company": doc.get("company"),
- "currency": doc.get("currency")
- or get_party_account_currency("Supplier", doc.get("supplier"), doc.get("company")),
- "buying_price_list": doc.get("buying_price_list")
- or frappe.db.get_single_value("Buying Settings", "buying_price_list"),
- }
- )
- add_items(sq_doc, doc.get("supplier"), doc.get("items"))
- sq_doc.flags.ignore_permissions = True
- sq_doc.run_method("set_missing_values")
- sq_doc.save()
- frappe.msgprint(_("Supplier Quotation {0} Created").format(sq_doc.name))
- return sq_doc.name
- except Exception:
- return None
+ sq_doc = frappe.get_doc(
+ {
+ "doctype": "Supplier Quotation",
+ "supplier": doc.get("supplier"),
+ "terms": doc.get("terms"),
+ "company": doc.get("company"),
+ "currency": doc.get("currency")
+ or get_party_account_currency("Supplier", doc.get("supplier"), doc.get("company")),
+ "buying_price_list": doc.get("buying_price_list")
+ or frappe.db.get_single_value("Buying Settings", "buying_price_list"),
+ }
+ )
+ add_items(sq_doc, doc.get("supplier"), doc.get("items"))
+ sq_doc.flags.ignore_permissions = True
+ sq_doc.run_method("set_missing_values")
+ sq_doc.save()
+ frappe.msgprint(_("Supplier Quotation {0} Created").format(sq_doc.name))
+ return sq_doc.name
def add_items(sq_doc, supplier, items):
diff --git a/erpnext/buying/print_format/purchase_order_bordered/__init__.py b/erpnext/buying/print_format/purchase_order_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/purchase_order_bordered/purchase_order_bordered.json b/erpnext/buying/print_format/purchase_order_bordered/purchase_order_bordered.json
new file mode 100644
index 00000000000..9dbc152d0a8
--- /dev/null
+++ b/erpnext/buying/print_format/purchase_order_bordered/purchase_order_bordered.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:24:39.319219",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Supplier Name\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Purchase Order\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Company:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"billing_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu_zBGrfDlm\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Supplier:
\\n
{{ doc.supplier }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Order Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":9},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":11}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[]},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":65,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"f\",\"v\":\"tax_amount\"}],\"align\":\"right\",\"width\":35,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1.5px solid #e5e7eb;margin-top:6px;padding-top:10px;font-weight:700;\",\"label_color\":\"#1f2328\"}]}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":40,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0},\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":5,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:25:39.090257",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/buying/print_format/purchase_order_classic/__init__.py b/erpnext/buying/print_format/purchase_order_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/purchase_order_classic/purchase_order_classic.json b/erpnext/buying/print_format/purchase_order_classic/purchase_order_classic.json
new file mode 100644
index 00000000000..9223627ca62
--- /dev/null
+++ b/erpnext/buying/print_format/purchase_order_classic/purchase_order_classic.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:24:39.307778",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom\":1},{\"label\":\"Supplier Name\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"Purchase Order\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Order Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":47},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":50}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":24,\"margin\":{\"top\":10,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\"}]}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-24 16:25:39.227835",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/buying/print_format/purchase_order_modern/__init__.py b/erpnext/buying/print_format/purchase_order_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/purchase_order_modern/purchase_order_modern.json b/erpnext/buying/print_format/purchase_order_modern/purchase_order_modern.json
new file mode 100644
index 00000000000..edbb80a4221
--- /dev/null
+++ b/erpnext/buying/print_format/purchase_order_modern/purchase_order_modern.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:24:39.333867",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Purchase Order\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Supplier\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nOn Hold\\nTo Receive and Bill\\nTo Bill\\nTo Receive\\nCompleted\\nCancelled\\nClosed\\nDelivered\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Order Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":14},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":45},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\",\"custom_style\":\"\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":49}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:25:39.254992",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/buying/print_format/purchase_order_modern_with_images/__init__.py b/erpnext/buying/print_format/purchase_order_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/purchase_order_modern_with_images/purchase_order_modern_with_images.json b/erpnext/buying/print_format/purchase_order_modern_with_images/purchase_order_modern_with_images.json
new file mode 100644
index 00000000000..f0b07f5134e
--- /dev/null
+++ b/erpnext/buying/print_format/purchase_order_modern_with_images/purchase_order_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:24:39.145685",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Purchase Order\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Supplier Name\",\"fieldname\":\"supplier_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"},{\"label\":\"Supplier\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\",\"align\":\"left\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nOn Hold\\nTo Receive and Bill\\nTo Bill\\nTo Receive\\nCompleted\\nCancelled\\nClosed\\nDelivered\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Purchase Order Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":51},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":0,\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":70,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"color\":\"#1f2328\",\"width\":30}],\"custom_style\":\"\",\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"border-top: 1px solid #e5e7eb;\\nmargin-top:5px;\\nfont-weight: bold;\\npadding-top:9px\\n\"}],\"width\":49}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words:\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"label_gap\":2,\"custom_style\":\"\"}]}],\"field_orientation\":\"left-right\",\"background\":\"#f8f8f8\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:25:39.279269",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/buying/print_format/request_for_quotation_bordered/__init__.py b/erpnext/buying/print_format/request_for_quotation_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/request_for_quotation_bordered/request_for_quotation_bordered.json b/erpnext/buying/print_format/request_for_quotation_bordered/request_for_quotation_bordered.json
new file mode 100644
index 00000000000..daa11250718
--- /dev/null
+++ b/erpnext/buying/print_format/request_for_quotation_bordered/request_for_quotation_bordered.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:15:36.456156",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Request for Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Supplier\",\"fieldname\":\"vendor\",\"fieldtype\":\"Link\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Request for Quotation\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Company:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"billing_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Request for Quotation Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 17:25:22.878108",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Request for Quotation Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/buying/print_format/request_for_quotation_classic/__init__.py b/erpnext/buying/print_format/request_for_quotation_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/request_for_quotation_classic/request_for_quotation_classic.json b/erpnext/buying/print_format/request_for_quotation_classic/request_for_quotation_classic.json
new file mode 100644
index 00000000000..6433f4b275c
--- /dev/null
+++ b/erpnext/buying/print_format/request_for_quotation_classic/request_for_quotation_classic.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:15:36.313234",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Request for Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom\":1},{\"label\":\"Supplier\",\"fieldname\":\"vendor\",\"fieldtype\":\"Link\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"Request for Quotation\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Request for Quotation Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-24 17:19:05.063875",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Request for Quotation Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/buying/print_format/request_for_quotation_modern/__init__.py b/erpnext/buying/print_format/request_for_quotation_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/request_for_quotation_modern/request_for_quotation_modern.json b/erpnext/buying/print_format/request_for_quotation_modern/request_for_quotation_modern.json
new file mode 100644
index 00000000000..75290d31f64
--- /dev/null
+++ b/erpnext/buying/print_format/request_for_quotation_modern/request_for_quotation_modern.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:15:36.444078",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Request for Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Request for Quotation\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Supplier\",\"fieldname\":\"vendor\",\"fieldtype\":\"Link\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nSubmitted\\nCancelled\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Request for Quotation Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 17:19:05.091173",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Request for Quotation Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/buying/print_format/request_for_quotation_modern_with_images/__init__.py b/erpnext/buying/print_format/request_for_quotation_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/print_format/request_for_quotation_modern_with_images/request_for_quotation_modern_with_images.json b/erpnext/buying/print_format/request_for_quotation_modern_with_images/request_for_quotation_modern_with_images.json
new file mode 100644
index 00000000000..00868f9e963
--- /dev/null
+++ b/erpnext/buying/print_format/request_for_quotation_modern_with_images/request_for_quotation_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:15:36.431075",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Request for Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Request for Quotation\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Supplier\",\"fieldname\":\"vendor\",\"fieldtype\":\"Link\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Required By\",\"fieldname\":\"schedule_date\",\"fieldtype\":\"Date\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nSubmitted\\nCancelled\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Request for Quotation Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 17:19:05.124407",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Request for Quotation Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 56e6e381bb5..70c5f3e77fe 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -236,7 +236,9 @@ class AccountsController(TransactionBase):
else:
from erpnext.accounts.services.deferred_accounting import DeferredAccountingService
- DeferredAccountingService(self).validate_start_and_end_date()
+ deferred_service = DeferredAccountingService(self)
+ deferred_service.clear_stale_deferred_fields()
+ deferred_service.validate_start_and_end_date()
from erpnext.accounts.services.internal_transfer import InternalTransferService
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 842114f6512..3605d60a0fc 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -338,9 +338,6 @@ class BuyingController(SubcontractingController):
if not details.get(field):
details[field] = frappe.get_cached_value("Company", self.company, field)
- if not any(details.get(field) for field in fields):
- return None
-
for field in fields:
if not details.get(field):
frappe.throw(
@@ -358,12 +355,26 @@ class BuyingController(SubcontractingController):
if self.doctype == "Purchase Invoice" and not self.update_stock:
return
+ stock_items = self.get_stock_items()
+
for row in self.items:
+ # A service item holds no stock value, so there is nothing to book against it - and it
+ # must not make the expense accounts mandatory either.
+ if row.item_code not in stock_items:
+ continue
+
details = self.get_validated_purchase_expense_details(row.item_code)
if not details:
continue
amount = flt(row.valuation_rate * row.stock_qty, row.precision("base_amount"))
+ if row.landed_cost_voucher_amount:
+ amount -= flt(row.landed_cost_voucher_amount, row.precision("base_amount"))
+
+ if not amount:
+ # GL Entry rejects a row with neither a debit nor a credit.
+ continue
+
self.add_gl_entry(
gl_entries=gl_entries,
account=details.purchase_expense_account,
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 6528c2cb23c..9c6dc4d9ce5 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -911,8 +911,8 @@ class SellingController(StockController):
if self.get("is_return"):
return
- sample_retention_warehouse = frappe.db.get_single_value(
- "Stock Settings", "sample_retention_warehouse"
+ sample_retention_warehouse = frappe.get_cached_value(
+ "Company", self.company, "sample_retention_warehouse"
)
if not sample_retention_warehouse:
return
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index a2afea8c8ec..968bb1fc7b5 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -307,33 +307,32 @@ class calculate_taxes_and_totals:
for item in self.doc.items:
item._unrounded_net_amount = None
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
- cumulated_tax_fraction = 0
- total_inclusive_tax_amount_per_qty = 0
+ total_tax_slope = 0
+ total_tax_intercept = 0
for i, tax in enumerate(self.doc.get("taxes")):
(
tax.tax_fraction_for_current_item,
- inclusive_tax_amount_per_qty,
- ) = self.get_current_tax_fraction(tax, item_tax_map)
+ tax_intercept_per_qty,
+ ) = self.get_current_tax_fraction(tax, item_tax_map, item)
+ tax.inclusive_amount_per_qty = tax_intercept_per_qty
if i == 0:
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
+ tax.grand_total_amount_per_qty = tax_intercept_per_qty
else:
+ prev = self.doc.get("taxes")[i - 1]
tax.grand_total_fraction_for_current_item = (
- self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
- + tax.tax_fraction_for_current_item
+ prev.grand_total_fraction_for_current_item + tax.tax_fraction_for_current_item
)
+ tax.grand_total_amount_per_qty = prev.grand_total_amount_per_qty + tax_intercept_per_qty
- cumulated_tax_fraction += tax.tax_fraction_for_current_item
- total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
+ total_tax_slope += tax.tax_fraction_for_current_item
+ total_tax_intercept += tax_intercept_per_qty * flt(item.qty)
- if (
- not self.discount_amount_applied
- and item.qty
- and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
- ):
- amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
+ if not self.discount_amount_applied and item.qty and (total_tax_slope or total_tax_intercept):
+ amount = flt(item.amount) - total_tax_intercept
- item._unrounded_net_amount = amount / (1 + cumulated_tax_fraction)
+ item._unrounded_net_amount = amount / (1 + total_tax_slope)
item.net_amount = flt(item._unrounded_net_amount, item.precision("net_amount"))
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
item.discount_percentage = flt(
@@ -345,41 +344,48 @@ class calculate_taxes_and_totals:
def _load_item_tax_rate(self, item_tax_rate):
return frappe.parse_json(item_tax_rate) if item_tax_rate else {}
- def get_current_tax_fraction(self, tax, item_tax_map):
+ def get_current_tax_fraction(self, tax, item_tax_map, item):
"""
- Get tax fraction for calculating tax exclusive amount
- from tax inclusive amount
+ tax = slope * net + intercept.
+ Returns (slope, intercept_per_qty)
"""
- current_tax_fraction = 0
- inclusive_tax_amount_per_qty = 0
+ tax_slope = 0
+ tax_intercept = 0
if cint(tax.included_in_print_rate):
tax_rate = self._get_tax_rate(tax, item_tax_map)
if tax_rate == NOT_APPLICABLE_TAX:
- return current_tax_fraction, inclusive_tax_amount_per_qty
+ return tax_slope, tax_intercept
if tax.charge_type == "On Net Total":
- current_tax_fraction = tax_rate / 100.0
+ tax_slope = tax_rate / 100.0
elif tax.charge_type == "On Previous Row Amount":
- current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
- cint(tax.row_id) - 1
- ].tax_fraction_for_current_item
+ row = self.doc.get("taxes")[cint(tax.row_id) - 1]
+ tax_slope = (tax_rate / 100.0) * row.tax_fraction_for_current_item
+ tax_intercept = (tax_rate / 100.0) * flt(getattr(row, "inclusive_amount_per_qty", 0))
elif tax.charge_type == "On Previous Row Total":
- current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
- cint(tax.row_id) - 1
- ].grand_total_fraction_for_current_item
+ row = self.doc.get("taxes")[cint(tax.row_id) - 1]
+ tax_slope = (tax_rate / 100.0) * row.grand_total_fraction_for_current_item
+ tax_intercept = (tax_rate / 100.0) * flt(getattr(row, "grand_total_amount_per_qty", 0))
elif tax.charge_type == "On Item Quantity":
- inclusive_tax_amount_per_qty = flt(tax_rate)
+ tax_intercept = flt(tax_rate)
+
+ else:
+ # Custom charge_type: the rate applies to a resolved (fixed) base,
+ # e.g. a tax on MRP included in the printed price.
+ qty = flt(item.qty) or 1
+ base = self.get_item_taxable_base(item, tax)
+ tax_intercept = (tax_rate / 100.0) * base / qty
if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
- current_tax_fraction *= -1.0
- inclusive_tax_amount_per_qty *= -1.0
+ tax_slope *= -1.0
+ tax_intercept *= -1.0
- return current_tax_fraction, inclusive_tax_amount_per_qty
+ return tax_slope, tax_intercept
def _get_tax_rate(self, tax, item_tax_map):
if tax.account_head in item_tax_map:
@@ -605,7 +611,6 @@ class calculate_taxes_and_totals:
elif tax.charge_type == "On Net Total":
if tax.account_head in item_tax_map:
current_net_amount = item.net_amount
-
# Use unrounded net for inclusive taxes to avoid double rounding
if (
cint(tax.included_in_print_rate)
@@ -624,12 +629,46 @@ class calculate_taxes_and_totals:
elif tax.charge_type == "On Item Quantity":
# don't sum current net amount due to the field being a currency field
current_tax_amount = tax_rate * item.qty
+ else:
+ # Custom charge_type: rate applies to the resolver-provided base.
+ base = self.get_item_taxable_base(item, tax)
+ current_net_amount = base
+ current_tax_amount = (tax_rate / 100.0) * base
if not tax.get("dont_recompute_tax"):
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount, current_net_amount)
return current_net_amount, current_tax_amount
+ def get_item_taxable_base(self, item, tax):
+ """Per-item base a custom charge_type's rate is applied to.
+
+ Override the base (gross, MRP, net of other taxes, …) via the
+ `erpnext_taxable_base_resolvers` hook
+
+ Register a resolver in `hooks.py`, keyed by charge_type:
+
+ erpnext_taxable_base_resolvers = {"On Gross Amount": "my_app.taxes.gross_base"}
+
+ It receives (calc, item, tax) — calc is this instance, calc.doc the parent —
+ and returns the base (flt-coerced by the caller):
+
+ def gross_base(calc, item, tax):
+ return item.custom_field_mrp * item.qty
+
+ A resolver may stamp transient attributes on `item`; it can be called more than once
+ per item, so such stamping must be idempotent.
+ """
+ resolvers = frappe.get_hooks("erpnext_taxable_base_resolvers") or {}
+ path = resolvers.get(tax.charge_type)
+
+ if path:
+ method = path[-1] if isinstance(path, list | tuple) else path
+ return flt(frappe.get_attr(method)(self, item, tax))
+
+ # fallback
+ return flt(item.net_amount)
+
def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount, current_net_amount):
# store tax breakup for each item
multiplier = -1 if tax.get("add_deduct_tax") == "Deduct" else 1
diff --git a/erpnext/controllers/tests/test_taxes_and_totals.py b/erpnext/controllers/tests/test_taxes_and_totals.py
index 54067c4ce22..90eba36bcd7 100644
--- a/erpnext/controllers/tests/test_taxes_and_totals.py
+++ b/erpnext/controllers/tests/test_taxes_and_totals.py
@@ -1,12 +1,24 @@
+from unittest import mock
from unittest.mock import patch
import frappe
+from frappe.utils import flt
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.tests.utils import ERPNextTestSuite
+def resolve_on_gross(calc, item, tax):
+ # base = gross printed line amount
+ return flt(item.amount)
+
+
+def resolve_on_mrp(calc, item, tax):
+ # base = MRP, not net
+ return flt(item.price_list_rate) * flt(item.qty)
+
+
class TestTaxesAndTotals(ERPNextTestSuite):
def test_regional_round_off_accounts(self):
"""
@@ -30,6 +42,93 @@ class TestTaxesAndTotals(ERPNextTestSuite):
self.assertIn(test_account, frappe.flags.round_off_applicable_accounts)
+ def test_exclusive_custom_charge_on_resolved_base(self):
+ """Added (exclusive) custom charge_type whose base is resolved by the
+ `erpnext_taxable_base_resolvers` hook. IPI 10% on the gross product value 1000
+ -> tax 100, net 1000, grand 1100."""
+ so = make_sales_order(do_not_save=True)
+ so.items = []
+ so.append(
+ "items",
+ {
+ "item_code": "_Test Item",
+ "qty": 1,
+ "rate": 1000,
+ "price_list_rate": 1000,
+ "warehouse": "_Test Warehouse - _TC",
+ },
+ )
+ so.set("taxes", [])
+ so.append(
+ "taxes",
+ {
+ "charge_type": "On Gross Value",
+ "account_head": "_Test Account Excise Duty - _TC",
+ "description": "IPI 10% on gross product value",
+ "rate": 10,
+ "cost_center": "_Test Cost Center - _TC",
+ },
+ )
+
+ real_get_hooks = frappe.get_hooks
+
+ def fake_get_hooks(hook=None, *args, **kwargs):
+ if hook == "erpnext_taxable_base_resolvers":
+ return {
+ "On Gross Value": ["erpnext.controllers.tests.test_taxes_and_totals.resolve_on_gross"]
+ }
+ return real_get_hooks(hook, *args, **kwargs)
+
+ with mock.patch("frappe.get_hooks", side_effect=fake_get_hooks):
+ calculate_taxes_and_totals(so)
+
+ self.assertEqual(so.net_total, 1000.0)
+ self.assertEqual(so.taxes[0].tax_amount, 100.0)
+ self.assertEqual(so.grand_total, 1100.0)
+
+ def test_inclusive_custom_charge_on_resolved_base(self):
+ """Inclusive custom charge on a resolved base backs out non-compounding
+ (tax = rate x resolved base) — a resolved base is fixed, so it never
+ compounds. MRP 1200, printed 1000, rate 10%: tax 120, net 880."""
+ so = make_sales_order(do_not_save=True)
+ so.items = []
+ so.append(
+ "items",
+ {
+ "item_code": "_Test Item",
+ "qty": 1,
+ "rate": 1000,
+ "price_list_rate": 1200,
+ "warehouse": "_Test Warehouse - _TC",
+ },
+ )
+ so.set("taxes", [])
+ so.append(
+ "taxes",
+ {
+ "charge_type": "On MRP",
+ "account_head": "_Test Account VAT - _TC",
+ "description": "Tax 10% on MRP, inclusive",
+ "rate": 10,
+ "included_in_print_rate": 1,
+ "cost_center": "_Test Cost Center - _TC",
+ },
+ )
+
+ real_get_hooks = frappe.get_hooks
+
+ def fake_get_hooks(hook=None, *args, **kwargs):
+ if hook == "erpnext_taxable_base_resolvers":
+ return {"On MRP": ["erpnext.controllers.tests.test_taxes_and_totals.resolve_on_mrp"]}
+ return real_get_hooks(hook, *args, **kwargs)
+
+ with mock.patch("frappe.get_hooks", side_effect=fake_get_hooks):
+ calculate_taxes_and_totals(so)
+
+ self.assertEqual(so.taxes[0].tax_amount, 120.0)
+ self.assertEqual(so.net_total, 880.0)
+ self.assertEqual(so.grand_total, 1000.0)
+
def test_disabling_rounded_total_resets_base_fields(self):
"""Disabling rounded total should also clear base rounded values."""
so = make_sales_order(do_not_save=True)
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index 93d35a7facf..91ad6018ae8 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -280,13 +280,17 @@ class Opportunity(TransactionBase, CRMNote):
self.save()
else:
- frappe.throw(_("Cannot declare as lost, because Quotation has been made."))
+ frappe.throw(_("Cannot declare as Lost because an active Quotation exists."))
def has_active_quotation(self):
if not self.get("items", []):
return frappe.get_all(
"Quotation",
- {"opportunity": self.name, "status": ("not in", ["Lost", "Closed"]), "docstatus": 1},
+ {
+ "opportunity": self.name,
+ "status": ("not in", ["Lost", "Cancelled", "Expired"]),
+ "docstatus": 1,
+ },
"name",
)
else:
@@ -300,7 +304,7 @@ class Opportunity(TransactionBase, CRMNote):
.where(
(q.docstatus == 1)
& (qi.prevdoc_docname == self.name)
- & q.status.notin(["Lost", "Closed"])
+ & q.status.notin(["Lost", "Cancelled", "Expired"])
)
.run()
)
@@ -308,7 +312,13 @@ class Opportunity(TransactionBase, CRMNote):
def has_ordered_quotation(self):
if not self.get("items", []):
return frappe.get_all(
- "Quotation", {"opportunity": self.name, "status": "Ordered", "docstatus": 1}, "name"
+ "Quotation",
+ {
+ "opportunity": self.name,
+ "status": ("in", ["Ordered", "Partially Ordered"]),
+ "docstatus": 1,
+ },
+ "name",
)
else:
q = frappe.qb.DocType("Quotation")
@@ -318,7 +328,11 @@ class Opportunity(TransactionBase, CRMNote):
.inner_join(qi)
.on(q.name == qi.parent)
.select(q.name)
- .where((q.docstatus == 1) & (qi.prevdoc_docname == self.name) & (q.status == "Ordered"))
+ .where(
+ (q.docstatus == 1)
+ & (qi.prevdoc_docname == self.name)
+ & (q.status.isin(["Ordered", "Partially Ordered"]))
+ )
.run()
)
diff --git a/erpnext/locale/main.pot b/erpnext/locale/main.pot
index 682b81eb014..fb3a147fe59 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-07-19 10:04+0000\n"
-"PO-Revision-Date: 2026-07-19 10:04+0000\n"
+"POT-Creation-Date: 2026-07-26 10:12+0000\n"
+"PO-Revision-Date: 2026-07-26 10:12+0000\n"
"Last-Translator: hello@frappe.io\n"
"Language-Team: hello@frappe.io\n"
"MIME-Version: 1.0\n"
@@ -84,15 +84,15 @@ msgstr ""
msgid " Summary"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:286
+#: erpnext/stock/doctype/item/item.py:284
msgid "\"Customer Provided Item\" cannot be Purchase Item also"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:288
+#: erpnext/stock/doctype/item/item.py:286
msgid "\"Customer Provided Item\" cannot have Valuation Rate"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:390
+#: erpnext/stock/doctype/item/item.py:388
msgid "\"Is Fixed Asset\" cannot be unchecked, as Asset record exists against the item"
msgstr ""
@@ -100,6 +100,10 @@ msgstr ""
msgid "\"SN-01::10\" for \"SN-01\" to \"SN-10\""
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:764
+msgid "\"SN-01::10\" for \"SN-01\" to \"SN-10\". Missing Serial Nos will be created on Save"
+msgstr ""
+
#: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:157
msgid "# In Stock"
msgstr ""
@@ -134,6 +138,10 @@ msgstr ""
msgid "% Complete Method"
msgstr ""
+#: erpnext/projects/doctype/project/project.py:282
+msgid "% Complete must be between 0 and 100"
+msgstr ""
+
#. Label of the percent_complete (Percent) field in DocType 'Project'
#: erpnext/projects/doctype/project/project.json
msgid "% Completed"
@@ -282,7 +290,7 @@ msgid "'Entries' cannot be empty"
msgstr ""
#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:24
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:132
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:127
#: erpnext/stock/report/stock_analytics/stock_analytics.py:322
msgid "'From Date' is required"
msgstr ""
@@ -291,7 +299,7 @@ msgstr ""
msgid "'From Date' must be after 'To Date'"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:473
+#: erpnext/stock/doctype/item/item.py:471
msgid "'Has Serial No' cannot be 'Yes' for non-stock item"
msgstr ""
@@ -310,7 +318,7 @@ msgid "'Opening'"
msgstr ""
#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:27
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:134
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:129
#: erpnext/stock/report/stock_analytics/stock_analytics.py:328
msgid "'To Date' is required"
msgstr ""
@@ -327,6 +335,10 @@ msgstr ""
msgid "'Update Stock' cannot be checked for fixed asset sale"
msgstr ""
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:112
+msgid "'Verification Link Expiry Duration' must be between 15 to 60 minutes."
+msgstr ""
+
#: erpnext/accounts/doctype/bank_account/bank_account.py:79
msgid "'{0}' account is already used by {1}. Use another account."
msgstr ""
@@ -621,8 +633,8 @@ msgstr ""
msgid "90 Above"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1294
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1295
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1289
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1290
msgid "<0"
msgstr ""
@@ -630,7 +642,7 @@ msgstr ""
msgid "Cannot create asset.
You're trying to create {0} asset(s) from {2} {3}.
However, only {1} item(s) were purchased and {4} asset(s) already exist against {5}."
msgstr ""
-#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:59
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:69
msgid "From Time cannot be later than To Time for {0}"
msgstr ""
@@ -953,11 +965,11 @@ msgstr ""
msgid "Your Shortcuts"
msgstr ""
-#: erpnext/accounts/doctype/payment_request/payment_request.py:1301
+#: erpnext/accounts/doctype/payment_request/payment_request.py:1302
msgid "Grand Total: {0}"
msgstr ""
-#: erpnext/accounts/doctype/payment_request/payment_request.py:1302
+#: erpnext/accounts/doctype/payment_request/payment_request.py:1303
msgid "Outstanding Amount: {0}"
msgstr ""
@@ -1012,7 +1024,7 @@ msgstr ""
msgid "A - C"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:372
+#: erpnext/selling/doctype/customer/customer.py:370
msgid "A Customer Group exists with the same name. Please change the Customer name or rename the Customer Group"
msgstr ""
@@ -1042,6 +1054,10 @@ msgstr ""
msgid "A Product or a Service that is bought, sold or kept in stock."
msgstr ""
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:156
+msgid "A Proforma Invoice can only be created against a submitted Sales Order."
+msgstr ""
+
#: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py:603
msgid "A Reconciliation Job {0} is running for the same filters. Cannot reconcile now"
msgstr ""
@@ -1050,6 +1066,10 @@ msgstr ""
msgid "A Reverse Journal Entry {0} already exists for this Journal Entry."
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:306
+msgid "A cancelled Proforma Invoice cannot be emailed."
+msgstr ""
+
#. Description of a DocType
#: erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.json
msgid "A condition for a Shipping Rule"
@@ -1066,6 +1086,10 @@ msgstr ""
msgid "A disabled Product Bundle cannot be selected in transactions."
msgstr ""
+#: erpnext/public/js/utils/draft_link_guard.js:49
+msgid "A draft {0} already exists for this {1}: {2}. Do you still want to create a new one?"
+msgstr ""
+
#: erpnext/stock/doctype/delivery_trip/delivery_trip.py:59
msgid "A driver must be set to submit."
msgstr ""
@@ -1116,6 +1140,10 @@ msgstr ""
msgid "A third party distributor / dealer / commission agent / affiliate / reseller who sells the companies products for a commission."
msgstr ""
+#: erpnext/crm/doctype/appointment/appointment.py:70
+msgid "A verified appointment cannot be moved back to 'Unverified' status."
+msgstr ""
+
#. Option for the 'Blood Group' (Select) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json
msgid "A+"
@@ -1205,7 +1233,7 @@ msgstr ""
msgid "Abbreviation: {0} must appear only once"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1291
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1286
msgid "Above"
msgstr ""
@@ -1263,7 +1291,7 @@ msgid "Accepted Qty in Stock UOM"
msgstr ""
#. Label of the qty (Float) field in DocType 'Purchase Receipt Item'
-#: erpnext/public/js/controllers/transaction.js:2963
+#: erpnext/public/js/controllers/transaction.js:2955
#: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
msgid "Accepted Quantity"
msgstr ""
@@ -1443,7 +1471,7 @@ msgstr ""
msgid "Account Name"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:377
+#: erpnext/accounts/doctype/account/account.py:408
msgid "Account Not Found"
msgstr ""
@@ -1456,7 +1484,7 @@ msgstr ""
msgid "Account Number"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:363
+#: erpnext/accounts/doctype/account/account.py:394
msgid "Account Number {0} already used in account {1}"
msgstr ""
@@ -1495,7 +1523,7 @@ msgstr ""
#. Label of the account_type (Select) field in DocType 'Payment Ledger Entry'
#. Label of the account_type (Select) field in DocType 'Party Type'
#: erpnext/accounts/doctype/account/account.json
-#: erpnext/accounts/doctype/account/account.py:210
+#: erpnext/accounts/doctype/account/account.py:211
#: erpnext/accounts/doctype/account/account_tree.js:154
#: erpnext/accounts/doctype/bank_account/bank_account.json
#: erpnext/accounts/doctype/bank_account_type/bank_account_type.json
@@ -1511,11 +1539,11 @@ msgstr ""
msgid "Account Value"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:332
+#: erpnext/accounts/doctype/account/account.py:363
msgid "Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:326
+#: erpnext/accounts/doctype/account/account.py:357
msgid "Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"
msgstr ""
@@ -1585,24 +1613,24 @@ msgstr ""
msgid "Account where the cost of this item will be debited on purchase"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:431
+#: erpnext/accounts/doctype/account/account.py:462
msgid "Account with child nodes cannot be converted to ledger"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:283
+#: erpnext/accounts/doctype/account/account.py:314
msgid "Account with child nodes cannot be set as ledger"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:442
+#: erpnext/accounts/doctype/account/account.py:473
msgid "Account with existing transaction can not be converted to group."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:467
+#: erpnext/accounts/doctype/account/account.py:498
msgid "Account with existing transaction can not be deleted"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:277
-#: erpnext/accounts/doctype/account/account.py:433
+#: erpnext/accounts/doctype/account/account.py:308
+#: erpnext/accounts/doctype/account/account.py:464
msgid "Account with existing transaction cannot be converted to ledger"
msgstr ""
@@ -1610,11 +1638,11 @@ msgstr ""
msgid "Account {0} added multiple times"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:295
+#: erpnext/accounts/doctype/account/account.py:326
msgid "Account {0} cannot be converted to Group as it is already set as {1} for {2}."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:292
+#: erpnext/accounts/doctype/account/account.py:323
msgid "Account {0} cannot be disabled as it is already set as {1} for {2}."
msgstr ""
@@ -1626,7 +1654,7 @@ msgstr ""
msgid "Account {0} does not belong to company: {1}"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:602
+#: erpnext/accounts/doctype/account/account.py:633
msgid "Account {0} does not exist"
msgstr ""
@@ -1642,11 +1670,11 @@ msgstr ""
msgid "Account {0} doesn't belong to Company {1}"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:557
+#: erpnext/accounts/doctype/account/account.py:588
msgid "Account {0} exists in parent company {1}."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:415
+#: erpnext/accounts/doctype/account/account.py:446
msgid "Account {0} is added in the child company {1}"
msgstr ""
@@ -1666,19 +1694,19 @@ msgstr ""
msgid "Account {0} should be of type Expense"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:153
+#: erpnext/accounts/doctype/account/account.py:154
msgid "Account {0}: Parent account {1} can not be a ledger"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:159
+#: erpnext/accounts/doctype/account/account.py:160
msgid "Account {0}: Parent account {1} does not belong to company: {2}"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:147
+#: erpnext/accounts/doctype/account/account.py:148
msgid "Account {0}: Parent account {1} does not exist"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:150
+#: erpnext/accounts/doctype/account/account.py:151
msgid "Account {0}: You can not assign itself as parent account"
msgstr ""
@@ -2022,7 +2050,7 @@ msgstr ""
#: erpnext/assets/doctype/asset/asset.js:198
#: erpnext/assets/doctype/asset_repair/asset_repair.js:101
#: erpnext/buying/doctype/supplier/supplier.js:132
-#: erpnext/public/js/controllers/stock_controller.js:88
+#: erpnext/public/js/controllers/stock_controller.js:118
#: erpnext/public/js/utils/ledger_preview.js:8
#: erpnext/selling/doctype/customer/customer.js:182
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:51
@@ -2082,7 +2110,7 @@ msgstr ""
#: erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
#: erpnext/assets/doctype/asset_category/asset_category.json
#: erpnext/setup/doctype/company/company.json
-#: erpnext/setup/doctype/company/company.py:515
+#: erpnext/setup/doctype/company/company.py:516
#: erpnext/setup/doctype/customer_group/customer_group.json
#: erpnext/setup/doctype/email_digest/email_digest.json
#: erpnext/setup/doctype/incoterm/incoterm.json
@@ -2121,7 +2149,7 @@ msgstr ""
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:261
#: erpnext/accounts/doctype/journal_entry/journal_entry.json
#: erpnext/accounts/report/accounts_payable/accounts_payable.json
-#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:126
+#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:129
#: erpnext/buying/doctype/supplier/supplier.js:144
#: erpnext/workspace_sidebar/financial_reports.json
#: erpnext/workspace_sidebar/invoicing.json
@@ -2135,7 +2163,7 @@ msgid "Accounts Payable Ageing"
msgstr ""
#. Name of a report
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:191
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:194
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.json
msgid "Accounts Payable Summary"
msgstr ""
@@ -2151,7 +2179,7 @@ msgstr ""
#: erpnext/accounts/doctype/journal_entry/journal_entry.json
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.json
-#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:149
+#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:152
#: erpnext/selling/doctype/customer/customer.js:171
#: erpnext/workspace_sidebar/financial_reports.json
#: erpnext/workspace_sidebar/invoicing.json
@@ -2189,7 +2217,7 @@ msgid "Accounts Receivable Discounted Account"
msgstr ""
#. Name of a report
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:204
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:207
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.json
msgid "Accounts Receivable Summary"
msgstr ""
@@ -2305,6 +2333,12 @@ msgstr ""
msgid "Action Initialised"
msgstr ""
+#. Label of the action_for_expired_unverified_appointments (Select) field in
+#. DocType 'Appointment Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "Action for Expired Unverified Appointments"
+msgstr ""
+
#. Label of the action_if_accumulated_monthly_budget_exceeded (Select) field in
#. DocType 'Budget'
#: erpnext/accounts/doctype/budget/budget.json
@@ -2563,8 +2597,9 @@ msgstr ""
#: erpnext/stock/doctype/bin/bin.json
#: erpnext/stock/doctype/material_request_item/material_request_item.json
#: erpnext/stock/doctype/packed_item/packed_item.json
+#: erpnext/stock/page/stock_balance/stock_balance.js:63
#: erpnext/stock/report/product_bundle_balance/product_bundle_balance.py:95
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:143
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:144
msgid "Actual Qty"
msgstr ""
@@ -2635,10 +2670,6 @@ msgstr ""
msgid "Actual Time in Hours (via Timesheet)"
msgstr ""
-#: erpnext/stock/page/stock_balance/stock_balance.js:55
-msgid "Actual qty in stock"
-msgstr ""
-
#: erpnext/accounts/doctype/payment_entry/payment_entry.js:1534
#: erpnext/public/js/controllers/accounts.js:194
msgid "Actual type tax cannot be included in Item rate in row {0}"
@@ -2903,7 +2934,7 @@ msgstr ""
msgid "Added On"
msgstr ""
-#: erpnext/buying/doctype/supplier/supplier.py:143
+#: erpnext/buying/doctype/supplier/supplier.py:142
msgid "Added Supplier Role to User {0}."
msgstr ""
@@ -3308,7 +3339,7 @@ msgstr ""
msgid "Address and Contacts"
msgstr ""
-#: erpnext/accounts/custom/address.py:33
+#: erpnext/accounts/custom/address.py:35
msgid "Address needs to be linked to a Company. Please add a row for Company in the Links table."
msgstr ""
@@ -3355,6 +3386,10 @@ msgstr ""
msgid "Advance Amount"
msgstr ""
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:93
+msgid "Advance Booking Days is mandatory for Appointment Scheduling."
+msgstr ""
+
#. Label of the advance_paid (Currency) field in DocType 'Sales Order'
#: erpnext/selling/doctype/sales_order/sales_order.json
msgid "Advance Paid"
@@ -3663,7 +3698,7 @@ msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:154
#: erpnext/accounts/report/accounts_payable/accounts_payable.html:138
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.html:139
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1225
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1220
msgid "Age (Days)"
msgstr ""
@@ -3716,12 +3751,6 @@ msgstr ""
msgid "Agent Busy Message"
msgstr ""
-#. Label of the agent_detail_section (Section Break) field in DocType
-#. 'Appointment Booking Settings'
-#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
-msgid "Agent Details"
-msgstr ""
-
#. Label of the agent_group (Link) field in DocType 'Incoming Call Handling
#. Schedule'
#: erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.json
@@ -3824,21 +3853,6 @@ msgstr ""
#: erpnext/patches/v11_0/update_department_lft_rgt.py:9
#: erpnext/patches/v11_0/update_department_lft_rgt.py:11
#: erpnext/patches/v11_0/update_department_lft_rgt.py:16
-#: erpnext/setup/doctype/company/company.py:508
-#: erpnext/setup/doctype/company/company.py:511
-#: erpnext/setup/doctype/company/company.py:516
-#: erpnext/setup/doctype/company/company.py:522
-#: erpnext/setup/doctype/company/company.py:528
-#: erpnext/setup/doctype/company/company.py:534
-#: erpnext/setup/doctype/company/company.py:540
-#: erpnext/setup/doctype/company/company.py:546
-#: erpnext/setup/doctype/company/company.py:552
-#: erpnext/setup/doctype/company/company.py:558
-#: erpnext/setup/doctype/company/company.py:564
-#: erpnext/setup/doctype/company/company.py:570
-#: erpnext/setup/doctype/company/company.py:576
-#: erpnext/setup/doctype/company/company.py:582
-#: erpnext/setup/doctype/company/company.py:588
msgid "All Departments"
msgstr ""
@@ -3847,8 +3861,6 @@ msgstr ""
msgid "All Employee (Active)"
msgstr ""
-#: erpnext/setup/doctype/item_group/item_group.py:35
-#: erpnext/setup/doctype/item_group/item_group.py:36
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:33
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:41
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:48
@@ -3957,7 +3969,7 @@ msgstr ""
msgid "All items have already been transferred for this Work Order."
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:3086
+#: erpnext/public/js/controllers/transaction.js:3078
msgid "All items in this document already have a linked Quality Inspection."
msgstr ""
@@ -3969,7 +3981,7 @@ msgstr ""
msgid "All linked Sales Orders must be subcontracted."
msgstr ""
-#: erpnext/stock/doctype/pick_list/mapper.py:309
+#: erpnext/stock/doctype/pick_list/mapper.py:314
msgid "All picked items have already been transferred against this Pick List"
msgstr ""
@@ -4109,7 +4121,7 @@ msgstr ""
#. Label of the allow_account_creation_against_child_company (Check) field in
#. DocType 'Company'
-#: erpnext/accounts/doctype/account/account.py:555
+#: erpnext/accounts/doctype/account/account.py:586
#: erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py:68
#: erpnext/setup/doctype/company/company.json
msgid "Allow Account Creation Against Child Company"
@@ -4191,8 +4203,8 @@ msgstr ""
#. Valuation'
#: erpnext/stock/doctype/item/item.json
#: erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:225
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:237
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:226
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:238
msgid "Allow Negative Stock"
msgstr ""
@@ -4373,6 +4385,12 @@ msgstr ""
msgid "Allow internal transfers at user-defined rate"
msgstr ""
+#. Description of the 'Enable Proforma Invoice' (Check) field in DocType
+#. 'Selling Settings'
+#: erpnext/selling/doctype/selling_settings/selling_settings.json
+msgid "Allow issuing Proforma Invoices against a Sales Order."
+msgstr ""
+
#. Description of the 'Allow Continuous Material Consumption' (Check) field in
#. DocType 'Manufacturing Settings'
#: erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -4512,7 +4530,7 @@ msgstr ""
msgid "Allowed Companies"
msgstr ""
-#: erpnext/stock/doctype/company_restriction/company_restriction.py:74
+#: erpnext/stock/doctype/company_restriction/company_restriction.py:106
msgid "Allowed Companies is required when Restrict to Companies is checked"
msgstr ""
@@ -4723,6 +4741,8 @@ msgstr ""
#. Label of the amount (Currency) field in DocType 'BOM Item'
#. Label of the amount (Currency) field in DocType 'Work Order Additional Item'
#. Label of the amount (Currency) field in DocType 'Work Order Item'
+#. Option for the 'Based On' (Select) field in DocType 'Proforma Invoice'
+#. Label of the amount (Currency) field in DocType 'Proforma Invoice Item'
#. Option for the 'Margin Type' (Select) field in DocType 'Quotation Item'
#. Label of the amount (Currency) field in DocType 'Quotation Item'
#. Option for the 'Margin Type' (Select) field in DocType 'Sales Order Item'
@@ -4825,7 +4845,10 @@ msgstr ""
#: erpnext/manufacturing/doctype/bom_item/bom_item.json
#: erpnext/manufacturing/doctype/work_order_additional_item/work_order_additional_item.json
#: erpnext/manufacturing/doctype/work_order_item/work_order_item.json
-#: erpnext/public/js/controllers/transaction.js:573
+#: erpnext/public/js/controllers/transaction.js:584
+#: erpnext/public/js/sales_order_proforma.js:142
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
#: erpnext/selling/doctype/quotation/quotation.js:315
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -5045,6 +5068,10 @@ msgstr ""
msgid "An Item Group is a way to classify items based on types."
msgstr ""
+#: erpnext/crm/doctype/appointment/appointment.py:74
+msgid "An appointment booked through the portal can only be opened via email verification."
+msgstr ""
+
#. Description of the 'Notify by email on creation of automatic Material
#. Request' (Check) field in DocType 'Stock Settings'
#: erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -5055,8 +5082,8 @@ msgstr ""
msgid "An error has been appeared while reposting item valuation via {0}"
msgstr ""
-#: erpnext/public/js/controllers/buying.js:378
-#: erpnext/public/js/utils/sales_common.js:493
+#: erpnext/public/js/controllers/buying.js:383
+#: erpnext/public/js/utils/sales_common.js:498
msgid "An error occurred during the update process"
msgstr ""
@@ -5117,7 +5144,7 @@ msgstr ""
msgid "Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}"
msgstr ""
-#: erpnext/accounts/doctype/payment_request/payment_request.py:1045
+#: erpnext/accounts/doctype/payment_request/payment_request.py:1046
msgid "Another Payment Request is already processed"
msgstr ""
@@ -5438,6 +5465,12 @@ msgstr ""
msgid "Appointment"
msgstr ""
+#. Label of the success_details (Section Break) field in DocType 'Appointment
+#. Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "Appointment Booking Portal Settings"
+msgstr ""
+
#. Name of a DocType
#. Label of a Workspace Sidebar Item
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
@@ -5450,10 +5483,14 @@ msgstr ""
msgid "Appointment Booking Slots"
msgstr ""
-#: erpnext/crm/doctype/appointment/appointment.py:95
+#: erpnext/crm/doctype/appointment/appointment.py:181
msgid "Appointment Confirmation"
msgstr ""
+#: erpnext/crm/doctype/appointment/appointment.py:189
+msgid "Appointment Confirmed"
+msgstr ""
+
#. Label of the appointment_details_section (Section Break) field in DocType
#. 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
@@ -5466,25 +5503,59 @@ msgstr ""
msgid "Appointment Duration (In Minutes)"
msgstr ""
-#: erpnext/www/book_appointment/index.py:23
-msgid "Appointment Scheduling Disabled"
+#. Label of the agent_detail_section (Section Break) field in DocType
+#. 'Appointment Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "Appointment Scheduling"
msgstr ""
#: erpnext/www/book_appointment/index.py:24
+msgid "Appointment Scheduling Disabled"
+msgstr ""
+
+#: erpnext/www/book_appointment/index.py:25
msgid "Appointment Scheduling has been disabled for this site"
msgstr ""
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:101
+msgid "Appointment Scheduling needs to be enabled for Appointment Booking through portal."
+msgstr ""
+
#. Label of the appointment_with (Link) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Appointment With"
msgstr ""
+#: erpnext/crm/doctype/appointment/appointment.py:86
+msgid "Appointment can only be scheduled up to {0} day(s) in advance."
+msgstr ""
+
+#: erpnext/crm/doctype/appointment/appointment.py:79
+msgid "Appointment cannot be scheduled for a past time."
+msgstr ""
+
+#: erpnext/crm/doctype/appointment/appointment.py:98
+msgid "Appointment cannot be scheduled on a holiday."
+msgstr ""
+
#: erpnext/www/book_appointment/index.js:237
msgid "Appointment created successfully"
msgstr ""
-#: erpnext/crm/doctype/appointment/appointment.py:101
-msgid "Appointment was created. But no lead was found. Please check the email to confirm"
+#: erpnext/www/book_appointment/verify/index.py:28
+msgid "Appointment has been closed. Please book the appointment again."
+msgstr ""
+
+#: erpnext/www/book_appointment/verify/index.py:33
+msgid "Appointment is already verified."
+msgstr ""
+
+#: erpnext/crm/doctype/appointment/appointment.py:116
+msgid "Appointment must be scheduled within the available slot timings."
+msgstr ""
+
+#: erpnext/crm/doctype/appointment/appointment.py:66
+msgid "Appointments created manually cannot have 'Unverified' status."
msgstr ""
#. Label of the approving_role (Link) field in DocType 'Authorization Rule'
@@ -5611,7 +5682,7 @@ msgstr ""
msgid "As the field {0} is enabled, the value of the field {1} should be more than 1."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1127
+#: erpnext/stock/doctype/item/item.py:1125
msgid "As there are existing submitted transactions against item {0}, you can not change the value of {1}."
msgstr ""
@@ -5623,12 +5694,12 @@ msgstr ""
msgid "As there are sufficient raw materials, Material Request is not required for Warehouse {0}."
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:250
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:251
msgid "As there is reserved stock, you cannot disable {0}."
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:224
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:236
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:225
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:237
msgid "As {0} is enabled, you can not enable {1}."
msgstr ""
@@ -5761,7 +5832,7 @@ msgstr ""
msgid "Asset Category Name"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:382
+#: erpnext/stock/doctype/item/item.py:380
msgid "Asset Category is mandatory for Fixed Asset item"
msgstr ""
@@ -6202,7 +6273,7 @@ msgstr ""
msgid "Assets {assets_link} created for {item_code}"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:712
+#: erpnext/manufacturing/doctype/job_card/job_card.js:711
msgid "Assign Job to Employee"
msgstr ""
@@ -6213,7 +6284,7 @@ msgid "Assign to Name"
msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order.js:593
-#: erpnext/public/js/controllers/buying.js:555
+#: erpnext/public/js/controllers/buying.js:560
msgid "Assigning {0} to {1} (row {2})"
msgstr ""
@@ -6376,11 +6447,11 @@ msgstr ""
msgid "Attribute Value"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:893
+#: erpnext/stock/doctype/item/item.py:891
msgid "Attribute Value {0} is not valid for the selected attribute {1}."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1039
+#: erpnext/stock/doctype/item/item.py:1037
msgid "Attribute table is mandatory"
msgstr ""
@@ -6388,19 +6459,19 @@ msgstr ""
msgid "Attribute value: {0} must appear only once"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:882
+#: erpnext/stock/doctype/item/item.py:880
msgid "Attribute {0} is disabled."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:870
+#: erpnext/stock/doctype/item/item.py:868
msgid "Attribute {0} is not valid for the selected template."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1043
+#: erpnext/stock/doctype/item/item.py:1041
msgid "Attribute {0} selected multiple times in Attributes Table"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:971
+#: erpnext/stock/doctype/item/item.py:969
msgid "Attributes"
msgstr ""
@@ -6487,6 +6558,16 @@ msgstr ""
msgid "Auto Fetch"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:225
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:573
+msgid "Auto Fetch Batch Nos"
+msgstr ""
+
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:224
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:573
+msgid "Auto Fetch Serial Nos"
+msgstr ""
+
#: erpnext/selling/page/point_of_sale/pos_item_details.js:228
msgid "Auto Fetch Serial Numbers"
msgstr ""
@@ -6607,8 +6688,8 @@ msgstr ""
msgid "Auto reconcile Payments"
msgstr ""
-#: erpnext/public/js/controllers/buying.js:373
-#: erpnext/public/js/utils/sales_common.js:488
+#: erpnext/public/js/controllers/buying.js:378
+#: erpnext/public/js/utils/sales_common.js:493
msgid "Auto repeat document updated"
msgstr ""
@@ -6953,8 +7034,8 @@ msgstr ""
#: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:118
#: erpnext/manufacturing/workspace/manufacturing/manufacturing.json
#: erpnext/selling/doctype/sales_order/sales_order.js:1496
-#: erpnext/stock/doctype/material_request/material_request.js:352
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:809
+#: erpnext/stock/doctype/material_request/material_request.js:353
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:785
#: erpnext/stock/report/bom_search/bom_search.py:38
#: erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:524
@@ -7213,8 +7294,8 @@ msgstr ""
msgid "BOM and Production"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:387
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:861
+#: erpnext/stock/doctype/material_request/material_request.js:388
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:837
msgid "BOM does not contain any stock item"
msgstr ""
@@ -7345,7 +7426,7 @@ msgstr ""
#: erpnext/stock/report/available_batch_report/available_batch_report.py:62
#: erpnext/stock/report/available_serial_no/available_serial_no.py:126
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:89
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:84
#: erpnext/stock/report/stock_balance/stock_balance.py:517
#: erpnext/stock/report/stock_ledger/stock_ledger.py:331
msgid "Balance Qty"
@@ -7418,7 +7499,7 @@ msgid "Balance Type"
msgstr ""
#: erpnext/stock/report/available_serial_no/available_serial_no.py:174
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:91
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:86
#: erpnext/stock/report/stock_balance/stock_balance.py:525
#: erpnext/stock/report/stock_ledger/stock_ledger.py:388
msgid "Balance Value"
@@ -7848,11 +7929,11 @@ msgstr ""
msgid "Barcode Type"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:552
+#: erpnext/stock/doctype/item/item.py:550
msgid "Barcode {0} already used in Item {1}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:567
+#: erpnext/stock/doctype/item/item.py:565
msgid "Barcode {0} is not a valid {1} code"
msgstr ""
@@ -7955,10 +8036,10 @@ msgstr ""
#. Label of the based_on_payment_terms (Check) field in DocType 'Process
#. Statement Of Accounts'
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:131
-#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:108
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:153
-#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:126
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:134
+#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:111
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:156
+#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:129
msgid "Based On Payment Terms"
msgstr ""
@@ -8007,7 +8088,7 @@ msgstr ""
#. Label of a Link in the Stock Workspace
#: erpnext/stock/doctype/batch/batch.json
#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:34
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:85
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:80
#: erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py:158
#: erpnext/stock/report/stock_ledger/stock_ledger.py:418
#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:182
@@ -8090,8 +8171,9 @@ msgstr ""
#: erpnext/manufacturing/doctype/job_card/job_card.json
#: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js:89
#: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py:115
-#: erpnext/public/js/controllers/transaction.js:2989
+#: erpnext/public/js/controllers/transaction.js:2981
#: erpnext/public/js/utils/barcode_scanner.js:286
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:929
#: erpnext/public/js/utils/serial_no_batch_selector.js:450
#: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
#: erpnext/stock/doctype/item_price/item_price.json
@@ -8125,7 +8207,7 @@ msgstr ""
msgid "Batch No is mandatory"
msgstr ""
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3572
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3640
msgid "Batch No {0} does not exist"
msgstr ""
@@ -8283,7 +8365,7 @@ msgstr ""
#. Label of the bill_date (Date) field in DocType 'Journal Entry'
#. Label of the bill_date (Date) field in DocType 'Subcontracting Receipt'
#: erpnext/accounts/doctype/journal_entry/journal_entry.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1210
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1205
#: erpnext/accounts/report/purchase_register/purchase_register.py:232
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
msgid "Bill Date"
@@ -8304,7 +8386,7 @@ msgstr ""
#. Label of the bill_no (Data) field in DocType 'Journal Entry'
#. Label of the bill_no (Data) field in DocType 'Subcontracting Receipt'
#: erpnext/accounts/doctype/journal_entry/journal_entry.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1209
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1204
#: erpnext/accounts/report/purchase_register/purchase_register.py:231
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
msgid "Bill No"
@@ -8321,8 +8403,8 @@ msgstr ""
#. Label of the bom_info_section (Section Break) field in DocType 'Stock Entry'
#: erpnext/manufacturing/doctype/bom/bom.py:1168
#: erpnext/manufacturing/workspace/manufacturing/manufacturing.json
-#: erpnext/stock/doctype/material_request/material_request.js:142
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:795
+#: erpnext/stock/doctype/material_request/material_request.js:143
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:771
#: erpnext/stock/doctype/stock_entry/stock_entry.json
msgid "Bill of Materials"
msgstr ""
@@ -8556,7 +8638,7 @@ msgid "Bin"
msgstr ""
#: erpnext/stock/doctype/bin/bin.js:16
-msgid "Bin Qty Recalculated"
+msgid "Bin Values Recalculated"
msgstr ""
#. Label of the bio (Text Editor) field in DocType 'Employee'
@@ -8692,10 +8774,10 @@ msgstr ""
msgid "Block Supplier"
msgstr ""
-#. Description of the 'Enable Overdue Billing Threshold' (Check) field in
-#. DocType 'Accounts Settings'
+#. Description of the 'Restrict Customer Over Billing' (Check) field in DocType
+#. 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
-msgid "Block submitting a new Sales Invoice when the customer's overdue amount exceeds the Overdue Billing Threshold set on the customer."
+msgid "Block a new Sales Invoice when the customer's overdue amount exceeds the Overdue Limit set on the customer."
msgstr ""
#. Description of the 'Is Frozen' (Check) field in DocType 'Customer'
@@ -9288,7 +9370,7 @@ msgstr ""
msgid "COGS By Item Group"
msgstr ""
-#: erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py:55
+#: erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py:44
msgid "COGS Debit"
msgstr ""
@@ -9618,7 +9700,7 @@ msgstr ""
msgid "Can't change the valuation method, as there are transactions against some items which do not have its own valuation method"
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:191
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:192
msgid "Can't change the valuation method, as there are transactions against some items which do not have their own valuation method"
msgstr ""
@@ -9650,7 +9732,7 @@ msgstr ""
msgid "Cancelation Date"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1592
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1599
msgid "Cancelled Job Card cannot be processed."
msgstr ""
@@ -9666,9 +9748,9 @@ msgstr ""
msgid "Cannot Create Return"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:695
-#: erpnext/stock/doctype/item/item.py:708
-#: erpnext/stock/doctype/item/item.py:724
+#: erpnext/stock/doctype/item/item.py:693
+#: erpnext/stock/doctype/item/item.py:706
+#: erpnext/stock/doctype/item/item.py:722
msgid "Cannot Merge"
msgstr ""
@@ -9692,7 +9774,7 @@ msgstr ""
msgid "Cannot apply TDS against multiple parties in one entry"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:385
+#: erpnext/stock/doctype/item/item.py:383
msgid "Cannot be a fixed asset item as Stock Ledger is created."
msgstr ""
@@ -9741,11 +9823,11 @@ msgstr ""
msgid "Cannot cancel transaction for Completed Work Order."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:991
+#: erpnext/stock/doctype/item/item.py:989
msgid "Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1152
+#: erpnext/stock/doctype/item/item.py:1150
msgid "Cannot change Item {0} from serialized to non-serialized because a Serial and Batch Bundle exists for it. Please delete or cancel the Serial and Batch Bundle first."
msgstr ""
@@ -9757,7 +9839,7 @@ msgstr ""
msgid "Cannot change Service Stop Date for item in row {0}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:982
+#: erpnext/stock/doctype/item/item.py:980
msgid "Cannot change Variant properties after stock transaction. You will have to make a new Item to do this."
msgstr ""
@@ -9765,7 +9847,7 @@ msgstr ""
msgid "Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."
msgstr ""
-#: erpnext/projects/doctype/task/task.py:146
+#: erpnext/projects/doctype/task/task.py:147
msgid "Cannot complete task {0} as its dependent task {1} is not completed / cancelled."
msgstr ""
@@ -9777,11 +9859,11 @@ msgstr ""
msgid "Cannot convert Task to non-group because the following child Tasks exist: {0}."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:444
+#: erpnext/accounts/doctype/account/account.py:475
msgid "Cannot convert to Group because Account Type is selected."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:280
+#: erpnext/accounts/doctype/account/account.py:311
msgid "Cannot covert to Group because Account Type is selected."
msgstr ""
@@ -9797,7 +9879,7 @@ msgstr ""
msgid "Cannot create Stock Reservation Entries for future dated Purchase Receipts."
msgstr ""
-#: erpnext/selling/doctype/sales_order/mapper.py:981
+#: erpnext/selling/doctype/sales_order/mapper.py:983
#: erpnext/stock/doctype/pick_list/pick_list.py:258
msgid "Cannot create a pick list for Sales Order {0} because it has reserved stock. Please unreserve the stock in order to create a pick list."
msgstr ""
@@ -9848,15 +9930,15 @@ msgstr ""
msgid "Cannot delete virtual DocType: {0}. Virtual DocTypes do not have database tables."
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:147
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:148
msgid "Cannot disable Serial and Batch No for Item, as there are existing records for serial / batch."
msgstr ""
-#: erpnext/setup/doctype/company/company.py:631
+#: erpnext/setup/doctype/company/company.py:632
msgid "Cannot disable perpetual inventory, as there are existing Stock Ledger Entries for the company {0}. Please cancel the stock transactions first and try again."
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:128
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:129
msgid "Cannot disable {0} as it may lead to incorrect stock valuation."
msgstr ""
@@ -9947,7 +10029,7 @@ msgstr ""
msgid "Cannot retrieve link token. Check Error Log for more information"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:385
+#: erpnext/selling/doctype/customer/customer.py:383
msgid "Cannot select a Group type Customer Group. Please select a non-group Customer Group."
msgstr ""
@@ -9972,7 +10054,7 @@ msgstr ""
msgid "Cannot set authorization on basis of Discount for {0}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:782
+#: erpnext/stock/doctype/item/item.py:780
msgid "Cannot set multiple Item Defaults for a company."
msgstr ""
@@ -9996,7 +10078,7 @@ msgstr ""
msgid "Cannot start deletion. Another deletion {0} is already queued/running. Please wait for it to complete."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:924
+#: erpnext/manufacturing/doctype/job_card/job_card.py:919
msgid "Cannot submit Job Card {0} while it is On Hold. Please resume and complete the job before submission."
msgstr ""
@@ -10390,7 +10472,7 @@ msgstr ""
msgid "Change this date manually to setup the next synchronization start date"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:168
+#: erpnext/selling/doctype/customer/customer.py:167
msgid "Changed customer name to '{0}' as '{1}' already exists."
msgstr ""
@@ -10610,7 +10692,7 @@ msgstr ""
#. Label of the reference_date (Date) field in DocType 'Payment Entry'
#: erpnext/accounts/doctype/payment_entry/payment_entry.json
-#: erpnext/public/js/controllers/transaction.js:2900
+#: erpnext/public/js/controllers/transaction.js:2892
msgid "Cheque/Reference Date"
msgstr ""
@@ -10668,7 +10750,7 @@ msgstr ""
#. Label of the child_row_reference (Data) field in DocType 'Quality
#. Inspection'
-#: erpnext/public/js/controllers/transaction.js:2995
+#: erpnext/public/js/controllers/transaction.js:2987
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
msgid "Child Row Reference"
msgstr ""
@@ -10677,7 +10759,7 @@ msgstr ""
msgid "Child Table Not Allowed"
msgstr ""
-#: erpnext/projects/doctype/task/task.py:326
+#: erpnext/projects/doctype/task/task.py:327
msgid "Child Task exists for this Task. You cannot delete this Task."
msgstr ""
@@ -10695,7 +10777,7 @@ msgstr ""
msgid "Child warehouse exists for this warehouse. You can not delete this warehouse."
msgstr ""
-#: erpnext/projects/doctype/task/task.py:256
+#: erpnext/projects/doctype/task/task.py:257
msgid "Circular Reference Error"
msgstr ""
@@ -10797,6 +10879,10 @@ msgstr ""
msgid "Clearing Demo Data..."
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:991
+msgid "Click on 'Add row' to add Serial / Batch entries"
+msgstr ""
+
#: erpnext/manufacturing/doctype/production_plan/production_plan.js:747
msgid "Click on 'Get Finished Goods for Manufacture' to fetch the items from the above Sales Orders. Items only for which a BOM is present will be fetched."
msgstr ""
@@ -11083,6 +11169,10 @@ msgstr ""
msgid "Combined invoice portion must equal 100%"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:340
+msgid "Comma separated email addresses"
+msgstr ""
+
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:178
msgid "Commercial"
msgstr ""
@@ -11296,6 +11386,7 @@ msgstr ""
#. Option for the 'Customer Type' (Select) field in DocType 'Customer'
#. Label of the company (Link) field in DocType 'Customer Credit Limit'
#. Label of the company (Link) field in DocType 'Installation Note'
+#. Label of the company (Link) field in DocType 'Proforma Invoice'
#. Label of the company (Link) field in DocType 'Quotation'
#. Label of the company (Link) field in DocType 'Sales Order'
#. Label of the company (Link) field in DocType 'Supplier Number At Customer'
@@ -11542,6 +11633,7 @@ msgstr ""
#: erpnext/selling/doctype/customer/customer.json
#: erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
#: erpnext/selling/doctype/installation_note/installation_note.json
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
#: erpnext/selling/doctype/quotation/quotation.json
#: erpnext/selling/doctype/sales_order/sales_order.json
#: erpnext/selling/doctype/supplier_number_at_customer/supplier_number_at_customer.json
@@ -11802,7 +11894,7 @@ msgstr ""
msgid "Company Name cannot be Company"
msgstr ""
-#: erpnext/accounts/custom/address.py:36
+#: erpnext/accounts/custom/address.py:38
msgid "Company Not Linked"
msgstr ""
@@ -11848,8 +11940,8 @@ msgstr ""
msgid "Company currencies of both the companies should match for Inter Company Transactions."
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:381
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:855
+#: erpnext/stock/doctype/material_request/material_request.js:382
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:831
msgid "Company field is required"
msgstr ""
@@ -11918,7 +12010,7 @@ msgstr ""
msgid "Company {0} added multiple times"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:519
+#: erpnext/accounts/doctype/account/account.py:550
#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1308
msgid "Company {0} does not exist"
msgstr ""
@@ -11960,12 +12052,12 @@ msgstr ""
#. Label of the competitors (Table MultiSelect) field in DocType 'Opportunity'
#. Label of the competitors (Table MultiSelect) field in DocType 'Quotation'
#: erpnext/crm/doctype/opportunity/opportunity.json
-#: erpnext/public/js/utils/sales_common.js:610
+#: erpnext/public/js/utils/sales_common.js:615
#: erpnext/selling/doctype/quotation/quotation.json
msgid "Competitors"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:663
+#: erpnext/manufacturing/doctype/job_card/job_card.js:662
msgid "Complete Job"
msgstr ""
@@ -11987,7 +12079,7 @@ msgstr ""
msgid "Completed On"
msgstr ""
-#: erpnext/projects/doctype/task/task.py:186
+#: erpnext/projects/doctype/task/task.py:187
msgid "Completed On cannot be greater than Today"
msgstr ""
@@ -12019,8 +12111,8 @@ msgstr ""
msgid "Completed Qty cannot be greater than 'Qty to Manufacture'"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:258
-#: erpnext/manufacturing/doctype/job_card/job_card.js:392
+#: erpnext/manufacturing/doctype/job_card/job_card.js:256
+#: erpnext/manufacturing/doctype/job_card/job_card.js:390
#: erpnext/public/js/shop_floor/shop_floor.js:804
msgid "Completed Quantity"
msgstr ""
@@ -12755,7 +12847,7 @@ msgstr ""
msgid "Conversion Rate"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:468
+#: erpnext/stock/doctype/item/item.py:466
msgid "Conversion factor for default Unit of Measure must be 1 in row {0}"
msgstr ""
@@ -12848,13 +12940,13 @@ msgstr ""
msgid "Corrective Action"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:446
+#: erpnext/manufacturing/doctype/job_card/job_card.js:444
msgid "Corrective Job Card"
msgstr ""
#. Label of the corrective_operation_section (Tab Break) field in DocType 'Job
#. Card'
-#: erpnext/manufacturing/doctype/job_card/job_card.js:455
+#: erpnext/manufacturing/doctype/job_card/job_card.js:453
#: erpnext/manufacturing/doctype/job_card/job_card.json
msgid "Corrective Operation"
msgstr ""
@@ -13022,7 +13114,7 @@ msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:28
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:47
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:30
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1195
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1190
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:47
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js:42
#: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:199
@@ -13112,7 +13204,7 @@ msgstr ""
msgid "Cost Center and Budgeting"
msgstr ""
-#: erpnext/public/js/utils/sales_common.js:544
+#: erpnext/public/js/utils/sales_common.js:549
msgid "Cost Center for Item rows has been updated to {0}"
msgstr ""
@@ -13580,8 +13672,8 @@ msgstr ""
msgid "Create POS Opening Entry"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:212
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:285
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:215
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:288
msgid "Create Payment Entries"
msgstr ""
@@ -13596,7 +13688,7 @@ msgstr ""
msgid "Create Payment Entry for Consolidated POS Invoices."
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:580
+#: erpnext/public/js/controllers/transaction.js:592
msgid "Create Payment Request"
msgstr ""
@@ -13608,6 +13700,10 @@ msgstr ""
msgid "Create Print Format"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:61
+msgid "Create Proforma Invoice"
+msgstr ""
+
#. Title of an Onboarding Step
#. Label of an action in the Onboarding Step 'Create Project'
#: erpnext/projects/onboarding_step/create_project/create_project.json
@@ -13693,6 +13789,11 @@ msgstr ""
msgid "Create Sales Orders to help you plan your work and deliver on-time"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:234
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:757
+msgid "Create Serial Nos from Range"
+msgstr ""
+
#. Title of an Onboarding Step
#. Label of an action in the Onboarding Step 'Create Service Item'
#: erpnext/subcontracting/onboarding_step/create_service_item/create_service_item.json
@@ -13700,7 +13801,7 @@ msgid "Create Service Item"
msgstr ""
#: erpnext/stock/dashboard/item_dashboard.js:283
-#: erpnext/stock/doctype/material_request/material_request.js:479
+#: erpnext/stock/doctype/material_request/material_request.js:480
msgid "Create Stock Entry"
msgstr ""
@@ -13828,7 +13929,7 @@ msgstr ""
msgid "Create a variant with the template image."
msgstr ""
-#: erpnext/stock/stock_ledger.py:2205
+#: erpnext/stock/stock_ledger.py:2220
msgid "Create an incoming stock transaction for the Item."
msgstr ""
@@ -13862,6 +13963,11 @@ msgstr ""
msgid "Created By Migration"
msgstr ""
+#. Label of the created_through_portal (Check) field in DocType 'Appointment'
+#: erpnext/crm/doctype/appointment/appointment.json
+msgid "Created through Portal"
+msgstr ""
+
#: erpnext/accounts/bulk_payment.py:77
msgid "Created {0} draft Grouped Payment Entries"
msgstr ""
@@ -13915,6 +14021,10 @@ msgstr ""
msgid "Creating Packing Slip ..."
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:231
+msgid "Creating Proforma Invoice..."
+msgstr ""
+
#: erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js:68
msgid "Creating Purchase Invoices ..."
msgstr ""
@@ -14104,7 +14214,7 @@ msgstr ""
msgid "Credit Limit"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:559
+#: erpnext/selling/doctype/customer/customer.py:557
msgid "Credit Limit Crossed"
msgstr ""
@@ -14139,8 +14249,7 @@ msgstr ""
#: erpnext/accounts/doctype/journal_entry/journal_entry.json
#: erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:176
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1219
-#: erpnext/controllers/sales_and_purchase_return.py:462
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1214
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:303
#: erpnext/stock/doctype/delivery_note/delivery_note.js:89
#: erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -14184,16 +14293,16 @@ msgstr ""
msgid "Credit in Company Currency"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:525
-#: erpnext/selling/doctype/customer/customer.py:581
+#: erpnext/selling/doctype/customer/customer.py:523
+#: erpnext/selling/doctype/customer/customer.py:579
msgid "Credit limit has been crossed for customer {0} ({1}/{2})"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:412
+#: erpnext/selling/doctype/customer/customer.py:410
msgid "Credit limit is already defined for the Company {0}"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:580
+#: erpnext/selling/doctype/customer/customer.py:578
msgid "Credit limit reached for customer {0}"
msgstr ""
@@ -14373,7 +14482,7 @@ msgstr ""
msgid "Currency and Price List"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:350
+#: erpnext/accounts/doctype/account/account.py:381
msgid "Currency can not be changed after making entries using some other currency"
msgstr ""
@@ -14622,6 +14731,7 @@ msgstr ""
#. Name of a DocType
#. Label of the customer (Link) field in DocType 'Installation Note'
#. Option for the 'Party Type' (Select) field in DocType 'Party Specific Item'
+#. Label of the customer (Link) field in DocType 'Proforma Invoice'
#. Label of the customer (Link) field in DocType 'Sales Order'
#. Label of the customer (Link) field in DocType 'SMS Center'
#. Label of a Link in the Selling Workspace
@@ -14706,6 +14816,7 @@ msgstr ""
#: erpnext/selling/doctype/customer/customer.json
#: erpnext/selling/doctype/installation_note/installation_note.json
#: erpnext/selling/doctype/party_specific_item/party_specific_item.json
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
#: erpnext/selling/doctype/sales_order/sales_order.js:1237
#: erpnext/selling/doctype/sales_order/sales_order.json
#: erpnext/selling/doctype/sales_order/sales_order_calendar.js:19
@@ -14841,7 +14952,7 @@ msgstr ""
#. Label of the customer_contact_display (Small Text) field in DocType
#. 'Purchase Order'
#. Label of the customer_contact (Small Text) field in DocType 'Delivery Stop'
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1189
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1184
#: erpnext/buying/doctype/purchase_order/purchase_order.json
#: erpnext/stock/doctype/delivery_stop/delivery_stop.json
msgid "Customer Contact"
@@ -14947,7 +15058,7 @@ msgstr ""
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#: erpnext/accounts/doctype/tax_rule/tax_rule.json
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:115
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1247
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1242
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:96
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:187
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:56
@@ -15009,7 +15120,7 @@ msgstr ""
msgid "Customer Items"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1238
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1233
msgid "Customer LPO"
msgstr ""
@@ -15046,6 +15157,7 @@ msgstr ""
#. Label of the customer_name (Data) field in DocType 'Maintenance Visit'
#. Label of the customer_name (Data) field in DocType 'Blanket Order'
#. Label of the customer_name (Data) field in DocType 'Customer'
+#. Label of the customer_name (Data) field in DocType 'Proforma Invoice'
#. Label of the customer_name (Data) field in DocType 'Quotation'
#. Label of the customer_name (Data) field in DocType 'Sales Order'
#. Option for the 'Customer Naming By' (Select) field in DocType 'Selling
@@ -15061,7 +15173,7 @@ msgstr ""
#: erpnext/accounts/doctype/pos_invoice/pos_invoice.json
#: erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1178
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1173
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:157
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:92
#: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py:35
@@ -15075,6 +15187,7 @@ msgstr ""
#: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
#: erpnext/manufacturing/doctype/blanket_order/blanket_order.json
#: erpnext/selling/doctype/customer/customer.json
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
#: erpnext/selling/doctype/quotation/quotation.json
#: erpnext/selling/doctype/sales_order/sales_order.json
#: erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -15168,7 +15281,7 @@ msgstr ""
msgid "Customer Provided Item Cost"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:557
+#: erpnext/setup/doctype/company/company.py:558
msgid "Customer Service"
msgstr ""
@@ -15231,10 +15344,6 @@ msgstr ""
msgid "Customer {0} does not belong to project {1}"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:605
-msgid "Customer {0} has an overdue billing limit. Overdue amount {1} exceeds the allowed threshold {2}."
-msgstr ""
-
#. Label of the customer_item_code (Data) field in DocType 'POS Invoice Item'
#. Label of the customer_item_code (Data) field in DocType 'Sales Invoice Item'
#. Label of the customer_item_code (Data) field in DocType 'Quotation Item'
@@ -15343,7 +15452,7 @@ msgstr ""
msgid "DFS"
msgstr ""
-#: erpnext/projects/doctype/project/project.py:781
+#: erpnext/projects/doctype/project/project.py:783
msgid "Daily Project Summary for {0}"
msgstr ""
@@ -15458,7 +15567,7 @@ msgstr ""
msgid "Date of Joining"
msgstr ""
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:272
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:270
msgid "Date of Transaction"
msgstr ""
@@ -15650,8 +15759,7 @@ msgstr ""
#: erpnext/accounts/doctype/journal_entry/journal_entry.json
#: erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:178
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1222
-#: erpnext/controllers/sales_and_purchase_return.py:466
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1217
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:304
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:45
#: erpnext/workspace_sidebar/invoicing.json
@@ -15760,7 +15868,7 @@ msgstr ""
msgid "Decimeter"
msgstr ""
-#: erpnext/public/js/utils/sales_common.js:637
+#: erpnext/public/js/utils/sales_common.js:642
msgid "Declare Lost"
msgstr ""
@@ -15855,11 +15963,11 @@ msgstr ""
msgid "Default BOM"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:511
+#: erpnext/stock/doctype/item/item.py:509
msgid "Default BOM ({0}) must be active for this item or its template"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/mapper.py:87
+#: erpnext/manufacturing/doctype/work_order/mapper.py:88
msgid "Default BOM for {0} not found"
msgstr ""
@@ -15867,7 +15975,7 @@ msgstr ""
msgid "Default BOM not found for FG Item {0}"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/mapper.py:83
+#: erpnext/manufacturing/doctype/work_order/mapper.py:84
msgid "Default BOM not found for Item {0} and Project {1}"
msgstr ""
@@ -16086,6 +16194,12 @@ msgstr ""
msgid "Default Priority"
msgstr ""
+#. Label of the default_proforma_print_format (Link) field in DocType 'Selling
+#. Settings'
+#: erpnext/selling/doctype/selling_settings/selling_settings.json
+msgid "Default Proforma Print Format"
+msgstr ""
+
#. Label of the default_provisional_account (Link) field in DocType 'Company'
#: erpnext/setup/doctype/company/company.json
msgid "Default Provisional Account"
@@ -16183,15 +16297,15 @@ msgstr ""
msgid "Default Unit of Measure"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1433
+#: erpnext/stock/doctype/item/item.py:1431
msgid "Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1413
+#: erpnext/stock/doctype/item/item.py:1411
msgid "Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1017
+#: erpnext/stock/doctype/item/item.py:1015
msgid "Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'"
msgstr ""
@@ -16236,6 +16350,12 @@ msgstr ""
msgid "Default price list for buying or selling this item"
msgstr ""
+#. Description of the 'Default Proforma Print Format' (Link) field in DocType
+#. 'Selling Settings'
+#: erpnext/selling/doctype/selling_settings/selling_settings.json
+msgid "Default print format used when generating a Proforma Invoice PDF."
+msgstr ""
+
#. Description of a DocType
#: erpnext/stock/doctype/stock_settings/stock_settings.json
msgid "Default settings for your stock-related transactions"
@@ -16397,6 +16517,10 @@ msgstr ""
msgid "Delete Accounting and Stock Ledger entries on deletion of transaction"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:1061
+msgid "Delete All"
+msgstr ""
+
#. Label of the delete_bin_data_status (Select) field in DocType 'Transaction
#. Deletion Record'
#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
@@ -16425,6 +16549,12 @@ msgstr ""
msgid "Delete Leads and Addresses"
msgstr ""
+#. Option for the 'Action for Expired Unverified Appointments' (Select) field
+#. in DocType 'Appointment Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "Delete Permanently"
+msgstr ""
+
#. Label of the delete_transactions_status (Select) field in DocType
#. 'Transaction Deletion Record'
#: erpnext/setup/doctype/company/company.js:184
@@ -16486,23 +16616,6 @@ msgstr ""
msgid "Deliver secondary Items"
msgstr ""
-#. Option for the 'Status' (Select) field in DocType 'Purchase Order'
-#. Option for the 'Status' (Select) field in DocType 'Serial No'
-#. Option for the 'Tracking Status' (Select) field in DocType 'Shipment'
-#. Option for the 'Status' (Select) field in DocType 'Stock Reservation Entry'
-#. Option for the 'Status' (Select) field in DocType 'Subcontracting Inward
-#. Order'
-#: erpnext/buying/doctype/purchase_order/purchase_order.json
-#: erpnext/buying/doctype/purchase_order/purchase_order_list.js:20
-#: erpnext/controllers/website_list_for_contact.py:218
-#: erpnext/stock/doctype/serial_no/serial_no.json
-#: erpnext/stock/doctype/shipment/shipment.json
-#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
-#: erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py:61
-#: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json
-msgid "Delivered"
-msgstr ""
-
#: erpnext/stock/report/item_wise_consumption/item_wise_consumption.py:64
msgid "Delivered Amount"
msgstr ""
@@ -16715,7 +16828,7 @@ msgstr ""
msgid "Delivery Note {0} is not submitted"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1242
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1237
#: erpnext/stock/doctype/delivery_trip/delivery_trip.js:75
msgid "Delivery Notes"
msgstr ""
@@ -16862,7 +16975,7 @@ msgstr ""
msgid "Dependent Task"
msgstr ""
-#: erpnext/projects/doctype/task/task.py:179
+#: erpnext/projects/doctype/task/task.py:180
msgid "Dependent Task {0} is not a Template Task"
msgstr ""
@@ -17083,7 +17196,7 @@ msgstr ""
#. Label of the order_lost_reason (Small Text) field in DocType 'Opportunity'
#. Label of the order_lost_reason (Small Text) field in DocType 'Quotation'
#: erpnext/crm/doctype/opportunity/opportunity.json
-#: erpnext/public/js/utils/sales_common.js:616
+#: erpnext/public/js/utils/sales_common.js:621
#: erpnext/selling/doctype/quotation/quotation.json
msgid "Detailed Reason"
msgstr ""
@@ -17752,7 +17865,7 @@ msgstr ""
msgid "Dislikes"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:551
+#: erpnext/setup/doctype/company/company.py:552
msgid "Dispatch"
msgstr ""
@@ -17976,7 +18089,7 @@ msgstr ""
msgid "Do Not Explode"
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:129
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:130
msgid "Do Not Use Batchwise Valuation"
msgstr ""
@@ -18658,7 +18771,7 @@ msgstr ""
msgid "Either target qty or target amount is mandatory."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:677
+#: erpnext/manufacturing/doctype/job_card/job_card.js:676
msgid "Elapsed Time"
msgstr ""
@@ -18764,6 +18877,11 @@ msgstr ""
msgid "Email Sent to Supplier {0}"
msgstr ""
+#. Label of the email_verified (Check) field in DocType 'Appointment'
+#: erpnext/crm/doctype/appointment/appointment.json
+msgid "Email Verified"
+msgstr ""
+
#: erpnext/setup/doctype/employee/employee.py:443
msgid "Email is required to create a user"
msgstr ""
@@ -18789,8 +18907,9 @@ msgstr ""
msgid "Email sent to {0}"
msgstr ""
-#: erpnext/crm/doctype/appointment/appointment.py:114
-msgid "Email verification failed."
+#. Label of the emailed_to (Small Text) field in DocType 'Proforma Invoice'
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
+msgid "Emailed To"
msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js:20
@@ -18965,7 +19084,7 @@ msgstr ""
msgid "Employee {0} does not belong to the company {1}"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:411
+#: erpnext/manufacturing/doctype/job_card/job_card.py:406
msgid "Employee {0} is currently working on another workstation. Please assign another employee."
msgstr ""
@@ -18990,7 +19109,7 @@ msgstr ""
msgid "Ems(Pica)"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:3058
+#: erpnext/public/js/controllers/transaction.js:3050
msgid "Enable {0} on the Item master to proceed with {1} inspection."
msgstr ""
@@ -19004,6 +19123,12 @@ msgstr ""
msgid "Enable Allow Partial Reservation in the Stock Settings to reserve partial stock."
msgstr ""
+#. Label of the enable_appointment_portal (Check) field in DocType 'Appointment
+#. Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "Enable Appointment Booking Through Portal"
+msgstr ""
+
#. Label of the enable_scheduling (Check) field in DocType 'Appointment Booking
#. Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
@@ -19016,7 +19141,7 @@ msgstr ""
msgid "Enable Auto Email"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1221
+#: erpnext/stock/doctype/item/item.py:1219
msgid "Enable Auto Re-Order"
msgstr ""
@@ -19111,12 +19236,6 @@ msgstr ""
msgid "Enable Opportunity Creation from Contact Us"
msgstr ""
-#. Label of the enable_overdue_billing_threshold (Check) field in DocType
-#. 'Accounts Settings'
-#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
-msgid "Enable Overdue Billing Threshold"
-msgstr ""
-
#. Label of the enable_parallel_reposting (Check) field in DocType 'Stock
#. Reposting Settings'
#: erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -19128,6 +19247,12 @@ msgstr ""
msgid "Enable Perpetual Inventory"
msgstr ""
+#. Label of the enable_proforma_invoice (Check) field in DocType 'Selling
+#. Settings'
+#: erpnext/selling/doctype/selling_settings/selling_settings.json
+msgid "Enable Proforma Invoice"
+msgstr ""
+
#. Label of the enable_provisional_accounting_for_non_stock_items (Check) field
#. in DocType 'Company'
#: erpnext/setup/doctype/company/company.json
@@ -19351,8 +19476,8 @@ msgstr ""
#. Label of the end_time (Time) field in DocType 'Stock Reposting Settings'
#. Label of the end_time (Time) field in DocType 'Service Day'
#. Label of the end_time (Datetime) field in DocType 'Call Log'
-#: erpnext/manufacturing/doctype/job_card/job_card.js:331
-#: erpnext/manufacturing/doctype/job_card/job_card.js:399
+#: erpnext/manufacturing/doctype/job_card/job_card.js:329
+#: erpnext/manufacturing/doctype/job_card/job_card.js:397
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
#: erpnext/public/js/shop_floor/shop_floor.js:851
#: erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -19450,8 +19575,8 @@ msgstr ""
msgid "Enter Serial Nos"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:360
-#: erpnext/manufacturing/doctype/job_card/job_card.js:422
+#: erpnext/manufacturing/doctype/job_card/job_card.js:358
+#: erpnext/manufacturing/doctype/job_card/job_card.js:420
msgid "Enter Value"
msgstr ""
@@ -19459,7 +19584,7 @@ msgstr ""
msgid "Enter Visit Details"
msgstr ""
-#: erpnext/manufacturing/doctype/routing/routing.js:88
+#: erpnext/manufacturing/doctype/routing/routing.js:93
msgid "Enter a name for Routing."
msgstr ""
@@ -19512,7 +19637,7 @@ msgstr ""
msgid "Enter the Item Code that this customer uses at their end. This will be shown in Sales Orders for the customer's reference."
msgstr ""
-#: erpnext/manufacturing/doctype/routing/routing.js:93
+#: erpnext/manufacturing/doctype/routing/routing.js:98
msgid ""
"Enter the Operation, the table will fetch the Operation details like Hourly Rate, Workstation automatically.\n"
"\n"
@@ -19701,7 +19826,7 @@ msgstr ""
msgid "Example URL"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1133
+#: erpnext/stock/doctype/item/item.py:1131
msgid "Example of a linked document: {0}"
msgstr ""
@@ -19721,7 +19846,7 @@ msgstr ""
msgid "Example: If the transaction amount is 200, then this will be calculated as {} = {}"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2494
+#: erpnext/stock/stock_ledger.py:2509
msgid "Example: Serial No {0} reserved in {1}."
msgstr ""
@@ -19743,7 +19868,7 @@ msgstr ""
msgid "Excess Materials Consumed"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1235
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1230
msgid "Excess Transfer"
msgstr ""
@@ -19779,7 +19904,7 @@ msgstr ""
#: erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
#: erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
#: erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
-#: erpnext/setup/doctype/company/company.py:745
+#: erpnext/setup/doctype/company/company.py:746
msgid "Exchange Gain/Loss"
msgstr ""
@@ -19884,7 +20009,7 @@ msgstr ""
msgid "Excise Entry"
msgstr ""
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:1524
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:1501
msgid "Excise Invoice"
msgstr ""
@@ -19956,6 +20081,10 @@ msgstr ""
msgid "Existing Customer"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:581
+msgid "Existing entries will be replaced with the fetched entries"
+msgstr ""
+
#: banking/src/components/features/BankStatementImporter/CSV/StatementDetails.tsx:307
msgid "Existing transactions in the system belonging to the same bank account and date range"
msgstr ""
@@ -20028,7 +20157,7 @@ msgstr ""
msgid "Expected End Date"
msgstr ""
-#: erpnext/projects/doctype/task/task.py:113
+#: erpnext/projects/doctype/task/task.py:114
msgid "Expected End Date should be less than or equal to parent task's Expected End Date {0}."
msgstr ""
@@ -20300,7 +20429,7 @@ msgstr ""
msgid "Extra Consumed Qty"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:272
+#: erpnext/manufacturing/doctype/job_card/job_card.py:267
msgid "Extra Job Card Quantity"
msgstr ""
@@ -20403,7 +20532,7 @@ msgstr ""
msgid "Failed to install presets"
msgstr ""
-#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:163
+#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:187
msgid "Failed to parse MT940 format. Error: {0}"
msgstr ""
@@ -20437,7 +20566,7 @@ msgstr ""
msgid "Failed to setup defaults"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:925
+#: erpnext/setup/doctype/company/company.py:926
msgid "Failed to setup defaults for country {0}. Please contact support."
msgstr ""
@@ -20500,6 +20629,11 @@ msgstr ""
msgid "Fees"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:591
+msgid "Fetch"
+msgstr ""
+
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:586
#: erpnext/public/js/utils/serial_no_batch_selector.js:396
msgid "Fetch Based On"
msgstr ""
@@ -20510,7 +20644,7 @@ msgstr ""
msgid "Fetch Customers"
msgstr ""
-#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js:82
+#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js:72
msgid "Fetch Items from Warehouse"
msgstr ""
@@ -20548,8 +20682,8 @@ msgstr ""
msgid "Fetch Value From"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:373
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:832
+#: erpnext/stock/doctype/material_request/material_request.js:374
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:808
msgid "Fetch exploded BOM (including sub-assemblies)"
msgstr ""
@@ -20577,7 +20711,7 @@ msgid "Fetching Sales Orders..."
msgstr ""
#: erpnext/accounts/doctype/dunning/dunning.js:135
-#: erpnext/public/js/controllers/transaction.js:1661
+#: erpnext/public/js/controllers/transaction.js:1645
msgid "Fetching exchange rates ..."
msgstr ""
@@ -21138,7 +21272,7 @@ msgstr ""
msgid "Fixed Asset Defaults"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:379
+#: erpnext/stock/doctype/item/item.py:377
msgid "Fixed Asset Item must be a non-stock item."
msgstr ""
@@ -21263,7 +21397,7 @@ msgstr ""
msgid "For"
msgstr ""
-#: erpnext/public/js/utils/sales_common.js:393
+#: erpnext/public/js/utils/sales_common.js:398
msgid "For 'Product Bundle' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Product Bundle' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table."
msgstr ""
@@ -21294,7 +21428,7 @@ msgid "For Job Card"
msgstr ""
#. Label of the for_operation (Link) field in DocType 'Job Card'
-#: erpnext/manufacturing/doctype/job_card/job_card.js:464
+#: erpnext/manufacturing/doctype/job_card/job_card.js:462
#: erpnext/manufacturing/doctype/job_card/job_card.json
msgid "For Operation"
msgstr ""
@@ -21363,7 +21497,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/production_plan/production_plan.json
#: erpnext/manufacturing/doctype/production_plan/services/material_request.py:180
#: erpnext/selling/doctype/sales_order/sales_order.js:1488
-#: erpnext/stock/doctype/material_request/material_request.js:362
+#: erpnext/stock/doctype/material_request/material_request.js:363
#: erpnext/templates/form_grid/material_request_grid.html:36
msgid "For Warehouse"
msgstr ""
@@ -21432,7 +21566,7 @@ msgstr ""
msgid "For operation {0} at row {1}, please add raw materials or set a BOM against it."
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/mapper.py:379
+#: erpnext/manufacturing/doctype/work_order/mapper.py:383
msgid "For operation {0}: Quantity ({1}) can not be greater than pending quantity ({2})"
msgstr ""
@@ -21486,7 +21620,7 @@ msgstr ""
msgid "For the item {0}, the consumed quantity should be {1} according to the BOM {2}."
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:1461
+#: erpnext/public/js/controllers/transaction.js:1445
msgctxt "Clear payment terms template and/or payment schedule when due date is changed"
msgid "For the new {0} to take effect, would you like to clear the current {1}?"
msgstr ""
@@ -21704,11 +21838,7 @@ msgstr ""
msgid "From Date and To Date are mandatory"
msgstr ""
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:29
-msgid "From Date and To Date are required"
-msgstr ""
-
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:40
+#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:29
msgid "From Date and To Date lie in different Fiscal Year"
msgstr ""
@@ -21730,10 +21860,7 @@ msgstr ""
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:53
#: erpnext/accounts/report/general_ledger/general_ledger.py:86
#: erpnext/accounts/report/pos_register/pos_register.py:124
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:32
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:35
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:39
-#: erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py:49
+#: erpnext/accounts/report/utils.py:30
msgid "From Date must be before To Date"
msgstr ""
@@ -21954,7 +22081,7 @@ msgstr ""
msgid "From date cannot be greater than To date"
msgstr ""
-#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:79
+#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:78
msgid "From value must be less than to value in row {0}"
msgstr ""
@@ -22093,13 +22220,13 @@ msgid "Further nodes can be only created under 'Group' type nodes"
msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:188
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1234
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1229
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:179
msgid "Future Payment Amount"
msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:187
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1233
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1228
msgid "Future Payment Ref"
msgstr ""
@@ -22190,7 +22317,7 @@ msgstr ""
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:138
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:225
-#: erpnext/setup/doctype/company/company.py:753
+#: erpnext/setup/doctype/company/company.py:754
msgid "Gain/Loss on Asset Disposal"
msgstr ""
@@ -22430,21 +22557,21 @@ msgstr ""
#: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js:119
#: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js:142
#: erpnext/manufacturing/doctype/production_plan/production_plan.json
-#: erpnext/public/js/controllers/buying.js:325
+#: erpnext/public/js/controllers/buying.js:330
#: erpnext/selling/doctype/quotation/quotation.js:182
#: erpnext/selling/doctype/sales_order/sales_order.js:201
#: erpnext/selling/doctype/sales_order/sales_order.js:1254
#: erpnext/stock/doctype/delivery_note/delivery_note.js:187
#: erpnext/stock/doctype/delivery_note/delivery_note.js:239
-#: erpnext/stock/doctype/material_request/material_request.js:144
-#: erpnext/stock/doctype/material_request/material_request.js:241
+#: erpnext/stock/doctype/material_request/material_request.js:145
+#: erpnext/stock/doctype/material_request/material_request.js:242
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:144
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:244
#: erpnext/stock/doctype/stock_entry/stock_entry.js:460
#: erpnext/stock/doctype/stock_entry/stock_entry.js:507
#: erpnext/stock/doctype/stock_entry/stock_entry.js:540
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:631
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:799
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:607
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:775
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:165
msgid "Get Items From"
msgstr ""
@@ -22459,9 +22586,9 @@ msgstr ""
msgid "Get Items for Purchase Only"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:347
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:835
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:848
+#: erpnext/stock/doctype/material_request/material_request.js:348
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:811
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:824
msgid "Get Items from BOM"
msgstr ""
@@ -22469,7 +22596,7 @@ msgstr ""
msgid "Get Items from Material Requests against this Supplier"
msgstr ""
-#: erpnext/public/js/controllers/buying.js:602
+#: erpnext/public/js/controllers/buying.js:607
msgid "Get Items from Product Bundle"
msgstr ""
@@ -22754,6 +22881,7 @@ msgstr ""
#. Label of the grand_total (Currency) field in DocType 'Supplier Quotation'
#. Label of the grand_total (Currency) field in DocType 'Production Plan Sales
#. Order'
+#. Label of the grand_total (Currency) field in DocType 'Proforma Invoice'
#. Option for the 'Apply Additional Discount On' (Select) field in DocType
#. 'Quotation'
#. Label of the base_grand_total (Currency) field in DocType 'Quotation'
@@ -22792,6 +22920,8 @@ msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order.json
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
#: erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.json
+#: erpnext/public/js/sales_order_proforma.js:283
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
#: erpnext/selling/doctype/quotation/quotation.json
#: erpnext/selling/doctype/sales_order/sales_order.json
#: erpnext/selling/page/point_of_sale/pos_item_cart.js:105
@@ -22813,12 +22943,12 @@ msgstr ""
#. Label of the base_grand_total (Currency) field in DocType 'Supplier
#. Quotation'
#: erpnext/accounts/doctype/pos_invoice/pos_invoice.json
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:246
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:244
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
msgid "Grand Total (Company Currency)"
msgstr ""
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:252
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:250
msgid "Grand Total (Transaction Currency)"
msgstr ""
@@ -22928,11 +23058,11 @@ msgstr ""
msgid "Gross and Net Profit Report"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:148
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:151
msgid "Group By Customer"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:126
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:129
msgid "Group By Supplier"
msgstr ""
@@ -22950,7 +23080,7 @@ msgstr ""
msgid "Group Same Items"
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:157
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:158
msgid "Group Warehouses cannot be used in transactions. Please change the value of {0}"
msgstr ""
@@ -22980,8 +23110,8 @@ msgstr ""
msgid "Group by Sales Order"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:156
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:188
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:159
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:191
msgid "Group by Voucher"
msgstr ""
@@ -23087,7 +23217,7 @@ msgstr ""
msgid "Hand"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:161
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:164
msgid "Handle Employee Advances"
msgstr ""
@@ -23288,7 +23418,7 @@ msgstr ""
msgid "Here are the error logs for the aforementioned failed depreciation entries: {0}"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2190
+#: erpnext/stock/stock_ledger.py:2205
msgid "Here are the options to proceed:"
msgstr ""
@@ -23351,6 +23481,12 @@ msgstr ""
msgid "Hide Images"
msgstr ""
+#. Label of the hide_item_qty (Check) field in DocType 'Proforma Invoice'
+#: erpnext/public/js/sales_order_proforma.js:99
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
+msgid "Hide Item Quantity in Print"
+msgstr ""
+
#: erpnext/selling/page/point_of_sale/pos_controller.js:261
msgid "Hide Recent Orders"
msgstr ""
@@ -23360,6 +23496,12 @@ msgstr ""
msgid "Hide Unavailable Items"
msgstr ""
+#. Description of the 'Hide Item Quantity in Print' (Check) field in DocType
+#. 'Proforma Invoice'
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
+msgid "Hide the item quantity and rate on the printed proforma."
+msgstr ""
+
#. Description of the 'Hide If Zero' (Check) field in DocType 'Financial Report
#. Row'
#: erpnext/accounts/doctype/financial_report_row/financial_report_row.json
@@ -23424,6 +23566,10 @@ msgstr ""
msgid "Holiday List"
msgstr ""
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:89
+msgid "Holiday List - {0} is not valid for current date."
+msgstr ""
+
#. Label of the holiday_list_name (Data) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json
msgid "Holiday List Name"
@@ -23519,7 +23665,7 @@ msgstr ""
msgid "Hrs"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:563
+#: erpnext/setup/doctype/company/company.py:564
msgid "Human Resources"
msgstr ""
@@ -23972,7 +24118,7 @@ msgstr ""
msgid "If no taxes are set, and Taxes and Charges Template is selected, the system will automatically apply the taxes from the chosen template."
msgstr ""
-#: erpnext/stock/stock_ledger.py:2200
+#: erpnext/stock/stock_ledger.py:2215
msgid "If not, you can Cancel / Submit this entry"
msgstr ""
@@ -24018,7 +24164,7 @@ msgstr ""
msgid "If the account is frozen, entries are allowed to restricted users."
msgstr ""
-#: erpnext/stock/stock_ledger.py:2193
+#: erpnext/stock/stock_ledger.py:2208
msgid "If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table."
msgstr ""
@@ -24188,7 +24334,7 @@ msgstr ""
msgid "Ignore Employee Time Overlap"
msgstr ""
-#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js:145
+#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js:135
msgid "Ignore Empty Stock"
msgstr ""
@@ -24286,7 +24432,7 @@ msgstr ""
msgid "Ignores legacy Is Opening field in GL Entry that allows adding opening balance post the system is in use while generating reports"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:274
+#: erpnext/stock/doctype/item/item.py:272
msgid "Image in the description has been removed. To disable this behavior, uncheck \"{0}\" in {1}."
msgstr ""
@@ -24423,8 +24569,14 @@ msgstr ""
msgid "In Mins"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:146
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:178
+#. Description of the 'Verification Link Expiry Duration' (Int) field in
+#. DocType 'Appointment Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "In Minutes (min: 15 mins, max: 60 mins)"
+msgstr ""
+
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:149
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:181
msgid "In Party Currency"
msgstr ""
@@ -24451,7 +24603,7 @@ msgid "In Production"
msgstr ""
#: erpnext/stock/report/available_serial_no/available_serial_no.py:112
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:87
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:82
#: erpnext/stock/report/stock_balance/stock_balance.py:547
#: erpnext/stock/report/stock_ledger/stock_ledger.py:317
msgid "In Qty"
@@ -24475,11 +24627,11 @@ msgstr ""
msgid "In Transit"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:478
+#: erpnext/stock/doctype/material_request/material_request.js:479
msgid "In Transit Transfer"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:447
+#: erpnext/stock/doctype/material_request/material_request.js:448
msgid "In Transit Warehouse"
msgstr ""
@@ -24936,7 +25088,7 @@ msgstr ""
msgid "Incorrect Batch Consumed"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:609
+#: erpnext/stock/doctype/item/item.py:607
msgid "Incorrect Check in (group) Warehouse for Reorder"
msgstr ""
@@ -24994,7 +25146,7 @@ msgstr ""
#: erpnext/stock/doctype/pick_list/pick_list.py:190
#: erpnext/stock/doctype/pick_list/pick_list.py:214
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:160
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:161
msgid "Incorrect Warehouse"
msgstr ""
@@ -25168,7 +25320,7 @@ msgstr ""
msgid "Inspected By"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:892
+#: erpnext/manufacturing/doctype/job_card/job_card.py:887
#: erpnext/public/js/shop_floor/shop_floor.js:1038
#: erpnext/stock/services/quality_inspection_service.py:147
msgid "Inspection Rejected"
@@ -25193,7 +25345,7 @@ msgstr ""
msgid "Inspection Required before Purchase"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:882
+#: erpnext/manufacturing/doctype/job_card/job_card.py:877
#: erpnext/stock/services/quality_inspection_service.py:132
msgid "Inspection Submission"
msgstr ""
@@ -25275,12 +25427,12 @@ msgstr ""
#: erpnext/stock/doctype/pick_list/pick_list.py:148
#: erpnext/stock/doctype/pick_list/pick_list.py:166
#: erpnext/stock/doctype/pick_list/pick_list.py:1139
-#: erpnext/stock/serial_batch_bundle.py:1243 erpnext/stock/stock_ledger.py:1875
-#: erpnext/stock/stock_ledger.py:2382
+#: erpnext/stock/serial_batch_bundle.py:1243 erpnext/stock/stock_ledger.py:1890
+#: erpnext/stock/stock_ledger.py:2397
msgid "Insufficient Stock"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2397
+#: erpnext/stock/stock_ledger.py:2412
msgid "Insufficient Stock for Batch"
msgstr ""
@@ -25435,7 +25587,7 @@ msgstr ""
msgid "Internal Customer Accounting"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:271
+#: erpnext/selling/doctype/customer/customer.py:269
msgid "Internal Customer for company {0} already exists"
msgstr ""
@@ -25461,7 +25613,7 @@ msgstr ""
msgid "Internal Supplier Details"
msgstr ""
-#: erpnext/buying/doctype/supplier/supplier.py:190
+#: erpnext/buying/doctype/supplier/supplier.py:188
msgid "Internal Supplier for company {0} already exists"
msgstr ""
@@ -25536,7 +25688,7 @@ msgid "Invalid Accounting Dimension"
msgstr ""
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:402
-#: erpnext/accounts/doctype/payment_request/payment_request.py:1167
+#: erpnext/accounts/doctype/payment_request/payment_request.py:1168
msgid "Invalid Allocated Amount"
msgstr ""
@@ -25565,7 +25717,7 @@ msgstr ""
msgid "Invalid Barcode. There is no Item attached to this barcode."
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:3277
+#: erpnext/public/js/controllers/transaction.js:3269
msgid "Invalid Blanket Order for the selected Customer and Item"
msgstr ""
@@ -25595,7 +25747,7 @@ msgstr ""
msgid "Invalid Cost Center"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:386
+#: erpnext/selling/doctype/customer/customer.py:384
msgid "Invalid Customer Group"
msgstr ""
@@ -25650,7 +25802,7 @@ msgstr ""
msgid "Invalid Item"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1571
+#: erpnext/stock/doctype/item/item.py:1569
msgid "Invalid Item Defaults"
msgstr ""
@@ -25672,11 +25824,11 @@ msgstr ""
msgid "Invalid POS Invoices"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:391
+#: erpnext/accounts/doctype/account/account.py:422
msgid "Invalid Parent Account"
msgstr ""
-#: erpnext/public/js/controllers/buying.js:424
+#: erpnext/public/js/controllers/buying.js:429
msgid "Invalid Part Number"
msgstr ""
@@ -25789,7 +25941,7 @@ msgstr ""
msgid "Invalid lost reason {0}, please create a new lost reason"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:483
+#: erpnext/stock/doctype/item/item.py:481
msgid "Invalid naming series (. missing) for {0}"
msgstr ""
@@ -25797,6 +25949,10 @@ msgstr ""
msgid "Invalid parameter. 'dn' should be of type str"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:773
+msgid "Invalid range. Use the format {0}"
+msgstr ""
+
#: erpnext/utilities/transaction_base.py:126
msgid "Invalid reference {0} {1}"
msgstr ""
@@ -25958,7 +26114,7 @@ msgstr ""
msgid "Invoice Document Type Selection Error"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1214
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1209
msgid "Invoice Grand Total"
msgstr ""
@@ -26063,7 +26219,7 @@ msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:171
#: erpnext/accounts/report/accounts_payable/accounts_payable.html:139
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.html:140
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1216
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1211
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:166
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:194
msgid "Invoiced Amount"
@@ -26085,7 +26241,7 @@ msgstr ""
#: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
#: erpnext/accounts/doctype/pos_profile/pos_profile.json
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1204
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:270
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:273
#: erpnext/buying/doctype/supplier/supplier.json
#: erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py:64
msgid "Invoices"
@@ -26695,7 +26851,7 @@ msgstr ""
msgid "Issue Date"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:183
+#: erpnext/stock/doctype/material_request/material_request.js:184
msgid "Issue Material"
msgstr ""
@@ -26742,8 +26898,10 @@ msgid "Issue a debit note against an existing Sales Invoice to adjust the rate.
msgstr ""
#. Option for the 'Current State' (Select) field in DocType 'Share Balance'
+#. Option for the 'Status' (Select) field in DocType 'Proforma Invoice'
#. Option for the 'Status' (Select) field in DocType 'Material Request'
#: erpnext/accounts/doctype/share_balance/share_balance.json
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
#: erpnext/stock/doctype/material_request/material_request.json
#: erpnext/stock/doctype/material_request/material_request_list.js:44
msgid "Issued"
@@ -26769,7 +26927,7 @@ msgstr ""
msgid "Issuing Date"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:654
+#: erpnext/stock/doctype/item/item.py:652
msgid "It can take upto few hours for accurate stock values to be visible after merging items."
msgstr ""
@@ -26852,6 +27010,7 @@ msgstr ""
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:385
#: erpnext/public/js/purchase_trends_filters.js:48
#: erpnext/public/js/purchase_trends_filters.js:63
+#: erpnext/public/js/sales_order_proforma.js:116
#: erpnext/public/js/sales_trends_filters.js:23
#: erpnext/public/js/sales_trends_filters.js:39
#: erpnext/public/js/stock_analytics.js:92
@@ -26881,7 +27040,7 @@ msgstr ""
#: erpnext/stock/report/available_serial_no/available_serial_no.py:93
#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js:24
#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:32
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:81
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:76
#: erpnext/stock/report/item_price_stock/item_price_stock.js:8
#: erpnext/stock/report/item_prices/item_prices.py:50
#: erpnext/stock/report/item_shortage_report/item_shortage_report.py:88
@@ -27043,6 +27202,7 @@ msgstr ""
#. Label of the item_code (Link) field in DocType 'Import Supplier Invoice'
#. Label of the item_code (Link) field in DocType 'Delivery Schedule Item'
#. Label of the item_code (Link) field in DocType 'Installation Note Item'
+#. Label of the item_code (Link) field in DocType 'Proforma Invoice Item'
#. Label of the item_code (Link) field in DocType 'Quotation Item'
#. Label of the item_code (Link) field in DocType 'Sales Order Item'
#. Label of the item_code (Link) field in DocType 'Bin'
@@ -27146,7 +27306,7 @@ msgstr ""
#: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py:86
#: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:128
#: erpnext/projects/doctype/timesheet/timesheet.js:216
-#: erpnext/public/js/controllers/transaction.js:2951
+#: erpnext/public/js/controllers/transaction.js:2943
#: erpnext/public/js/stock_reservation.js:112
#: erpnext/public/js/stock_reservation.js:318 erpnext/public/js/utils.js:608
#: erpnext/public/js/utils.js:765
@@ -27154,6 +27314,7 @@ msgstr ""
#: 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
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
#: erpnext/selling/doctype/quotation/quotation.js:297
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order/sales_order.js:369
@@ -27209,7 +27370,7 @@ msgstr ""
#: erpnext/stock/report/serial_no_and_batch_traceability/serial_no_and_batch_traceability.py:433
#: erpnext/stock/report/serial_no_ledger/serial_no_ledger.js:7
#: erpnext/stock/report/stock_ageing/stock_ageing.py:177
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:104
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:105
#: erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.py:25
#: erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py:26
#: erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json
@@ -27400,7 +27561,7 @@ msgstr ""
#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
#: erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
#: erpnext/stock/page/stock_balance/stock_balance.js:35
-#: erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py:54
+#: erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py:43
#: erpnext/stock/report/delayed_item_report/delayed_item_report.js:48
#: erpnext/stock/report/delayed_order_report/delayed_order_report.js:48
#: erpnext/stock/report/item_prices/item_prices.py:52
@@ -27416,7 +27577,7 @@ msgstr ""
#: erpnext/stock/report/stock_ledger/stock_ledger.js:71
#: erpnext/stock/report/stock_ledger/stock_ledger.py:345
#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.js:39
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:114
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:115
#: erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js:33
#: erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py:99
#: erpnext/stock/workspace/stock/stock.json
@@ -27546,6 +27707,7 @@ msgstr ""
#. Label of the item_name (Data) field in DocType 'Sales Forecast Item'
#. Label of the item_name (Data) field in DocType 'Work Order'
#. Label of the item_name (Data) field in DocType 'Work Order Item'
+#. Label of the item_name (Data) field in DocType 'Proforma Invoice Item'
#. Label of the item_name (Data) field in DocType 'Quotation Item'
#. Label of the item_name (Data) field in DocType 'Sales Order Item'
#. Label of the item_name (Data) field in DocType 'Batch'
@@ -27636,8 +27798,9 @@ msgstr ""
#: erpnext/manufacturing/report/production_planning_report/production_planning_report.py:378
#: 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:2957
+#: erpnext/public/js/controllers/transaction.js:2949
#: erpnext/public/js/utils.js:856
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order/sales_order.js:1324
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -27667,7 +27830,7 @@ msgstr ""
#: erpnext/stock/report/available_batch_report/available_batch_report.py:32
#: erpnext/stock/report/available_serial_no/available_serial_no.py:99
#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:33
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:82
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:77
#: erpnext/stock/report/delayed_item_report/delayed_item_report.py:153
#: erpnext/stock/report/item_price_stock/item_price_stock.py:24
#: erpnext/stock/report/item_prices/item_prices.py:51
@@ -27680,7 +27843,7 @@ msgstr ""
#: erpnext/stock/report/stock_analytics/stock_analytics.py:45
#: erpnext/stock/report/stock_balance/stock_balance.py:477
#: erpnext/stock/report/stock_ledger/stock_ledger.py:293
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:111
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:112
#: erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.py:31
#: erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py:32
#: erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py:98
@@ -27694,7 +27857,7 @@ msgstr ""
msgid "Item Name"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:417
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:418
msgid "Item Name is required."
msgstr ""
@@ -27750,7 +27913,7 @@ msgstr ""
msgid "Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:187
+#: erpnext/stock/doctype/item/item.py:186
msgid "Item Price created at rate {0}"
msgstr ""
@@ -27957,7 +28120,7 @@ msgstr ""
msgid "Item Variant {0} already exists with same attributes"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:845
+#: erpnext/stock/doctype/item/item.py:843
msgid "Item Variants updated"
msgstr ""
@@ -28065,7 +28228,7 @@ msgstr ""
msgid "Item for row {0} does not match Material Request"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:904
+#: erpnext/stock/doctype/item/item.py:902
msgid "Item has variants."
msgstr ""
@@ -28114,7 +28277,7 @@ msgstr ""
msgid "Item valuation reposting in progress. Report might show incorrect item valuation."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1061
+#: erpnext/stock/doctype/item/item.py:1059
msgid "Item variant {0} exists with same attributes"
msgstr ""
@@ -28139,7 +28302,7 @@ msgid "Item {0} cannot be received in more than {1} qty against the {2} {3}"
msgstr ""
#: erpnext/assets/doctype/asset/asset.py:347
-#: erpnext/stock/doctype/item/item.py:700
+#: erpnext/stock/doctype/item/item.py:698
msgid "Item {0} does not exist"
msgstr ""
@@ -28172,7 +28335,7 @@ msgstr ""
msgid "Item {0} has no changes in delivered quantity. Please unselect the row if you do not wish to update its quantity."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1283
+#: erpnext/stock/doctype/item/item.py:1281
msgid "Item {0} has reached its end of life on {1}"
msgstr ""
@@ -28188,11 +28351,11 @@ msgstr ""
msgid "Item {0} is already reserved/delivered against Sales Order {1}."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1303
+#: erpnext/stock/doctype/item/item.py:1301
msgid "Item {0} is cancelled"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1287
+#: erpnext/stock/doctype/item/item.py:1285
msgid "Item {0} is disabled"
msgstr ""
@@ -28204,7 +28367,7 @@ msgstr ""
msgid "Item {0} is not a serialized Item"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1295
+#: erpnext/stock/doctype/item/item.py:1293
msgid "Item {0} is not a stock Item"
msgstr ""
@@ -28212,7 +28375,7 @@ msgstr ""
msgid "Item {0} is not a subcontracted item"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:862
+#: erpnext/stock/doctype/item/item.py:860
msgid "Item {0} is not a template item."
msgstr ""
@@ -28428,7 +28591,7 @@ msgstr ""
#: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
#: erpnext/manufacturing/doctype/bom/bom.json
#: erpnext/manufacturing/doctype/job_card/job_card.json
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1078
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1073
#: erpnext/manufacturing/doctype/operation/operation.json
#: erpnext/manufacturing/doctype/work_order/work_order.js:417
#: erpnext/manufacturing/doctype/work_order/work_order.json
@@ -28457,7 +28620,7 @@ msgstr ""
msgid "Job Card Item"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:927
+#: erpnext/manufacturing/doctype/job_card/job_card.py:922
msgid "Job Card On Hold"
msgstr ""
@@ -28500,7 +28663,7 @@ msgstr ""
msgid "Job Card and Capacity Planning"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1629
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1636
msgid "Job Card {0} has been completed"
msgstr ""
@@ -28521,7 +28684,7 @@ msgstr ""
msgid "Job Card {0} was not found."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1422
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1429
msgid "Job Card {0}: As per the sequence of the operations in the work order {1}, complete the operation {2} before the operation {3}."
msgstr ""
@@ -28587,7 +28750,7 @@ msgstr ""
msgid "Job Worker Warehouse"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/mapper.py:460
+#: erpnext/manufacturing/doctype/work_order/mapper.py:464
msgid "Job card {0} created"
msgstr ""
@@ -28822,7 +28985,7 @@ msgstr ""
msgid "Kilowatt-Hour"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1080
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1075
msgid "Kindly cancel the Manufacturing Entries first against the work order {0}."
msgstr ""
@@ -28950,7 +29113,7 @@ msgstr ""
msgid "Last Fiscal Year"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:680
+#: erpnext/accounts/doctype/account/account.py:711
msgid "Last GL Entry update was done {0}. This operation is not allowed while system is actively being used. Please wait for 5 minutes before retrying."
msgstr ""
@@ -29458,7 +29621,7 @@ msgstr ""
msgid "Linked Location"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1137
+#: erpnext/stock/doctype/item/item.py:1135
msgid "Linked with submitted documents"
msgstr ""
@@ -29647,7 +29810,7 @@ msgstr ""
#. 'Quotation'
#: erpnext/crm/doctype/opportunity/opportunity.json
#: erpnext/crm/report/lost_opportunity/lost_opportunity.py:54
-#: erpnext/public/js/utils/sales_common.js:600
+#: erpnext/public/js/utils/sales_common.js:605
#: erpnext/selling/doctype/quotation/quotation.json
msgid "Lost Reasons"
msgstr ""
@@ -29809,7 +29972,7 @@ msgstr ""
msgid "MRP Log documents are being created in the background."
msgstr ""
-#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:156
+#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:180
msgid "MT940 file detected. Please enable 'Import MT940 Format' to proceed."
msgstr ""
@@ -29834,10 +29997,10 @@ msgstr ""
msgid "Machine operator errors"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:791
-#: erpnext/setup/doctype/company/company.py:806
+#: erpnext/setup/doctype/company/company.py:792
#: erpnext/setup/doctype/company/company.py:807
#: erpnext/setup/doctype/company/company.py:808
+#: erpnext/setup/doctype/company/company.py:809
msgid "Main"
msgstr ""
@@ -29974,11 +30137,11 @@ msgstr ""
msgid "Maintenance Schedule Item"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:372
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:373
msgid "Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule'"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:251
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:252
msgid "Maintenance Schedule {0} exists against {1}"
msgstr ""
@@ -30072,7 +30235,7 @@ msgstr ""
msgid "Maintenance Visit Purpose"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:354
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:355
msgid "Maintenance start date can not be before delivery date for Serial No {0}"
msgstr ""
@@ -30083,7 +30246,7 @@ msgstr ""
#. Label of the make (Data) field in DocType 'Vehicle'
#: erpnext/accounts/doctype/journal_entry/journal_entry.js:271
-#: erpnext/manufacturing/doctype/job_card/job_card.js:479
+#: erpnext/manufacturing/doctype/job_card/job_card.js:477
#: erpnext/manufacturing/doctype/work_order/work_order.js:864
#: erpnext/manufacturing/doctype/work_order/work_order.js:898
#: erpnext/setup/doctype/vehicle/vehicle.json
@@ -30149,7 +30312,7 @@ msgstr ""
msgid "Make Stock Entry"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:368
+#: erpnext/manufacturing/doctype/job_card/job_card.js:366
msgid "Make Subcontracting PO"
msgstr ""
@@ -30188,7 +30351,7 @@ msgstr ""
msgid "Manage your orders"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:569
+#: erpnext/setup/doctype/company/company.py:570
msgid "Management"
msgstr ""
@@ -30383,7 +30546,7 @@ msgstr ""
msgid "Manufacturer Part Number"
msgstr ""
-#: erpnext/public/js/controllers/buying.js:421
+#: erpnext/public/js/controllers/buying.js:426
msgid "Manufacturer Part Number {0} is invalid"
msgstr ""
@@ -30408,7 +30571,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
#: erpnext/manufacturing/workspace/manufacturing/manufacturing.json
#: erpnext/public/js/setup_wizard.js:94
-#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:29
+#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:30
#: erpnext/setup/doctype/company/company.json erpnext/setup/install.py:399
#: erpnext/setup/setup_wizard/data/industry_type.txt:31
#: erpnext/stock/doctype/batch/batch.json erpnext/stock/doctype/item/item.json
@@ -30623,6 +30786,12 @@ msgstr ""
msgid "Mark As Closed"
msgstr ""
+#. Option for the 'Action for Expired Unverified Appointments' (Select) field
+#. in DocType 'Appointment Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "Mark as Closed"
+msgstr ""
+
#. Description of the 'Is Internal Customer' (Check) field in DocType
#. 'Customer'
#: erpnext/selling/doctype/customer/customer.json
@@ -30643,7 +30812,7 @@ msgstr ""
msgid "Market Segment"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:521
+#: erpnext/setup/doctype/company/company.py:522
msgid "Marketing"
msgstr ""
@@ -30739,7 +30908,7 @@ msgstr ""
msgid "Material Consumption for Manufacture"
msgstr ""
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:687
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:663
msgid "Material Consumption is not set in Manufacturing Settings."
msgstr ""
@@ -30769,7 +30938,7 @@ msgstr ""
#. Option for the 'Purpose' (Select) field in DocType 'Stock Entry'
#. Option for the 'Purpose' (Select) field in DocType 'Stock Entry Type'
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:77
-#: erpnext/stock/doctype/material_request/material_request.js:191
+#: erpnext/stock/doctype/material_request/material_request.js:192
#: erpnext/stock/doctype/stock_entry/stock_entry.json
#: erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
msgid "Material Receipt"
@@ -30815,7 +30984,7 @@ msgstr ""
#: erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js:33
#: erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.py:186
#: erpnext/buying/workspace/buying/buying.json
-#: erpnext/manufacturing/doctype/job_card/job_card.js:216
+#: erpnext/manufacturing/doctype/job_card/job_card.js:214
#: erpnext/manufacturing/doctype/production_plan/production_plan.js:185
#: erpnext/manufacturing/doctype/production_plan/production_plan.json
#: erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
@@ -30918,7 +31087,7 @@ msgstr ""
msgid "Material Request already created for the ordered quantity"
msgstr ""
-#: erpnext/selling/doctype/sales_order/mapper.py:929
+#: erpnext/selling/doctype/sales_order/mapper.py:931
msgid "Material Request not created, as quantity for Raw Materials already available."
msgstr ""
@@ -30986,11 +31155,11 @@ msgstr ""
#. Option for the 'Purpose' (Select) field in DocType 'Pick List'
#. Option for the 'Purpose' (Select) field in DocType 'Stock Entry'
#. Option for the 'Purpose' (Select) field in DocType 'Stock Entry Type'
-#: erpnext/manufacturing/doctype/job_card/job_card.js:224
+#: erpnext/manufacturing/doctype/job_card/job_card.js:222
#: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:83
#: erpnext/stock/doctype/item/item.json
-#: erpnext/stock/doctype/material_request/material_request.js:169
+#: erpnext/stock/doctype/material_request/material_request.js:170
#: erpnext/stock/doctype/material_request/material_request.json
#: erpnext/stock/doctype/pick_list/pick_list.json
#: erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -30998,7 +31167,7 @@ msgstr ""
msgid "Material Transfer"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:175
+#: erpnext/stock/doctype/material_request/material_request.js:176
msgid "Material Transfer (In Transit)"
msgstr ""
@@ -31059,8 +31228,8 @@ msgstr ""
msgid "Materials are already received against the {0} {1}"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:190
-#: erpnext/manufacturing/doctype/job_card/job_card.py:904
+#: erpnext/manufacturing/doctype/job_card/job_card.py:185
+#: erpnext/manufacturing/doctype/job_card/job_card.py:899
msgid "Materials need to be transferred to the work in progress warehouse for the job card {0}"
msgstr ""
@@ -31234,7 +31403,7 @@ msgstr ""
msgid "Megawatt"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2206
+#: erpnext/stock/stock_ledger.py:2221
msgid "Mention Valuation Rate in the Item master."
msgstr ""
@@ -31282,7 +31451,7 @@ msgstr ""
msgid "Merged"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:616
+#: erpnext/accounts/doctype/account/account.py:647
msgid "Merging is only possible if following properties are same in both records. Is Group, Root Type, Company and Account Currency"
msgstr ""
@@ -31675,7 +31844,7 @@ msgstr ""
msgid "Missing Parameter"
msgstr ""
-#: erpnext/utilities/__init__.py:84
+#: erpnext/utilities/__init__.py:83 erpnext/utilities/__init__.py:88
msgid "Missing Payments App"
msgstr ""
@@ -31683,6 +31852,10 @@ msgstr ""
msgid "Missing Required Filter"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:671
+msgid "Missing Serial / Batch Nos will be created on Save"
+msgstr ""
+
#: erpnext/assets/doctype/asset_repair/asset_repair.py:300
msgid "Missing Serial No Bundle"
msgstr ""
@@ -31955,7 +32128,7 @@ msgstr ""
msgid "Multiple Accounts (Journal Template)"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:460
+#: erpnext/selling/doctype/customer/customer.py:458
msgid "Multiple Loyalty Programs found for Customer {0}. Please select manually."
msgstr ""
@@ -32552,6 +32725,10 @@ msgstr ""
msgid "New Note"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:320
+msgid "New Proforma Invoice"
+msgstr ""
+
#. Label of the purchase_invoice (Check) field in DocType 'Email Digest'
#: erpnext/setup/doctype/email_digest/email_digest.json
msgid "New Purchase Invoice"
@@ -32580,10 +32757,10 @@ msgstr ""
msgid "New Sales Invoice"
msgstr ""
-#. Description of the 'Overdue Billing Threshold' (Currency) field in DocType
-#. 'Customer Credit Limit'
+#. Description of the 'Overdue Limit' (Currency) field in DocType 'Customer
+#. Credit Limit'
#: erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
-msgid "New Sales Invoices are blocked when the customer's overdue amount exceeds this. Requires 'Enable Overdue Billing Threshold' in Accounts Settings."
+msgid "New Sales Invoices are blocked when the customer's overdue amount exceeds this. Requires 'Restrict Customer Over Billing' in Accounts Settings."
msgstr ""
#. Label of the sales_order (Check) field in DocType 'Email Digest'
@@ -32618,7 +32795,7 @@ msgstr ""
msgid "New Workplace"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:425
+#: erpnext/selling/doctype/customer/customer.py:423
msgid "New credit limit is less than current outstanding amount for the customer. Credit limit has to be at least {0}"
msgstr ""
@@ -32692,7 +32869,7 @@ msgstr ""
msgid "No Account Data row found"
msgstr ""
-#: erpnext/setup/doctype/company/test_company.py:104
+#: erpnext/setup/doctype/company/test_company.py:106
msgid "No Account matched these filters: {}"
msgstr ""
@@ -32772,7 +32949,7 @@ msgstr ""
#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1124
#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1200
#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1221
-#: erpnext/stock/doctype/item/item.py:1530
+#: erpnext/stock/doctype/item/item.py:1528
msgid "No Permission"
msgstr ""
@@ -32796,7 +32973,7 @@ msgstr ""
msgid "No Serial / Batches are available for return"
msgstr ""
-#: erpnext/stock/stock_ledger.py:976
+#: erpnext/stock/stock_ledger.py:991
msgid "No Standard Valuation Rate found for Item {0} in Company {1} as on {2}. Please create an Item Standard Cost record."
msgstr ""
@@ -32874,6 +33051,10 @@ msgstr ""
msgid "No additional fields available"
msgstr ""
+#: erpnext/crm/doctype/appointment/appointment.py:103
+msgid "No availability of slots are found. Please add on Appointment Booking Settings."
+msgstr ""
+
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1391
msgid "No available quantity to reserve for item {0} in warehouse {1}"
msgstr ""
@@ -32939,6 +33120,10 @@ msgstr ""
msgid "No entries found"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:302
+msgid "No entries found in the uploaded file"
+msgstr ""
+
#: banking/src/components/features/BankReconciliation/BankReconciliationStatement.tsx:214
msgid "No entries with a payment document in this list."
msgstr ""
@@ -33096,7 +33281,7 @@ msgstr ""
msgid "No page image is available for this page."
msgstr ""
-#: erpnext/public/js/controllers/buying.js:531
+#: erpnext/public/js/controllers/buying.js:536
msgid "No pending Material Requests found to link for the given items."
msgstr ""
@@ -33108,6 +33293,10 @@ msgstr ""
msgid "No products found."
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:260
+msgid "No proforma invoices yet."
+msgstr ""
+
#: erpnext/selling/page/point_of_sale/pos_item_cart.js:1029
msgid "No recent transactions found"
msgstr ""
@@ -33164,6 +33353,10 @@ msgstr ""
msgid "No rules setup yet"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:620
+msgid "No stock available for Item {0} in Warehouse {1}"
+msgstr ""
+
#: erpnext/stock/doctype/batch/batch.js:77
msgid "No stock available for this batch."
msgstr ""
@@ -33205,7 +33398,7 @@ msgstr ""
msgid "No vouchers found for this transaction"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1787
+#: erpnext/stock/doctype/item/item.py:1785
msgid "No warehouse found for company {0}. Please set a Default Warehouse in Item Defaults or Stock Settings."
msgstr ""
@@ -33393,7 +33586,7 @@ msgstr ""
msgid "Not permitted to make Purchase Orders"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1821
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1828
msgid "Not permitted to read Job Card"
msgstr ""
@@ -33427,7 +33620,7 @@ msgstr ""
msgid "Note: This Cost Center is a Group. Cannot make accounting entries against groups."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:691
+#: erpnext/stock/doctype/item/item.py:689
msgid "Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
msgstr ""
@@ -33886,7 +34079,7 @@ msgstr ""
msgid "Only Include Allocated Payments"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:137
+#: erpnext/accounts/doctype/account/account.py:138
msgid "Only Parent can be of type {0}"
msgstr ""
@@ -33894,6 +34087,10 @@ msgstr ""
msgid "Only Value available for Payment Entry"
msgstr ""
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:216
+msgid "Only an issued Proforma Invoice can be emailed."
+msgstr ""
+
#. Description of the 'Posting Date inheritance for exchange gain / loss'
#. (Select) field in DocType 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -34237,7 +34434,7 @@ msgstr ""
msgid "Opening Purchase Invoice(s) have been created."
msgstr ""
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:86
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:81
#: erpnext/stock/report/stock_balance/stock_balance.py:533
msgid "Opening Qty"
msgstr ""
@@ -34249,30 +34446,30 @@ msgstr ""
#. Label of the opening_stock (Float) field in DocType 'Item'
#. Option for the 'Purpose' (Select) field in DocType 'Stock Reconciliation'
#: erpnext/stock/doctype/item/item.js:986 erpnext/stock/doctype/item/item.json
-#: erpnext/stock/doctype/item/item.py:358
-#: erpnext/stock/doctype/item/item.py:1687
+#: erpnext/stock/doctype/item/item.py:356
+#: erpnext/stock/doctype/item/item.py:1685
#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
msgid "Opening Stock"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1641
+#: erpnext/stock/doctype/item/item.py:1639
msgid "Opening Stock can only be set for stock items."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1648
+#: erpnext/stock/doctype/item/item.py:1646
msgid "Opening Stock cannot be created as stock transactions already exist for item {0}."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1644
+#: erpnext/stock/doctype/item/item.py:1642
msgid "Opening Stock for serialised or batch items must be set via the Stock Reconciliation form."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:363
+#: erpnext/stock/doctype/item/item.py:361
msgid "Opening Stock reconciliation created with zero valuation rate: {0}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:371
-#: erpnext/stock/doctype/item/item.py:1690
+#: erpnext/stock/doctype/item/item.py:369
+#: erpnext/stock/doctype/item/item.py:1688
msgid "Opening Stock reconciliation created: {0}"
msgstr ""
@@ -34294,7 +34491,7 @@ msgstr ""
msgid "Opening and Closing balance is not supported for dimension grouped cash flow statement"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:203
+#: erpnext/stock/doctype/item/item.py:202
msgid "Opening stock creation has been queued and will be created in the background. Please check the Stock Reconciliation after some time."
msgstr ""
@@ -34386,6 +34583,10 @@ msgstr ""
msgid "Operation ID"
msgstr ""
+#: erpnext/manufacturing/doctype/job_card/job_card.js:521
+msgid "Operation Row"
+msgstr ""
+
#. Label of the operation_row_id (Int) field in DocType 'Job Card'
#: erpnext/manufacturing/doctype/job_card/job_card.json
msgid "Operation Row ID"
@@ -34396,11 +34597,6 @@ msgstr ""
msgid "Operation Row Id"
msgstr ""
-#. Label of the operation_row_number (Select) field in DocType 'Job Card'
-#: erpnext/manufacturing/doctype/job_card/job_card.json
-msgid "Operation Row Number"
-msgstr ""
-
#. Label of the time_in_mins (Float) field in DocType 'BOM Operation'
#. Label of the time_in_mins (Float) field in DocType 'BOM Website Operation'
#. Label of the time_in_mins (Float) field in DocType 'Sub Operation'
@@ -34425,14 +34621,18 @@ msgstr ""
msgid "Operation time does not depend on quantity to produce"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:517
-msgid "Operation {0} added multiple times in the work order {1}"
-msgstr ""
-
#: erpnext/manufacturing/doctype/job_card/job_card.py:1358
msgid "Operation {0} does not belong to the work order {1}"
msgstr ""
+#: erpnext/manufacturing/doctype/job_card/job_card.js:524
+msgid "Operation {0} is added multiple times in the work order {1}"
+msgstr ""
+
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1366
+msgid "Operation {0} is added multiple times in the work order {1}. Please select the operation row."
+msgstr ""
+
#: erpnext/manufacturing/doctype/workstation/workstation.py:384
msgid "Operation {0} is longer than any available working hours in workstation {1}, break down the operation into multiple operations"
msgstr ""
@@ -34448,7 +34648,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/work_order/work_order.js:334
#: erpnext/manufacturing/doctype/work_order/work_order.json
#: erpnext/public/js/shop_floor/shop_floor.js:387
-#: erpnext/setup/doctype/company/company.py:539
+#: erpnext/setup/doctype/company/company.py:540
#: erpnext/setup/doctype/email_digest/email_digest.json
#: erpnext/templates/generators/bom.html:61
msgid "Operations"
@@ -34768,7 +34968,8 @@ msgstr ""
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/stock/doctype/bin/bin.json
#: erpnext/stock/doctype/packed_item/packed_item.json
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:164
+#: erpnext/stock/page/stock_balance/stock_balance.js:60
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:165
msgid "Ordered Qty"
msgstr ""
@@ -34896,7 +35097,7 @@ msgid "Ounce/Gallon (US)"
msgstr ""
#: erpnext/stock/report/available_serial_no/available_serial_no.py:119
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:88
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:83
#: erpnext/stock/report/stock_balance/stock_balance.py:555
#: erpnext/stock/report/stock_ledger/stock_ledger.py:324
msgid "Out Qty"
@@ -35005,7 +35206,7 @@ msgstr ""
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#: erpnext/accounts/report/accounts_payable/accounts_payable.html:140
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.html:141
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1223
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1218
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:169
#: erpnext/accounts/report/purchase_register/purchase_register.py:307
#: erpnext/accounts/report/sales_register/sales_register.py:333
@@ -35122,19 +35323,23 @@ msgstr ""
msgid "Overdue"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:612
-msgid "Overdue Billing Limit Crossed"
+#. Label of the overdue_days (Data) field in DocType 'Overdue Payment'
+#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
+msgid "Overdue Days"
msgstr ""
#. Label of the overdue_billing_threshold (Currency) field in DocType 'Customer
#. Credit Limit'
#: erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
-msgid "Overdue Billing Threshold"
+msgid "Overdue Limit"
msgstr ""
-#. Label of the overdue_days (Data) field in DocType 'Overdue Payment'
-#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
-msgid "Overdue Days"
+#: erpnext/selling/doctype/customer/customer.py:608
+msgid "Overdue Limit Crossed"
+msgstr ""
+
+#: erpnext/selling/doctype/customer/customer.py:603
+msgid "Overdue Limit crossed for customer {0}. Overdue amount {1} exceeds the allowed limit {2}."
msgstr ""
#. Name of a DocType
@@ -35159,7 +35364,7 @@ msgstr ""
msgid "Overdue and Discounted"
msgstr ""
-#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:206
+#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:205
msgid "Overlapping conditions found between:"
msgstr ""
@@ -35193,15 +35398,6 @@ msgstr ""
msgid "Owned"
msgstr ""
-#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js:29
-#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:24
-#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:40
-#: erpnext/accounts/report/sales_register/sales_register.js:46
-#: erpnext/accounts/report/sales_register/sales_register.py:250
-#: erpnext/crm/report/lead_details/lead_details.py:45
-msgid "Owner"
-msgstr ""
-
#. Label of the asset_owner_section (Section Break) field in DocType 'Asset'
#: erpnext/assets/doctype/asset/asset.json
msgid "Ownership"
@@ -35689,7 +35885,7 @@ msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:173
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1217
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1212
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:167
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:201
#: erpnext/accounts/report/pos_register/pos_register.py:225
@@ -35849,7 +36045,7 @@ msgstr ""
msgid "Parent Company"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:674
+#: erpnext/setup/doctype/company/company.py:675
msgid "Parent Company must be a group company"
msgstr ""
@@ -35934,11 +36130,11 @@ msgstr ""
msgid "Parent Task"
msgstr ""
-#: erpnext/projects/doctype/task/task.py:169
+#: erpnext/projects/doctype/task/task.py:170
msgid "Parent Task {0} is not a Template Task"
msgstr ""
-#: erpnext/projects/doctype/task/task.py:192
+#: erpnext/projects/doctype/task/task.py:193
msgid "Parent Task {0} must be a Group Task"
msgstr ""
@@ -35958,7 +36154,7 @@ msgstr ""
msgid "Parent Warehouse"
msgstr ""
-#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:166
+#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:190
msgid "Parsed file is not in valid MT940 format or contains no transactions."
msgstr ""
@@ -36196,7 +36392,7 @@ msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:105
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:82
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:65
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1150
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1145
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:82
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:147
#: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js:49
@@ -36225,7 +36421,7 @@ msgstr ""
#. Name of a DocType
#: erpnext/accounts/doctype/party_account/party_account.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1162
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1157
msgid "Party Account"
msgstr ""
@@ -36410,7 +36606,7 @@ msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:92
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:69
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:52
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1144
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1139
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:69
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:141
#: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js:42
@@ -36535,7 +36731,7 @@ msgstr ""
msgid "Pause / Resume job"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:662
+#: erpnext/manufacturing/doctype/job_card/job_card.js:661
msgid "Pause Job"
msgstr ""
@@ -36586,15 +36782,15 @@ msgid "Payable"
msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:50
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:262
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1160
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:265
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1155
#: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:209
#: erpnext/accounts/report/purchase_register/purchase_register.py:212
#: erpnext/accounts/report/purchase_register/purchase_register.py:253
msgid "Payable Account"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:278
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:281
msgid "Payable Amount"
msgstr ""
@@ -36629,7 +36825,7 @@ msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order.js:395
#: erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py:24
#: erpnext/selling/doctype/sales_order/sales_order.js:1213
-#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:31
+#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:32
msgid "Payment"
msgstr ""
@@ -37069,7 +37265,7 @@ msgstr ""
msgid "Payment Schedule based Payment Requests cannot be created because a Payment Entry already exists for this document."
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:544
+#: erpnext/public/js/controllers/transaction.js:547
msgid "Payment Schedules"
msgstr ""
@@ -37087,10 +37283,10 @@ msgstr ""
#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json
#: erpnext/accounts/doctype/payment_term/payment_term.json
#: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1213
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1208
#: erpnext/accounts/report/gross_profit/gross_profit.py:451
#: erpnext/accounts/workspace/invoicing/invoicing.json
-#: erpnext/public/js/controllers/transaction.js:559
+#: erpnext/public/js/controllers/transaction.js:562
#: erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py:32
msgid "Payment Term"
msgstr ""
@@ -37362,7 +37558,7 @@ msgstr ""
#: erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py:54
#: erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py:44
-#: erpnext/manufacturing/doctype/job_card/job_card.js:272
+#: erpnext/manufacturing/doctype/job_card/job_card.js:270
#: erpnext/public/js/shop_floor/shop_floor.js:818
msgid "Pending Quantity"
msgstr ""
@@ -37403,11 +37599,11 @@ msgstr ""
msgid "Pending processing"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1605
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1612
msgid "Pending quantity cannot be greater than the for quantity."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1599
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1606
msgid "Pending quantity cannot be negative."
msgstr ""
@@ -37769,7 +37965,7 @@ msgstr ""
#. Label of a Workspace Sidebar Item
#: erpnext/selling/doctype/sales_order/sales_order.js:1066
#: erpnext/stock/doctype/delivery_note/delivery_note.js:199
-#: erpnext/stock/doctype/material_request/material_request.js:159
+#: erpnext/stock/doctype/material_request/material_request.js:160
#: erpnext/stock/doctype/pick_list/pick_list.json
#: erpnext/stock/doctype/stock_entry/stock_entry.json
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
@@ -38033,7 +38229,8 @@ msgstr ""
#: erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1031
#: erpnext/stock/doctype/bin/bin.json
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:150
+#: erpnext/stock/page/stock_balance/stock_balance.js:62
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:151
msgid "Planned Qty"
msgstr ""
@@ -38130,7 +38327,7 @@ msgstr ""
msgid "Please Specify Account"
msgstr ""
-#: erpnext/buying/doctype/supplier/supplier.py:137
+#: erpnext/buying/doctype/supplier/supplier.py:136
msgid "Please add 'Supplier' role to user {0}."
msgstr ""
@@ -38154,6 +38351,10 @@ msgstr ""
msgid "Please add a Temporary Opening account in Chart of Accounts"
msgstr ""
+#: erpnext/crm/doctype/appointment/appointment.py:95
+msgid "Please add a valid Holiday List on Appointment Booking Settings."
+msgstr ""
+
#: erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.py:119
msgid "Please add an account for the Bank Entry rule."
msgstr ""
@@ -38162,6 +38363,10 @@ msgstr ""
msgid "Please add at least one Serial No / Batch No"
msgstr ""
+#: erpnext/stock/doctype/serial_and_batch_bundle/inline_editor.py:132
+msgid "Please add at least one Serial No or Batch to save"
+msgstr ""
+
#: erpnext/stock/doctype/item/item.js:942
msgid "Please add at least one row in Item Defaults with a Company before setting opening stock."
msgstr ""
@@ -38174,7 +38379,7 @@ msgstr ""
msgid "Please add the Bank Account column"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:237
+#: erpnext/accounts/doctype/account/account.py:268
#: erpnext/accounts/doctype/account/account_tree.js:240
msgid "Please add the account to root level Company - {0}"
msgstr ""
@@ -38233,20 +38438,23 @@ msgstr ""
msgid "Please check your Plaid client ID and secret values"
msgstr ""
-#: erpnext/crm/doctype/appointment/appointment.py:98
#: erpnext/www/book_appointment/index.js:235
msgid "Please check your email to confirm the appointment"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:379
+#: erpnext/crm/doctype/appointment/appointment.py:184
+msgid "Please check your email to confirm the appointment."
+msgstr ""
+
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:380
msgid "Please click on 'Generate Schedule'"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:391
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:392
msgid "Please click on 'Generate Schedule' to fetch Serial No added for Item {0}"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:104
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:105
msgid "Please click on 'Generate Schedule' to get schedule"
msgstr ""
@@ -38266,15 +38474,15 @@ msgstr ""
msgid "Please contact any of the following users for this transaction."
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:551
+#: erpnext/selling/doctype/customer/customer.py:549
msgid "Please contact any of the following users to extend the credit limits for {0}: {1}"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:544
+#: erpnext/selling/doctype/customer/customer.py:542
msgid "Please contact your administrator to extend the credit limits for {0}."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:388
+#: erpnext/accounts/doctype/account/account.py:419
msgid "Please convert the parent account in corresponding child company to a group account."
msgstr ""
@@ -38298,7 +38506,7 @@ msgstr ""
msgid "Please create purchase receipt or purchase invoice for the item {0}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:721
+#: erpnext/stock/doctype/item/item.py:719
msgid "Please delete Product Bundle {0}, before merging {1} into {2}"
msgstr ""
@@ -38392,7 +38600,7 @@ msgstr ""
msgid "Please enter Item Code to get Batch Number"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:3134
+#: erpnext/public/js/controllers/transaction.js:3126
msgid "Please enter Item Code to get batch no"
msgstr ""
@@ -38400,7 +38608,7 @@ msgstr ""
msgid "Please enter Item first"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:222
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:223
msgid "Please enter Maintenance Details first"
msgstr ""
@@ -38449,6 +38657,11 @@ msgstr ""
msgid "Please enter Write Off Account"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:215
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:179
+msgid "Please enter a quantity or amount for at least one item."
+msgstr ""
+
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:511
msgid "Please enter a valid Write Off Account"
msgstr ""
@@ -38537,6 +38750,14 @@ msgstr ""
msgid "Please fill the Sales Orders table"
msgstr ""
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:57
+msgid "Please fill up the Availability of Slots table to enable Appointment Scheduling."
+msgstr ""
+
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:226
+msgid "Please find attached the proforma invoice {0}."
+msgstr ""
+
#: erpnext/stock/doctype/shipment/shipment.js:277
msgid "Please first set Full Name, Email and Phone for the user"
msgstr ""
@@ -38582,7 +38803,7 @@ msgstr ""
msgid "Please mention '{0}' in Company: {1}"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:230
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:231
msgid "Please mention no of visits required"
msgstr ""
@@ -38628,7 +38849,7 @@ msgstr ""
msgid "Please select Apply Discount On"
msgstr ""
-#: erpnext/selling/doctype/sales_order/mapper.py:851
+#: erpnext/selling/doctype/sales_order/mapper.py:853
msgid "Please select BOM against item {0}"
msgstr ""
@@ -38674,7 +38895,7 @@ msgstr ""
msgid "Please select Customer first"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:605
+#: erpnext/setup/doctype/company/company.py:606
msgid "Please select Existing Company for creating Chart of Accounts"
msgstr ""
@@ -38720,11 +38941,11 @@ msgstr ""
msgid "Please select Price List"
msgstr ""
-#: erpnext/selling/doctype/sales_order/mapper.py:853
+#: erpnext/selling/doctype/sales_order/mapper.py:855
msgid "Please select Qty against item {0}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:395
+#: erpnext/stock/doctype/item/item.py:393
msgid "Please select Sample Retention Warehouse in Stock Settings first"
msgstr ""
@@ -38732,7 +38953,7 @@ msgstr ""
msgid "Please select Serial/Batch Nos to reserve or change Reservation Based On to Qty."
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:228
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:229
msgid "Please select Start Date and End Date for Item {0}"
msgstr ""
@@ -38762,7 +38983,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/bom/bom.js:734
#: erpnext/manufacturing/doctype/bom/bom.py:302
#: erpnext/public/js/controllers/accounts.js:274
-#: erpnext/public/js/controllers/transaction.js:3433
+#: erpnext/public/js/controllers/transaction.js:3425
msgid "Please select a Company first."
msgstr ""
@@ -38775,6 +38996,10 @@ msgstr ""
msgid "Please select a Delivery Note"
msgstr ""
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:81
+msgid "Please select a Holiday List to enable Appointment Scheduling."
+msgstr ""
+
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:152
msgid "Please select a Subcontracting Purchase Order."
msgstr ""
@@ -38787,7 +39012,7 @@ msgstr ""
msgid "Please select a Warehouse"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1724
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1731
msgid "Please select a Work Order first."
msgstr ""
@@ -38857,6 +39082,10 @@ msgstr ""
msgid "Please select a valid document type."
msgstr ""
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1346
+msgid "Please select a valid {0}"
+msgstr ""
+
#: erpnext/selling/doctype/quotation/quotation.js:245
msgid "Please select a value for {0} quotation_to {1}"
msgstr ""
@@ -38893,7 +39122,7 @@ msgstr ""
msgid "Please select at least one row with difference value"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:587
+#: erpnext/public/js/controllers/transaction.js:599
msgid "Please select at least one schedule."
msgstr ""
@@ -38914,11 +39143,11 @@ msgstr ""
msgid "Please select dates to view the bank reconciliation statement."
msgstr ""
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:30
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:31
msgid "Please select either the Item or Warehouse or Warehouse Type filter to generate the report."
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:226
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:227
msgid "Please select item code"
msgstr ""
@@ -39059,6 +39288,12 @@ msgstr ""
msgid "Please set Parent Row No for item {0}"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:325
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:656
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:752
+msgid "Please set Rejected Warehouse first"
+msgstr ""
+
#: erpnext/accounts/doctype/ledger_merge/ledger_merge.js:24
#: erpnext/accounts/doctype/ledger_merge/ledger_merge.js:35
msgid "Please set Root Type"
@@ -39080,6 +39315,10 @@ msgstr ""
msgid "Please set Vat Accounts for Company: \"{0}\" in UAE VAT Settings"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:565
+msgid "Please set Warehouse first"
+msgstr ""
+
#: erpnext/accounts/doctype/account/account_tree.js:19
msgid "Please set a Company"
msgstr ""
@@ -39096,12 +39335,12 @@ msgstr ""
msgid "Please set a Purchase Price Variance Account for Item {0} or a Default Purchase Price Variance Account in Company {1}."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:346
-#: erpnext/stock/doctype/item/item.py:1674
+#: erpnext/stock/doctype/item/item.py:344
+#: erpnext/stock/doctype/item/item.py:1672
msgid "Please set a Temporary Opening account for company {0} to create an Opening Stock reconciliation."
msgstr ""
-#: erpnext/projects/doctype/project/project.py:837
+#: erpnext/projects/doctype/project/project.py:839
msgid "Please set a default Holiday List for Company {0}"
msgstr ""
@@ -39187,7 +39426,7 @@ msgstr ""
msgid "Please set opening number of booked depreciations"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:2800
+#: erpnext/public/js/controllers/transaction.js:2784
msgid "Please set recurring after saving"
msgstr ""
@@ -39294,7 +39533,7 @@ msgstr ""
msgid "Please specify from/to range"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:2656
+#: erpnext/public/js/controllers/transaction.js:2640
msgid "Please specify {0}. It is needed to fetch Item Details."
msgstr ""
@@ -39487,7 +39726,7 @@ msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:16
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:15
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:18
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1142
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1137
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:15
#: erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py:38
#: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html:7
@@ -39546,7 +39785,7 @@ msgstr ""
msgid "Posting Date inheritance for exchange gain / loss"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:1171
+#: erpnext/public/js/controllers/transaction.js:1155
msgid "Posting Date will change to today's date as Edit Posting Date and Time is unchecked. Are you sure want to proceed?"
msgstr ""
@@ -40514,7 +40753,7 @@ msgstr ""
msgid "Process Loss Qty"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:288
+#: erpnext/manufacturing/doctype/job_card/job_card.js:286
#: erpnext/public/js/shop_floor/shop_floor.js:834
msgid "Process Loss Quantity"
msgstr ""
@@ -40595,7 +40834,7 @@ msgstr ""
msgid "Process in Single Transaction"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1602
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1609
msgid "Process loss quantity cannot be negative."
msgstr ""
@@ -40702,8 +40941,8 @@ msgstr ""
#: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
#: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
#: erpnext/buying/workspace/buying/buying.json
-#: erpnext/public/js/controllers/buying.js:321
-#: erpnext/public/js/controllers/buying.js:606
+#: erpnext/public/js/controllers/buying.js:326
+#: erpnext/public/js/controllers/buying.js:611
#: erpnext/selling/doctype/product_bundle/product_bundle.json
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -40802,7 +41041,7 @@ msgstr ""
#. Label of a Card Break in the Manufacturing Workspace
#: erpnext/manufacturing/doctype/workstation/workstation.json
#: erpnext/manufacturing/workspace/manufacturing/manufacturing.json
-#: erpnext/setup/doctype/company/company.py:545
+#: erpnext/setup/doctype/company/company.py:546
msgid "Production"
msgstr ""
@@ -41013,7 +41252,58 @@ msgstr ""
msgid "Profitability Analysis"
msgstr ""
-#: erpnext/projects/doctype/task/task.py:155
+#. Label of the proforma_tab (Tab Break) field in DocType 'Sales Order'
+#: erpnext/selling/doctype/sales_order/sales_order.json
+#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:27
+msgid "Proforma"
+msgstr ""
+
+#. Name of a DocType
+#. Label of the proforma_invoice_section (Section Break) field in DocType
+#. 'Selling Settings'
+#: erpnext/public/js/sales_order_proforma.js:15
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
+#: erpnext/selling/doctype/selling_settings/selling_settings.js:53
+#: erpnext/selling/doctype/selling_settings/selling_settings.json
+msgid "Proforma Invoice"
+msgstr ""
+
+#. Name of a DocType
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
+msgid "Proforma Invoice Item"
+msgstr ""
+
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:235
+msgid "Proforma Invoice is not enabled in Selling Settings."
+msgstr ""
+
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:225
+msgid "Proforma Invoice {0}"
+msgstr ""
+
+#: erpnext/public/js/sales_order_proforma.js:236
+msgid "Proforma Invoice {0} created"
+msgstr ""
+
+#. Label of the proforma_html (HTML) field in DocType 'Sales Order'
+#: erpnext/selling/doctype/sales_order/sales_order.json
+msgid "Proforma Invoices"
+msgstr ""
+
+#: erpnext/public/js/sales_order_proforma.js:272
+msgid "Proforma No"
+msgstr ""
+
+#. Label of the proforma_pdf (Attach) field in DocType 'Proforma Invoice'
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
+msgid "Proforma PDF"
+msgstr ""
+
+#: erpnext/public/js/sales_order_proforma.js:349
+msgid "Proforma emailed"
+msgstr ""
+
+#: erpnext/projects/doctype/task/task.py:156
#, python-format
msgid "Progress % for a task cannot be more than 100."
msgstr ""
@@ -41022,7 +41312,7 @@ msgstr ""
msgid "Progress (%)"
msgstr ""
-#: erpnext/projects/doctype/project/project.py:434
+#: erpnext/projects/doctype/project/project.py:436
msgid "Project Collaboration Invitation"
msgstr ""
@@ -41070,7 +41360,7 @@ msgstr ""
msgid "Project Summary"
msgstr ""
-#: erpnext/projects/doctype/project/project.py:775
+#: erpnext/projects/doctype/project/project.py:777
msgid "Project Summary for {0}"
msgstr ""
@@ -41178,8 +41468,9 @@ msgstr ""
#: erpnext/stock/doctype/bin/bin.json
#: erpnext/stock/doctype/material_request_item/material_request_item.json
#: erpnext/stock/doctype/packed_item/packed_item.json
+#: erpnext/stock/page/stock_balance/stock_balance.js:51
#: erpnext/stock/report/item_shortage_report/item_shortage_report.py:73
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:206
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:214
#: erpnext/templates/emails/reorder_item.html:12
msgid "Projected Qty"
msgstr ""
@@ -41192,19 +41483,15 @@ msgstr ""
msgid "Projected Quantity Formula"
msgstr ""
-#: erpnext/stock/page/stock_balance/stock_balance.js:51
-msgid "Projected qty"
-msgstr ""
-
#. Label of a Desktop Icon
#. Name of a Workspace
#. Label of a Card Break in the Projects Workspace
#. Title of a Workspace Sidebar
#: erpnext/config/projects.py:7 erpnext/desktop_icon/projects.json
-#: erpnext/projects/doctype/project/project.py:542
+#: erpnext/projects/doctype/project/project.py:544
#: erpnext/projects/workspace/projects/projects.json
#: erpnext/selling/doctype/customer/customer_dashboard.py:26
-#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:28
+#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:29
#: erpnext/setup/doctype/company/company_dashboard.py:25
#: erpnext/workspace_sidebar/projects.json
msgid "Projects"
@@ -41362,7 +41649,7 @@ msgstr ""
msgid "Providing"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:644
+#: erpnext/setup/doctype/company/company.py:645
msgid "Provisional Account"
msgstr ""
@@ -41442,7 +41729,7 @@ msgstr ""
#: erpnext/accounts/doctype/tax_rule/tax_rule.json
#: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
#: erpnext/projects/doctype/project/project_dashboard.py:16
-#: erpnext/setup/doctype/company/company.py:533 erpnext/setup/install.py:413
+#: erpnext/setup/doctype/company/company.py:534 erpnext/setup/install.py:413
#: erpnext/stock/doctype/item/item.json
#: erpnext/stock/doctype/item/item_list.js:30
#: erpnext/stock/doctype/item_lead_time/item_lead_time.json
@@ -41664,7 +41951,7 @@ msgstr ""
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/setup/doctype/authorization_rule/authorization_rule.json
#: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
-#: erpnext/stock/doctype/material_request/material_request.js:199
+#: erpnext/stock/doctype/material_request/material_request.js:200
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:217
#: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
#: erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -42028,7 +42315,7 @@ msgstr ""
#. Option for the 'Order Type' (Select) field in DocType 'Blanket Order'
#. Label of the purchasing_tab (Tab Break) field in DocType 'Item'
#: erpnext/manufacturing/doctype/blanket_order/blanket_order.json
-#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:27
+#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:28
#: erpnext/stock/doctype/item/item.json
msgid "Purchasing"
msgstr ""
@@ -42162,8 +42449,10 @@ msgstr ""
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:333
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:398
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:499
+#: erpnext/public/js/sales_order_proforma.js:123
#: erpnext/public/js/stock_reservation.js:134
#: erpnext/public/js/stock_reservation.js:336 erpnext/public/js/utils.js:894
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:930
#: 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:395
@@ -42292,7 +42581,7 @@ msgstr ""
msgid "Qty To Manufacture ({0}) cannot be a fraction for the UOM {2}. To allow this, disable '{1}' in the UOM {2}."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:269
+#: erpnext/manufacturing/doctype/job_card/job_card.py:264
msgid "Qty To Manufacture in the job card cannot be greater than Qty To Manufacture in the work order for the operation {0}.
Solution: Either you can reduce the Qty To Manufacture in the job card or set the 'Overproduction Percentage For Work Order' in the {1}."
msgstr ""
@@ -42395,12 +42684,13 @@ msgstr ""
msgid "Qty to Disassemble"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:578
#: erpnext/public/js/utils/serial_no_batch_selector.js:385
msgid "Qty to Fetch"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:246
-#: erpnext/manufacturing/doctype/job_card/job_card.py:963
+#: erpnext/manufacturing/doctype/job_card/job_card.js:244
+#: erpnext/manufacturing/doctype/job_card/job_card.py:958
#: erpnext/public/js/shop_floor/shop_floor.js:792
msgid "Qty to Manufacture"
msgstr ""
@@ -42425,6 +42715,10 @@ msgstr ""
msgid "Qty to Receive"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:910
+msgid "Qty updated to {0} to match the Serial and Batch Bundle. Please save the document."
+msgstr ""
+
#. Label of the qualification_tab (Section Break) field in DocType 'Lead'
#. Label of the qualification (Data) field in DocType 'Employee Education'
#: erpnext/crm/doctype/lead/lead.json
@@ -42574,7 +42868,7 @@ msgstr ""
msgid "Quality Inspection Analysis"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:3057
+#: erpnext/public/js/controllers/transaction.js:3049
msgid "Quality Inspection Not Configured"
msgstr ""
@@ -42643,7 +42937,7 @@ msgstr ""
msgid "Quality Inspection Template Name"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:858
+#: erpnext/manufacturing/doctype/job_card/job_card.py:853
msgid "Quality Inspection is required for the item {0} before completing the job card {1}"
msgstr ""
@@ -42651,11 +42945,11 @@ msgstr ""
msgid "Quality Inspection {0} is Rejected. Resolve the issue or follow your rejection process before submitting the job card."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:877
+#: erpnext/manufacturing/doctype/job_card/job_card.py:872
msgid "Quality Inspection {0} is not submitted for the item: {1}"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:887
+#: erpnext/manufacturing/doctype/job_card/job_card.py:882
msgid "Quality Inspection {0} is rejected for the item: {1}"
msgstr ""
@@ -42669,7 +42963,7 @@ msgstr ""
msgid "Quality Inspections"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:575
+#: erpnext/setup/doctype/company/company.py:576
msgid "Quality Management"
msgstr ""
@@ -42760,6 +43054,8 @@ msgstr ""
#. Label of the qty (Float) field in DocType 'BOM Creator'
#. Label of the section_break_4rxf (Section Break) field in DocType 'Production
#. Plan Sub Assembly Item'
+#. Option for the 'Based On' (Select) field in DocType 'Proforma Invoice'
+#. Label of the qty (Float) field in DocType 'Proforma Invoice Item'
#. Label of the qty (Float) field in DocType 'Quotation Item'
#. Label of the qty (Float) field in DocType 'Sales Order Item'
#. Label of the qty (Float) field in DocType 'Delivery Note Item'
@@ -42801,9 +43097,11 @@ msgstr ""
#: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:194
#: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:218
#: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
-#: erpnext/public/js/controllers/buying.js:616
+#: erpnext/public/js/controllers/buying.js:621
#: erpnext/public/js/stock_analytics.js:50
#: erpnext/public/js/utils/serial_no_batch_selector.js:500
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/selling/page/point_of_sale/pos_item_cart.js:51
@@ -42812,11 +43110,11 @@ msgstr ""
#: erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py:39
#: erpnext/stock/dashboard/item_dashboard.js:248
#: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
-#: erpnext/stock/doctype/material_request/material_request.js:369
+#: erpnext/stock/doctype/material_request/material_request.js:370
#: erpnext/stock/doctype/material_request_item/material_request_item.json
#: erpnext/stock/doctype/packing_slip_item/packing_slip_item.json
#: erpnext/stock/doctype/pick_list_item/pick_list_item.json
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:828
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:804
#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
#: erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:36
@@ -42942,7 +43240,7 @@ msgstr ""
msgid "Quantity must be greater than zero"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1654
+#: erpnext/stock/doctype/item/item.py:1652
msgid "Quantity must be greater than zero."
msgstr ""
@@ -42960,8 +43258,8 @@ msgid "Quantity required for Item {0} in row {1}"
msgstr ""
#: erpnext/manufacturing/doctype/bom/bom.py:673
-#: erpnext/manufacturing/doctype/job_card/job_card.js:341
-#: erpnext/manufacturing/doctype/job_card/job_card.js:409
+#: erpnext/manufacturing/doctype/job_card/job_card.js:339
+#: erpnext/manufacturing/doctype/job_card/job_card.js:407
msgid "Quantity should be greater than 0"
msgstr ""
@@ -42969,7 +43267,7 @@ msgstr ""
msgid "Quantity to Manufacture"
msgstr ""
-#: erpnext/manufacturing/doctype/work_order/mapper.py:372
+#: erpnext/manufacturing/doctype/work_order/mapper.py:376
msgid "Quantity to Manufacture can not be zero for the operation {0}"
msgstr ""
@@ -43203,6 +43501,7 @@ msgstr ""
#. Label of the rate (Currency) field in DocType 'Work Order Additional Item'
#. Label of the rate (Currency) field in DocType 'Work Order Item'
#. Label of the rate (Float) field in DocType 'Product Bundle Item'
+#. Label of the rate (Currency) field in DocType 'Proforma Invoice Item'
#. Label of the rate (Currency) field in DocType 'Quotation Item'
#. Label of the rate (Currency) field in DocType 'Sales Order Item'
#. Label of the rate (Currency) field in DocType 'Delivery Note Item'
@@ -43252,6 +43551,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/work_order_item/work_order_item.json
#: erpnext/public/js/utils.js:904
#: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py:46
@@ -43660,7 +43960,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/work_order/work_order.js:788
#: erpnext/selling/doctype/sales_order/sales_order.js:1012
#: erpnext/selling/doctype/sales_order/sales_order_list.js:70
-#: erpnext/stock/doctype/material_request/material_request.js:246
+#: erpnext/stock/doctype/material_request/material_request.js:247
#: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.js:116
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:163
msgid "Re-open"
@@ -43790,10 +44090,6 @@ msgstr ""
msgid "Recalculate Batch Qty"
msgstr ""
-#: erpnext/stock/doctype/bin/bin.js:10
-msgid "Recalculate Bin Qty"
-msgstr ""
-
#. Label of the recalculate_rate (Check) field in DocType 'Stock Ledger Entry'
#: erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
msgid "Recalculate Incoming/Outgoing Rate"
@@ -43805,6 +44101,10 @@ msgstr ""
msgid "Recalculate Valuation Rate"
msgstr ""
+#: erpnext/stock/doctype/bin/bin.js:10
+msgid "Recalculate Values"
+msgstr ""
+
#. Option for the 'Status' (Select) field in DocType 'Asset'
#. Option for the 'Purpose' (Select) field in DocType 'Asset Movement'
#. Option for the 'Asset Status' (Select) field in DocType 'Serial No'
@@ -43856,7 +44156,7 @@ msgid "Receivable / Payable Account"
msgstr ""
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:79
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1158
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1153
#: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:241
#: erpnext/accounts/report/sales_register/sales_register.py:231
#: erpnext/accounts/report/sales_register/sales_register.py:285
@@ -44320,7 +44620,7 @@ msgstr ""
msgid "Reference #{0} dated {1}"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:2913
+#: erpnext/public/js/controllers/transaction.js:2905
msgid "Reference Date for Early Payment Discount"
msgstr ""
@@ -44370,7 +44670,7 @@ msgstr ""
msgid "Reference No is mandatory if you entered Reference Date"
msgstr ""
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:265
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:263
msgid "Reference No."
msgstr ""
@@ -44452,7 +44752,7 @@ msgstr ""
msgid "Reference number of the invoice from the previous system"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:142
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:143
msgid "Reference: {0}, Item Code: {1} and Customer: {2}"
msgstr ""
@@ -44540,6 +44840,18 @@ msgstr ""
msgid "Rejected Quantity"
msgstr ""
+#. Label of the rejected_serial_batch_entries_section (Section Break) field in
+#. DocType 'Purchase Invoice Item'
+#. Label of the rejected_serial_batch_entries_section (Section Break) field in
+#. DocType 'Purchase Receipt Item'
+#. Label of the rejected_serial_batch_entries_section (Section Break) field in
+#. DocType 'Subcontracting Receipt Item'
+#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+#: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+#: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
+msgid "Rejected Serial / Batch Entries"
+msgstr ""
+
#. Label of the rejected_serial_no (Text) field in DocType 'Purchase Invoice
#. Item'
#. Label of the rejected_serial_no (Text) field in DocType 'Purchase Receipt
@@ -44631,7 +44943,7 @@ msgid "Remaining Amount"
msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:189
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1235
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1230
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:180
msgid "Remaining Balance"
msgstr ""
@@ -44689,7 +45001,7 @@ msgstr ""
#: erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html:11
#: erpnext/accounts/report/accounts_payable/accounts_payable.html:135
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.html:136
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1267
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1262
#: erpnext/accounts/report/general_ledger/general_ledger.html:163
#: erpnext/accounts/report/general_ledger/general_ledger.py:818
#: erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py:121
@@ -44753,7 +45065,7 @@ msgstr ""
msgid "Rename Log"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:569
+#: erpnext/accounts/doctype/account/account.py:600
msgid "Rename Not Allowed"
msgstr ""
@@ -44770,7 +45082,7 @@ msgstr ""
msgid "Rename jobs for doctype {0} have not been enqueued."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:561
+#: erpnext/accounts/doctype/account/account.py:592
msgid "Renaming it is only allowed via parent company {0}, to avoid mismatch."
msgstr ""
@@ -44791,13 +45103,13 @@ msgstr ""
#. Label of the reorder_level (Float) field in DocType 'Material Request Item'
#: erpnext/stock/doctype/material_request_item/material_request_item.json
#: erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py:64
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:213
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:221
msgid "Reorder Level"
msgstr ""
#. Label of the reorder_qty (Float) field in DocType 'Material Request Item'
#: erpnext/stock/doctype/material_request_item/material_request_item.json
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:220
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:228
msgid "Reorder Qty"
msgstr ""
@@ -44890,7 +45202,7 @@ msgstr ""
msgid "Report Template"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:462
+#: erpnext/accounts/doctype/account/account.py:493
msgid "Report Type is mandatory"
msgstr ""
@@ -45144,7 +45456,7 @@ msgstr ""
#: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js:70
#: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py:270
#: erpnext/buying/workspace/buying/buying.json
-#: erpnext/stock/doctype/material_request/material_request.js:205
+#: erpnext/stock/doctype/material_request/material_request.js:206
#: erpnext/workspace_sidebar/buying.json
msgid "Request for Quotation"
msgstr ""
@@ -45202,7 +45514,8 @@ msgstr ""
#: erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py:45
#: erpnext/stock/doctype/bin/bin.json
#: erpnext/stock/doctype/packed_item/packed_item.json
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:157
+#: erpnext/stock/page/stock_balance/stock_balance.js:61
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:158
msgid "Requested Qty"
msgstr ""
@@ -45319,7 +45632,7 @@ msgstr ""
msgid "Research"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:581
+#: erpnext/setup/doctype/company/company.py:582
msgid "Research & Development"
msgstr ""
@@ -45410,7 +45723,7 @@ msgstr ""
msgid "Reserved"
msgstr ""
-#: erpnext/stock/services/serial_batch_bundle_service.py:664
+#: erpnext/stock/services/serial_batch_bundle_service.py:665
msgid "Reserved Batch Conflict"
msgstr ""
@@ -45428,8 +45741,9 @@ msgstr ""
#: erpnext/stock/dashboard/item_dashboard_list.html:20
#: erpnext/stock/doctype/bin/bin.json
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
+#: erpnext/stock/page/stock_balance/stock_balance.js:52
#: erpnext/stock/report/reserved_stock/reserved_stock.py:124
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:171
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:172
#: erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json
msgid "Reserved Qty"
msgstr ""
@@ -45443,11 +45757,13 @@ msgstr ""
#. Label of the reserved_qty_for_production (Float) field in DocType 'Bin'
#: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
#: erpnext/stock/doctype/bin/bin.json
+#: erpnext/stock/page/stock_balance/stock_balance.js:53
msgid "Reserved Qty for Production"
msgstr ""
#. Label of the reserved_qty_for_production_plan (Float) field in DocType 'Bin'
#: erpnext/stock/doctype/bin/bin.json
+#: erpnext/stock/page/stock_balance/stock_balance.js:57
msgid "Reserved Qty for Production Plan"
msgstr ""
@@ -45457,6 +45773,7 @@ msgstr ""
#. Label of the reserved_qty_for_sub_contract (Float) field in DocType 'Bin'
#: erpnext/stock/doctype/bin/bin.json
+#: erpnext/stock/page/stock_balance/stock_balance.js:54
msgid "Reserved Qty for Subcontract"
msgstr ""
@@ -45480,7 +45797,7 @@ msgstr ""
msgid "Reserved Quantity for Production"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2500
+#: erpnext/stock/stock_ledger.py:2515
msgid "Reserved Serial No."
msgstr ""
@@ -45494,15 +45811,17 @@ msgstr ""
#: erpnext/stock/dashboard/item_dashboard_list.html:15
#: erpnext/stock/doctype/bin/bin.json
#: erpnext/stock/doctype/pick_list/pick_list.js:178
+#: erpnext/stock/page/stock_balance/stock_balance.js:59
#: erpnext/stock/report/reserved_stock/reserved_stock.json
#: erpnext/stock/report/stock_balance/stock_balance.py:573
-#: erpnext/stock/stock_ledger.py:2484
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:207
+#: erpnext/stock/stock_ledger.py:2499
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:204
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:332
msgid "Reserved Stock"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2529
+#: erpnext/stock/stock_ledger.py:2544
msgid "Reserved Stock for Batch"
msgstr ""
@@ -45514,34 +45833,22 @@ msgstr ""
msgid "Reserved Stock for Sub-assembly"
msgstr ""
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:199
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:200
msgid "Reserved for POS Transactions"
msgstr ""
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:178
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:179
msgid "Reserved for Production"
msgstr ""
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:185
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:186
msgid "Reserved for Production Plan"
msgstr ""
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:192
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:193
msgid "Reserved for Sub Contracting"
msgstr ""
-#: erpnext/stock/page/stock_balance/stock_balance.js:53
-msgid "Reserved for manufacturing"
-msgstr ""
-
-#: erpnext/stock/page/stock_balance/stock_balance.js:52
-msgid "Reserved for sale"
-msgstr ""
-
-#: erpnext/stock/page/stock_balance/stock_balance.js:54
-msgid "Reserved for sub contracting"
-msgstr ""
-
#: erpnext/public/js/stock_reservation.js:203
#: erpnext/selling/doctype/sales_order/sales_order.js:421
#: erpnext/stock/doctype/pick_list/pick_list.js:307
@@ -45725,6 +46032,12 @@ msgstr ""
msgid "Restrict"
msgstr ""
+#. Label of the enable_overdue_billing_threshold (Check) field in DocType
+#. 'Accounts Settings'
+#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+msgid "Restrict Customer Over Billing"
+msgstr ""
+
#. Label of the restrict_based_on (Select) field in DocType 'Party Specific
#. Item'
#: erpnext/selling/doctype/party_specific_item/party_specific_item.json
@@ -45746,6 +46059,10 @@ msgstr ""
msgid "Restrict to Countries"
msgstr ""
+#: erpnext/stock/doctype/company_restriction/company_restriction.py:151
+msgid "Restricted to Other Companies"
+msgstr ""
+
#. Label of the result_key (Table) field in DocType 'Currency Exchange
#. Settings'
#: erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
@@ -45777,7 +46094,7 @@ msgstr ""
msgid "Resume"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:661
+#: erpnext/manufacturing/doctype/job_card/job_card.js:660
#: erpnext/public/js/templates/shop_floor_template.html:779
msgid "Resume Job"
msgstr ""
@@ -46021,10 +46338,10 @@ msgstr ""
msgid "Revaluation Journal: {0}"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:151
-#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:113
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:183
-#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:141
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:154
+#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:116
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:186
+#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:144
msgid "Revaluation Journals"
msgstr ""
@@ -46191,6 +46508,12 @@ msgstr ""
msgid "Rod"
msgstr ""
+#. Label of the role_allowed_to_bypass_overdue_billing (Link) field in DocType
+#. 'Accounts Settings'
+#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+msgid "Role Allowed to Bypass Over Billing Restriction"
+msgstr ""
+
#. Label of the role_allowed_to_over_deliver_receive (Link) field in DocType
#. 'Stock Settings'
#: erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -46208,12 +46531,6 @@ msgstr ""
msgid "Role allowed to bypass credit limit"
msgstr ""
-#. Label of the role_allowed_to_bypass_overdue_billing (Link) field in DocType
-#. 'Accounts Settings'
-#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
-msgid "Role allowed to bypass overdue billing limit"
-msgstr ""
-
#. Description of the 'Exempted Role' (Link) field in DocType 'Accounting
#. Period'
#: erpnext/accounts/doctype/accounting_period/accounting_period.json
@@ -46279,11 +46596,11 @@ msgstr ""
msgid "Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:459
+#: erpnext/accounts/doctype/account/account.py:490
msgid "Root Type is mandatory"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:219
+#: erpnext/accounts/doctype/account/account.py:250
msgid "Root cannot be edited."
msgstr ""
@@ -46497,7 +46814,7 @@ msgstr ""
msgid "Row #{0} (Payment Table): Amount must be positive"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:590
+#: erpnext/stock/doctype/item/item.py:588
msgid "Row #{0}: A reorder entry already exists for warehouse {1} with reorder type {2}."
msgstr ""
@@ -46603,7 +46920,7 @@ msgstr ""
msgid "Row #{0}: Cannot set Rate if the billed amount is greater than the amount for Item {1}."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1232
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1227
msgid "Row #{0}: Cannot transfer more than Required Qty {1} for Item {2} against Job Card {3}"
msgstr ""
@@ -46776,7 +47093,7 @@ msgstr ""
msgid "Row #{0}: From Date cannot be before To Date"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:944
+#: erpnext/manufacturing/doctype/job_card/job_card.py:939
msgid "Row #{0}: From Time and To Time fields are required"
msgstr ""
@@ -46926,7 +47243,7 @@ msgstr ""
msgid "Row #{0}: Please select the Sub Assembly Warehouse"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:597
+#: erpnext/stock/doctype/item/item.py:595
msgid "Row #{0}: Please set reorder quantity"
msgstr ""
@@ -47167,7 +47484,7 @@ msgstr ""
msgid "Row #{0}: The original Invoice {1} of return invoice {2} is not consolidated."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:606
+#: erpnext/stock/doctype/item/item.py:604
msgid "Row #{0}: The warehouse {1} is not a child warehouse of a group warehouse {2}"
msgstr ""
@@ -47224,7 +47541,7 @@ msgstr ""
msgid "Row #{0}: {1} account is not of type {2}"
msgstr ""
-#: erpnext/public/js/controllers/buying.js:261
+#: erpnext/public/js/controllers/buying.js:266
msgid "Row #{0}: {1} can not be negative for item {2}"
msgstr ""
@@ -47240,7 +47557,7 @@ msgstr ""
msgid "Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1562
+#: erpnext/stock/doctype/item/item.py:1560
msgid "Row #{0}: {1} {2} does not belong to Company {3}. Please select valid {4}."
msgstr ""
@@ -47296,7 +47613,7 @@ msgstr ""
msgid "Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2}"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:807
+#: erpnext/manufacturing/doctype/job_card/job_card.py:802
msgid "Row {0} : Operation is required against the raw material item {1}"
msgstr ""
@@ -47425,7 +47742,7 @@ msgstr ""
msgid "Row {0}: From Time and To Time is mandatory."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:356
+#: erpnext/manufacturing/doctype/job_card/job_card.py:351
msgid "Row {0}: From Time and To Time of {1} are overlapping with {2}"
msgstr ""
@@ -47437,7 +47754,7 @@ msgstr ""
msgid "Row {0}: From Warehouse is mandatory for internal transfers"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:337
+#: erpnext/manufacturing/doctype/job_card/job_card.py:332
msgid "Row {0}: From time must be less than to time"
msgstr ""
@@ -47593,7 +47910,7 @@ msgstr ""
msgid "Row {0}: The {3} Account {1} does not belong to the company {2}"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:215
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:216
msgid "Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}"
msgstr ""
@@ -47923,8 +48240,8 @@ msgstr ""
#: erpnext/regional/report/vat_audit_report/vat_audit_report.py:143
#: erpnext/selling/doctype/quotation/quotation.json
#: erpnext/selling/doctype/sales_order/sales_order.json
-#: erpnext/setup/doctype/company/company.py:527
-#: erpnext/setup/doctype/company/company.py:720
+#: erpnext/setup/doctype/company/company.py:528
+#: erpnext/setup/doctype/company/company.py:721
#: erpnext/setup/doctype/company/company_dashboard.py:9
#: erpnext/setup/doctype/sales_person/sales_person_dashboard.py:12
#: erpnext/setup/install.py:408
@@ -47939,7 +48256,7 @@ msgstr ""
msgid "Sales & Purchase"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:720
+#: erpnext/setup/doctype/company/company.py:721
msgid "Sales Account"
msgstr ""
@@ -48181,6 +48498,7 @@ msgstr ""
#. Label of the sales_order (Link) field in DocType 'Work Order'
#. Label of the sales_order (Link) field in DocType 'Project'
#. Label of the sales_order (Link) field in DocType 'Delivery Schedule Item'
+#. Label of the sales_order (Link) field in DocType 'Proforma Invoice'
#. Name of a DocType
#. Label of a Link in the Selling Workspace
#. Option for the 'Transaction' (Select) field in DocType 'Authorization Rule'
@@ -48215,6 +48533,7 @@ msgstr ""
#: erpnext/manufacturing/report/work_order_summary/work_order_summary.py:217
#: erpnext/projects/doctype/project/project.json
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
#: erpnext/selling/doctype/quotation/quotation.js:134
#: erpnext/selling/doctype/quotation/quotation_dashboard.py:11
#: erpnext/selling/doctype/quotation/quotation_list.js:16
@@ -48228,7 +48547,7 @@ msgstr ""
#: erpnext/setup/doctype/authorization_rule/authorization_rule.json
#: erpnext/stock/doctype/delivery_note/delivery_note.js:157
#: erpnext/stock/doctype/delivery_note/delivery_note.js:223
-#: erpnext/stock/doctype/material_request/material_request.js:239
+#: erpnext/stock/doctype/material_request/material_request.js:240
#: erpnext/stock/doctype/material_request_item/material_request_item.json
#: erpnext/stock/doctype/pick_list_item/pick_list_item.json
#: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -48271,6 +48590,7 @@ msgstr ""
#. Label of the sales_order_item (Data) field in DocType 'Work Order'
#. Label of the sales_order_item (Data) field in DocType 'Delivery Schedule
#. Item'
+#. Label of the so_detail (Data) field in DocType 'Proforma Invoice Item'
#. Name of a DocType
#. Label of the sales_order_item (Data) field in DocType 'Material Request
#. Item'
@@ -48289,6 +48609,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
#: erpnext/manufacturing/doctype/work_order/work_order.json
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
#: erpnext/selling/doctype/sales_order/sales_order.js:1351
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/stock/doctype/material_request_item/material_request_item.json
@@ -48344,8 +48665,8 @@ msgstr ""
msgid "Sales Order {0} is already linked to Project {1}, skipping the link."
msgstr ""
-#: erpnext/selling/doctype/sales_order/mapper.py:888
-#: erpnext/selling/doctype/sales_order/mapper.py:901
+#: erpnext/selling/doctype/sales_order/mapper.py:890
+#: erpnext/selling/doctype/sales_order/mapper.py:903
msgid "Sales Order {0} is not available for production"
msgstr ""
@@ -48410,8 +48731,8 @@ msgstr ""
#: erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:130
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1256
-#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:114
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1251
+#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:117
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:196
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:74
#: erpnext/selling/doctype/customer/customer.json
@@ -48516,8 +48837,8 @@ msgstr ""
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:158
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:136
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1253
-#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:120
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1248
+#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:123
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:193
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:80
#: erpnext/accounts/report/gross_profit/gross_profit.js:50
@@ -48779,7 +49100,7 @@ msgstr ""
#. Label of the sample_size (Float) field in DocType 'Quality Inspection'
#: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py:93
-#: erpnext/public/js/controllers/transaction.js:2970
+#: erpnext/public/js/controllers/transaction.js:2962
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
msgid "Sample Size"
msgstr ""
@@ -48822,6 +49143,10 @@ msgstr ""
msgid "Sazhen"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:368
+msgid "Scan / select Serial No"
+msgstr ""
+
#. Label of the scan_barcode (Data) field in DocType 'POS Invoice'
#. Label of the scan_barcode (Data) field in DocType 'Purchase Invoice'
#. Label of the scan_barcode (Data) field in DocType 'Sales Invoice'
@@ -48850,10 +49175,16 @@ msgstr ""
msgid "Scan Barcode"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:670
#: erpnext/public/js/utils/serial_no_batch_selector.js:171
msgid "Scan Batch No"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:230
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:664
+msgid "Scan Batch Nos"
+msgstr ""
+
#: erpnext/public/js/shop_floor/shop_floor.js:88
#: erpnext/public/js/shop_floor/shop_floor.js:1431
msgid "Scan Job Card"
@@ -48866,10 +49197,16 @@ msgstr ""
msgid "Scan Mode"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:670
#: erpnext/public/js/utils/serial_no_batch_selector.js:156
msgid "Scan Serial No"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:230
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:664
+msgid "Scan Serial Nos"
+msgstr ""
+
#: erpnext/public/js/utils/barcode_scanner.js:205
msgid "Scan barcode for item {0}"
msgstr ""
@@ -48878,7 +49215,7 @@ msgstr ""
msgid "Scan job card"
msgstr ""
-#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js:111
+#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js:101
msgid "Scan mode enabled, existing quantity will not be fetched."
msgstr ""
@@ -48896,6 +49233,10 @@ msgstr ""
msgid "Scanned Quantity"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:680
+msgid "Scanned: {0}"
+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'
@@ -48905,7 +49246,7 @@ msgstr ""
msgid "Schedule Date"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:553
+#: erpnext/public/js/controllers/transaction.js:556
msgid "Schedule Name"
msgstr ""
@@ -48916,7 +49257,7 @@ msgstr ""
msgid "Scheduled Date"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:432
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:433
msgid "Scheduled Date is required."
msgstr ""
@@ -49236,7 +49577,9 @@ msgid "Select BOM and Qty for Production"
msgstr ""
#: erpnext/assets/doctype/asset_repair/asset_repair.js:243
-#: erpnext/public/js/utils/sales_common.js:447
+#: erpnext/public/js/utils/sales_common.js:452
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:376
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:453
#: erpnext/stock/doctype/pick_list/pick_list.js:399
msgid "Select Batch No"
msgstr ""
@@ -49265,7 +49608,7 @@ msgstr ""
msgid "Select Company Address"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:476
+#: erpnext/manufacturing/doctype/job_card/job_card.js:474
msgid "Select Corrective Operation"
msgstr ""
@@ -49301,7 +49644,7 @@ msgstr ""
msgid "Select Dispatch Address "
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:705
+#: erpnext/manufacturing/doctype/job_card/job_card.js:704
msgid "Select Employees"
msgstr ""
@@ -49326,7 +49669,7 @@ msgstr ""
msgid "Select Items based on Delivery Date"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:3005
+#: erpnext/public/js/controllers/transaction.js:2997
msgid "Select Items for Quality Inspection"
msgstr ""
@@ -49356,7 +49699,11 @@ msgstr ""
msgid "Select Loyalty Program"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:539
+#: erpnext/manufacturing/doctype/job_card/job_card.js:534
+msgid "Select Operation Row"
+msgstr ""
+
+#: erpnext/public/js/controllers/transaction.js:542
msgid "Select Payment Schedule"
msgstr ""
@@ -49370,13 +49717,14 @@ msgid "Select Quantity"
msgstr ""
#: erpnext/assets/doctype/asset_repair/asset_repair.js:243
-#: erpnext/public/js/utils/sales_common.js:447
+#: erpnext/public/js/utils/sales_common.js:452
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:462
#: erpnext/stock/doctype/pick_list/pick_list.js:399
msgid "Select Serial No"
msgstr ""
#: erpnext/assets/doctype/asset_repair/asset_repair.js:246
-#: erpnext/public/js/utils/sales_common.js:450
+#: erpnext/public/js/utils/sales_common.js:455
#: erpnext/stock/doctype/pick_list/pick_list.js:402
msgid "Select Serial and Batch"
msgstr ""
@@ -49513,7 +49861,7 @@ msgstr ""
msgid "Select number of days"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:230
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:233
msgid "Select one or more Purchase Invoice rows"
msgstr ""
@@ -49719,7 +50067,7 @@ msgstr ""
#: erpnext/selling/doctype/selling_settings/selling_settings.json
#: erpnext/selling/workspace/selling/selling.json
#: erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:268
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:269
#: erpnext/workspace_sidebar/erpnext_settings.json
msgid "Selling Settings"
msgstr ""
@@ -49765,6 +50113,7 @@ msgstr ""
#. Label of the send_email (Check) field in DocType 'Request for Quotation
#. Supplier'
#: erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
+#: erpnext/public/js/sales_order_proforma.js:303
msgid "Send Email"
msgstr ""
@@ -49776,8 +50125,12 @@ msgstr ""
msgid "Send Emails to Suppliers"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:354
+msgid "Send Proforma Invoice"
+msgstr ""
+
#. Label of the send_sms (Button) field in DocType 'SMS Center'
-#: erpnext/public/js/controllers/transaction.js:762
+#: erpnext/public/js/controllers/transaction.js:746
#: erpnext/selling/doctype/sms_center/sms_center.json
msgid "Send SMS"
msgstr ""
@@ -49855,6 +50208,48 @@ msgstr ""
msgid "Serial / Batch Bundle Missing"
msgstr ""
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'POS Invoice Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Purchase Invoice Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Sales Invoice Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Asset Capitalization Stock Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Asset Repair Consumed Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Delivery Note Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Packed Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Pick List Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Purchase Receipt Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Stock Entry Detail'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Stock Reconciliation Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Subcontracting Receipt Item'
+#. Label of the serial_batch_entries_section (Section Break) field in DocType
+#. 'Subcontracting Receipt Supplied Item'
+#: erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+#: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+#: erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
+#: erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
+#: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+#: erpnext/stock/doctype/packed_item/packed_item.json
+#: erpnext/stock/doctype/pick_list_item/pick_list_item.json
+#: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+#: erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+#: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
+#: erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
+msgid "Serial / Batch Entries"
+msgstr ""
+
#. Label of the serial_no_and_batch_no_tab (Section Break) field in DocType
#. 'Serial and Batch Bundle'
#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
@@ -49919,7 +50314,8 @@ msgstr ""
#: erpnext/manufacturing/doctype/job_card/job_card.json
#: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js:74
#: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py:114
-#: erpnext/public/js/controllers/transaction.js:2983
+#: erpnext/public/js/controllers/transaction.js:2975
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:928
#: erpnext/public/js/utils/serial_no_batch_selector.js:433
#: erpnext/selling/doctype/installation_note_item/installation_note_item.json
#: erpnext/stock/doctype/batch/batch.py:393
@@ -49981,6 +50377,7 @@ msgstr ""
msgid "Serial No Ledger"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:762
#: erpnext/public/js/utils/serial_no_batch_selector.js:271
msgid "Serial No Range"
msgstr ""
@@ -49989,7 +50386,7 @@ msgstr ""
msgid "Serial No Reserved"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:501
+#: erpnext/stock/doctype/item/item.py:499
msgid "Serial No Series Overlap"
msgstr ""
@@ -50050,6 +50447,10 @@ msgstr ""
msgid "Serial No is mandatory for Item {0}"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:724
+msgid "Serial No {0} already added"
+msgstr ""
+
#: erpnext/public/js/utils/serial_no_batch_selector.js:604
msgid "Serial No {0} already exists"
msgstr ""
@@ -50062,13 +50463,13 @@ msgstr ""
msgid "Serial No {0} does not belong to Delivery Note {1}"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:326
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:327
msgid "Serial No {0} does not belong to Item {1}"
msgstr ""
#: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py:52
#: erpnext/selling/doctype/installation_note/installation_note.py:84
-#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3566
+#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3634
msgid "Serial No {0} does not exist"
msgstr ""
@@ -50088,15 +50489,15 @@ msgstr ""
msgid "Serial No {0} is not present in the {1} {2}, hence you can't return it against the {1} {2}"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:343
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:344
msgid "Serial No {0} is under maintenance contract until {1}"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:336
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:337
msgid "Serial No {0} is under warranty until {1}"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:322
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:323
msgid "Serial No {0} not found"
msgstr ""
@@ -50127,7 +50528,7 @@ msgstr ""
msgid "Serial Nos are created successfully"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2490
+#: erpnext/stock/stock_ledger.py:2505
msgid "Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding."
msgstr ""
@@ -50208,7 +50609,7 @@ msgstr ""
msgid "Serial and Batch Bundle"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1155
+#: erpnext/stock/doctype/item/item.py:1153
msgid "Serial and Batch Bundle Exists"
msgstr ""
@@ -50228,11 +50629,12 @@ msgstr ""
msgid "Serial and Batch Bundle {0} is not submitted"
msgstr ""
+#: erpnext/stock/doctype/serial_and_batch_bundle/inline_editor.py:173
#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2337
msgid "Serial and Batch Bundle {0} is submitted and its entries cannot be modified."
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:298
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:299
msgid "Serial and Batch Bundle {0} should have voucher type as 'Maintenance Schedule'"
msgstr ""
@@ -50297,7 +50699,7 @@ msgstr ""
msgid "Series for Asset Depreciation Entry (Journal Entry)"
msgstr ""
-#: erpnext/buying/doctype/supplier/supplier.py:151
+#: erpnext/buying/doctype/supplier/supplier.py:150
msgid "Series is mandatory"
msgstr ""
@@ -50489,12 +50891,12 @@ msgid "Service Stop Date"
msgstr ""
#: erpnext/accounts/deferred_revenue.py:45
-#: erpnext/public/js/controllers/transaction.js:1843
+#: erpnext/public/js/controllers/transaction.js:1827
msgid "Service Stop Date cannot be after Service End Date"
msgstr ""
#: erpnext/accounts/deferred_revenue.py:42
-#: erpnext/public/js/controllers/transaction.js:1840
+#: erpnext/public/js/controllers/transaction.js:1824
msgid "Service Stop Date cannot be before Service Start Date"
msgstr ""
@@ -50537,8 +50939,8 @@ msgstr ""
msgid "Set Dropship Items Delivered Quantity"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:362
-#: erpnext/manufacturing/doctype/job_card/job_card.js:424
+#: erpnext/manufacturing/doctype/job_card/job_card.js:360
+#: erpnext/manufacturing/doctype/job_card/job_card.js:422
msgid "Set Finished Good Quantity"
msgstr ""
@@ -50638,7 +51040,7 @@ msgstr ""
#. Label of the set_warehouse (Link) field in DocType 'Sales Order'
#. Label of the set_warehouse (Link) field in DocType 'Delivery Note'
#. Label of the set_from_warehouse (Link) field in DocType 'Material Request'
-#: erpnext/public/js/utils/sales_common.js:572
+#: erpnext/public/js/utils/sales_common.js:577
#: erpnext/selling/doctype/sales_order/sales_order.json
#: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/material_request/material_request.json
@@ -50656,7 +51058,7 @@ msgstr ""
#. Label of the set_warehouse (Link) field in DocType 'Subcontracting Order'
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#: erpnext/buying/doctype/purchase_order/purchase_order.json
-#: erpnext/public/js/utils/sales_common.js:569
+#: erpnext/public/js/utils/sales_common.js:574
#: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/material_request/material_request.json
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
@@ -50682,7 +51084,7 @@ msgstr ""
msgid "Set as Completed"
msgstr ""
-#: erpnext/public/js/utils/sales_common.js:596
+#: erpnext/public/js/utils/sales_common.js:601
#: erpnext/selling/doctype/quotation/quotation.js:146
msgid "Set as Lost"
msgstr ""
@@ -50709,11 +51111,11 @@ msgstr ""
msgid "Set closing balance as per bank statement"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:617
+#: erpnext/setup/doctype/company/company.py:618
msgid "Set default inventory account for perpetual inventory"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:643
+#: erpnext/setup/doctype/company/company.py:644
msgid "Set default {0} account for non stock items"
msgstr ""
@@ -50833,7 +51235,7 @@ msgstr ""
msgid "Setting Account Type helps in selecting this Account in transactions."
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:129
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:130
msgid "Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}"
msgstr ""
@@ -51104,7 +51506,7 @@ msgstr ""
msgid "Shipping Address does not belong to the {0}"
msgstr ""
-#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:134
+#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:133
msgid "Shipping Address does not have country, which is required for this Shipping Rule"
msgstr ""
@@ -51197,15 +51599,15 @@ msgstr ""
msgid "Shipping Zipcode"
msgstr ""
-#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:138
+#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:137
msgid "Shipping rule not applicable for country {0} in Shipping Address"
msgstr ""
-#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:157
+#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:156
msgid "Shipping rule only applicable for Buying"
msgstr ""
-#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:152
+#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:151
msgid "Shipping rule only applicable for Selling"
msgstr ""
@@ -51261,7 +51663,7 @@ msgstr ""
msgid "Short-term Provisions"
msgstr ""
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:227
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:235
msgid "Shortage Qty"
msgstr ""
@@ -51316,14 +51718,14 @@ msgstr ""
#. Label of the show_future_payments (Check) field in DocType 'Process
#. Statement Of Accounts'
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:141
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:158
-#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:131
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:144
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:161
+#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:134
msgid "Show Future Payments"
msgstr ""
-#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:118
-#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:136
+#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:121
+#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:139
msgid "Show GL Balance"
msgstr ""
@@ -51357,7 +51759,7 @@ msgstr ""
msgid "Show Ledger View"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:163
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:166
msgid "Show Linked Delivery Notes"
msgstr ""
@@ -51405,8 +51807,8 @@ msgstr ""
#. Label of the show_remarks (Check) field in DocType 'Process Statement Of
#. Accounts'
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:136
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:173
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:139
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:176
#: erpnext/accounts/report/general_ledger/general_ledger.js:219
msgid "Show Remarks"
msgstr ""
@@ -51416,7 +51818,7 @@ msgstr ""
msgid "Show Return Entries"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:168
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:171
msgid "Show Sales Person"
msgstr ""
@@ -51436,6 +51838,12 @@ msgstr ""
msgid "Show Warehouse-wise Stock"
msgstr ""
+#. Description of the 'Use Inline Serial / Batch Editor' (Check) field in
+#. DocType 'Stock Settings'
+#: erpnext/stock/doctype/stock_settings/stock_settings.json
+msgid "Show an inline editable table for serial numbers / batches on the item row instead of the dialog"
+msgstr ""
+
#: erpnext/manufacturing/report/bom_stock_analysis/bom_stock_analysis.js:26
msgid "Show availability of exploded items"
msgstr ""
@@ -51838,11 +52246,11 @@ msgstr ""
#: erpnext/manufacturing/doctype/work_order_item/work_order_item.json
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
#: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:135
-#: erpnext/public/js/utils/sales_common.js:568
+#: erpnext/public/js/utils/sales_common.js:573
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
#: erpnext/stock/dashboard/item_dashboard.js:227
#: erpnext/stock/doctype/material_request_item/material_request_item.json
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:819
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:795
#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
msgid "Source Warehouse"
msgstr ""
@@ -51982,7 +52390,7 @@ msgid "Split commission credit across multiple sales persons."
msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order.js:600
-#: erpnext/public/js/controllers/buying.js:558
+#: erpnext/public/js/controllers/buying.js:563
msgid "Splitting {0} units of {1}"
msgstr ""
@@ -52072,8 +52480,7 @@ msgstr ""
#: erpnext/setup/setup_wizard/operations/defaults_setup.py:70
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:493
-#: erpnext/stock/doctype/item/item.py:296 erpnext/tests/utils.py:284
-#: erpnext/tests/utils.py:2524
+#: erpnext/tests/utils.py:284 erpnext/tests/utils.py:2524
msgid "Standard Selling"
msgstr ""
@@ -52158,7 +52565,7 @@ msgstr ""
msgid "Start Date should be lower than End Date"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.js:660
+#: erpnext/manufacturing/doctype/job_card/job_card.js:659
#: erpnext/public/js/shop_floor/shop_floor.js:710
#: erpnext/public/js/templates/shop_floor_template.html:728
msgid "Start Job"
@@ -52201,7 +52608,7 @@ msgstr ""
msgid "Start date of current invoice's period"
msgstr ""
-#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:233
+#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:234
msgid "Start date should be less than end date for Item {0}"
msgstr ""
@@ -52301,7 +52708,7 @@ msgstr ""
msgid "Status and Reference"
msgstr ""
-#: erpnext/projects/doctype/project/project.py:818
+#: erpnext/projects/doctype/project/project.py:820
msgid "Status must be Cancelled or Completed"
msgstr ""
@@ -52320,6 +52727,7 @@ msgstr ""
#. Name of a Workspace
#. Title of a Workspace Sidebar
#: erpnext/accounts/doctype/account/account.json
+#: erpnext/accounts/doctype/account/account.py:228
#: erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py:11
#: erpnext/accounts/report/account_balance/account_balance.js:57
#: erpnext/desktop_icon/stock.json
@@ -52523,7 +52931,7 @@ msgstr ""
msgid "Stock Entry {0} created"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1645
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1652
msgid "Stock Entry {0} has been created"
msgstr ""
@@ -52563,7 +52971,7 @@ msgstr ""
#. Name of a report
#. Label of a Link in the Stock Workspace
#. Label of a Workspace Sidebar Item
-#: erpnext/public/js/controllers/stock_controller.js:67
+#: erpnext/public/js/controllers/stock_controller.js:97
#: erpnext/public/js/utils/ledger_preview.js:37
#: erpnext/stock/doctype/item/item.js:191
#: erpnext/stock/doctype/item/item_dashboard.py:8
@@ -52736,7 +53144,7 @@ msgstr ""
#. Label of a Link in the Stock Workspace
#. Label of a Workspace Sidebar Item
#: erpnext/setup/workspace/home/home.json
-#: erpnext/stock/doctype/item/item.py:682
+#: erpnext/stock/doctype/item/item.py:680
#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
#: erpnext/stock/doctype/stock_settings/stock_settings.js:137
#: erpnext/stock/workspace/stock/stock.json
@@ -52755,7 +53163,7 @@ msgstr ""
msgid "Stock Reconciliation that revalues on-hand stock to this standard rate: auto-created when the rate is changed here, or the reconciliation that captured this rate (opening entry or rate change)."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:682
+#: erpnext/stock/doctype/item/item.py:680
msgid "Stock Reconciliations"
msgstr ""
@@ -52803,9 +53211,9 @@ msgstr ""
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1732
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1749
#: erpnext/stock/doctype/stock_settings/stock_settings.json
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:225
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:237
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:251
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:226
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:238
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:252
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:181
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:194
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:206
@@ -52819,7 +53227,7 @@ msgid "Stock Reservation Entries Cancelled"
msgstr ""
#: erpnext/controllers/subcontracting_inward_controller.py:1062
-#: erpnext/manufacturing/doctype/production_plan/services/reservation.py:152
+#: erpnext/manufacturing/doctype/production_plan/services/reservation.py:147
#: erpnext/manufacturing/doctype/work_order/services/reservation.py:597
#: erpnext/selling/doctype/sales_order/services/reservation.py:133
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1810
@@ -52886,7 +53294,7 @@ msgstr ""
#. Name of a DocType
#. Label of a Link in the Stock Workspace
#. Label of a Workspace Sidebar Item
-#: erpnext/selling/doctype/selling_settings/selling_settings.py:115
+#: erpnext/selling/doctype/selling_settings/selling_settings.py:117
#: erpnext/setup/doctype/company/company.json
#: erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
#: erpnext/stock/doctype/item/item.js:497
@@ -53201,8 +53609,8 @@ msgstr ""
#: erpnext/setup/doctype/company/company.py:454
#: erpnext/setup/setup_wizard/operations/defaults_setup.py:33
#: erpnext/setup/setup_wizard/operations/install_fixtures.py:537
-#: erpnext/stock/doctype/item/item.py:334
-#: erpnext/stock/doctype/item/item.py:1781 erpnext/tests/utils.py:249
+#: erpnext/stock/doctype/item/item.py:332
+#: erpnext/stock/doctype/item/item.py:1779 erpnext/tests/utils.py:249
msgid "Stores"
msgstr ""
@@ -53265,7 +53673,7 @@ msgstr ""
#. Label of the operation (Link) field in DocType 'Job Card Time Log'
#. Name of a DocType
-#: erpnext/manufacturing/doctype/job_card/job_card.js:309
+#: erpnext/manufacturing/doctype/job_card/job_card.js:307
#: erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json
#: erpnext/manufacturing/doctype/sub_operation/sub_operation.json
msgid "Sub Operation"
@@ -53342,7 +53750,7 @@ msgstr ""
msgid "Subcontracted Item To Be Received"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:227
+#: erpnext/stock/doctype/material_request/material_request.js:228
msgid "Subcontracted Purchase Order"
msgstr ""
@@ -53411,7 +53819,7 @@ msgstr ""
#. Label of the subcontracting_inward_tab (Tab Break) field in DocType 'Selling
#. Settings'
-#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:33
+#: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:34
#: erpnext/selling/doctype/selling_settings/selling_settings.json
msgid "Subcontracting Inward"
msgstr ""
@@ -53621,7 +54029,7 @@ msgstr ""
msgid "Submit your Quotation"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1595
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1602
msgid "Submitted Job Card cannot be processed."
msgstr ""
@@ -53750,12 +54158,6 @@ msgstr ""
msgid "Success Redirect URL"
msgstr ""
-#. Label of the success_details (Section Break) field in DocType 'Appointment
-#. Booking Settings'
-#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
-msgid "Success Settings"
-msgstr ""
-
#. Option for the 'Depreciation Entry Posting Status' (Select) field in DocType
#. 'Asset'
#: erpnext/assets/doctype/asset/asset.json
@@ -53770,7 +54172,7 @@ msgstr ""
msgid "Successfully Set Supplier"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:414
+#: erpnext/stock/doctype/item/item.py:412
msgid "Successfully changed Stock UOM, please redefine conversion factors for new UOM."
msgstr ""
@@ -53918,7 +54320,7 @@ msgstr ""
#: erpnext/accounts/doctype/supplier_item/supplier_item.json
#: erpnext/accounts/doctype/tax_rule/tax_rule.json
#: erpnext/accounts/report/accounts_payable/accounts_payable.html:113
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:254
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:257
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.html:112
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.html:134
#: erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py:60
@@ -54065,7 +54467,7 @@ msgstr ""
#: erpnext/accounts/doctype/tax_rule/tax_rule.json
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:119
#: erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js:102
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1260
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1255
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:200
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:178
#: erpnext/accounts/report/purchase_register/purchase_register.js:27
@@ -54113,7 +54515,7 @@ msgstr ""
#. Label of the bill_date (Date) field in DocType 'Purchase Invoice'
#: erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:232
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:230
msgid "Supplier Invoice Date"
msgstr ""
@@ -54124,7 +54526,7 @@ msgstr ""
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#: erpnext/accounts/report/general_ledger/general_ledger.html:202
#: erpnext/accounts/report/general_ledger/general_ledger.py:813
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:226
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:224
msgid "Supplier Invoice No"
msgstr ""
@@ -54166,7 +54568,7 @@ msgstr ""
#. Label of the supplier_name (Data) field in DocType 'Purchase Receipt'
#. Label of the supplier_name (Data) field in DocType 'Stock Entry'
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1175
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1170
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:157
#: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:196
#: erpnext/accounts/report/purchase_register/purchase_register.py:195
@@ -54206,7 +54608,7 @@ msgstr ""
msgid "Supplier Numbers"
msgstr ""
-#: erpnext/accounts/report/accounts_payable/accounts_payable.js:290
+#: erpnext/accounts/report/accounts_payable/accounts_payable.js:293
msgid "Supplier Overview"
msgstr ""
@@ -54253,7 +54655,7 @@ msgstr ""
#: erpnext/buying/workspace/buying/buying.json
#: erpnext/crm/doctype/opportunity/opportunity.js:81
#: erpnext/selling/doctype/quotation/quotation.json
-#: erpnext/stock/doctype/material_request/material_request.js:211
+#: erpnext/stock/doctype/material_request/material_request.js:212
#: erpnext/workspace_sidebar/buying.json
msgid "Supplier Quotation"
msgstr ""
@@ -54509,7 +54911,7 @@ msgstr ""
msgid "Synchronize all accounts every hour"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:683
+#: erpnext/accounts/doctype/account/account.py:714
msgid "System In Use"
msgstr ""
@@ -54712,7 +55114,7 @@ msgstr ""
#: erpnext/stock/dashboard/item_dashboard.js:234
#: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
#: erpnext/stock/doctype/material_request_item/material_request_item.json
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:825
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:801
#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
msgid "Target Warehouse"
msgstr ""
@@ -54831,8 +55233,8 @@ msgstr ""
#. Label of the amount (Currency) field in DocType 'Item Wise Tax Detail'
#: erpnext/accounts/doctype/item_wise_tax_detail/item_wise_tax_detail.json
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:244
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:101
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:242
+#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:90
msgid "Tax Amount"
msgstr ""
@@ -54968,8 +55370,8 @@ msgstr ""
#: erpnext/accounts/report/purchase_register/purchase_register.py:210
#: erpnext/accounts/report/sales_register/sales_register.py:229
#: erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js:67
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:205
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:67
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:203
+#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:56
#: erpnext/selling/doctype/sales_order/sales_order.json
#: erpnext/stock/doctype/delivery_note/delivery_note.json
msgid "Tax Id"
@@ -55008,8 +55410,8 @@ msgstr ""
msgid "Tax Rate"
msgstr ""
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:237
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:94
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:235
+#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:83
msgid "Tax Rate %"
msgstr ""
@@ -55095,8 +55497,8 @@ msgstr ""
#: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
#: erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
#: erpnext/accounts/doctype/tax_withholding_entry/tax_withholding_entry.json
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:199
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:82
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:197
+#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:71
#: erpnext/accounts/workspace/invoicing/invoicing.json
#: erpnext/buying/doctype/supplier/supplier.json
#: erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
@@ -55201,7 +55603,7 @@ msgstr ""
#. Label of the taxable_amount (Currency) field in DocType 'Item Wise Tax
#. Detail'
#: erpnext/accounts/doctype/item_wise_tax_detail/item_wise_tax_detail.json
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:239
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:237
#: erpnext/controllers/taxes_and_totals.py:1246
msgid "Taxable Amount"
msgstr ""
@@ -55362,7 +55764,7 @@ msgstr ""
msgid "Taxes and Charges Deducted (Company Currency)"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:427
+#: erpnext/stock/doctype/item/item.py:425
msgid "Taxes row #{0}: {1} cannot be smaller than {2}"
msgstr ""
@@ -55623,7 +56025,7 @@ msgstr ""
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#: erpnext/accounts/doctype/territory_item/territory_item.json
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:142
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1244
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1239
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:108
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:184
#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:68
@@ -55777,7 +56179,7 @@ msgstr ""
msgid "The Loyalty Program isn't valid for the selected company"
msgstr ""
-#: erpnext/accounts/doctype/payment_request/payment_request.py:1270
+#: erpnext/accounts/doctype/payment_request/payment_request.py:1271
msgid "The Payment Request {0} is already paid, cannot process payment twice"
msgstr ""
@@ -55827,7 +56229,11 @@ msgstr ""
msgid "The account head under Liability or Equity, in which Profit/Loss will be booked"
msgstr ""
-#: erpnext/accounts/doctype/payment_request/payment_request.py:1164
+#: erpnext/accounts/doctype/account/account.py:226
+msgid "The account type of {0} cannot be changed from {1} because stock ledger entries exist against it."
+msgstr ""
+
+#: erpnext/accounts/doctype/payment_request/payment_request.py:1165
msgid "The allocated amount is greater than the outstanding amount of Payment Request {0}"
msgstr ""
@@ -55839,6 +56245,10 @@ msgstr ""
msgid "The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document."
msgstr ""
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:222
+msgid "The attached PDF file could not be found."
+msgstr ""
+
#: erpnext/accounts/doctype/bank_statement_import_log/bank_statement_import_log.py:97
#: erpnext/accounts/doctype/bank_statement_import_log/bank_statement_import_log.py:505
msgid "The bank account is disabled. Please enable it"
@@ -55849,7 +56259,7 @@ msgstr ""
msgid "The bank account is not a company account. Please select a company account"
msgstr ""
-#: erpnext/stock/services/serial_batch_bundle_service.py:655
+#: erpnext/stock/services/serial_batch_bundle_service.py:656
msgid "The batch {0} is reserved for {1} in the warehouse {2} and the remaining quantity is not enough to cover the reservations. So, cannot proceed with the {3} {4}."
msgstr ""
@@ -55861,7 +56271,7 @@ msgstr ""
msgid "The company {0} is not in United Arab Emirates. UAE VAT 201 report is only available for companies in United Arab Emirates."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1435
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1442
msgid "The completed quantity {0} of an operation {1} cannot be greater than the completed quantity {2} of a previous operation {3}."
msgstr ""
@@ -55889,7 +56299,7 @@ msgstr ""
msgid "The description of the transaction"
msgstr ""
-#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:67
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:77
msgid "The difference between from time and To Time must be a multiple of Appointment"
msgstr ""
@@ -55963,7 +56373,7 @@ msgstr ""
msgid "The following cancelled repost entries exist for {0}:
{1}
Kindly delete these entries before continuing."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:958
+#: erpnext/stock/doctype/item/item.py:956
msgid "The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template."
msgstr ""
@@ -56012,7 +56422,7 @@ msgstr ""
msgid "The item {item} is not marked as {type_of} item. You can enable it as {type_of} item from its Item master."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:684
+#: erpnext/stock/doctype/item/item.py:682
msgid "The items {0} and {1} are present in the following {2} :"
msgstr ""
@@ -56133,7 +56543,7 @@ msgstr ""
msgid "The reserved stock will be released. Are you certain you wish to proceed?"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:222
+#: erpnext/accounts/doctype/account/account.py:253
msgid "The root account {0} must be a group"
msgstr ""
@@ -56149,6 +56559,10 @@ msgstr ""
msgid "The selected item cannot have Batch"
msgstr ""
+#: erpnext/stock/doctype/serial_and_batch_bundle/inline_editor.py:151
+msgid "The selected row does not belong to the {0}"
+msgstr ""
+
#: erpnext/assets/doctype/asset/asset.js:670
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 ""
@@ -56178,7 +56592,7 @@ msgstr ""
msgid "The shares don't exist with the {0}"
msgstr ""
-#: erpnext/stock/stock_ledger.py:956
+#: erpnext/stock/stock_ledger.py:971
msgid "The stock for the item {0} in the {1} warehouse was negative on the {2}. You should create a positive entry {3} before the date {4} and time {5} to post the correct valuation rate. For more details, please read the documentation."
msgstr ""
@@ -56224,7 +56638,7 @@ msgstr ""
msgid "The uploaded file could not be parsed as a genericode XML document."
msgstr ""
-#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:153
+#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:177
msgid "The uploaded file does not appear to be in valid MT940 format."
msgstr ""
@@ -56276,15 +56690,15 @@ msgstr ""
msgid "The withdrawal or deposit amounts - only required if there's no amount column."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:960
+#: erpnext/manufacturing/doctype/job_card/job_card.py:955
msgid "The {0} ({1}) must be equal to {2} ({3})"
msgstr ""
-#: erpnext/public/js/controllers/transaction.js:3473
+#: erpnext/public/js/controllers/transaction.js:3465
msgid "The {0} contains Unit Price Items."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:498
+#: erpnext/stock/doctype/item/item.py:496
msgid "The {0} prefix '{1}' already exists. Please change the Serial No Series, otherwise you will get a Duplicate Entry error."
msgstr ""
@@ -56300,7 +56714,7 @@ msgstr ""
msgid "The {0} {1} is in submitted state, please cancel it first"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1076
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1071
msgid "The {0} {1} is used to calculate the valuation cost for the finished good {2}."
msgstr ""
@@ -56316,7 +56730,7 @@ msgstr ""
msgid "There are inconsistencies between the rate, no of shares and the amount calculated"
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:207
+#: erpnext/accounts/doctype/account/account.py:208
msgid "There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
msgstr ""
@@ -56365,7 +56779,7 @@ msgstr ""
msgid "There can only be 1 Account per Company in {0} {1}"
msgstr ""
-#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:86
+#: erpnext/accounts/doctype/shipping_rule/shipping_rule.py:85
msgid "There can only be one Shipping Rule Condition with 0 or blank value for \"To Value\""
msgstr ""
@@ -56457,11 +56871,15 @@ msgstr ""
msgid "This Product Bundle is linked with {0}. You will have to cancel these documents in order to delete this Product Bundle"
msgstr ""
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.py:218
+msgid "This Proforma Invoice has no PDF to send."
+msgstr ""
+
#: erpnext/buying/doctype/purchase_order/mapper.py:253
msgid "This Purchase Order has been fully subcontracted."
msgstr ""
-#: erpnext/selling/doctype/sales_order/mapper.py:1058
+#: erpnext/selling/doctype/sales_order/mapper.py:1060
msgid "This Sales Order has been fully subcontracted."
msgstr ""
@@ -56505,6 +56923,10 @@ msgstr ""
msgid "This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?"
msgstr ""
+#: erpnext/templates/emails/appointment_confirmed.html:6
+msgid "This email was sent from {0}"
+msgstr ""
+
#: erpnext/stock/doctype/delivery_note/delivery_note.js:496
msgid "This field is used to set the 'Customer'."
msgstr ""
@@ -56643,6 +57065,10 @@ msgstr ""
msgid "This item filter has already been applied for the {0}"
msgstr ""
+#: erpnext/templates/emails/confirm_appointment.html:4
+msgid "This link is valid for {0} minutes"
+msgstr ""
+
#: erpnext/public/js/shop_floor/shop_floor.js:699
msgid "This machine can run at most {0} job(s) in parallel. Pause or complete a running job before starting another."
msgstr ""
@@ -56768,6 +57194,10 @@ msgstr ""
msgid "This value shall be used when no matching Common Code for a record is found."
msgstr ""
+#: erpnext/www/book_appointment/verify/index.py:18
+msgid "This verification link is invalid. Please book the appointment again."
+msgstr ""
+
#: banking/src/components/features/Settings/Preferences.tsx:86
msgid "This will automatically run transaction matching rules on unreconciled transactions every hour."
msgstr ""
@@ -56788,10 +57218,18 @@ msgstr ""
msgid "This will be auto-populated if not set."
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:1120
+msgid "This will delete all {0} entries. Continue?"
+msgstr ""
+
#: banking/src/components/features/BankReconciliation/Rules/RuleForm.tsx:265
msgid "This will just suggest creating a new entry, and will not automatically create it."
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:307
+msgid "This will replace the existing entries. Continue?"
+msgstr ""
+
#. Description of the 'Create User Permission' (Check) field in DocType
#. 'Employee'
#: erpnext/setup/doctype/employee/employee.json
@@ -56909,11 +57347,11 @@ msgstr ""
msgid "Time in mins."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:936
+#: erpnext/manufacturing/doctype/job_card/job_card.py:931
msgid "Time logs are required for {0} {1}"
msgstr ""
-#: erpnext/crm/doctype/appointment/appointment.py:60
+#: erpnext/crm/doctype/appointment/appointment.py:133
msgid "Time slot is not available"
msgstr ""
@@ -57313,7 +57751,7 @@ msgstr ""
msgid "To include tax in row {0} in Item rate, taxes in rows {1} must also be included"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:706
+#: erpnext/stock/doctype/item/item.py:704
msgid "To merge, following properties must be same for both items"
msgstr ""
@@ -57321,7 +57759,7 @@ msgstr ""
msgid "To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:565
+#: erpnext/accounts/doctype/account/account.py:596
msgid "To overrule this, enable '{0}' in company {1}"
msgstr ""
@@ -57641,12 +58079,12 @@ msgstr ""
#. Label of the total_completed_qty (Float) field in DocType 'Job Card'
#: erpnext/manufacturing/doctype/job_card/job_card.json
-#: erpnext/manufacturing/doctype/job_card/job_card.py:961
+#: erpnext/manufacturing/doctype/job_card/job_card.py:956
#: erpnext/manufacturing/report/job_card_summary/job_card_summary.py:174
msgid "Total Completed Qty"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:197
+#: erpnext/manufacturing/doctype/job_card/job_card.py:192
msgid "Total Completed Qty is required for Job Card {0}, please start and complete the job card before submission"
msgstr ""
@@ -57997,12 +58435,17 @@ msgstr ""
msgid "Total Qty"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:1066
+msgid "Total Qty: {0}"
+msgstr ""
+
#. Label of the total_quantity (Float) field in DocType 'POS Closing Entry'
#. Label of the total_qty (Float) field in DocType 'POS Invoice'
#. Label of the total_qty (Float) field in DocType 'Purchase Invoice'
#. Label of the total_qty (Float) field in DocType 'Sales Invoice'
#. Label of the total_qty (Float) field in DocType 'Purchase Order'
#. Label of the total_qty (Float) field in DocType 'Supplier Quotation'
+#. Label of the total_qty (Float) field in DocType 'Proforma Invoice'
#. Label of the total_qty (Float) field in DocType 'Quotation'
#. Label of the total_qty (Float) field in DocType 'Sales Order'
#. Label of the total_qty (Float) field in DocType 'Delivery Note'
@@ -58017,6 +58460,7 @@ msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order.json
#: erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
#: erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py:147
+#: erpnext/selling/doctype/proforma_invoice/proforma_invoice.json
#: erpnext/selling/doctype/quotation/quotation.json
#: erpnext/selling/doctype/sales_order/sales_order.json
#: erpnext/selling/page/point_of_sale/pos_item_cart.js:543
@@ -58084,7 +58528,7 @@ msgstr ""
msgid "Total Tax"
msgstr ""
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:96
+#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:85
msgid "Total Taxable Amount"
msgstr ""
@@ -58248,7 +58692,7 @@ msgstr ""
msgid "Total allocated percentage for sales team should be 100"
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:205
+#: erpnext/selling/doctype/customer/customer.py:203
msgid "Total contribution percentage should be equal to 100"
msgstr ""
@@ -58273,6 +58717,10 @@ msgstr ""
msgid "Total percentage against cost centers should be 100"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:199
+msgid "Total proforma {0} (including past proformas) exceeds the ordered {0} for: {1}"
+msgstr ""
+
#: erpnext/selling/doctype/sales_order/sales_order.js:703
msgid "Total quantity in delivery schedule cannot be greater than the item quantity"
msgstr ""
@@ -58407,7 +58855,7 @@ msgstr ""
msgid "Transaction Dates"
msgstr ""
-#: erpnext/setup/doctype/company/company.py:1142
+#: erpnext/setup/doctype/company/company.py:1143
msgid "Transaction Deletion Document {0} has been triggered for company {1}"
msgstr ""
@@ -58504,7 +58952,7 @@ msgstr ""
#: erpnext/accounts/doctype/bank_transaction/bank_transaction.json
#: erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.json
#: erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py:38
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:259
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:257
msgid "Transaction Type"
msgstr ""
@@ -58540,7 +58988,7 @@ msgstr ""
msgid "Transaction from which tax is withheld"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:912
+#: erpnext/manufacturing/doctype/job_card/job_card.py:907
#: erpnext/stock/doctype/stock_entry/services/stock_entry_base.py:38
msgid "Transaction not allowed against stopped Work Order {0}"
msgstr ""
@@ -58591,7 +59039,7 @@ msgstr ""
#. Description of the 'Credit & Overdue Limits' (Table) field in DocType
#. 'Customer'
#: erpnext/selling/doctype/customer/customer.json
-msgid "Transactions are blocked when the outstanding balance exceeds the credit limit. When the overdue billing setting is enabled, new invoices are also blocked when the customer's overdue amount exceeds the overdue billing threshold."
+msgid "Transactions are blocked when the outstanding balance exceeds the credit limit. When Restrict Customer Over Billing is enabled, new invoices are also blocked when the customer's overdue amount exceeds the Overdue Limit."
msgstr ""
#: banking/src/components/features/BankStatementImporter/CSV/StatementDetails.tsx:239
@@ -58740,7 +59188,7 @@ msgstr ""
msgid "Transit"
msgstr ""
-#: erpnext/stock/doctype/stock_entry/stock_entry.js:610
+#: erpnext/stock/doctype/stock_entry/stock_entry.js:586
msgid "Transit Entry"
msgstr ""
@@ -58996,6 +59444,7 @@ msgstr ""
#. Label of the uom (Link) field in DocType 'Quality Review Objective'
#. Label of the uom (Link) field in DocType 'Delivery Schedule Item'
#. Label of the uom (Link) field in DocType 'Product Bundle Item'
+#. Label of the uom (Link) field in DocType 'Proforma Invoice Item'
#. Label of the uom (Link) field in DocType 'Quotation Item'
#. Label of the uom (Link) field in DocType 'Sales Order Item'
#. Name of a DocType
@@ -59051,6 +59500,7 @@ msgstr ""
#: erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
#: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json
#: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
+#: erpnext/selling/doctype/proforma_invoice_item/proforma_invoice_item.json
#: erpnext/selling/doctype/quotation_item/quotation_item.json
#: erpnext/selling/doctype/sales_order/sales_order.js:1734
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -59074,14 +59524,14 @@ msgstr ""
#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
#: erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.json
#: erpnext/stock/report/available_serial_no/available_serial_no.py:101
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:92
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:87
#: erpnext/stock/report/item_prices/item_prices.py:55
#: erpnext/stock/report/item_where_used/item_where_used.py:69
#: erpnext/stock/report/item_wise_consumption/item_wise_consumption.py:60
#: erpnext/stock/report/product_bundle_balance/product_bundle_balance.py:93
#: erpnext/stock/report/stock_ageing/stock_ageing.py:225
#: erpnext/stock/report/stock_analytics/stock_analytics.py:59
-#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:136
+#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:137
#: erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.json
#: erpnext/templates/emails/reorder_item.html:11
#: erpnext/templates/includes/rfq/rfq_items.html:17
@@ -59140,7 +59590,7 @@ msgstr ""
msgid "UOM Conversion Factor"
msgstr ""
-#: erpnext/manufacturing/doctype/production_plan/services/material_request.py:526
+#: erpnext/manufacturing/doctype/production_plan/services/material_request.py:532
msgid "UOM Conversion factor ({0} -> {1}) not found for item: {2}"
msgstr ""
@@ -59352,7 +59802,7 @@ msgstr ""
msgid "Unit of Measure (UOM)"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:459
+#: erpnext/stock/doctype/item/item.py:457
msgid "Unit of Measure {0} has been entered more than once in Conversion Factor Table"
msgstr ""
@@ -59796,7 +60246,7 @@ msgstr ""
msgid "Updating Costing and Billing fields against this Project..."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1546
+#: erpnext/stock/doctype/item/item.py:1544
msgid "Updating Variants..."
msgstr ""
@@ -59911,6 +60361,12 @@ msgstr ""
msgid "Use HTTP Protocol"
msgstr ""
+#. Label of the use_inline_serial_batch_editor (Check) field in DocType 'Stock
+#. Settings'
+#: erpnext/stock/doctype/stock_settings/stock_settings.json
+msgid "Use Inline Serial / Batch Editor"
+msgstr ""
+
#. Label of the item_based_reposting (Check) field in DocType 'Stock Reposting
#. Settings'
#: erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -59934,7 +60390,7 @@ msgstr ""
#. Label of the use_posting_datetime_for_naming_documents (Check) field in
#. DocType 'Global Defaults'
#: erpnext/setup/doctype/global_defaults/global_defaults.json
-msgid "Use Posting Datetime for Naming Documents"
+msgid "Use Posting Date for Naming Documents"
msgstr ""
#. Label of the use_serial_batch_fields (Check) field in DocType 'Stock
@@ -59994,7 +60450,7 @@ msgstr ""
msgid "Use Transaction Date Exchange Rate"
msgstr ""
-#: erpnext/projects/doctype/project/project.py:669
+#: erpnext/projects/doctype/project/project.py:671
msgid "Use a name that is different from previous project name"
msgstr ""
@@ -60151,10 +60607,10 @@ msgstr ""
msgid "Users with this role are allowed to over deliver/receive against orders above the allowance percentage"
msgstr ""
-#. Description of the 'Role allowed to bypass overdue billing limit' (Link)
+#. Description of the 'Role Allowed to Bypass Over Billing Restriction' (Link)
#. field in DocType 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
-msgid "Users with this role can still submit invoices for customers over their overdue billing threshold."
+msgid "Users with this role can still submit invoices for customers who have crossed their Overdue Limit."
msgstr ""
#. Description of the 'Role to Notify on Depreciation Failure' (Link) field in
@@ -60372,7 +60828,7 @@ msgstr ""
msgid "Valuation Method"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1079
+#: erpnext/stock/doctype/item/item.py:1077
msgid "Valuation Method cannot be changed to or from 'Standard Cost' for {0} because stock transactions already exist for it."
msgstr ""
@@ -60417,7 +60873,7 @@ msgstr ""
#: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
#: erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
#: erpnext/stock/report/available_serial_no/available_serial_no.py:164
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:90
+#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:85
#: erpnext/stock/report/item_prices/item_prices.py:57
#: erpnext/stock/report/serial_no_ledger/serial_no_ledger.py:68
#: erpnext/stock/report/stock_balance/stock_balance.py:563
@@ -60428,19 +60884,19 @@ msgstr ""
msgid "Valuation Rate (In / Out)"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2209
+#: erpnext/stock/stock_ledger.py:2224
msgid "Valuation Rate Missing"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1657
+#: erpnext/stock/doctype/item/item.py:1655
msgid "Valuation Rate cannot be negative."
msgstr ""
-#: erpnext/stock/stock_ledger.py:2187
+#: erpnext/stock/stock_ledger.py:2202
msgid "Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:321
+#: erpnext/stock/doctype/item/item.py:319
msgid "Valuation Rate is mandatory if Opening Stock entered"
msgstr ""
@@ -60604,7 +61060,7 @@ msgstr ""
msgid "Variant"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:973
+#: erpnext/stock/doctype/item/item.py:971
msgid "Variant Attribute Error"
msgstr ""
@@ -60623,7 +61079,7 @@ msgstr ""
msgid "Variant Based On"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1001
+#: erpnext/stock/doctype/item/item.py:999
msgid "Variant Based On cannot be changed"
msgstr ""
@@ -60641,7 +61097,7 @@ msgstr ""
msgid "Variant Item"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:971
+#: erpnext/stock/doctype/item/item.py:969
msgid "Variant Items"
msgstr ""
@@ -60660,11 +61116,6 @@ msgstr ""
msgid "Variant {0} and its template {1} cannot both be added to the same Pricing Rule"
msgstr ""
-#. Label of the variants_section (Tab Break) field in DocType 'Item'
-#: erpnext/stock/doctype/item/item.json
-msgid "Variants"
-msgstr ""
-
#. Name of a DocType
#. Label of the vehicle (Link) field in DocType 'Delivery Trip'
#: erpnext/setup/doctype/vehicle/vehicle.json
@@ -60716,16 +61167,31 @@ msgstr ""
msgid "Venture Capital"
msgstr ""
+#. Label of the verification_link_expiry_duration (Int) field in DocType
+#. 'Appointment Booking Settings'
+#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+msgid "Verification Link Expiry Duration"
+msgstr ""
+
+#. Label of the verification_token (Data) field in DocType 'Appointment'
+#: erpnext/crm/doctype/appointment/appointment.json
+msgid "Verification Token"
+msgstr ""
+
#: erpnext/www/book_appointment/verify/index.html:15
msgid "Verification failed please check the link"
msgstr ""
+#: erpnext/www/book_appointment/verify/index.py:38
+msgid "Verification link has expired."
+msgstr ""
+
#. Label of the verified_by (Data) field in DocType 'Quality Inspection'
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
msgid "Verified By"
msgstr ""
-#: erpnext/templates/emails/confirm_appointment.html:6
+#: erpnext/templates/emails/confirm_appointment.html:7
#: erpnext/www/book_appointment/verify/index.html:4
msgid "Verify Email"
msgstr ""
@@ -60820,6 +61286,10 @@ msgstr ""
msgid "View Now"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:298
+msgid "View PDF"
+msgstr ""
+
#. Title of an Onboarding Step
#. Label of an action in the Onboarding Step 'View Project Summary'
#. Description of a report in the Onboarding Step 'View Project Summary'
@@ -61026,7 +61496,7 @@ msgstr ""
#: erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
#: erpnext/accounts/doctype/repost_payment_ledger_items/repost_payment_ledger_items.json
#: erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1199
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1194
#: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js:56
#: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py:221
#: erpnext/accounts/report/general_ledger/general_ledger.js:49
@@ -61100,7 +61570,7 @@ msgstr ""
#: erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.json
#: erpnext/accounts/doctype/repost_payment_ledger_items/repost_payment_ledger_items.json
#: erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1197
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1192
#: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py:212
#: erpnext/accounts/report/general_ledger/general_ledger.py:760
#: erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py:31
@@ -61327,7 +61797,7 @@ msgstr ""
msgid "Warehouse {0} can not be deleted as quantity exists for Item {1}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1662
+#: erpnext/stock/doctype/item/item.py:1660
#: erpnext/stock/doctype/putaway_rule/putaway_rule.py:67
msgid "Warehouse {0} does not belong to Company {1}."
msgstr ""
@@ -61457,7 +61927,7 @@ msgstr ""
msgid "Warning - Row {0}: Billing Hours are more than Actual Hours"
msgstr ""
-#: erpnext/stock/stock_ledger.py:966
+#: erpnext/stock/stock_ledger.py:981
msgid "Warning on Negative Stock"
msgstr ""
@@ -61473,7 +61943,7 @@ msgstr ""
msgid "Warning: Another {0} # {1} exists against stock entry {2}"
msgstr ""
-#: erpnext/stock/doctype/material_request/material_request.js:535
+#: erpnext/stock/doctype/material_request/material_request.js:536
msgid "Warning: Material Requested Qty is less than Minimum Order Qty"
msgstr ""
@@ -61575,6 +62045,10 @@ msgstr ""
msgid "We can see {0} is made against {1}. If you want {1}'s outstanding to be updated, uncheck the '{2}' checkbox."
msgstr ""
+#: erpnext/templates/emails/appointment_confirmed.html:3
+msgid "We look forward to meeting you"
+msgstr ""
+
#: banking/src/pages/BankStatementImporter.tsx:169
msgid "We support uploading CSV, XLSX, XLS and PDF files. Please make sure the file contains the correct columns."
msgstr ""
@@ -61763,10 +62237,10 @@ msgstr ""
msgid "When checked, only transaction threshold will be applied for transaction individually"
msgstr ""
-#. Description of the 'Use Posting Datetime for Naming Documents' (Check) field
-#. in DocType 'Global Defaults'
+#. Description of the 'Use Posting Date for Naming Documents' (Check) field in
+#. DocType 'Global Defaults'
#: erpnext/setup/doctype/global_defaults/global_defaults.json
-msgid "When checked, the system will use the posting datetime of the document for naming the document instead of the creation datetime of the document."
+msgid "When checked, the system will use the posting date of the document for naming instead of the creation date."
msgstr ""
#: erpnext/stock/doctype/item/item.js:1615
@@ -61788,11 +62262,11 @@ msgstr ""
msgid "When there are multiple finished goods ({0}) in a Repack stock entry, the basic rate for all finished goods must be set manually. To set rate manually, enable the checkbox 'Set Basic Rate Manually' in the respective finished good row."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:384
+#: erpnext/accounts/doctype/account/account.py:415
msgid "While creating account for Child Company {0}, parent account {1} found as a ledger account."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:374
+#: erpnext/accounts/doctype/account/account.py:405
msgid "While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA"
msgstr ""
@@ -61885,7 +62359,7 @@ msgstr ""
msgid "Withholding Date"
msgstr ""
-#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:278
+#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:276
msgid "Withholding Document"
msgstr ""
@@ -61977,7 +62451,7 @@ msgstr ""
#: erpnext/manufacturing/workspace/manufacturing/manufacturing.json
#: erpnext/public/js/shop_floor/shop_floor.js:230
#: erpnext/selling/doctype/sales_order/sales_order.js:1094
-#: erpnext/stock/doctype/material_request/material_request.js:219
+#: erpnext/stock/doctype/material_request/material_request.js:220
#: erpnext/stock/doctype/material_request/material_request.json
#: erpnext/stock/doctype/material_request/material_request.py:612
#: erpnext/stock/doctype/pick_list/pick_list.json
@@ -62258,7 +62732,7 @@ msgstr ""
#: erpnext/accounts/doctype/pos_profile/pos_profile.json
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
-#: erpnext/setup/doctype/company/company.py:738
+#: erpnext/setup/doctype/company/company.py:739
msgid "Write Off"
msgstr ""
@@ -62423,11 +62897,11 @@ msgstr ""
msgid "You are not authorized to make/edit Stock Transactions for Item {0} under warehouse {1} before this time."
msgstr ""
-#: erpnext/accounts/doctype/account/account.py:316
+#: erpnext/accounts/doctype/account/account.py:347
msgid "You are not authorized to set Frozen value"
msgstr ""
-#: erpnext/stock/doctype/company_restriction/company_restriction.py:93
+#: erpnext/stock/doctype/company_restriction/company_restriction.py:125
msgid "You are not permitted to add or remove Company {0} in Allowed Companies"
msgstr ""
@@ -62443,7 +62917,7 @@ msgstr ""
msgid "You can also add credit or debit values to pre-fill - these support both static values (like 200) or formulas (like transaction_amount * 0.25)."
msgstr ""
-#: erpnext/templates/emails/confirm_appointment.html:10
+#: erpnext/templates/emails/confirm_appointment.html:11
msgid "You can also copy-paste this link in your browser"
msgstr ""
@@ -62532,7 +63006,7 @@ msgstr ""
msgid "You cannot enable both the settings '{0}' and '{1}'."
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1447
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1454
msgid "You cannot make any changes to Job Card since Work Order is closed."
msgstr ""
@@ -62621,15 +63095,15 @@ msgstr ""
msgid "You have already selected items from {0} {1}"
msgstr ""
-#: erpnext/projects/doctype/project/project.py:422
+#: erpnext/projects/doctype/project/project.py:424
msgid "You have been invited to collaborate on the project {0}."
msgstr ""
-#: erpnext/stock/doctype/stock_settings/stock_settings.py:263
+#: erpnext/stock/doctype/stock_settings/stock_settings.py:264
msgid "You have enabled {0} and {1} in {2}. This can lead to prices from the default price list being inserted in the transaction price list."
msgstr ""
-#: erpnext/selling/doctype/selling_settings/selling_settings.py:110
+#: erpnext/selling/doctype/selling_settings/selling_settings.py:112
msgid "You have enabled {0} and {1} in {2}. This can lead to prices from the default price list being inserted into the transaction price list."
msgstr ""
@@ -62645,7 +63119,7 @@ msgstr ""
msgid "You have not performed any reconciliations in this session yet."
msgstr ""
-#: erpnext/stock/doctype/item/item.py:1220
+#: erpnext/stock/doctype/item/item.py:1218
msgid "You have to enable auto re-order in Stock Settings to maintain re-order levels."
msgstr ""
@@ -62679,6 +63153,10 @@ msgstr ""
msgid "Your Name (required)"
msgstr ""
+#: erpnext/templates/emails/appointment_confirmed.html:2
+msgid "Your email has been verified and your appointment has been confirmed for {0}"
+msgstr ""
+
#: erpnext/www/book_appointment/verify/index.html:11
msgid "Your email has been verified and your appointment has been scheduled"
msgstr ""
@@ -62747,10 +63225,14 @@ msgstr ""
msgid "`Allow Negative rates for Items`"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2201
+#: erpnext/stock/stock_ledger.py:2216
msgid "after"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:195
+msgid "amount"
+msgstr ""
+
#: erpnext/edi/doctype/code_list/code_list_import.js:58
msgid "as Code"
msgstr ""
@@ -62837,7 +63319,7 @@ msgstr ""
msgid "fieldname"
msgstr ""
-#: erpnext/setup/doctype/item_group/item_group.py:49
+#: erpnext/setup/doctype/item_group/item_group.py:50
msgid "for tax category {0}"
msgstr ""
@@ -62935,7 +63417,7 @@ msgstr ""
msgid "per hour"
msgstr ""
-#: erpnext/stock/stock_ledger.py:2202
+#: erpnext/stock/stock_ledger.py:2217
msgid "performing either one below:"
msgstr ""
@@ -62951,6 +63433,10 @@ msgstr ""
msgid "production"
msgstr ""
+#: erpnext/public/js/sales_order_proforma.js:195
+msgid "quantity"
+msgstr ""
+
#. Label of the quotation_item (Data) field in DocType 'Sales Order Item'
#: erpnext/selling/doctype/sales_order_item/sales_order_item.json
msgid "quotation_item"
@@ -63131,10 +63617,14 @@ msgstr ""
msgid "{0} Request for {1}"
msgstr ""
-#: erpnext/stock/doctype/item/item.py:398
+#: erpnext/stock/doctype/item/item.py:396
msgid "{0} Retain Sample is based on batch, please check Has Batch No to retain sample of item"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:798
+msgid "{0} Serial Nos added. They will be saved with the document."
+msgstr ""
+
#: erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py:1048
msgid "{0} Transaction(s) Reconciled"
msgstr ""
@@ -63181,9 +63671,7 @@ msgstr ""
#: erpnext/accounts/report/general_ledger/general_ledger.py:63
#: erpnext/accounts/report/pos_register/pos_register.py:120
-#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:28
-#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:35
-#: erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py:42
+#: erpnext/accounts/report/utils.py:26
msgid "{0} and {1} are mandatory"
msgstr ""
@@ -63207,7 +63695,7 @@ msgstr ""
msgid "{0} cannot be changed with opened Opening Entries."
msgstr ""
-#: erpnext/public/js/utils/sales_common.js:334
+#: erpnext/public/js/utils/sales_common.js:339
msgid "{0} cannot be greater than 100"
msgstr ""
@@ -63225,7 +63713,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/production_plan/services/material_request.py:137
#: erpnext/manufacturing/doctype/production_plan/services/work_order_planning.py:214
-#: erpnext/stock/doctype/pick_list/mapper.py:79
+#: erpnext/stock/doctype/pick_list/mapper.py:81
#: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.py:323
msgid "{0} created"
msgstr ""
@@ -63266,15 +63754,23 @@ msgstr ""
msgid "{0} draft job cards awaiting submission"
msgstr ""
+#: erpnext/public/js/utils/draft_link_guard.js:55
+msgid "{0} draft {1} documents already exist for this {2}: {3}. Do you still want to create a new one?"
+msgstr ""
+
#: erpnext/accounts/doctype/item_tax_template/item_tax_template.py:74
msgid "{0} entered twice in Item Tax"
msgstr ""
-#: erpnext/setup/doctype/item_group/item_group.py:47
-#: erpnext/stock/doctype/item/item.py:529
+#: erpnext/setup/doctype/item_group/item_group.py:48
+#: erpnext/stock/doctype/item/item.py:527
msgid "{0} entered twice {1} in Item Taxes"
msgstr ""
+#: erpnext/public/js/utils/serial_batch_inline_editor.js:648
+msgid "{0} entries fetched"
+msgstr ""
+
#: erpnext/accounts/utils.py:138
#: erpnext/projects/doctype/activity_cost/activity_cost.py:40
msgid "{0} for {1}"
@@ -63375,7 +63871,7 @@ msgstr ""
msgid "{0} is not a CSV file."
msgstr ""
-#: erpnext/selling/doctype/customer/customer.py:251
+#: erpnext/selling/doctype/customer/customer.py:249
msgid "{0} is not a company bank account"
msgstr ""
@@ -63419,6 +63915,10 @@ msgstr ""
msgid "{0} is not running. Cannot trigger events for this document"
msgstr ""
+#: erpnext/stock/doctype/serial_and_batch_bundle/inline_editor.py:147
+msgid "{0} is not supported for the inline Serial / Batch editor"
+msgstr ""
+
#: erpnext/stock/doctype/material_request/material_request.py:517
msgid "{0} is not the default supplier for any items."
msgstr ""
@@ -63532,16 +64032,16 @@ msgstr ""
msgid "{0} units of {1} are required in {2} with the inventory dimension: {3} on {4} {5} for {6} to complete the transaction."
msgstr ""
-#: erpnext/stock/stock_ledger.py:1848 erpnext/stock/stock_ledger.py:2373
-#: erpnext/stock/stock_ledger.py:2387
+#: erpnext/stock/stock_ledger.py:1863 erpnext/stock/stock_ledger.py:2388
+#: erpnext/stock/stock_ledger.py:2402
msgid "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
msgstr ""
-#: erpnext/stock/stock_ledger.py:2477 erpnext/stock/stock_ledger.py:2522
+#: erpnext/stock/stock_ledger.py:2492 erpnext/stock/stock_ledger.py:2537
msgid "{0} units of {1} needed in {2} on {3} {4} to complete this transaction."
msgstr ""
-#: erpnext/stock/stock_ledger.py:1842
+#: erpnext/stock/stock_ledger.py:1857
msgid "{0} units of {1} needed in {2} to complete this transaction."
msgstr ""
@@ -63569,7 +64069,7 @@ msgstr ""
msgid "{0} will be set as the {1} in subsequently scanned items"
msgstr ""
-#: erpnext/manufacturing/doctype/job_card/job_card.py:1085
+#: erpnext/manufacturing/doctype/job_card/job_card.py:1080
msgid "{0} {1}"
msgstr ""
@@ -63585,6 +64085,10 @@ msgstr ""
msgid "{0} {1} cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one."
msgstr ""
+#: erpnext/stock/doctype/company_restriction/company_restriction.py:145
+msgid "{0} {1} cannot be used with Company {2} because of Company Restrictions"
+msgstr ""
+
#: erpnext/accounts/doctype/payment_order/payment_order.py:130
msgid "{0} {1} created"
msgstr ""
@@ -63776,7 +64280,7 @@ msgstr ""
msgid "{0}% of total invoice value will be given as discount."
msgstr ""
-#: erpnext/projects/doctype/task/task.py:129
+#: erpnext/projects/doctype/task/task.py:130
msgid "{0}'s {1} cannot be after {2}'s Expected End Date."
msgstr ""
@@ -63812,7 +64316,7 @@ msgstr ""
msgid "{0}: {1} does not belong to the Company: {2}"
msgstr ""
-#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1356
+#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1355
msgid "{0}: {1} does not exist"
msgstr ""
diff --git a/erpnext/manufacturing/doctype/bom/services/costing.py b/erpnext/manufacturing/doctype/bom/services/costing.py
index 86ed2c97a9a..b72d1b8909d 100644
--- a/erpnext/manufacturing/doctype/bom/services/costing.py
+++ b/erpnext/manufacturing/doctype/bom/services/costing.py
@@ -225,7 +225,7 @@ class BOMCostingService:
for d in self.doc.get("items"):
old_rate = d.rate
- if not self.doc.bom_creator and (d.is_stock_item or d.is_phantom_item):
+ if d.is_stock_item or d.is_phantom_item:
d.rate = self.get_rm_rate(self._rm_rate_args(d), notify=False)
self._set_item_amounts(d)
diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py
index 84f10f1c1ee..94b40704b1f 100644
--- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py
+++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py
@@ -538,15 +538,8 @@ class BOMCreator(Document):
row.delete()
updated = True
- items = get_children(parent=kwargs.fg_item, parent_id=self.name)
- if items:
- for item in items:
- updated = True
- child_row = next((row for row in self.items if row.name == item.name), None)
- if child_row:
- child_row.delete()
- if item.expandable:
- self.delete_node(fg_item=item.value)
+ if self.delete_child_nodes(kwargs.docname or self.name):
+ updated = True
if updated:
self.set_rate_for_items()
@@ -556,6 +549,19 @@ class BOMCreator(Document):
return frappe._dict()
+ def delete_child_nodes(self, fg_reference_id: str):
+ deleted = False
+ for item in get_children(parent=fg_reference_id, parent_id=self.name):
+ child_row = next((row for row in self.items if row.name == item.name), None)
+ if child_row:
+ child_row.delete()
+
+ deleted = True
+ if item.expandable:
+ self.delete_child_nodes(item.name)
+
+ return deleted
+
@frappe.whitelist()
def get_children(doctype: str | None = None, parent: str | None = None, **kwargs):
@@ -568,7 +574,7 @@ def get_children(doctype: str | None = None, parent: str | None = None, **kwargs
kwargs = frappe._dict(kwargs)
fields = [
- "item_code as value",
+ "name as value",
"item_name as title",
"is_expandable as expandable",
"parent as parent_id",
@@ -576,6 +582,7 @@ def get_children(doctype: str | None = None, parent: str | None = None, **kwargs
"idx",
ValueWrapper("BOM Creator Item").as_("doctype"),
"name",
+ "item_code",
"uom",
"rate",
"amount",
@@ -584,7 +591,7 @@ def get_children(doctype: str | None = None, parent: str | None = None, **kwargs
]
query_filters = {
- "fg_item": parent,
+ "fg_reference_id": parent,
"parent": kwargs.parent_id,
}
diff --git a/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py
index 94a8b0b607b..e11f43e97e6 100644
--- a/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py
+++ b/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py
@@ -241,6 +241,26 @@ class TestBOMCreator(ERPNextTestSuite):
data = frappe.get_all("BOM", filters={"bom_creator": doc.name, "docstatus": 1})
self.assertEqual(len(data), 2)
+ def test_repeated_sub_assembly_keeps_own_raw_materials(self):
+ doc, first_wheel, second_wheel = make_repeated_sub_assembly_bom(
+ "Bicycle BOM with Repeated Sub Assembly"
+ )
+
+ self.assertEqual(child_items(doc, doc.name), ["Frame Assembly", "Seat Assembly"])
+ self.assertEqual(child_items(doc, first_wheel), ["Rim", "Spokes"])
+ self.assertEqual(child_items(doc, second_wheel), ["Hub"])
+
+ def test_delete_repeated_sub_assembly_keeps_sibling_raw_materials(self):
+ doc, first_wheel, second_wheel = make_repeated_sub_assembly_bom(
+ "Bicycle BOM with Deleted Sub Assembly"
+ )
+
+ doc.delete_node(doctype="BOM Creator Item", docname=second_wheel)
+ doc.reload()
+
+ self.assertEqual(child_items(doc, first_wheel), ["Rim", "Spokes"])
+ self.assertFalse([row for row in doc.items if row.item_code == "Hub"])
+
def test_edit_and_delete_reject_unknown_item(self):
final_product = "Bicycle"
make_item(
@@ -327,6 +347,55 @@ def create_items():
)
+def make_repeated_sub_assembly_bom(name):
+ """Bicycle > (Frame Assembly > Wheel Assembly > Rim, Spokes), (Seat Assembly > Wheel Assembly > Hub)"""
+ final_product = "Bicycle"
+ make_item(final_product, {"item_group": "Raw Material", "stock_uom": "Nos"})
+
+ doc = make_bom_creator(
+ name=name,
+ company="_Test Company",
+ item_code=final_product,
+ qty=1,
+ rm_cosy_as_per="Valuation Rate",
+ currency="INR",
+ plc_conversion_rate=1,
+ conversion_rate=1,
+ )
+
+ def add_sub_assembly(fg_item, fg_reference_id, item_code, raw_materials):
+ doc.add_sub_assembly(
+ fg_item=fg_item,
+ fg_reference_id=fg_reference_id,
+ bom_item={
+ "item_code": item_code,
+ "qty": 1,
+ "items": [{"item_code": item, "qty": 1} for item in raw_materials],
+ },
+ )
+ doc.reload()
+
+ return next(
+ row.name
+ for row in doc.items
+ if row.item_code == item_code and row.fg_reference_id == fg_reference_id
+ )
+
+ frame = add_sub_assembly(final_product, doc.name, "Frame Assembly", ["Frame"])
+ first_wheel = add_sub_assembly("Frame Assembly", frame, "Wheel Assembly", ["Rim", "Spokes"])
+
+ seat = add_sub_assembly(final_product, doc.name, "Seat Assembly", ["Seat"])
+ second_wheel = add_sub_assembly("Seat Assembly", seat, "Wheel Assembly", ["Hub"])
+
+ return doc, first_wheel, second_wheel
+
+
+def child_items(doc, parent):
+ from erpnext.manufacturing.doctype.bom_creator.bom_creator import get_children
+
+ return sorted(row.item_code for row in get_children(parent=parent, parent_id=doc.name))
+
+
def make_bom_creator(**kwargs):
if isinstance(kwargs, str) or isinstance(kwargs, dict):
kwargs = frappe.parse_json(kwargs)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 084782ab12d..4e21d1e32cc 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -1037,18 +1037,31 @@ class JobCard(Document):
return for_quantity, time_in_mins, process_loss_qty, pending_qty
def update_semi_finished_good_details(self):
- if self.operation_id:
- qty = max(flt(self.manufactured_qty), flt(self.total_completed_qty))
+ if not self.operation_id:
+ return
- frappe.db.set_value("Work Order Operation", self.operation_id, "completed_qty", qty)
- if (
- self.finished_good
- and frappe.get_cached_value("Work Order", self.work_order, "production_item")
- == self.finished_good
- ):
- _wo_doc = frappe.get_doc("Work Order", self.work_order)
- _wo_doc.db_set("produced_qty", self.manufactured_qty)
- _wo_doc.db_set("status", _wo_doc.get_status())
+ job_cards = frappe.get_all(
+ "Job Card",
+ filters={
+ "work_order": self.work_order,
+ "operation_id": self.operation_id,
+ "docstatus": 1,
+ "is_corrective_job_card": 0,
+ },
+ fields=["manufactured_qty", "total_completed_qty"],
+ )
+
+ completed_qty = sum(max(flt(row.manufactured_qty), flt(row.total_completed_qty)) for row in job_cards)
+
+ frappe.db.set_value("Work Order Operation", self.operation_id, "completed_qty", completed_qty)
+ if (
+ self.finished_good
+ and frappe.get_cached_value("Work Order", self.work_order, "production_item")
+ == self.finished_good
+ ):
+ _wo_doc = frappe.get_doc("Work Order", self.work_order)
+ _wo_doc.db_set("produced_qty", sum(flt(row.manufactured_qty) for row in job_cards))
+ _wo_doc.db_set("status", _wo_doc.get_status())
def update_corrective_in_work_order(self, wo):
wo.corrective_operation_cost = 0.0
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index 26260627c46..4b300e7862a 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -1186,6 +1186,109 @@ class TestJobCard(ERPNextTestSuite):
self.assertEqual(manufacturing_entry.items[2].qty, 9)
self.assertEqual(flt(manufacturing_entry.items[2].basic_rate, 3), 5.278)
+ def test_semi_fg_produced_qty_across_split_job_cards(self):
+ from erpnext.manufacturing.doctype.operation.test_operation import make_operation
+ from erpnext.manufacturing.doctype.work_order.mapper import make_job_card
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ warehouse = "Stores - _TC"
+ rm = make_item("Split JC RM 1", {"is_stock_item": 1}).name
+ fg = make_item("Split JC FG 1", {"is_stock_item": 1}).name
+
+ fg_bom = frappe.new_doc(
+ "BOM",
+ company="_Test Company",
+ item=fg,
+ quantity=1,
+ with_operations=1,
+ track_semi_finished_goods=1,
+ )
+ fg_bom.append("items", {"item_code": rm, "qty": 1, "operation_row_id": 1})
+
+ operation = {
+ "operation": "Split JC Op A",
+ "workstation": "_Test Workstation A",
+ "finished_good": fg,
+ "finished_good_qty": 1,
+ "is_final_finished_good": 1,
+ "sequence_id": 1,
+ "time_in_mins": 60,
+ "source_warehouse": warehouse,
+ "fg_warehouse": warehouse,
+ "skip_material_transfer": 1,
+ }
+ make_workstation(operation)
+ make_operation(operation)
+ fg_bom.append("operations", operation)
+ fg_bom.insert()
+ fg_bom.submit()
+
+ work_order = make_wo_order_test_record(
+ item=fg,
+ qty=8,
+ source_warehouse=warehouse,
+ fg_warehouse=warehouse,
+ bom_no=fg_bom.name,
+ skip_transfer=1,
+ do_not_save=True,
+ )
+ work_order.operations[0].time_in_mins = 60
+ work_order.save()
+ work_order.submit()
+
+ make_stock_entry(item_code=rm, target=warehouse, qty=100, basic_rate=100)
+
+ job_card = frappe.get_doc(
+ "Job Card", frappe.db.get_value("Job Card", {"work_order": work_order.name}, "name")
+ )
+ job_card.for_quantity = 5
+ job_card.append(
+ "time_logs",
+ {"from_time": "2024-02-01 08:00:00", "to_time": "2024-02-01 09:00:00", "completed_qty": 5},
+ )
+ job_card.save()
+ job_card.submit()
+ frappe.get_doc(job_card.make_stock_entry_for_semi_fg_item()).submit()
+
+ work_order.reload()
+ self.assertEqual(flt(work_order.produced_qty), 5)
+
+ make_job_card(
+ work_order.name,
+ [
+ {
+ "name": work_order.operations[0].name,
+ "operation": "Split JC Op A",
+ "qty": 3,
+ "pending_qty": 3,
+ "skip_material_transfer": 1,
+ }
+ ],
+ )
+
+ job_card = frappe.get_doc(
+ "Job Card", frappe.db.get_value("Job Card", {"work_order": work_order.name, "docstatus": 0})
+ )
+ job_card.append(
+ "time_logs",
+ {
+ "from_time": "2024-02-02 08:00:00",
+ "to_time": "2024-02-02 09:00:00",
+ "completed_qty": job_card.for_quantity,
+ },
+ )
+ job_card.save()
+ job_card.submit()
+ frappe.get_doc(job_card.make_stock_entry_for_semi_fg_item()).submit()
+
+ work_order.reload()
+ self.assertEqual(flt(work_order.produced_qty), 8)
+ self.assertEqual(work_order.status, "Completed")
+ self.assertEqual(
+ flt(frappe.db.get_value("Work Order Operation", work_order.operations[0].name, "completed_qty")),
+ 8,
+ )
+
def test_semi_fg_batch_auto_pull_on_manufacture(self):
"""Batch produced by an operation should auto-pull into the next operation's
semi-finished consumption row (skip-transfer Manufacture entry)."""
diff --git a/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py b/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py
index 615d840f08a..50ce07df729 100644
--- a/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py
+++ b/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py
@@ -10,12 +10,20 @@ from frappe.query_builder.functions import IfNull, Sum
from pypika.terms import ExistsCriterion
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
+from erpnext.stock.doctype.item.item import get_uom_conv_factor
def get_uom_conversion_factor(item_code, uom):
- return frappe.db.get_value(
+ item = frappe.get_cached_value("Item", item_code, ["variant_of", "stock_uom"], as_dict=True)
+ conversion_factor = frappe.db.get_value(
"UOM Conversion Detail", {"parent": item_code, "uom": uom}, "conversion_factor"
)
+ if not conversion_factor and item.variant_of:
+ conversion_factor = frappe.db.get_value(
+ "UOM Conversion Detail", {"parent": item.variant_of, "uom": uom}, "conversion_factor"
+ )
+
+ return conversion_factor or get_uom_conv_factor(uom, item.stock_uom)
@frappe.whitelist()
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index bc3c62bfffd..5917804c411 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -1903,6 +1903,118 @@ class TestProductionPlan(ERPNextTestSuite):
self.assertEqual(row.warehouse, mrp_warhouse)
self.assertEqual(row.quantity, 12.0)
+ def test_purchase_uom_falls_back_to_uom_conversion_factor(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+ if not frappe.db.exists("UOM Conversion Factor", {"from_uom": "Kg", "to_uom": "Gram"}):
+ frappe.get_doc(
+ doctype="UOM Conversion Factor",
+ category="Mass",
+ from_uom="Kg",
+ to_uom="Gram",
+ value=1000,
+ ).insert()
+
+ rm = make_item("Test RM Item Global CF", {"is_stock_item": 1, "stock_uom": "Gram"})
+ rm.purchase_uom = "Kg"
+ rm.save()
+ self.assertFalse([row for row in rm.uoms if row.uom == "Kg"])
+
+ bom_tree = {"Test FG Item Global CF": {rm.name: {}}}
+ parent_bom = create_nested_bom(bom_tree, prefix="")
+
+ plan = create_production_plan(
+ item_code=parent_bom.item,
+ planned_qty=2000,
+ ignore_existing_ordered_qty=1,
+ skip_getting_mr_items=1,
+ do_not_submit=1,
+ warehouse="_Test Warehouse - _TC",
+ )
+ plan.for_warehouse = "_Test Warehouse - _TC"
+
+ items = get_items_for_material_requests(
+ plan.as_dict(), warehouses=[{"warehouse": "_Test Warehouse - _TC"}]
+ )
+
+ row = frappe._dict(next(item for item in items if item["item_code"] == rm.name))
+ self.assertEqual(row.uom, "Kg")
+ self.assertEqual(row.conversion_factor, 1000)
+ self.assertEqual(row.quantity, 2)
+
+ def test_variant_inherits_purchase_uom_conversion_factor_of_template(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+ template = make_item(
+ "TRMVCF",
+ {
+ "is_stock_item": 1,
+ "stock_uom": "Nos",
+ "has_variants": 1,
+ "attributes": [{"attribute": "Colour"}],
+ },
+ )
+ if not [row for row in template.uoms if row.uom == "Box"]:
+ template.purchase_uom = "Box"
+ template.append("uoms", {"uom": "Box", "conversion_factor": 12})
+ template.save()
+
+ if not frappe.db.exists("Item", "TRMVCF-RED"):
+ create_variant("TRMVCF", {"Colour": "Red"}).insert()
+
+ variant = frappe.get_doc("Item", "TRMVCF-RED")
+ variant.uoms = [row for row in variant.uoms if row.uom != "Box"]
+ variant.purchase_uom = "Box"
+ variant.save()
+
+ bom_tree = {"Test FG Item Variant CF": {variant.name: {}}}
+ parent_bom = create_nested_bom(bom_tree, prefix="")
+
+ plan = create_production_plan(
+ item_code=parent_bom.item,
+ planned_qty=24,
+ ignore_existing_ordered_qty=1,
+ skip_getting_mr_items=1,
+ do_not_submit=1,
+ warehouse="_Test Warehouse - _TC",
+ )
+ plan.for_warehouse = "_Test Warehouse - _TC"
+
+ items = get_items_for_material_requests(
+ plan.as_dict(), warehouses=[{"warehouse": "_Test Warehouse - _TC"}]
+ )
+
+ row = frappe._dict(next(item for item in items if item["item_code"] == variant.name))
+ self.assertEqual(row.conversion_factor, 12)
+ self.assertEqual(row.quantity, 2)
+
+ def test_missing_purchase_uom_conversion_factor_throws(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+ rm = make_item("Test RM Item Missing CF", {"is_stock_item": 1, "stock_uom": "Nos"})
+ rm.purchase_uom = "Box"
+ rm.save()
+
+ bom_tree = {"Test FG Item Missing CF": {rm.name: {}}}
+ parent_bom = create_nested_bom(bom_tree, prefix="")
+
+ plan = create_production_plan(
+ item_code=parent_bom.item,
+ planned_qty=10,
+ ignore_existing_ordered_qty=1,
+ skip_getting_mr_items=1,
+ do_not_submit=1,
+ warehouse="_Test Warehouse - _TC",
+ )
+ plan.for_warehouse = "_Test Warehouse - _TC"
+
+ with self.assertRaises(frappe.ValidationError) as error:
+ get_items_for_material_requests(
+ plan.as_dict(), warehouses=[{"warehouse": "_Test Warehouse - _TC"}]
+ )
+
+ self.assertIn("UOM Conversion factor", str(error.exception))
+
def test_mr_qty_for_complex_bom(self):
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js
index 83b81690ec3..44103f210c2 100644
--- a/erpnext/manufacturing/doctype/routing/routing.js
+++ b/erpnext/manufacturing/doctype/routing/routing.js
@@ -79,6 +79,11 @@ frappe.ui.form.on("BOM Operation", {
const d = locals[cdt][cdn];
frm.events.calculate_operating_cost(frm, d);
},
+
+ hour_rate: function (frm, cdt, cdn) {
+ const d = locals[cdt][cdn];
+ frm.events.calculate_operating_cost(frm, d);
+ },
});
frappe.tour["Routing"] = [
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index b0154cb96f5..4c8e740e71e 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -80,7 +80,7 @@ class TestWorkstation(ERPNextTestSuite):
test_routing_operations = [
{"operation": "Test Operation A", "workstation": "_Test Workstation A", "time_in_mins": 60},
- {"operation": "Test Operation B", "workstation": "_Test Workstation A", "time_in_mins": 60},
+ {"operation": "Test Operation B", "workstation": "_Test Workstation A", "time_in_mins": 30},
]
routing_doc = create_routing(routing_name="Routing Test", operations=test_routing_operations)
bom_doc = setup_bom(item_code="_Testing Item", routing=routing_doc.name, currency="INR")
@@ -113,12 +113,16 @@ class TestWorkstation(ERPNextTestSuite):
# update_bom_operation() (run on w1.save()) must write the new rate directly onto the
# Routing's BOM Operation rows. This is the converted query's own effect (not the BOM
# update_cost above) and is what silently skipped on Postgres when parenttype was 'routing'.
- routing_op_rate = frappe.db.get_value(
- "BOM Operation",
- {"parent": routing_doc.name, "parenttype": "Routing", "workstation": "_Test Workstation A"},
- "hour_rate",
- )
- self.assertEqual(routing_op_rate, 250)
+ # It must also refresh operating_cost (hour_rate * time_in_mins / 60); the 30-min op
+ # exercises the arithmetic rather than a plain rate copy.
+ for operation, expected_operating_cost in (("Test Operation A", 250), ("Test Operation B", 125)):
+ hour_rate, operating_cost = frappe.db.get_value(
+ "BOM Operation",
+ {"parent": routing_doc.name, "parenttype": "Routing", "operation": operation},
+ ["hour_rate", "operating_cost"],
+ )
+ self.assertEqual(hour_rate, 250)
+ self.assertEqual(operating_cost, expected_operating_cost)
def make_workstation(*args, **kwargs):
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index 64be85f6a2f..f89e7700db6 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -206,6 +206,7 @@ class Workstation(Document):
(
frappe.qb.update(bom_op)
.set(bom_op.hour_rate, self.hour_rate)
+ .set(bom_op.operating_cost, self.hour_rate * bom_op.time_in_mins / 60)
.where(bom_op.parent.isin(bom_list) & (bom_op.workstation == self.name))
.run()
)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 4bc36ce882e..921296c3297 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -501,4 +501,9 @@ erpnext.patches.v16_0.crm_settings_handle_allowed_users_for_frappe_crm
erpnext.patches.v16_0.access_control_for_project_users
erpnext.patches.v16_0.enable_book_stock_expense_gl_entries
execute:frappe.db.set_single_value("Stock Settings", "use_inline_serial_batch_editor", 0)
-erpnext.patches.v16_0.recompute_production_plan_reserved_qty
+erpnext.patches.v16_0.recalculate_bins_for_production_plan_items
+erpnext.patches.v16_0.rename_ar_ap_ageing_filter
+erpnext.patches.v16_0.fix_subcontracting_titles
+erpnext.patches.v16_0.move_warehouse_defaults_to_company
+erpnext.patches.v16_0.backfill_repost_accounting_ledger_status
+erpnext.patches.v16_0.merge_seeded_item_group_root
diff --git a/erpnext/patches/v16_0/backfill_repost_accounting_ledger_status.py b/erpnext/patches/v16_0/backfill_repost_accounting_ledger_status.py
new file mode 100644
index 00000000000..f5a240a506c
--- /dev/null
+++ b/erpnext/patches/v16_0/backfill_repost_accounting_ledger_status.py
@@ -0,0 +1,25 @@
+import frappe
+from frappe.query_builder.functions import Coalesce
+
+
+def execute():
+ """Backfill the statuses of documents reposted before those fields existed.
+
+ Without it they show up as drafts and are offered a `Start Reposting` button that would
+ repost vouchers which are already reposted.
+ """
+ ral = frappe.qb.DocType("Repost Accounting Ledger")
+ items = frappe.qb.DocType("Repost Accounting Ledger Items")
+
+ reposted = (
+ frappe.qb.from_(ral).select(ral.name).where((ral.docstatus == 1) & (Coalesce(ral.status, "") == ""))
+ )
+ frappe.qb.update(items).set(items.status, "Reposted").where(items.parent.isin(reposted)).run()
+
+ for docstatus, status in ((1, "Completed"), (2, "Cancelled")):
+ (
+ frappe.qb.update(ral)
+ .set(ral.status, status)
+ .where((ral.docstatus == docstatus) & (Coalesce(ral.status, "") == ""))
+ .run()
+ )
diff --git a/erpnext/patches/v16_0/fix_subcontracting_titles.py b/erpnext/patches/v16_0/fix_subcontracting_titles.py
new file mode 100644
index 00000000000..71c55bfc80e
--- /dev/null
+++ b/erpnext/patches/v16_0/fix_subcontracting_titles.py
@@ -0,0 +1,28 @@
+import frappe
+
+
+def execute():
+ """
+ This patch corrects the titles of the subcontracting order doctypes set to
+ the text strings "{customer_name}" or "{supplier_name}" instead of the
+ actual customer or supplier name.
+
+ Their `title_field` never pointed at `title`, so the template default was
+ stored verbatim instead of being substituted.
+ """
+
+ party_fields = {
+ "Subcontracting Order": "supplier_name",
+ "Subcontracting Inward Order": "customer_name",
+ }
+
+ for doctype, party_field in party_fields.items():
+ if not frappe.db.has_column(doctype, "title"):
+ continue
+
+ table = frappe.qb.DocType(doctype)
+ (
+ frappe.qb.update(table)
+ .set(table.title, table[party_field])
+ .where(table.title == f"{{{party_field}}}")
+ ).run()
diff --git a/erpnext/patches/v16_0/merge_seeded_item_group_root.py b/erpnext/patches/v16_0/merge_seeded_item_group_root.py
new file mode 100644
index 00000000000..95683fc96f0
--- /dev/null
+++ b/erpnext/patches/v16_0/merge_seeded_item_group_root.py
@@ -0,0 +1,23 @@
+import frappe
+from frappe.utils.nestedset import get_root_of
+
+SEEDED_ROOT = "All Item Groups"
+
+
+def execute():
+ """Collapse the "All Item Groups" node seeded under a pre-existing root.
+
+ Setup seeding always inserted "All Item Groups" as a parentless group. On a
+ site where another app had already created the root (under a translated
+ name), it was re-parented instead, leaving a second group-root holding the
+ standard Item Groups.
+ """
+ root = get_root_of("Item Group")
+ if not root or root == SEEDED_ROOT:
+ return
+
+ seeded = frappe.db.get_value("Item Group", SEEDED_ROOT, ["parent_item_group", "is_group"], as_dict=True)
+ if not seeded or not seeded.is_group or seeded.parent_item_group != root:
+ return
+
+ frappe.rename_doc("Item Group", SEEDED_ROOT, root, merge=True, show_alert=False)
diff --git a/erpnext/patches/v16_0/move_warehouse_defaults_to_company.py b/erpnext/patches/v16_0/move_warehouse_defaults_to_company.py
new file mode 100644
index 00000000000..5299857b6a9
--- /dev/null
+++ b/erpnext/patches/v16_0/move_warehouse_defaults_to_company.py
@@ -0,0 +1,20 @@
+import frappe
+import frappe.defaults
+
+FIELDS = ("default_warehouse", "sample_retention_warehouse")
+
+
+def execute():
+ """Move the global warehouse defaults from Stock Settings onto the Company that owns them."""
+ settings = frappe.db.get_singles_dict("Stock Settings")
+ warehouses = {field: settings.get(field) for field in FIELDS if settings.get(field)}
+ if not warehouses:
+ return
+
+ for field, warehouse in warehouses.items():
+ company = frappe.db.get_value("Warehouse", warehouse, "company")
+ if company:
+ frappe.db.set_value("Company", company, field, warehouse)
+
+ frappe.db.delete("Singles", {"doctype": "Stock Settings", "field": ("in", FIELDS)})
+ frappe.defaults.clear_default("default_warehouse")
diff --git a/erpnext/patches/v16_0/recompute_production_plan_reserved_qty.py b/erpnext/patches/v16_0/recalculate_bins_for_production_plan_items.py
similarity index 85%
rename from erpnext/patches/v16_0/recompute_production_plan_reserved_qty.py
rename to erpnext/patches/v16_0/recalculate_bins_for_production_plan_items.py
index c33d59eea8f..a3fe864e404 100644
--- a/erpnext/patches/v16_0/recompute_production_plan_reserved_qty.py
+++ b/erpnext/patches/v16_0/recalculate_bins_for_production_plan_items.py
@@ -20,4 +20,4 @@ def execute():
bin_name = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse})
if not bin_name:
continue
- frappe.get_doc("Bin", bin_name, for_update=True).update_reserved_qty_for_production_plan()
+ frappe.get_doc("Bin", bin_name, for_update=True).recalculate_values()
diff --git a/erpnext/patches/v16_0/rename_ar_ap_ageing_filter.py b/erpnext/patches/v16_0/rename_ar_ap_ageing_filter.py
new file mode 100644
index 00000000000..8252c3b0aac
--- /dev/null
+++ b/erpnext/patches/v16_0/rename_ar_ap_ageing_filter.py
@@ -0,0 +1,45 @@
+import frappe
+
+REPORTS = (
+ "Accounts Receivable",
+ "Accounts Payable",
+ "Accounts Receivable Summary",
+ "Accounts Payable Summary",
+)
+
+
+def execute():
+ # filter `calculate_ageing_with` -> `age_as_on`, option "Today Date" -> "Today"
+ _migrate("Auto Email Report", "filters", "report")
+ _migrate("Dashboard Chart", "filters_json", "report_name", type_field="chart_type")
+ _migrate("Number Card", "filters_json", "report_name", type_field="type")
+
+
+def _migrate(doctype, filter_field, report_field, type_field=None):
+ conditions = {report_field: ("in", REPORTS)}
+ if type_field:
+ conditions[type_field] = "Report"
+
+ for row in frappe.get_all(doctype, filters=conditions, fields=["name", filter_field]):
+ updated = _rewrite(row.get(filter_field))
+ if updated is not None:
+ frappe.db.set_value(doctype, row.name, filter_field, updated, update_modified=False)
+
+
+def _rewrite(raw):
+ if not raw:
+ return None
+
+ try:
+ filters = frappe.parse_json(raw)
+ except ValueError:
+ return None
+
+ if not isinstance(filters, dict) or "calculate_ageing_with" not in filters:
+ return None
+
+ filters["age_as_on"] = filters.pop("calculate_ageing_with")
+ if filters["age_as_on"] == "Today Date":
+ filters["age_as_on"] = "Today"
+
+ return frappe.as_json(filters, indent=None)
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 28ba6cebdef..a21baa74893 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -453,6 +453,17 @@ class TestTimesheet(ERPNextTestSuite):
rate = get_timesheet_detail_rate(detail.name, timesheet.currency)
self.assertEqual(rate, detail.billing_amount)
+ def test_title_follows_employee(self):
+ first = make_employee("_test_timesheet_title_one@example.com", company="_Test Company")
+ second = make_employee("_test_timesheet_title_two@example.com", company="_Test Company")
+
+ timesheet = make_timesheet(first, simulate=True, do_not_submit=True)
+ self.assertEqual(timesheet.get_title(), frappe.db.get_value("Employee", first, "employee_name"))
+
+ timesheet.employee = second
+ timesheet.save()
+ self.assertEqual(timesheet.get_title(), frappe.db.get_value("Employee", second, "employee_name"))
+
@staticmethod
def _delete_if_exists(doctype, name):
if frappe.db.exists(doctype, name):
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index a703e6cd07f..ea50e074dbe 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -49,7 +49,6 @@
"fields": [
{
"allow_on_submit": 1,
- "default": "{employee_name}",
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
@@ -315,7 +314,7 @@
"idx": 1,
"is_submittable": 1,
"links": [],
- "modified": "2026-04-08 12:43:30.658074",
+ "modified": "2026-07-30 11:04:12.882140",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet",
@@ -409,5 +408,5 @@
"sort_field": "creation",
"sort_order": "ASC",
"states": [],
- "title_field": "title"
+ "title_field": "employee_name"
}
diff --git a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js
index b2d55dda9c4..f9a311cbc7e 100644
--- a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js
+++ b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js
@@ -57,6 +57,7 @@ class BOMConfigurator {
breadcrumb: "Manufacturing",
get_tree_nodes: "erpnext.manufacturing.doctype.bom_creator.bom_creator.get_children",
root_label: this.frm.doc.item_code,
+ get_label: (node) => this.get_node_label(node),
disable_add_node: true,
get_tree_root: false,
show_expand_all: false,
@@ -66,6 +67,23 @@ class BOMConfigurator {
};
}
+ get_node_label(node) {
+ const item_code = this.get_item_code(node);
+ const item_name = node.data?.title || item_code;
+
+ if (item_name === item_code) {
+ return frappe.utils.escape_html(item_code);
+ }
+
+ return `${frappe.utils.escape_html(item_name)} (${frappe.utils.escape_html(
+ item_code
+ )})`;
+ }
+
+ get_item_code(node) {
+ return node.data?.item_code || this.frm.doc.item_code;
+ }
+
tree_methods() {
let frm_obj = this;
let view = frappe.views.trees["BOM Configurator"];
@@ -73,7 +91,8 @@ class BOMConfigurator {
return {
onload: function (me) {
me.args["parent_id"] = frm_obj.frm.doc.name;
- me.args["parent"] = frm_obj.frm.doc.item_code;
+ me.args["parent"] = frm_obj.frm.doc.name;
+ me.root_value = frm_obj.frm.doc.name;
me.parent = frm_obj.$wrapper.get(0);
me.body = frm_obj.$wrapper.get(0);
me.make_tree();
@@ -83,7 +102,7 @@ class BOMConfigurator {
const uom = node.data.uom || frm_obj.frm.doc.uom;
const docname = node.data.name || frm_obj.frm.doc.name;
let amount = node.data.amount;
- if (node.data.value === frm_obj.frm.doc.item_code) {
+ if (node.is_root) {
amount = frm_obj.frm.doc.raw_material_cost;
}
@@ -243,7 +262,7 @@ class BOMConfigurator {
method: "add_item",
doc: this.frm.doc,
args: {
- fg_item: node.data.value,
+ fg_item: this.get_item_code(node),
item_code: data.item_code,
fg_reference_id: node.data.name || this.frm.doc.name,
qty: data.qty,
@@ -298,7 +317,7 @@ class BOMConfigurator {
method: "add_sub_assembly",
doc: this.frm.doc,
args: {
- fg_item: node.data.value,
+ fg_item: this.get_item_code(node),
fg_reference_id: node.data.name || this.frm.doc.name,
bom_item: bom_item,
operation: node.data.operation,
@@ -417,7 +436,7 @@ class BOMConfigurator {
});
dialog.set_values({
- item_code: node.data.value,
+ item_code: this.get_item_code(node),
qty: node.data.qty,
});
@@ -445,7 +464,7 @@ class BOMConfigurator {
method: "add_sub_assembly",
doc: this.frm.doc,
args: {
- fg_item: node.data.value,
+ fg_item: this.get_item_code(node),
bom_item: bom_item,
fg_reference_id: node.data.name || this.frm.doc.name,
convert_to_sub_assembly: true,
@@ -482,7 +501,6 @@ class BOMConfigurator {
method: "delete_node",
doc: this.frm.doc,
args: {
- fg_item: node.data.value,
doctype: node.data.doctype,
docname: node.data.name,
},
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 8a0719c6d3f..dfdf2827cee 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -3,6 +3,11 @@
const NOT_APPLICABLE_TAX = "N/A";
+// Per-charge_type base resolvers, mirror of the `erpnext_taxable_base_resolvers`
+// server hook. A localization registers `fn(calc, item, tax)` returning the per-item
+// base, so the client preview matches the server for custom charge types.
+erpnext.taxable_base_resolvers = erpnext.taxable_base_resolvers || {};
+
erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
setup() {
this.fetch_round_off_accounts();
@@ -263,32 +268,32 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
$.each(this.frm.doc.items || [], function (n, item) {
item._unrounded_net_amount = null;
var item_tax_map = me._load_item_tax_rate(item.item_tax_rate);
- var cumulated_tax_fraction = 0.0;
- var total_inclusive_tax_amount_per_qty = 0;
+ var total_tax_slope = 0.0;
+ var total_tax_intercept = 0;
$.each(me.frm.doc["taxes"] || [], function (i, tax) {
- var current_tax_fraction = me.get_current_tax_fraction(tax, item_tax_map);
- tax.tax_fraction_for_current_item = current_tax_fraction[0];
- var inclusive_tax_amount_per_qty = current_tax_fraction[1];
+ var tax_contribution = me.get_current_tax_fraction(tax, item_tax_map, item);
+ tax.tax_fraction_for_current_item = tax_contribution[0];
+ var tax_intercept_per_qty = tax_contribution[1];
+ tax.inclusive_amount_per_qty = tax_intercept_per_qty;
if (i == 0) {
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
+ tax.grand_total_amount_per_qty = tax_intercept_per_qty;
} else {
+ var prev = me.frm.doc["taxes"][i - 1];
tax.grand_total_fraction_for_current_item =
- me.frm.doc["taxes"][i - 1].grand_total_fraction_for_current_item +
- tax.tax_fraction_for_current_item;
+ prev.grand_total_fraction_for_current_item + tax.tax_fraction_for_current_item;
+ tax.grand_total_amount_per_qty =
+ flt(prev.grand_total_amount_per_qty) + tax_intercept_per_qty;
}
- cumulated_tax_fraction += tax.tax_fraction_for_current_item;
- total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty);
+ total_tax_slope += tax.tax_fraction_for_current_item;
+ total_tax_intercept += tax_intercept_per_qty * flt(item.qty);
});
- if (
- !me.discount_amount_applied &&
- item.qty &&
- (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)
- ) {
- var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty;
- item._unrounded_net_amount = amount / (1 + cumulated_tax_fraction);
+ if (!me.discount_amount_applied && item.qty && (total_tax_intercept || total_tax_slope)) {
+ var amount = flt(item.amount) - total_tax_intercept;
+ item._unrounded_net_amount = amount / (1 + total_tax_slope);
item.net_amount = flt(item._unrounded_net_amount, precision("net_amount", item));
item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0;
@@ -297,39 +302,53 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
});
}
- get_current_tax_fraction(tax, item_tax_map) {
- // Get tax fraction for calculating tax exclusive amount
- // from tax inclusive amount
- var current_tax_fraction = 0.0;
- var inclusive_tax_amount_per_qty = 0;
+ get_current_tax_fraction(tax, item_tax_map, item) {
+ // tax = slope * net + intercept.
+ // Returns [slope, intercept_per_qty]
+ var tax_slope = 0.0;
+ var tax_intercept = 0;
if (cint(tax.included_in_print_rate)) {
var tax_rate = this._get_tax_rate(tax, item_tax_map);
if (tax_rate === NOT_APPLICABLE_TAX) {
- return [current_tax_fraction, inclusive_tax_amount_per_qty];
+ return [tax_slope, tax_intercept];
}
if (tax.charge_type == "On Net Total") {
- current_tax_fraction = tax_rate / 100.0;
+ tax_slope = tax_rate / 100.0;
} else if (tax.charge_type == "On Previous Row Amount") {
- current_tax_fraction =
- (tax_rate / 100.0) *
- this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_fraction_for_current_item;
+ const row = this.frm.doc["taxes"][cint(tax.row_id) - 1];
+ tax_slope = (tax_rate / 100.0) * row.tax_fraction_for_current_item;
+ tax_intercept = (tax_rate / 100.0) * flt(row.inclusive_amount_per_qty);
} else if (tax.charge_type == "On Previous Row Total") {
- current_tax_fraction =
- (tax_rate / 100.0) *
- this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item;
+ const row = this.frm.doc["taxes"][cint(tax.row_id) - 1];
+ tax_slope = (tax_rate / 100.0) * row.grand_total_fraction_for_current_item;
+ tax_intercept = (tax_rate / 100.0) * flt(row.grand_total_amount_per_qty);
} else if (tax.charge_type == "On Item Quantity") {
- inclusive_tax_amount_per_qty = flt(tax_rate);
+ tax_intercept = flt(tax_rate);
+ } else {
+ // Custom charge_type: the rate applies to a resolved (fixed) base,
+ // e.g. a tax on MRP included in the printed price.
+ const qty = flt(item.qty) || 1;
+ const base = this.get_item_taxable_base(item, tax);
+ tax_intercept = ((tax_rate / 100.0) * base) / qty;
}
}
if (tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
- current_tax_fraction *= -1;
- inclusive_tax_amount_per_qty *= -1;
+ tax_slope *= -1;
+ tax_intercept *= -1;
}
- return [current_tax_fraction, inclusive_tax_amount_per_qty];
+ return [tax_slope, tax_intercept];
+ }
+
+ get_item_taxable_base(item, tax) {
+ // Mirror of the server get_item_taxable_base: a custom charge_type's resolver
+ // overrides the base value; otherwise the net amount.
+ const resolver = erpnext.taxable_base_resolvers[tax.charge_type];
+ if (resolver) return flt(resolver(this, item, tax));
+ return flt(item.net_amount);
}
_get_tax_rate(tax, item_tax_map) {
@@ -591,6 +610,11 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
} else if (tax.charge_type == "On Item Quantity") {
// don't sum current net amount due to the field being a currency field
current_tax_amount = tax_rate * item.qty;
+ } else {
+ // Custom charge_type: rate applies to the resolver-provided base.
+ var resolved_base = this.get_item_taxable_base(item, tax);
+ current_net_amount = resolved_base;
+ current_tax_amount = (tax_rate / 100.0) * resolved_base;
}
return [current_net_amount, current_tax_amount];
diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py
index ce2c7450f70..1cbede63ef1 100644
--- a/erpnext/regional/italy/utils.py
+++ b/erpnext/regional/italy/utils.py
@@ -219,7 +219,7 @@ def append_row_as_charges(items, tax, reference_row, summary_data):
# Preflight for successful e-invoice export.
def sales_invoice_validate(doc):
# Validate company
- if doc.doctype != "Sales Invoice":
+ if doc.doctype != "Sales Invoice" or doc.is_opening == "Yes":
return
if not doc.company_address:
@@ -303,7 +303,7 @@ def sales_invoice_validate(doc):
# Ensure payment details are valid for e-invoice.
def sales_invoice_on_submit(doc, method):
# Validate payment details
- if get_company_country(doc.company) not in [
+ if doc.is_opening == "Yes" or get_company_country(doc.company) not in [
"Italy",
"Italia",
"Italian Republic",
@@ -369,7 +369,7 @@ def generate_single_invoice(docname: str):
# Delete e-invoice attachment on cancel.
def sales_invoice_on_cancel(doc, method):
- if get_company_country(doc.company) not in [
+ if doc.is_opening == "Yes" or get_company_country(doc.company) not in [
"Italy",
"Italia",
"Italian Republic",
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index 721ea466938..807f653fc27 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -445,33 +445,44 @@ class TestCustomer(ERPNextTestSuite):
overdue = get_customer_overdue_amount("_Test Customer", "_Test Company")
settings = frappe.get_single("Accounts Settings")
- settings.enable_overdue_billing_threshold = 1
- settings.role_allowed_to_bypass_overdue_billing = None
- settings.save()
- set_overdue_billing_threshold("_Test Customer", "_Test Company", overdue - 100)
+ original_enable = settings.enable_overdue_billing_threshold
+ original_bypass_role = settings.role_allowed_to_bypass_overdue_billing
+ try:
+ settings.enable_overdue_billing_threshold = 1
+ settings.role_allowed_to_bypass_overdue_billing = None
+ settings.save()
+ set_overdue_billing_threshold("_Test Customer", "_Test Company", overdue - 100)
- # overdue is over the threshold and the user has no bypass role -> blocked
- si = create_sales_invoice(do_not_submit=True)
- self.assertRaises(frappe.ValidationError, si.submit)
+ # overdue is over the threshold and the user has no bypass role -> blocked
+ si = create_sales_invoice(do_not_submit=True)
+ self.assertRaises(frappe.ValidationError, si.submit)
- # a user holding the bypass role can still submit
- settings.role_allowed_to_bypass_overdue_billing = "Accounts Manager"
- settings.save()
- si = create_sales_invoice(do_not_submit=True)
- si.submit()
- self.assertEqual(si.docstatus, 1)
+ # a user holding the bypass role can still submit
+ settings.role_allowed_to_bypass_overdue_billing = "Accounts Manager"
+ settings.save()
+ si = create_sales_invoice(do_not_submit=True)
+ si.submit()
+ self.assertEqual(si.docstatus, 1)
- # threshold still crossed, but the feature is off -> never blocked
- settings.enable_overdue_billing_threshold = 0
- settings.role_allowed_to_bypass_overdue_billing = None
- settings.save()
- si = create_sales_invoice(do_not_submit=True)
- si.submit()
- self.assertEqual(si.docstatus, 1)
+ # threshold still crossed, but the feature is off -> never blocked
+ settings.enable_overdue_billing_threshold = 0
+ settings.role_allowed_to_bypass_overdue_billing = None
+ settings.save()
+ si = create_sales_invoice(do_not_submit=True)
+ si.submit()
+ self.assertEqual(si.docstatus, 1)
+ finally:
+ settings.enable_overdue_billing_threshold = original_enable
+ settings.role_allowed_to_bypass_overdue_billing = original_bypass_role
+ settings.save()
def test_overdue_billing_threshold_falls_back_to_customer_group(self):
customer_group = frappe.get_cached_value("Customer", "_Test Customer", "customer_group")
group = frappe.get_doc("Customer Group", customer_group)
+ customer = frappe.get_doc("Customer", "_Test Customer")
+ self._restore_credit_limits_after(group)
+ self._restore_credit_limits_after(customer)
+
group.credit_limits = []
group.append("credit_limits", {"company": "_Test Company", "overdue_billing_threshold": 5000})
group.save()
@@ -483,6 +494,22 @@ class TestCustomer(ERPNextTestSuite):
set_overdue_billing_threshold("_Test Customer", "_Test Company", 2000)
self.assertEqual(get_overdue_billing_threshold("_Test Customer", "_Test Company"), 2000)
+ # a 0 on the customer inherits the group's limit
+ set_overdue_billing_threshold("_Test Customer", "_Test Company", 0)
+ self.assertEqual(get_overdue_billing_threshold("_Test Customer", "_Test Company"), 5000)
+
+ def _restore_credit_limits_after(self, doc):
+ original = [row.as_dict(no_default_fields=True) for row in doc.credit_limits]
+
+ def restore():
+ fresh = frappe.get_doc(doc.doctype, doc.name)
+ fresh.credit_limits = []
+ for row in original:
+ fresh.append("credit_limits", row)
+ fresh.save()
+
+ self.addCleanup(restore)
+
def test_overdue_threshold_row_without_credit_limit(self):
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 8eaf33c084d..b1bca9e3e39 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -301,6 +301,7 @@ class Quotation(SellingController):
# update enquiry status
self.update_opportunity("Quotation")
self.update_lead()
+ self.carry_forward_communication()
def on_cancel(self):
if self.lost_reasons:
@@ -312,6 +313,18 @@ class Quotation(SellingController):
self.update_opportunity("Open")
self.update_lead()
+ def carry_forward_communication(self):
+ from erpnext.crm.utils import copy_comments, link_communications
+
+ if not (
+ self.opportunity
+ and frappe.get_single_value("CRM Settings", "carry_forward_communication_and_comments")
+ ):
+ return
+
+ copy_comments("Opportunity", self.opportunity, self)
+ link_communications("Opportunity", self.opportunity, self)
+
def print_other_charges(self, docname):
print_lst = []
for d in self.get("taxes"):
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index ec8de1f0d90..49464f94875 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -691,6 +691,51 @@ class TestSalesOrder(ERPNextTestSuite):
frappe.ValidationError, update_child_qty_rate, "Sales Order", trans_item, so.name
)
+ def test_update_child_removing_item_without_cancel_and_delete_perms(self):
+ for workflow_name in frappe.get_all(
+ "Workflow", filters={"document_type": "Sales Order", "is_active": 1}, pluck="name"
+ ):
+ workflow = frappe.get_doc("Workflow", workflow_name)
+ workflow.is_active = 0
+ workflow.save()
+
+ role = "_Test Sales Order Item Editor"
+ if not frappe.db.exists("Role", role):
+ frappe.get_doc({"doctype": "Role", "role_name": role, "desk_access": 1}).insert()
+
+ frappe.permissions.add_permission("Sales Order", role, 0)
+ for right, value in {
+ "read": 1,
+ "write": 1,
+ "create": 1,
+ "submit": 1,
+ "cancel": 0,
+ "delete": 0,
+ }.items():
+ frappe.permissions.update_permission_property("Sales Order", role, 0, right, value)
+ frappe.clear_cache()
+
+ so = make_sales_order(**{"item_list": [{"item_code": "_Test Item", "qty": 5, "rate": 1000}]})
+ trans_item = json.dumps(
+ [
+ {"item_code": "_Test Item", "qty": 5, "rate": 1000, "docname": so.items[0].name},
+ {"item_code": "_Test Item 2", "qty": 2, "rate": 500},
+ ]
+ )
+ update_child_qty_rate("Sales Order", trans_item, so.name)
+ so.reload()
+ self.assertEqual(len(so.items), 2)
+
+ test_user = create_user("test_so_item_editor@example.com", role, "Accounts User", "Stock User")
+ trans_item = json.dumps(
+ [{"item_code": "_Test Item", "qty": 5, "rate": 1000, "docname": so.items[0].name}]
+ )
+ with self.set_user(test_user.name):
+ update_child_qty_rate("Sales Order", trans_item, so.name)
+
+ so.reload()
+ self.assertEqual(len(so.items), 1)
+
def test_update_child_qty_rate_with_workflow(self):
from frappe.model.workflow import apply_workflow
@@ -935,8 +980,8 @@ class TestSalesOrder(ERPNextTestSuite):
self.assertEqual(so.taxes[0].tax_amount, 10)
self.assertEqual(so.taxes[0].total, 110)
- old_stock_settings_value = frappe.db.get_single_value("Stock Settings", "default_warehouse")
- frappe.db.set_single_value("Stock Settings", "default_warehouse", "_Test Warehouse - _TC")
+ old_default_warehouse = frappe.db.get_value("Company", "_Test Company", "default_warehouse")
+ frappe.db.set_value("Company", "_Test Company", "default_warehouse", "_Test Warehouse - _TC")
items = json.dumps(
[
@@ -974,7 +1019,7 @@ class TestSalesOrder(ERPNextTestSuite):
so.delete()
new_item_with_tax.delete()
frappe.get_doc("Item Tax Template", "Test Update Items Template - _TC").delete()
- frappe.db.set_single_value("Stock Settings", "default_warehouse", old_stock_settings_value)
+ frappe.db.set_value("Company", "_Test Company", "default_warehouse", old_default_warehouse)
def test_warehouse_user(self):
test_user = create_user("test_so_warehouse_user@example.com", "Sales User", "Stock User")
diff --git a/erpnext/selling/print_format/quotation_bordered/__init__.py b/erpnext/selling/print_format/quotation_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/quotation_bordered/quotation_bordered.json b/erpnext/selling/print_format/quotation_bordered/quotation_bordered.json
new file mode 100644
index 00000000000..1dab453266b
--- /dev/null
+++ b/erpnext/selling/print_format/quotation_bordered/quotation_bordered.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:05:57.465609",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Party Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Quotation\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill From:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"company_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Valid Till\",\"fieldname\":\"valid_till\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu_zBGrfDlm\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill To:
\\n
{{ doc.customer_name }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Quotation Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":9},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":11}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[]},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":65,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"f\",\"v\":\"tax_amount\"}],\"align\":\"right\",\"width\":35,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1.5px solid #e5e7eb;margin-top:6px;padding-top:10px;font-weight:700;\",\"label_color\":\"#1f2328\"}]}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":40,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0},\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":5,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 17:08:25.529426",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Quotation Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/selling/print_format/quotation_classic/__init__.py b/erpnext/selling/print_format/quotation_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/quotation_classic/quotation_classic.json b/erpnext/selling/print_format/quotation_classic/quotation_classic.json
new file mode 100644
index 00000000000..bb6f277e944
--- /dev/null
+++ b/erpnext/selling/print_format/quotation_classic/quotation_classic.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:05:57.475871",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Party Name\\n
\\n
\",\"custom\":1},{\"label\":\"Party Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"Quotation\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Posting Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Valid Till\",\"fieldname\":\"valid_till\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Quotation Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":47},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":50}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":24,\"margin\":{\"top\":10,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\"}]}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-24 17:08:25.665086",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Quotation Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/selling/print_format/quotation_modern/__init__.py b/erpnext/selling/print_format/quotation_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/quotation_modern/quotation_modern.json b/erpnext/selling/print_format/quotation_modern/quotation_modern.json
new file mode 100644
index 00000000000..f84fa72ee56
--- /dev/null
+++ b/erpnext/selling/print_format/quotation_modern/quotation_modern.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:05:57.454549",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Quotation\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Party Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Valid Till\",\"fieldname\":\"valid_till\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"Draft\\nOpen\\nReplied\\nPartially Ordered\\nOrdered\\nLost\\nCancelled\\nExpired\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Quotation Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":14},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":45},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\",\"custom_style\":\"\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":49}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 17:08:25.693075",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Quotation Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/selling/print_format/quotation_modern_with_images/__init__.py b/erpnext/selling/print_format/quotation_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/quotation_modern_with_images/quotation_modern_with_images.json b/erpnext/selling/print_format/quotation_modern_with_images/quotation_modern_with_images.json
new file mode 100644
index 00000000000..82aab9e418e
--- /dev/null
+++ b/erpnext/selling/print_format/quotation_modern_with_images/quotation_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 17:05:57.314489",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Quotation",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Quotation\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Party Name\\n
\\n
\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Party Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"},{\"label\":\"Bill To\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\",\"align\":\"left\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Valid Till\",\"fieldname\":\"valid_till\",\"fieldtype\":\"Date\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"Draft\\nOpen\\nReplied\\nPartially Ordered\\nOrdered\\nLost\\nCancelled\\nExpired\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Quotation Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":51},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":0,\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":70,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"color\":\"#1f2328\",\"width\":30}],\"custom_style\":\"\",\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"border-top: 1px solid #e5e7eb;\\nmargin-top:5px;\\nfont-weight: bold;\\npadding-top:9px\\n\"}],\"width\":49}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words:\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"label_gap\":2,\"custom_style\":\"\"}]}],\"field_orientation\":\"left-right\",\"background\":\"#f8f8f8\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 17:08:25.716701",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Quotation Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/selling/print_format/sales_order_bordered/__init__.py b/erpnext/selling/print_format/sales_order_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/sales_order_bordered/sales_order_bordered.json b/erpnext/selling/print_format/sales_order_bordered/sales_order_bordered.json
new file mode 100644
index 00000000000..b159f89c60d
--- /dev/null
+++ b/erpnext/selling/print_format/sales_order_bordered/sales_order_bordered.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 12:43:52.078481",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Sales Order\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill From:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"company_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Delivery Date\",\"fieldname\":\"delivery_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu_zBGrfDlm\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill To:
\\n
{{ doc.customer }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Order Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":9},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":11}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[]},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":65,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"f\",\"v\":\"tax_amount\"}],\"align\":\"right\",\"width\":35,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1.5px solid #e5e7eb;margin-top:6px;padding-top:10px;font-weight:700;\",\"label_color\":\"#1f2328\"}]}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":40,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0},\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":5,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 12:56:57.777517",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/selling/print_format/sales_order_classic/__init__.py b/erpnext/selling/print_format/sales_order_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/sales_order_classic/sales_order_classic.json b/erpnext/selling/print_format/sales_order_classic/sales_order_classic.json
new file mode 100644
index 00000000000..eba7a43f186
--- /dev/null
+++ b/erpnext/selling/print_format/sales_order_classic/sales_order_classic.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 12:43:52.266093",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom\":1},{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"Sales Order\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Delivery Date\",\"fieldname\":\"delivery_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Order Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":47},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":50}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":24,\"margin\":{\"top\":10,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\"}]}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-24 12:56:57.882204",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/selling/print_format/sales_order_modern/__init__.py b/erpnext/selling/print_format/sales_order_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/sales_order_modern/sales_order_modern.json b/erpnext/selling/print_format/sales_order_modern/sales_order_modern.json
new file mode 100644
index 00000000000..c64cfa910a2
--- /dev/null
+++ b/erpnext/selling/print_format/sales_order_modern/sales_order_modern.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 12:43:52.279182",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Sales Order\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Billed To\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Delivery Date\",\"fieldname\":\"delivery_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nOn Hold\\nTo Pay\\nTo Deliver and Bill\\nTo Bill\\nTo Deliver\\nCompleted\\nCancelled\\nClosed\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Order Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":14},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":45},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\",\"custom_style\":\"\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":49}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 12:56:57.903631",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/selling/print_format/sales_order_modern_with_images/__init__.py b/erpnext/selling/print_format/sales_order_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/selling/print_format/sales_order_modern_with_images/sales_order_modern_with_images.json b/erpnext/selling/print_format/sales_order_modern_with_images/sales_order_modern_with_images.json
new file mode 100644
index 00000000000..1639a62605c
--- /dev/null
+++ b/erpnext/selling/print_format/sales_order_modern_with_images/sales_order_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 12:43:52.291834",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Sales Order",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Sales Order\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Customer\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"},{\"label\":\"Bill To\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\",\"align\":\"left\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Order Date\",\"fieldname\":\"transaction_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Delivery Date\",\"fieldname\":\"delivery_date\",\"fieldtype\":\"Date\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nOn Hold\\nTo Pay\\nTo Deliver and Bill\\nTo Bill\\nTo Deliver\\nCompleted\\nCancelled\\nClosed\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Sales Order Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":51},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":0,\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":70,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"color\":\"#1f2328\",\"width\":30}],\"custom_style\":\"\",\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"border-top: 1px solid #e5e7eb;\\nmargin-top:5px;\\nfont-weight: bold;\\npadding-top:9px\\n\"}],\"width\":49}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words:\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"label_gap\":2,\"custom_style\":\"\"}]}],\"field_orientation\":\"left-right\",\"background\":\"#f8f8f8\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 12:56:57.932069",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index b1d75796c8f..4dc23d4b1e6 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -3,6 +3,17 @@
frappe.provide("erpnext.company");
+// Static filters (is_group / disabled / warehouse_type) live in the fields' link_filters.
+const WAREHOUSE_DEFAULT_FIELDS = [
+ "default_warehouse",
+ "sample_retention_warehouse",
+ "default_in_transit_warehouse",
+ "default_warehouse_for_sales_return",
+ "default_wip_warehouse",
+ "default_fg_warehouse",
+ "default_scrap_warehouse",
+];
+
frappe.ui.form.on("Company", {
onload: function (frm) {
if (frm.doc.__islocal && frm.doc.parent_company) {
@@ -51,23 +62,21 @@ frappe.ui.form.on("Company", {
return { filters: { buying: 1 } };
});
- frm.set_query("default_in_transit_warehouse", function () {
- return {
- filters: {
- warehouse_type: "Transit",
- is_group: 0,
- company: frm.doc.company_name,
- },
- };
+ WAREHOUSE_DEFAULT_FIELDS.forEach((fieldname) => {
+ frm.set_query(fieldname, function (doc) {
+ return { filters: { company: doc.name } };
+ });
});
- frm.set_query("default_warehouse_for_sales_return", function () {
- return {
- filters: {
- company: frm.doc.name,
- is_group: 0,
- },
- };
+ ["default_wip_warehouse", "default_fg_warehouse", "default_scrap_warehouse"].forEach((fieldname) => {
+ frm.set_query(fieldname, function (doc) {
+ return {
+ filters: {
+ company: doc.name,
+ is_group: 0,
+ },
+ };
+ });
});
frm.set_query("default_letter_head", function () {
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 9d0fcef0c4e..84d1a161b87 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -112,7 +112,6 @@
"column_break_goals",
"default_selling_terms",
"default_sales_contact",
- "default_warehouse_for_sales_return",
"credit_limit",
"transactions_annual_history",
"purchase_expense_section",
@@ -120,6 +119,10 @@
"service_expense_account",
"column_break_ereg",
"purchase_expense_contra_account",
+ "stock_expense_section",
+ "expenses_added_to_stock_account",
+ "column_break_gthb",
+ "expenses_added_to_stock_contra_account",
"stock_tab",
"auto_accounting_for_stock_settings",
"enable_perpetual_inventory",
@@ -136,14 +139,14 @@
"stock_delivered_but_not_billed",
"disable_sdbnb_in_sr",
"default_provisional_account",
- "default_in_transit_warehouse",
- "stock_expense_section",
- "expenses_added_to_stock_account",
- "column_break_gthb",
- "expenses_added_to_stock_contra_account",
"manufacturing_section",
"default_operating_cost_account",
- "column_break_9prc",
+ "warehouse_defaults_section",
+ "default_warehouse",
+ "sample_retention_warehouse",
+ "default_in_transit_warehouse",
+ "column_break_ware",
+ "default_warehouse_for_sales_return",
"default_wip_warehouse",
"default_fg_warehouse",
"default_scrap_warehouse",
@@ -278,6 +281,7 @@
"fieldname": "default_warehouse_for_sales_return",
"fieldtype": "Link",
"label": "Default Warehouse for Sales Return",
+ "link_filters": "[[\"Warehouse\",\"is_group\",\"=\",0],[\"Warehouse\",\"disabled\",\"=\",0]]",
"options": "Warehouse"
},
{
@@ -734,8 +738,33 @@
"fieldname": "default_in_transit_warehouse",
"fieldtype": "Link",
"label": "Default In-Transit Warehouse",
+ "link_filters": "[[\"Warehouse\",\"is_group\",\"=\",0],[\"Warehouse\",\"disabled\",\"=\",0],[\"Warehouse\",\"warehouse_type\",\"=\",\"Transit\"]]",
"options": "Warehouse"
},
+ {
+ "fieldname": "warehouse_defaults_section",
+ "fieldtype": "Section Break",
+ "label": "Warehouse Defaults"
+ },
+ {
+ "fieldname": "default_warehouse",
+ "fieldtype": "Link",
+ "label": "Default Warehouse",
+ "link_filters": "[[\"Warehouse\",\"is_group\",\"=\",0],[\"Warehouse\",\"disabled\",\"=\",0]]",
+ "options": "Warehouse"
+ },
+ {
+ "documentation_url": "https://docs.frappe.io/erpnext/retain-sample-stock",
+ "fieldname": "sample_retention_warehouse",
+ "fieldtype": "Link",
+ "label": "Sample Retention Warehouse",
+ "link_filters": "[[\"Warehouse\",\"is_group\",\"=\",0],[\"Warehouse\",\"disabled\",\"=\",0]]",
+ "options": "Warehouse"
+ },
+ {
+ "fieldname": "column_break_ware",
+ "fieldtype": "Column Break"
+ },
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "unrealized_profit_loss_account",
@@ -997,27 +1026,23 @@
"fieldname": "default_wip_warehouse",
"fieldtype": "Link",
"label": " Default Work In Progress Warehouse ",
- "link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
+ "link_filters": "[[\"Warehouse\",\"is_group\",\"=\",0],[\"Warehouse\",\"disabled\",\"=\",0]]",
"options": "Warehouse"
},
{
"fieldname": "default_fg_warehouse",
"fieldtype": "Link",
"label": "Default Finished Goods Warehouse",
- "link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
+ "link_filters": "[[\"Warehouse\",\"is_group\",\"=\",0],[\"Warehouse\",\"disabled\",\"=\",0]]",
"options": "Warehouse"
},
{
"fieldname": "default_scrap_warehouse",
"fieldtype": "Link",
"label": "Default Scrap Warehouse",
- "link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
+ "link_filters": "[[\"Warehouse\",\"is_group\",\"=\",0],[\"Warehouse\",\"disabled\",\"=\",0]]",
"options": "Warehouse"
},
- {
- "fieldname": "column_break_9prc",
- "fieldtype": "Column Break"
- },
{
"fieldname": "default_sales_contact",
"fieldtype": "Link",
@@ -1104,7 +1129,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
- "modified": "2026-07-15 15:38:29.214020",
+ "modified": "2026-07-26 21:14:52.739814",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index e0546122344..34022033aec 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -89,6 +89,7 @@ class Company(NestedSet):
default_sales_contact: DF.Link | None
default_scrap_warehouse: DF.Link | None
default_selling_terms: DF.Link | None
+ default_warehouse: DF.Link | None
default_warehouse_for_sales_return: DF.Link | None
default_wip_warehouse: DF.Link | None
depreciation_cost_center: DF.Link | None
@@ -128,6 +129,7 @@ class Company(NestedSet):
round_off_cost_center: DF.Link | None
round_off_for_opening: DF.Link | None
sales_monthly_history: DF.SmallText | None
+ sample_retention_warehouse: DF.Link | None
series_for_depreciation_entry: DF.Data | None
service_expense_account: DF.Link | None
stock_adjustment_account: DF.Link | None
@@ -187,6 +189,7 @@ class Company(NestedSet):
self.validate_parent_company()
self.set_reporting_currency()
self.validate_inventory_account_settings()
+ self.validate_warehouses()
self.cant_change_valuation_method()
self.validate_pending_reposts(old_doc)
self.validate_sdbnb_configuration()
@@ -299,6 +302,42 @@ class Company(NestedSet):
title=_("Cannot Change Inventory Account Setting"),
)
+ def validate_warehouses(self):
+ for fieldname in (
+ "default_warehouse",
+ "sample_retention_warehouse",
+ "default_in_transit_warehouse",
+ "default_warehouse_for_sales_return",
+ "default_wip_warehouse",
+ "default_fg_warehouse",
+ "default_scrap_warehouse",
+ ):
+ warehouse = self.get(fieldname)
+ if not warehouse:
+ continue
+
+ details = frappe.db.get_value("Warehouse", warehouse, ["is_group", "company"], as_dict=True)
+ if not details:
+ continue
+
+ label = _(self.meta.get_label(fieldname))
+
+ if details.is_group:
+ frappe.throw(
+ _(
+ "Group Warehouses cannot be used in transactions. Please change the value of {0}"
+ ).format(bold(label)),
+ title=_("Incorrect Warehouse"),
+ )
+
+ if details.company != self.name:
+ frappe.throw(
+ _("{0} {1} does not belong to company {2}").format(
+ bold(label), bold(warehouse), bold(self.name)
+ ),
+ title=_("Incorrect Warehouse"),
+ )
+
def validate_abbr(self):
if not self.abbr:
self.abbr = "".join(c[0] for c in self.company_name.split()).upper()
@@ -482,6 +521,11 @@ class Company(NestedSet):
if wh_detail["is_group"]:
parent_warehouse = warehouse.name
+ if not self.default_warehouse:
+ stores = frappe.db.get_value("Warehouse", {"warehouse_name": _("Stores"), "company": self.name})
+ if stores:
+ self.db_set("default_warehouse", stores)
+
def create_default_accounts(self):
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index 8c14bb9e47c..86e341c5ae0 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -196,7 +196,7 @@ const COMPANY_DEFAULTS_TO_VF = {
};
const FIELD_DEFAULT_SOURCE = {
- default_warehouse: "Stock Settings",
+ default_warehouse: "Company",
default_inventory_account: "Company",
buying_cost_center: "Company",
selling_cost_center: "Company",
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 0945438a02e..4035083a181 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -104,17 +104,15 @@ def get_company_resolved_defaults(company: str) -> dict:
"""
Returns effective default values for a company by checking:
1. Company document
- 2. Stock Settings (for warehouse fallback)
- 3. Accounts Settings (for deferred account fallbacks)
+ 2. Accounts Settings (for deferred account fallbacks)
"""
if not company:
return {}
company_doc = frappe.get_cached_doc("Company", company)
- default_warehouse = frappe.db.get_single_value("Stock Settings", "default_warehouse")
return {
- "default_warehouse": default_warehouse,
+ "default_warehouse": company_doc.get("default_warehouse"),
"default_inventory_account": company_doc.get("default_inventory_account"),
"buying_cost_center": company_doc.get("cost_center"),
"selling_cost_center": company_doc.get("cost_center"),
diff --git a/erpnext/setup/doctype/item_group/test_item_group.py b/erpnext/setup/doctype/item_group/test_item_group.py
index a37ab55d508..f44567ee021 100644
--- a/erpnext/setup/doctype/item_group/test_item_group.py
+++ b/erpnext/setup/doctype/item_group/test_item_group.py
@@ -1,6 +1,8 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
+from unittest.mock import patch
+
import frappe
from frappe.query_builder.functions import Max
from frappe.utils.nestedset import (
@@ -14,6 +16,8 @@ from frappe.utils.nestedset import (
from erpnext.tests.utils import ERPNextTestSuite
+TRANSLATED_ROOT = "Todos os Grupos de Itens"
+
class TestItemGroup(ERPNextTestSuite):
def setUp(self):
@@ -204,6 +208,54 @@ class TestItemGroup(ERPNextTestSuite):
merge=True,
)
+ def test_preset_records_use_existing_root(self):
+ from erpnext.setup.setup_wizard.operations import install_fixtures
+
+ with patch.object(install_fixtures, "get_root_of", return_value=TRANSLATED_ROOT):
+ records = [
+ r for r in install_fixtures.get_preset_records("India") if r["doctype"] == "Item Group"
+ ]
+
+ root_record, *child_records = records
+ self.assertEqual(root_record["item_group_name"], TRANSLATED_ROOT)
+ self.assertTrue(root_record["__condition"]())
+ self.assertEqual({r["parent_item_group"] for r in child_records}, {TRANSLATED_ROOT})
+
+ with patch.object(install_fixtures, "get_root_of", return_value="All Item Groups"):
+ root_record = next(
+ r for r in install_fixtures.get_preset_records("India") if r["doctype"] == "Item Group"
+ )
+ self.assertFalse(root_record["__condition"]())
+
+ def test_patch_merges_seeded_root_into_existing_root(self):
+ from erpnext.patches.v16_0.merge_seeded_item_group_root import execute
+
+ self._nest_root_under(TRANSLATED_ROOT)
+ self.assertEqual(
+ frappe.db.get_value("Item Group", "All Item Groups", "parent_item_group"), TRANSLATED_ROOT
+ )
+
+ execute()
+
+ self.assertFalse(frappe.db.exists("Item Group", "All Item Groups"))
+ self.assertEqual(
+ frappe.get_all("Item Group", filters={"parent_item_group": ("is", "not set")}, pluck="name"),
+ [TRANSLATED_ROOT],
+ )
+ self.assertEqual(
+ frappe.db.get_value("Item Group", "_Test Item Group B", "parent_item_group"), TRANSLATED_ROOT
+ )
+ self.test_basic_tree()
+
+ def _nest_root_under(self, new_root):
+ """Recreate the tree left behind by seeding a root under a pre-existing one."""
+ frappe.get_doc({"doctype": "Item Group", "item_group_name": new_root, "is_group": 1}).insert()
+
+ ig = frappe.qb.DocType("Item Group")
+ frappe.qb.update(ig).set(ig.parent_item_group, "").where(ig.name == new_root).run()
+ frappe.qb.update(ig).set(ig.parent_item_group, new_root).where(ig.name == "All Item Groups").run()
+ rebuild_tree("Item Group")
+
def _move_it_back(self):
group_b = frappe.get_doc("Item Group", "_Test Item Group B")
group_b.parent_item_group = "All Item Groups"
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 346b1834032..c2842c02c49 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -345,22 +345,28 @@ def update_pegged_currencies():
def set_default_print_formats():
+ # For each doctype, prefer the newer builder-made "Modern with Images" format,
+ # falling back to the older "with Item Image" format if it isn't present.
default_map = {
- "Sales Order": "Sales Order with Item Image",
- "Sales Invoice": "Sales Invoice with Item Image",
- "Delivery Note": "Delivery Note with Item Image",
- "Purchase Order": "Purchase Order with Item Image",
- "Purchase Invoice": "Purchase Invoice with Item Image",
- "POS Invoice": "POS Invoice with Item Image",
- "Quotation": "Quotation with Item Image",
- "Request for Quotation": "Request for Quotation with Item Image",
+ "Sales Order": ["Sales Order Modern with Images", "Sales Order with Item Image"],
+ "Sales Invoice": ["Sales Invoice Modern with Images", "Sales Invoice with Item Image"],
+ "Delivery Note": ["Delivery Note Modern with Images", "Delivery Note with Item Image"],
+ "Purchase Order": ["Purchase Order Modern with Images", "Purchase Order with Item Image"],
+ "Purchase Invoice": ["Purchase Invoice Modern with Images", "Purchase Invoice with Item Image"],
+ "POS Invoice": ["POS Invoice Modern with Images", "POS Invoice with Item Image"],
+ "Quotation": ["Quotation Modern with Images", "Quotation with Item Image"],
+ "Request for Quotation": [
+ "Request for Quotation Modern with Images",
+ "Request for Quotation with Item Image",
+ ],
}
- for doctype, print_format in default_map.items():
+ for doctype, print_formats in default_map.items():
if frappe.get_meta(doctype).default_print_format:
continue
- if not frappe.db.exists("Print Format", print_format):
+ print_format = next((pf for pf in print_formats if frappe.db.exists("Print Format", pf)), None)
+ if not print_format:
continue
frappe.make_property_setter(
diff --git a/erpnext/setup/setup_wizard/operations/defaults_setup.py b/erpnext/setup/setup_wizard/operations/defaults_setup.py
index 82698808250..960e5fe55a9 100644
--- a/erpnext/setup/setup_wizard/operations/defaults_setup.py
+++ b/erpnext/setup/setup_wizard/operations/defaults_setup.py
@@ -30,7 +30,6 @@ def set_default_settings(args):
stock_settings = frappe.get_doc("Stock Settings")
stock_settings.item_naming_by = "Item Code"
stock_settings.valuation_method = "FIFO"
- stock_settings.default_warehouse = frappe.db.get_value("Warehouse", {"warehouse_name": _("Stores")})
stock_settings.stock_uom = "Nos"
stock_settings.auto_indent = 1
stock_settings.auto_insert_price_list_rate_if_missing = 1
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 1d1edeaf949..42821707035 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -12,6 +12,7 @@ from frappe.desk.doctype.global_search_settings.global_search_settings import (
)
from frappe.desk.page.setup_wizard.setup_wizard import make_records
from frappe.utils import cstr, getdate
+from frappe.utils.nestedset import get_root_of
from erpnext.accounts.doctype.account.account import RootNotEditable
from erpnext.regional.address_template.setup import set_up_address_templates
@@ -24,46 +25,48 @@ def read_lines(filename: str) -> list[str]:
def get_preset_records(country=None):
+ root_item_group = get_root_of("Item Group") or _("All Item Groups")
records = [
# ensure at least an empty Address Template exists for this Country
{"doctype": "Address Template", "country": country},
# item group
{
"doctype": "Item Group",
- "item_group_name": _("All Item Groups"),
+ "item_group_name": root_item_group,
"is_group": 1,
"parent_item_group": "",
+ "__condition": lambda: not frappe.db.exists("Item Group", root_item_group),
},
{
"doctype": "Item Group",
"item_group_name": _("Products"),
"is_group": 0,
- "parent_item_group": _("All Item Groups"),
+ "parent_item_group": root_item_group,
"show_in_website": 1,
},
{
"doctype": "Item Group",
"item_group_name": _("Raw Material"),
"is_group": 0,
- "parent_item_group": _("All Item Groups"),
+ "parent_item_group": root_item_group,
},
{
"doctype": "Item Group",
"item_group_name": _("Services"),
"is_group": 0,
- "parent_item_group": _("All Item Groups"),
+ "parent_item_group": root_item_group,
},
{
"doctype": "Item Group",
"item_group_name": _("Sub Assemblies"),
"is_group": 0,
- "parent_item_group": _("All Item Groups"),
+ "parent_item_group": root_item_group,
},
{
"doctype": "Item Group",
"item_group_name": _("Consumable"),
"is_group": 0,
- "parent_item_group": _("All Item Groups"),
+ "parent_item_group": root_item_group,
},
# Stock Entry Type
{
@@ -534,7 +537,6 @@ def update_stock_settings():
stock_settings = frappe.get_doc("Stock Settings")
stock_settings.item_naming_by = "Item Code"
stock_settings.valuation_method = "FIFO"
- stock_settings.default_warehouse = frappe.db.get_value("Warehouse", {"warehouse_name": _("Stores")})
stock_settings.stock_uom = "Nos"
stock_settings.auto_indent = 1
stock_settings.auto_insert_price_list_rate_if_missing = 1
diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py
index 9e097099f01..18affbab66e 100644
--- a/erpnext/stock/deprecated_serial_batch.py
+++ b/erpnext/stock/deprecated_serial_batch.py
@@ -75,6 +75,7 @@ class DeprecatedSerialNoValuation:
| (table.serial_no.like("%\n" + serial_no))
| (table.serial_no.like("%\n" + serial_no + "\n%"))
)
+ & (table.item_code == self.sle.item_code)
& (table.company == self.sle.company)
& (table.warehouse == self.sle.warehouse)
& (table.serial_and_batch_bundle.isnull())
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index f5417439ded..b004975d2fe 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -164,11 +164,8 @@ class Bin(Document):
& (subcontract_order.name == supplied_item.parent)
& (subcontract_order.per_received < 100)
& (supplied_item.reserve_warehouse == self.warehouse)
- & (
- ((subcontract_order.status != "Closed") & (subcontract_order.docstatus == 1))
- if subcontract_doctype == "Purchase Order"
- else (subcontract_order.docstatus == 1)
- )
+ & (subcontract_order.status != "Closed")
+ & (subcontract_order.docstatus == 1)
)
reserved_qty_for_sub_contract = (
@@ -203,6 +200,7 @@ class Bin(Document):
else (
(Coalesce(se.subcontracting_order, "") != "")
& (subcontract_order.name == se.subcontracting_order)
+ & (subcontract_order.status != "Closed")
)
)
)
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index c5db9cdcecb..e8b2969b1a8 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -723,6 +723,76 @@ class TestDeliveryNote(ERPNextTestSuite):
self.assertEqual(gle_warehouse_amount, 1400)
+ def test_return_bundle_voucher_detail_no_as_packed_item(self):
+ """Return bundle whose voucher_detail_no is the Packed Item (SLE-driven path) must still value on repost."""
+ from erpnext.stock.doctype.delivery_note.mapper import make_sales_return
+
+ warehouse = "_Test Warehouse - _TC"
+ packed_item = make_item(
+ properties={
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "batch_number_series": "BATCH-DN-RET-VDN-.#####",
+ }
+ ).name
+ bundle_item = make_item(properties={"is_stock_item": 0, "is_sales_item": 1}).name
+ make_product_bundle(bundle_item, [packed_item], qty=20)
+
+ make_stock_entry(item_code=packed_item, target=warehouse, qty=60, basic_rate=35)
+
+ dn = create_delivery_note(item_code=bundle_item, warehouse=warehouse, qty=3)
+
+ return_dn = make_sales_return(dn.name)
+ return_dn.items[0].qty = -2
+ return_dn.submit()
+ return_dn.reload()
+
+ packed_row = return_dn.packed_items[0]
+ bundle = frappe.get_doc("Serial and Batch Bundle", packed_row.serial_and_batch_bundle)
+
+ # Reproduce the reported state: bundle points at the Packed Item (not the DN Item), valuation at 0.
+ bundle.db_set("voucher_detail_no", packed_row.name)
+ bundle.db_set({"avg_rate": 0, "total_amount": 0})
+ for entry in bundle.entries:
+ entry.db_set({"incoming_rate": 0, "stock_value_difference": 0})
+ packed_row.db_set("incoming_rate", 0)
+ frappe.db.set_value(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Delivery Note",
+ "voucher_no": return_dn.name,
+ "item_code": packed_item,
+ "is_cancelled": 0,
+ },
+ {"incoming_rate": 0, "stock_value_difference": 0},
+ )
+
+ frappe.get_doc(
+ doctype="Repost Item Valuation",
+ based_on="Transaction",
+ voucher_type="Delivery Note",
+ voucher_no=return_dn.name,
+ posting_date=return_dn.posting_date,
+ posting_time=return_dn.posting_time,
+ ).submit()
+
+ bundle.reload()
+ self.assertEqual(flt(bundle.avg_rate), 35)
+
+ incoming_rate, stock_value_difference = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Delivery Note",
+ "voucher_no": return_dn.name,
+ "item_code": packed_item,
+ "is_cancelled": 0,
+ },
+ ["incoming_rate", "stock_value_difference"],
+ )
+ self.assertEqual(flt(incoming_rate), 35)
+ self.assertEqual(flt(stock_value_difference), 1400)
+
def test_bin_details_of_packed_item(self):
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.stock.doctype.item.test_item import make_item
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 4594da25d6c..b3c6d4cb777 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -721,7 +721,7 @@
},
{
"default": "0",
- "description": "Expense for this item will be recognized over a period of months. Eg: prepaid insurance or annual software license",
+ "description": "Income from this item will be recognized over a period of months instead of all at once. Eg: annual subscription paid upfront.",
"fieldname": "enable_deferred_revenue",
"fieldtype": "Check",
"label": "Enable Deferred Revenue"
@@ -734,7 +734,7 @@
},
{
"default": "0",
- "description": "Income from this item will be recognized over a period of months instead of all at once. Eg: annual subscription paid upfront.",
+ "description": "Expense for this item will be recognized over a period of months. Eg: prepaid insurance or annual software license",
"fieldname": "enable_deferred_expense",
"fieldtype": "Check",
"label": "Enable Deferred Expense"
@@ -1095,19 +1095,19 @@
},
{
"default": "0",
+ "description": "If checked, this Item is only available for transactions in the companies listed below.",
"fieldname": "restrict_to_companies",
"fieldtype": "Check",
"label": "Restrict to Companies",
- "description": "If checked, this Item is only available for transactions in the companies listed below.",
"permlevel": 1
},
{
+ "depends_on": "eval:doc.restrict_to_companies",
"fieldname": "allowed_companies",
"fieldtype": "Table MultiSelect",
"label": "Allowed Companies",
- "options": "Company Restriction",
- "depends_on": "eval:doc.restrict_to_companies",
"mandatory_depends_on": "eval:doc.restrict_to_companies",
+ "options": "Company Restriction",
"permlevel": 1
}
],
@@ -1116,7 +1116,7 @@
"image_field": "image",
"links": [],
"make_attachments_public": 1,
- "modified": "2026-07-23 10:00:00.000000",
+ "modified": "2026-07-28 18:58:43.328497",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index d369e7f3e68..95e0967ca71 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -132,8 +132,8 @@ class Item(Document):
purchase_uom: DF.Link | None
quality_inspection_template: DF.Link | None
reorder_levels: DF.Table[ItemReorder]
- retain_sample: DF.Check
restrict_to_companies: DF.Check
+ retain_sample: DF.Check
safety_stock: DF.Float
sales_tax_withholding_category: DF.Link | None
sales_uom: DF.Link | None
@@ -321,13 +321,11 @@ class Item(Document):
for default in self.item_defaults or [
frappe._dict({"company": frappe.defaults.get_defaults().company})
]:
- default_warehouse = default.default_warehouse or frappe.get_single_value(
- "Stock Settings", "default_warehouse"
- )
- if default_warehouse:
- warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company")
+ default_warehouse = default.default_warehouse
+ if not default_warehouse and default.company:
+ default_warehouse = frappe.get_cached_value("Company", default.company, "default_warehouse")
- if not default_warehouse or warehouse_company != default.company:
+ if not default_warehouse:
default_warehouse = frappe.db.get_value(
"Warehouse", {"warehouse_name": _("Stores"), "company": default.company}
)
@@ -389,8 +387,10 @@ class Item(Document):
)
def validate_retain_sample(self):
- if self.retain_sample and not frappe.get_single_value("Stock Settings", "sample_retention_warehouse"):
- frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"))
+ if self.retain_sample and not frappe.db.exists(
+ "Company", {"sample_retention_warehouse": ("is", "set")}
+ ):
+ frappe.throw(_("Please select Sample Retention Warehouse in Company first"))
if self.retain_sample and not self.has_batch_no:
frappe.throw(
_(
@@ -1770,11 +1770,8 @@ def get_default_warehouse_for_opening_stock(item, company: str, warehouse: str |
if default.company == company and default.default_warehouse:
return default.default_warehouse
- settings_warehouse = frappe.get_single_value("Stock Settings", "default_warehouse")
- if settings_warehouse:
- warehouse_company = frappe.db.get_value("Warehouse", settings_warehouse, "company")
- if warehouse_company == company:
- return settings_warehouse
+ if company_warehouse := frappe.get_cached_value("Company", company, "default_warehouse"):
+ return company_warehouse
stores_warehouse = frappe.db.get_value("Warehouse", {"warehouse_name": _("Stores"), "company": company})
@@ -1783,7 +1780,7 @@ def get_default_warehouse_for_opening_stock(item, company: str, warehouse: str |
frappe.throw(
_(
- "No warehouse found for company {0}. Please set a Default Warehouse in Item Defaults or Stock Settings."
+ "No warehouse found for company {0}. Please set a Default Warehouse in Item Defaults or Company."
).format(frappe.bold(company))
)
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 425d5e4692a..4080d65aab4 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -976,10 +976,8 @@ class TestItem(ERPNextTestSuite):
)
self.consume_item_code_with_differet_stock_transactions(item_code=item.name)
- @ERPNextTestSuite.change_settings(
- "Stock Settings", {"sample_retention_warehouse": "_Test Warehouse - _TC"}
- )
def test_retain_sample(self):
+ frappe.db.set_value("Company", "_Test Company", "sample_retention_warehouse", "_Test Warehouse - _TC")
item = make_item("_TestRetainSample", {"has_batch_no": 1, "retain_sample": 1, "sample_quantity": 1})
self.assertEqual(item.has_batch_no, 1)
diff --git a/erpnext/stock/doctype/item_default/item_default.json b/erpnext/stock/doctype/item_default/item_default.json
index d1cc7e93253..d083d4a3dc6 100644
--- a/erpnext/stock/doctype/item_default/item_default.json
+++ b/erpnext/stock/doctype/item_default/item_default.json
@@ -77,6 +77,7 @@
{
"fieldname": "default_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Warehouse",
"options": "Warehouse",
@@ -94,6 +95,7 @@
{
"fieldname": "default_discount_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Discount Account",
"options": "Account"
},
@@ -101,6 +103,7 @@
"description": "Stock account where inventory value for this item will be tracked",
"fieldname": "default_inventory_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Inventory Account",
"options": "Account",
"show_description_on_click": 1
@@ -151,6 +154,7 @@
"description": "Cost center used for tracking purchase expenses for this item",
"fieldname": "buying_cost_center",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Buying Cost Center",
"options": "Cost Center",
"show_description_on_click": 1
@@ -167,6 +171,7 @@
"description": "Account where the cost of this item will be debited on purchase",
"fieldname": "expense_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Expense Account",
"options": "Account",
"show_description_on_click": 1
@@ -175,6 +180,7 @@
"description": "Provisional liability account used for service items before invoice is received",
"fieldname": "default_provisional_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Provisional Account (Service)",
"options": "Account",
"show_description_on_click": 1
@@ -183,6 +189,7 @@
"description": "Account to record additional purchase expenses like freight or customs",
"fieldname": "purchase_expense_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Purchase Expense Account",
"options": "Account",
"show_description_on_click": 1
@@ -191,6 +198,7 @@
"description": "Used to balance the books when recording extra purchase costs",
"fieldname": "purchase_expense_contra_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Purchase Expense Contra Account",
"options": "Account",
"show_description_on_click": 1
@@ -199,6 +207,7 @@
"description": "Account to track value added to stock via Stock Entry, Stock Reconciliation or Landed Cost Voucher",
"fieldname": "expenses_added_to_stock_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Expenses Added To Stock Account",
"options": "Account",
"show_description_on_click": 1
@@ -207,6 +216,7 @@
"description": "Used to balance the books when recording expenses added to stock",
"fieldname": "expenses_added_to_stock_contra_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Expenses Added To Stock Contra Account",
"options": "Account",
"show_description_on_click": 1
@@ -215,6 +225,7 @@
"description": "For Standard Cost items: the purchase price vs standard rate difference is booked here. Falls back to the Company's Default Purchase Price Variance Account.",
"fieldname": "purchase_price_variance_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Purchase Price Variance Account",
"options": "Account",
"show_description_on_click": 1
@@ -223,6 +234,7 @@
"description": "For Standard Cost items: the Manufacture/Repack consumed cost vs standard rate difference is booked here. Falls back to the Company's Default Manufacturing Variance Account.",
"fieldname": "manufacturing_variance_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Manufacturing Variance Account",
"options": "Account",
"show_description_on_click": 1
@@ -288,6 +300,7 @@
"description": "Cost center used for tracking sales revenue for this item",
"fieldname": "selling_cost_center",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Selling Cost Center",
"options": "Cost Center",
"show_description_on_click": 1
@@ -296,6 +309,7 @@
"description": "Account where revenue from selling this item will be credited",
"fieldname": "income_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Income Account",
"options": "Account",
"show_description_on_click": 1
@@ -325,6 +339,7 @@
"description": "Account where cost of goods sold will be posted when this item is sold",
"fieldname": "default_cogs_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "COGS Account",
"options": "Account",
"show_description_on_click": 1
@@ -348,6 +363,7 @@
"depends_on": "eval: parent.enable_deferred_expense",
"fieldname": "deferred_expense_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Expense Account",
"options": "Account",
"show_description_on_click": 1
@@ -356,6 +372,7 @@
"depends_on": "eval: parent.enable_deferred_revenue",
"fieldname": "deferred_revenue_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Revenue Account",
"options": "Account",
"show_description_on_click": 1
@@ -406,7 +423,7 @@
],
"istable": 1,
"links": [],
- "modified": "2026-07-15 10:00:00.000000",
+ "modified": "2026-07-28 15:39:44.848087",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Default",
diff --git a/erpnext/stock/doctype/item_default/item_default.py b/erpnext/stock/doctype/item_default/item_default.py
index d8e751c53a4..c4aa0bab19f 100644
--- a/erpnext/stock/doctype/item_default/item_default.py
+++ b/erpnext/stock/doctype/item_default/item_default.py
@@ -36,6 +36,7 @@ class ItemDefault(Document):
parenttype: DF.Data
purchase_expense_account: DF.Link | None
purchase_expense_contra_account: DF.Link | None
+ purchase_price_variance_account: DF.Link | None
selling_cost_center: DF.Link | None
# end: auto-generated types
diff --git a/erpnext/stock/doctype/item_reorder/item_reorder.json b/erpnext/stock/doctype/item_reorder/item_reorder.json
index a0b365cf601..847df354562 100644
--- a/erpnext/stock/doctype/item_reorder/item_reorder.json
+++ b/erpnext/stock/doctype/item_reorder/item_reorder.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_bulk_edit": 1,
"autoname": "hash",
"creation": "2013-03-07 11:42:59",
"doctype": "DocType",
@@ -18,6 +19,7 @@
"columns": 3,
"fieldname": "warehouse_group",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Check Availability in Warehouse",
"options": "Warehouse"
@@ -26,6 +28,7 @@
"columns": 2,
"fieldname": "warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Request for",
"options": "Warehouse",
@@ -59,7 +62,7 @@
"in_create": 1,
"istable": 1,
"links": [],
- "modified": "2025-12-02 16:02:23.254963",
+ "modified": "2026-07-28 15:54:36.089238",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Reorder",
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index 2a455a1437c..1c6d7db5296 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -68,7 +68,6 @@
},
{
"allow_on_submit": 1,
- "default": "{material_request_type}",
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
@@ -377,7 +376,7 @@
"idx": 70,
"is_submittable": 1,
"links": [],
- "modified": "2026-03-09 17:15:30.124509",
+ "modified": "2026-07-30 11:04:31.517204",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 4a6f8d960d2..2fc7a6ca12f 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -461,6 +461,7 @@ var validate_sample_quantity = function (frm, cdt, cdn) {
item_code: d.item_code,
sample_quantity: d.sample_quantity,
qty: d.qty,
+ company: frm.doc.company,
},
callback: (r) => {
frappe.model.set_value(cdt, cdn, "sample_quantity", r.message);
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index f1d4fb9cea6..6137305bfd3 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -423,31 +423,10 @@ class PurchaseReceipt(BuyingController):
row.received_qty,
)
- def check_next_docstatus(self):
- submit_rv = frappe.get_all(
- "Purchase Invoice Item",
- filters={"purchase_receipt": self.name, "docstatus": 1},
- fields=["parent"],
- as_list=True,
- limit=1,
- )
- if submit_rv:
- frappe.throw(_("Purchase Invoice {0} is already submitted").format(submit_rv[0][0]))
-
def on_cancel(self):
super().on_cancel()
self.check_for_on_hold_or_closed_status("Purchase Order", "purchase_order")
- # Check if Purchase Invoice has been submitted against current Purchase Order
- submitted = frappe.get_all(
- "Purchase Invoice Item",
- filters={"purchase_receipt": self.name, "docstatus": 1},
- fields=["parent"],
- as_list=True,
- limit=1,
- )
- if submitted:
- frappe.throw(_("Purchase Invoice {0} is already submitted").format(submitted[0][0]))
self.update_prevdoc_status()
self.update_billing_status()
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 20268d1b4d4..d731b6fd675 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2395,9 +2395,6 @@ class TestPurchaseReceipt(ERPNextTestSuite):
from erpnext.stock.doctype.delivery_note.mapper import make_inter_company_purchase_receipt
pr = make_inter_company_purchase_receipt(dn.name)
- pr.inter_company_reference = ""
- self.assertRaises(frappe.ValidationError, pr.save)
-
pr.inter_company_reference = dn.name
pr.items[0].qty = 10
pr.items[0].from_warehouse = target_warehouse
@@ -6134,17 +6131,31 @@ class TestPurchaseReceipt(ERPNextTestSuite):
# already received against this PO line, excluding pr2 itself, is pr1's 4
self.assertEqual(pr2.get_already_received_qty(po.name, po_detail), 4.0)
- def test_check_next_docstatus_blocks_with_submitted_invoice(self):
- """check_next_docstatus must flag a submitted Purchase Invoice drawn from the receipt —
- covers the converted child-table get_all (Purchase Invoice Item, docstatus=1)."""
+ def test_cancel_blocked_by_submitted_invoice_rolls_back(self):
+ """A submitted Purchase Invoice must block cancelling its Purchase Receipt. Frappe's backlink
+ check rejects the cancel only after on_cancel has run stock, GL, and status work, so the whole
+ transaction has to roll back: the receipt stays submitted with no leaked ledger entries."""
pr = make_purchase_receipt()
pi = make_purchase_invoice(pr.name)
pi.insert()
pi.submit()
- with self.assertRaises(frappe.ValidationError) as cm:
- pr.check_next_docstatus()
- self.assertIn("is already submitted", str(cm.exception))
+ pr.reload()
+ status_before = pr.status
+ sle_before = frappe.db.count("Stock Ledger Entry", {"voucher_no": pr.name})
+ gle_before = frappe.db.count("GL Entry", {"voucher_no": pr.name})
+
+ frappe.db.savepoint("before_blocked_cancel")
+ with self.assertRaises(frappe.LinkExistsError) as cm:
+ pr.cancel()
+ self.assertIn(pi.name, str(cm.exception))
+ frappe.db.rollback(save_point="before_blocked_cancel") # mimic the request-level rollback
+
+ pr.reload()
+ self.assertEqual(pr.docstatus, 1)
+ self.assertEqual(pr.status, status_before)
+ self.assertEqual(frappe.db.count("Stock Ledger Entry", {"voucher_no": pr.name}), sle_before)
+ self.assertEqual(frappe.db.count("GL Entry", {"voucher_no": pr.name}), gle_before)
def create_asset_category_for_pr_test():
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 1269dcb46dd..6a2e835f670 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
@@ -517,6 +517,11 @@ class SerialandBatchBundle(Document):
self.child_table, self.voucher_detail_no, field
)
+ if not return_against_voucher_detail_no and self.voucher_type in ("Delivery Note", "Sales Invoice"):
+ # Bundles built via the use_serial_batch_fields / SLE-driven path keep the Packed Item
+ # as voucher_detail_no (not remapped to the DN/SI Item), so the lookup above misses.
+ return_against_voucher_detail_no = self.get_return_against_packed_item(field)
+
filters = [
["Serial and Batch Bundle", "voucher_no", "=", return_against],
["Serial and Batch Entry", "docstatus", "=", 1],
@@ -560,6 +565,16 @@ class SerialandBatchBundle(Document):
return valuation_details
+ def get_return_against_packed_item(self, field):
+ """Resolve the original DN/SI Item when a return bundle's voucher_detail_no is the Packed Item."""
+ parent_detail_docname = frappe.db.get_value(
+ "Packed Item", self.voucher_detail_no, "parent_detail_docname"
+ )
+ if not parent_detail_docname:
+ return
+
+ return frappe.db.get_value(self.child_table, parent_detail_docname, field)
+
def get_legacy_valuation_rate_for_return_entry(
self, return_against, return_against_voucher_detail_no, return_warehouse=None
):
diff --git a/erpnext/stock/doctype/stock_entry/services/manufacturing.py b/erpnext/stock/doctype/stock_entry/services/manufacturing.py
index b8bb4d16a79..26655f41355 100644
--- a/erpnext/stock/doctype/stock_entry/services/manufacturing.py
+++ b/erpnext/stock/doctype/stock_entry/services/manufacturing.py
@@ -1178,7 +1178,7 @@ def ceil_qty_if_uom_has_whole_number(qty, stock_uom):
def move_sample_to_retention_warehouse(company: str, items: str | list):
items = frappe.parse_json(items)
- retention_warehouse = frappe.get_single_value("Stock Settings", "sample_retention_warehouse")
+ retention_warehouse = get_sample_retention_warehouse(company)
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.company = company
stock_entry.purpose = "Material Transfer"
@@ -1195,7 +1195,7 @@ def move_sample_to_retention_warehouse(company: str, items: str | list):
def _process_sample_item(stock_entry, item, retention_warehouse):
warehouse = item.get("t_warehouse") or item.get("warehouse")
sabb = _duplicate_sample_bundle(item, warehouse)
- total_qty, sabe_list = _collect_sample_batches(sabb, item, warehouse)
+ total_qty, sabe_list = _collect_sample_batches(sabb, item, warehouse, stock_entry.company)
if total_qty:
_append_sample_entry(stock_entry, sabb, item, warehouse, retention_warehouse, total_qty, sabe_list)
@@ -1212,21 +1212,22 @@ def _duplicate_sample_bundle(item, warehouse):
).duplicate_package()
-def _collect_sample_batches(sabb, item, warehouse):
+def _collect_sample_batches(sabb, item, warehouse, company):
batches = get_batch_nos(item.get("serial_and_batch_bundle"))
sabe_list, total_qty = [], 0
for batch_no in batches.keys():
- qty, entries = _process_sample_batch(sabb, item, warehouse, batch_no)
+ qty, entries = _process_sample_batch(sabb, item, warehouse, batch_no, company)
total_qty += qty
sabe_list.extend(entries)
return total_qty, sabe_list
-def _process_sample_batch(sabb, item, warehouse, batch_no):
+def _process_sample_batch(sabb, item, warehouse, batch_no, company):
sample_quantity = validate_sample_quantity(
item.get("item_code"),
item.get("sample_quantity"),
item.get("transfer_qty") or item.get("qty"),
+ company,
batch_no,
)
sabe = next(entry for entry in sabb.entries if entry.batch_no == batch_no)
@@ -1270,18 +1271,36 @@ def _append_sample_entry(stock_entry, sabb, item, warehouse, retention_warehouse
@frappe.whitelist()
-def validate_sample_quantity(item_code: str, sample_quantity: int, qty: float, batch_no: str | None = None):
+def validate_sample_quantity(
+ item_code: str, sample_quantity: int, qty: float, company: str, batch_no: str | None = None
+):
from erpnext.stock.doctype.batch.batch import get_batch_qty
if cint(qty) < cint(sample_quantity):
frappe.throw(
_("Sample quantity {0} cannot be more than received quantity {1}").format(sample_quantity, qty)
)
- return _adjust_sample_quantity(item_code, sample_quantity, batch_no, get_batch_qty)
+
+ retention_warehouse = get_sample_retention_warehouse(company)
+ return _adjust_sample_quantity(item_code, sample_quantity, batch_no, get_batch_qty, retention_warehouse)
-def _adjust_sample_quantity(item_code, sample_quantity, batch_no, get_batch_qty):
- retention_warehouse = frappe.get_single_value("Stock Settings", "sample_retention_warehouse")
+def get_sample_retention_warehouse(company: str) -> str:
+ # `company` arrives from whitelisted callers, so it decides which company's stock gets read.
+ frappe.has_permission("Company", "read", company, throw=True)
+
+ warehouse = frappe.get_cached_value("Company", company, "sample_retention_warehouse")
+ if not warehouse:
+ frappe.throw(
+ _("Please set {0} in Company {1} to retain samples.").format(
+ bold(_("Sample Retention Warehouse")), bold(company)
+ ),
+ title=_("Sample Retention Warehouse Missing"),
+ )
+ return warehouse
+
+
+def _adjust_sample_quantity(item_code, sample_quantity, batch_no, get_batch_qty, retention_warehouse):
retainted_qty = get_batch_qty(batch_no, retention_warehouse, item_code) if batch_no else 0
max_retain_qty = frappe.get_value("Item", item_code, "sample_quantity")
if retainted_qty >= max_retain_qty:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 997bb1f0065..da6228b0d9b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -65,30 +65,25 @@ frappe.ui.form.on("Stock Entry", {
};
});
- frappe.db.get_value(
- "Stock Settings",
- { name: "Stock Settings" },
- "sample_retention_warehouse",
- (r) => {
- if (r.sample_retention_warehouse) {
- let filters = [
- ["Warehouse", "company", "=", frm.doc.company],
- ["Warehouse", "is_group", "=", 0],
- ["Warehouse", "name", "!=", r.sample_retention_warehouse],
- ];
- frm.set_query("from_warehouse", function () {
- return {
- filters: filters,
- };
- });
- frm.set_query("s_warehouse", "items", function () {
- return {
- filters: filters,
- };
- });
- }
+ frappe.db.get_value("Company", frm.doc.company, "sample_retention_warehouse", (r) => {
+ if (r.sample_retention_warehouse) {
+ let filters = [
+ ["Warehouse", "company", "=", frm.doc.company],
+ ["Warehouse", "is_group", "=", 0],
+ ["Warehouse", "name", "!=", r.sample_retention_warehouse],
+ ];
+ frm.set_query("from_warehouse", function () {
+ return {
+ filters: filters,
+ };
+ });
+ frm.set_query("s_warehouse", "items", function () {
+ return {
+ filters: filters,
+ };
+ });
}
- );
+ });
frm.set_query("batch_no", "items", function (doc, cdt, cdn) {
let item = locals[cdt][cdn];
@@ -1173,6 +1168,7 @@ var validate_sample_quantity = function (frm, cdt, cdn) {
item_code: d.item_code,
sample_quantity: d.sample_quantity,
qty: d.transfer_qty,
+ company: frm.doc.company,
},
callback: (r) => {
frappe.model.set_value(cdt, cdn, "sample_quantity", r.message);
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 987a8205a79..b63df53bd32 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -557,6 +557,7 @@ class StockEntry(StockController, SubcontractingInwardController):
"""Set rate for outgoing, secondary and finished items."""
outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate, raise_error_if_no_rate)
raise_error_if_no_rate = raise_error_if_no_rate and not self.is_new()
+ has_consumption_basis = self.has_consumption_basis()
bom_cost_allocation_per = (
frappe.get_cached_value("BOM", self.bom_no, "cost_allocation_per") if self.bom_no else None
@@ -574,12 +575,42 @@ class StockEntry(StockController, SubcontractingInwardController):
continue
self._set_incoming_item_rate(
- d, outgoing_items_cost, raise_error_if_no_rate, zero_valuation_items, bom_cost_allocation_per
+ d,
+ outgoing_items_cost,
+ raise_error_if_no_rate,
+ zero_valuation_items,
+ bom_cost_allocation_per,
+ has_consumption_basis,
)
if zero_valuation_items:
self._notify_zero_valuation_rate(zero_valuation_items)
+ def has_consumption_basis(self) -> bool:
+ """Whether the cost of the consumed items is known, even when that cost is zero."""
+ if any(d.s_warehouse for d in self.get("items")):
+ return True
+
+ settings = frappe.get_single("Manufacturing Settings")
+ if settings.material_consumption and settings.get_rm_cost_from_consumption_entry and self.work_order:
+ return bool(self.get_consumption_entries())
+
+ return False
+
+ def get_consumption_entries(self) -> list[str]:
+ # Cached: queried in both has_consumption_basis() and _get_rm_cost_for_manufacture()
+ if getattr(self, "_consumption_entries", None) is None:
+ self._consumption_entries = frappe.get_all(
+ "Stock Entry",
+ filters={
+ "docstatus": 1,
+ "work_order": self.work_order,
+ "purpose": "Material Consumption for Manufacture",
+ },
+ pluck="name",
+ )
+ return self._consumption_entries
+
def _set_incoming_item_rate(
self,
d,
@@ -587,15 +618,23 @@ class StockEntry(StockController, SubcontractingInwardController):
raise_error_if_no_rate,
zero_valuation_items,
bom_cost_allocation_per=None,
+ has_consumption_basis=False,
):
+ rate_derived_from_consumption = False
+
if d.allow_zero_valuation_rate and d.basic_rate and self.purpose != "Receive from Customer":
d.basic_rate = 0.0
zero_valuation_items.append(d.item_code)
elif d.is_finished_item:
if self.purpose == "Manufacture":
- d.basic_rate = self.get_basic_rate_for_manufactured_item(d.transfer_qty, outgoing_items_cost)
+ d.basic_rate = self.get_basic_rate_for_manufactured_item(
+ d.transfer_qty, outgoing_items_cost, has_consumption_basis
+ )
+ rate_derived_from_consumption = has_consumption_basis
elif self.purpose == "Repack":
d.basic_rate = self.get_basic_rate_for_repacked_items(d.transfer_qty, outgoing_items_cost)
+ # Repack rate comes from consumed source-warehouse rows, not consumption entries
+ rate_derived_from_consumption = any(item.s_warehouse for item in self.get("items"))
if self.bom_no:
d.basic_rate *= bom_cost_allocation_per / 100
@@ -608,7 +647,9 @@ class StockEntry(StockController, SubcontractingInwardController):
if cost_allocation_per and flt(d.transfer_qty):
d.basic_rate = (outgoing_items_cost * (cost_allocation_per / 100)) / d.transfer_qty
- if not d.basic_rate and not d.allow_zero_valuation_rate:
+ # A rate of zero derived from the consumed items is their actual cost, not a missing
+ # rate. Falling back to the item's valuation here would value free inputs as output.
+ if not d.basic_rate and not d.allow_zero_valuation_rate and not rate_derived_from_consumption:
d.basic_rate = get_valuation_rate(
d.item_code,
d.t_warehouse,
@@ -691,31 +732,30 @@ class StockEntry(StockController, SubcontractingInwardController):
)
return flt(outgoing_items_cost / total_fg_qty)
- def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0) -> float:
+ def get_basic_rate_for_manufactured_item(
+ self, finished_item_qty, outgoing_items_cost=0, has_consumption_basis=False
+ ) -> float:
settings = frappe.get_single("Manufacturing Settings")
scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_legacy_scrap_item])
if settings.material_consumption:
outgoing_items_cost = self._get_rm_cost_for_manufacture(
- settings, finished_item_qty, outgoing_items_cost
+ settings, finished_item_qty, outgoing_items_cost, has_consumption_basis
)
return flt((outgoing_items_cost - scrap_items_cost) / finished_item_qty)
- def _get_rm_cost_for_manufacture(self, settings, finished_item_qty, outgoing_items_cost):
+ def _get_rm_cost_for_manufacture(
+ self, settings, finished_item_qty, outgoing_items_cost, has_consumption_basis=False
+ ):
if settings.get_rm_cost_from_consumption_entry and self.work_order:
- if frappe.db.exists(
- "Stock Entry",
- {
- "docstatus": 1,
- "work_order": self.work_order,
- "purpose": "Material Consumption for Manufacture",
- },
- ):
+ if self.get_consumption_entries():
self._validate_no_raw_materials_in_manufacture_entry(settings)
self._validate_single_manufacture_entry()
return self._fetch_consumption_entry_cost()
- elif not outgoing_items_cost:
+ # Estimate from the BOM only when nothing was consumed. A consumed cost of zero is a
+ # real cost, so substituting BOM rates would value free inputs as output.
+ elif not outgoing_items_cost and not has_consumption_basis:
bom_items = self.get_bom_raw_materials(finished_item_qty)
outgoing_items_cost = sum([flt(row.qty) * flt(row.rate) for row in bom_items.values()])
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 421e4c2ecf8..74a11dd41ae 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -2640,6 +2640,149 @@ class TestStockEntry(ERPNextTestSuite):
se.save()
se.submit()
+ def test_manufacture_with_zero_valued_raw_material(self):
+ # A finished good produced from free inputs is worth nothing. Falling back to the item's
+ # own valuation would create value out of nothing and inflate it on every production run.
+ fg_item = make_item(properties={"is_stock_item": 1}).name
+ rm_item = make_item(properties={"is_stock_item": 1}).name
+ warehouse = "_Test Warehouse - _TC"
+ fg_warehouse = "Finished Goods - _TC"
+
+ rm_receipt = make_stock_entry(item_code=rm_item, target=warehouse, qty=100, rate=0, do_not_save=True)
+ rm_receipt.items[0].allow_zero_valuation_rate = 1
+ rm_receipt.save()
+ rm_receipt.submit()
+
+ # the finished good already carries a valuation in the target warehouse
+ make_stock_entry(item_code=fg_item, target=fg_warehouse, qty=10, rate=100)
+
+ se = frappe.new_doc("Stock Entry")
+ se.purpose = se.stock_entry_type = "Manufacture"
+ se.company = "_Test Company"
+ se.append(
+ "items",
+ {"item_code": rm_item, "s_warehouse": warehouse, "qty": 10, "conversion_factor": 1},
+ )
+ se.append(
+ "items",
+ {
+ "item_code": fg_item,
+ "t_warehouse": fg_warehouse,
+ "qty": 10,
+ "is_finished_item": 1,
+ "conversion_factor": 1,
+ },
+ )
+ se.save()
+
+ self.assertEqual(se.items[0].basic_amount, 0)
+ self.assertEqual(se.items[1].basic_rate, 0)
+ self.assertEqual(se.items[1].basic_amount, 0)
+
+ se.submit()
+
+ fg_sle = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"voucher_no": se.name, "item_code": fg_item, "is_cancelled": 0},
+ ["incoming_rate", "stock_value_difference"],
+ as_dict=True,
+ )
+
+ self.assertEqual(fg_sle.incoming_rate, 0)
+ self.assertEqual(fg_sle.stock_value_difference, 0)
+
+ def _make_wo_for_free_raw_material(self, rm_item, fg_item, bom_no):
+ from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
+ from erpnext.manufacturing.doctype.work_order.work_order import (
+ make_stock_entry as make_stock_entry_from_wo,
+ )
+
+ receipt = make_stock_entry(item_code=rm_item, target="Stores - _TC", qty=10, rate=0, do_not_save=True)
+ receipt.items[0].allow_zero_valuation_rate = 1
+ receipt.save()
+ receipt.submit()
+
+ wo = make_wo_order_test_record(production_item=fg_item, bom_no=bom_no, qty=10)
+
+ transfer = frappe.get_doc(make_stock_entry_from_wo(wo.name, "Material Transfer for Manufacture", 10))
+ transfer.items[0].s_warehouse = "Stores - _TC"
+ transfer.insert().submit()
+
+ return wo
+
+ @ERPNextTestSuite.change_settings(
+ "Manufacturing Settings", {"material_consumption": 1, "get_rm_cost_from_consumption_entry": 0}
+ )
+ def test_manufacture_does_not_fall_back_to_bom_cost_for_free_raw_material(self):
+ # The BOM is only an estimate for when nothing was consumed. Items that were consumed and
+ # cost nothing are a real cost, so a BOM rate must not stand in for them.
+ from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+ from erpnext.manufacturing.doctype.work_order.work_order import (
+ make_stock_entry as make_stock_entry_from_wo,
+ )
+
+ rm_item = make_item(properties={"is_stock_item": 1}).name
+ fg_item = make_item(properties={"is_stock_item": 1}).name
+
+ frappe.get_doc(
+ {
+ "doctype": "Item Price",
+ "item_code": rm_item,
+ "price_list": "_Test Price List India",
+ "price_list_rate": 150,
+ "buying": 1,
+ }
+ ).insert()
+
+ # price the BOM off the price list so that it carries a rate the free stock does not
+ bom = make_bom(item=fg_item, raw_materials=[rm_item], do_not_save=True)
+ bom.rm_cost_as_per = "Price List"
+ bom.buying_price_list = "_Test Price List India"
+ bom.currency = "INR"
+ bom.save()
+ bom.submit()
+
+ wo = self._make_wo_for_free_raw_material(rm_item, fg_item, bom.name)
+
+ manufacture = frappe.get_doc(make_stock_entry_from_wo(wo.name, "Manufacture", 10))
+ manufacture.save()
+
+ fg_row = next(d for d in manufacture.items if d.is_finished_item)
+ self.assertEqual(fg_row.basic_rate, 0)
+ self.assertEqual(fg_row.basic_amount, 0)
+
+ @ERPNextTestSuite.change_settings(
+ "Manufacturing Settings", {"material_consumption": 1, "get_rm_cost_from_consumption_entry": 1}
+ )
+ def test_manufacture_with_zero_valued_consumption_entry(self):
+ # The raw material is consumed by a separate entry, so the Manufacture entry carries no
+ # consumed rows of its own. Its cost is still known, and it is zero.
+ from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+ from erpnext.manufacturing.doctype.work_order.work_order import (
+ make_stock_entry as make_stock_entry_from_wo,
+ )
+
+ rm_item = make_item(properties={"is_stock_item": 1}).name
+ fg_item = make_item(properties={"is_stock_item": 1}).name
+
+ # the finished good already carries a valuation in the work order's target warehouse
+ make_stock_entry(item_code=fg_item, target="_Test Warehouse 1 - _TC", qty=10, rate=100)
+
+ bom = make_bom(item=fg_item, raw_materials=[rm_item]).name
+ wo = self._make_wo_for_free_raw_material(rm_item, fg_item, bom)
+
+ consumption = frappe.get_doc(
+ make_stock_entry_from_wo(wo.name, "Material Consumption for Manufacture", 10)
+ )
+ consumption.insert().submit()
+
+ manufacture = frappe.get_doc(make_stock_entry_from_wo(wo.name, "Manufacture", 10))
+ manufacture.save()
+
+ fg_row = next(d for d in manufacture.items if d.is_finished_item)
+ self.assertEqual(fg_row.basic_rate, 0)
+ self.assertEqual(fg_row.basic_amount, 0)
+
def test_disassemble_entry_without_wo(self):
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
@@ -2725,14 +2868,14 @@ class TestStockEntry(ERPNextTestSuite):
self.assertRaises(frappe.ValidationError, se.save)
- @ERPNextTestSuite.change_settings(
- "Stock Settings", {"sample_retention_warehouse": "_Test Warehouse 1 - _TC"}
- )
def test_sample_retention_stock_entry(self):
from erpnext.stock.doctype.stock_entry.services.manufacturing import (
move_sample_to_retention_warehouse,
)
+ frappe.db.set_value(
+ "Company", "_Test Company", "sample_retention_warehouse", "_Test Warehouse 1 - _TC"
+ )
warehouse = "_Test Warehouse - _TC"
retain_sample_item = make_item(
"Retain Sample Item",
@@ -3079,19 +3222,77 @@ class TestStockEntryCoverage(ERPNextTestSuite):
# ── validate_sample_quantity ───────────────────────────────────────────────
- @ERPNextTestSuite.change_settings(
- "Stock Settings", {"sample_retention_warehouse": "_Test Warehouse 1 - _TC"}
- )
def test_validate_sample_quantity_raises_when_sample_exceeds_received_qty(self):
from erpnext.stock.doctype.stock_entry.services.manufacturing import (
validate_sample_quantity,
)
+ frappe.db.set_value(
+ "Company", "_Test Company", "sample_retention_warehouse", "_Test Warehouse 1 - _TC"
+ )
item = make_item(
"_Sample Qty Excess Item",
{"is_stock_item": 1, "retain_sample": 1, "sample_quantity": 2},
)
- self.assertRaises(frappe.ValidationError, validate_sample_quantity, item.name, 10, 5)
+ self.assertRaises(frappe.ValidationError, validate_sample_quantity, item.name, 10, 5, "_Test Company")
+
+ def test_validate_sample_quantity_raises_when_company_has_no_retention_warehouse(self):
+ """Item.retain_sample only needs *some* company configured, so the transaction company may not be."""
+ from erpnext.stock.doctype.stock_entry.services.manufacturing import (
+ validate_sample_quantity,
+ )
+
+ frappe.db.set_value(
+ "Company", "_Test Company", "sample_retention_warehouse", "_Test Warehouse 1 - _TC"
+ )
+ frappe.db.set_value("Company", "_Test Company 1", "sample_retention_warehouse", None)
+ item = make_item(
+ "_Sample Qty No Retention Item",
+ {"is_stock_item": 1, "retain_sample": 1, "sample_quantity": 2, "has_batch_no": 1},
+ )
+ self.assertRaises(
+ frappe.ValidationError,
+ validate_sample_quantity,
+ item.name,
+ 1,
+ 5,
+ "_Test Company 1",
+ "_Sample Batch",
+ )
+
+ def test_sample_retention_warehouse_denied_for_other_company(self):
+ """`company` comes from whitelisted callers, so it must not read another company's stock."""
+ from erpnext.stock.doctype.stock_entry.services.manufacturing import (
+ get_sample_retention_warehouse,
+ )
+
+ frappe.db.set_value(
+ "Company", "_Test Company", "sample_retention_warehouse", "_Test Warehouse 1 - _TC"
+ )
+
+ user = "test_sample_retention_perm@example.com"
+ if not frappe.db.exists("User", user):
+ frappe.get_doc(
+ {
+ "doctype": "User",
+ "email": user,
+ "first_name": "Sample Retention",
+ "send_welcome_email": 0,
+ "roles": [{"role": "Stock User"}],
+ }
+ ).insert(ignore_permissions=True)
+
+ frappe.get_doc(
+ {
+ "doctype": "User Permission",
+ "user": user,
+ "allow": "Company",
+ "for_value": "_Test Company 1",
+ }
+ ).insert(ignore_permissions=True)
+
+ with self.set_user(user):
+ self.assertRaises(frappe.PermissionError, get_sample_retention_warehouse, "_Test Company")
# ── get_expired_batches ────────────────────────────────────────────────────
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 6bc49503012..b51e52c98bb 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -1547,7 +1547,7 @@ def get_stock_balance_for(
)
serial_nos = "\n".join(d.serial_no for d in serial_no_details if d.batch_no == batch_no)
- if row.use_serial_batch_fields and row.batch_no and (qty or row.current_qty):
+ if row and row.use_serial_batch_fields and row.batch_no and (qty or row.current_qty):
rate = get_incoming_rate(
frappe._dict(
{
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js
index 4e1052475d6..1a08cb7bf8d 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.js
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.js
@@ -3,17 +3,6 @@
frappe.ui.form.on("Stock Settings", {
refresh: function (frm) {
- let filters = function () {
- return {
- filters: {
- is_group: 0,
- },
- };
- };
-
- frm.set_query("default_warehouse", filters);
- frm.set_query("sample_retention_warehouse", filters);
-
if (!frm.naming_controller) frm.naming_controller = new frappe.ui.NamingSeriesController(frm);
const item_display = frm.doc.item_naming_by === "Naming Series";
const serial_and_batch_naming_display =
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index bdd06893828..5c2111b8f7c 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -23,9 +23,6 @@
"allow_to_edit_stock_uom_qty_for_purchase",
"allow_to_edit_stock_uom_qty_for_stock_entry",
"allow_uom_with_conversion_rate_defined_in_item",
- "warehouse_defaults_section",
- "default_warehouse",
- "sample_retention_warehouse",
"stock_validations_tab",
"negative_stock_section",
"allow_negative_stock",
@@ -113,19 +110,6 @@
"label": "Default Stock UOM",
"options": "UOM"
},
- {
- "fieldname": "default_warehouse",
- "fieldtype": "Link",
- "label": "Default Warehouse",
- "options": "Warehouse"
- },
- {
- "documentation_url": "https://docs.frappe.io/erpnext/retain-sample-stock",
- "fieldname": "sample_retention_warehouse",
- "fieldtype": "Link",
- "label": "Sample Retention Warehouse",
- "options": "Warehouse"
- },
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
@@ -527,11 +511,6 @@
"fieldtype": "Check",
"label": "Activate Serial / Batch No for Item"
},
- {
- "fieldname": "warehouse_defaults_section",
- "fieldtype": "Section Break",
- "label": "Warehouse Defaults"
- },
{
"fieldname": "internal_transfer_rules_section",
"fieldtype": "Section Break",
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 557c3a1d901..f3890cc9dfe 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -41,7 +41,6 @@ class StockSettings(Document):
auto_reserve_stock: DF.Check
auto_reserve_stock_for_sales_order_on_purchase: DF.Check
clean_description_html: DF.Check
- default_warehouse: DF.Link | None
disable_serial_no_and_batch_selector: DF.Check
do_not_update_serial_batch_on_creation_of_auto_bundle: DF.Check
do_not_use_batchwise_valuation: DF.Check
@@ -57,7 +56,6 @@ class StockSettings(Document):
reorder_email_notify: DF.Check
role_allowed_to_create_edit_back_dated_transactions: DF.Link | None
role_allowed_to_over_deliver_receive: DF.Link | None
- sample_retention_warehouse: DF.Link | None
set_serial_and_batch_bundle_naming_based_on_naming_series: DF.Check
show_barcode_field: DF.Check
stock_auth_role: DF.Link | None
@@ -79,7 +77,6 @@ class StockSettings(Document):
"item_group",
"stock_uom",
"allow_negative_stock",
- "default_warehouse",
"set_qty_in_transactions_based_on_serial_no_input",
"use_serial_batch_fields",
"enable_serial_and_batch_no_for_item",
@@ -104,7 +101,6 @@ class StockSettings(Document):
validate_fields_for_doctype=False,
)
- self.validate_warehouses()
self.validate_serial_and_batch_no_settings()
self.cant_change_valuation_method()
self.validate_clean_description_html()
@@ -150,17 +146,6 @@ class StockSettings(Document):
)
)
- def validate_warehouses(self):
- warehouse_fields = ["default_warehouse", "sample_retention_warehouse"]
- for field in warehouse_fields:
- if frappe.db.get_value("Warehouse", self.get(field), "is_group"):
- frappe.throw(
- _(
- "Group Warehouses cannot be used in transactions. Please change the value of {0}"
- ).format(frappe.bold(self.meta.get_field(field).label)),
- title=_("Incorrect Warehouse"),
- )
-
def cant_change_valuation_method(self):
doc_before_save = self.get_doc_before_save()
if not doc_before_save:
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index 8a289c29591..cac2319196a 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -15,7 +15,7 @@ from frappe.utils.caching import request_cache
from frappe.utils.nestedset import NestedSet
from pypika.terms import ExistsCriterion
-from erpnext.stock import get_warehouse_account
+from erpnext.stock import get_warehouse_account, get_warehouse_account_map
class Warehouse(NestedSet):
@@ -220,11 +220,19 @@ def get_child_warehouses(warehouse):
def get_warehouses_based_on_account(account, company=None):
warehouses = []
+ warehouse_account_map = None
for d in frappe.get_all(
"Warehouse", fields=["name", "is_group"], filters={"account": account, "disabled": 0}
):
if d.is_group:
- warehouses.extend(get_child_warehouses(d.name))
+ # Keep only children whose effective account matches; a child can override the group's account
+ if warehouse_account_map is None:
+ warehouse_account_map = get_warehouse_account_map(company)
+ warehouses.extend(
+ w
+ for w in get_child_warehouses(d.name)
+ if (warehouse_account_map.get(w) or {}).get("account") == account
+ )
else:
warehouses.append(d.name)
diff --git a/erpnext/stock/doctype_settings_map/item_(standard)/item_(standard).json b/erpnext/stock/doctype_settings_map/item_(standard)/item_(standard).json
index 62fbfd2f761..b07d359d89b 100644
--- a/erpnext/stock/doctype_settings_map/item_(standard)/item_(standard).json
+++ b/erpnext/stock/doctype_settings_map/item_(standard)/item_(standard).json
@@ -15,18 +15,10 @@
"setting_field": "clean_description_html",
"settings_doctype": "Stock Settings"
},
- {
- "setting_field": "default_warehouse",
- "settings_doctype": "Stock Settings"
- },
{
"setting_field": "valuation_method",
"settings_doctype": "Stock Settings"
},
- {
- "setting_field": "sample_retention_warehouse",
- "settings_doctype": "Stock Settings"
- },
{
"setting_field": "selling_price_list",
"settings_doctype": "Selling Settings"
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index aace6a69d94..49008d27bab 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -97,6 +97,7 @@ def get_item_details(
for_validate = parse_json(for_validate)
overwrite_warehouse = parse_json(overwrite_warehouse)
item = frappe.get_cached_doc("Item", ctx.item_code)
+ item.check_permission()
validate_item_details(ctx, item)
doc = frappe.parse_json(doc)
@@ -708,13 +709,8 @@ def get_item_warehouse_(ctx: ItemDetailsCtx, item, overwrite_warehouse, defaults
else:
warehouse = ctx.warehouse
- if not warehouse:
- default_warehouse = frappe.get_single_value("Stock Settings", "default_warehouse")
- if (
- default_warehouse
- and frappe.get_cached_value("Warehouse", default_warehouse, "company") == ctx.company
- ):
- return default_warehouse
+ if not warehouse and ctx.company:
+ return frappe.get_cached_value("Company", ctx.company, "default_warehouse")
return warehouse
diff --git a/erpnext/stock/print_format/delivery_note_bordered/__init__.py b/erpnext/stock/print_format/delivery_note_bordered/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/stock/print_format/delivery_note_bordered/delivery_note_bordered.json b/erpnext/stock/print_format/delivery_note_bordered/delivery_note_bordered.json
new file mode 100644
index 00000000000..fadd570630f
--- /dev/null
+++ b/erpnext/stock/print_format/delivery_note_bordered/delivery_note_bordered.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:10:34.206282",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Delivery Note",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 12,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Delivery Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill From:
\\n
{{ doc.company }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"company_address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Customer's PO No\",\"fieldname\":\"po_no\",\"fieldtype\":\"Small Text\",\"show_label\":\"inline\",\"label_gap\":6},{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_vytjghmu_zBGrfDlm\",\"fieldtype\":\"HTML\",\"html\":\"\\n
Bill To:
\\n
{{ doc.customer }}
\\n
\",\"custom\":1},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"cell_padding\":10,\"custom_style\":\"\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0},\"field_borders\":true,\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Delivery Note Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":22,\"column_condition\":\"\"},{\"label\":\"Item\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":13},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":9},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":11}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":8,\"table_radius\":10,\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[]},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":65,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"f\",\"v\":\"tax_amount\"}],\"align\":\"right\",\"width\":35,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1.5px solid #e5e7eb;margin-top:6px;padding-top:10px;font-weight:700;\",\"label_color\":\"#1f2328\"}]}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":40,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0},\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":5,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_dGLQjxHJ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"has_fields\":true,\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:14:44.365914",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Delivery Note Bordered",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/stock/print_format/delivery_note_classic/__init__.py b/erpnext/stock/print_format/delivery_note_classic/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/stock/print_format/delivery_note_classic/delivery_note_classic.json b/erpnext/stock/print_format/delivery_note_classic/delivery_note_classic.json
new file mode 100644
index 00000000000..c2265c04d67
--- /dev/null
+++ b/erpnext/stock/print_format/delivery_note_classic/delivery_note_classic.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:10:34.216937",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Delivery Note",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jtLStRVi\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom\":1},{\"label\":\"Customer Name\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"custom_style\":\"font-weight: bold;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":53},{\"label\":\"\",\"fields\":[{\"label\":\"Delivery Note\",\"fieldname\":\"name\",\"fieldtype\":\"Data\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"font-weight: bold;\\nborder-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\",\"label_color\":\"#292929\"},{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"},{\"label\":\"Customer's PO No\",\"fieldname\":\"po_no\",\"fieldtype\":\"Small Text\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-bottom: 1px solid #e5e7eb;\\npadding-bottom: 10px;\"}],\"width\":44}],\"show_label\":\"hide\",\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Delivery Note Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":21},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":13,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":14,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":true,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":10,\"table_header_bg\":\"#f3f3f3\",\"show_label\":\"hide\"}]}],\"has_fields\":true,\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":47},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":50}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":24,\"margin\":{\"top\":10,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\"}]}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_LeiIYjph\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 10.0,
+ "modified": "2026-07-24 16:14:44.512564",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Delivery Note Classic",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/stock/print_format/delivery_note_modern/__init__.py b/erpnext/stock/print_format/delivery_note_modern/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/stock/print_format/delivery_note_modern/delivery_note_modern.json b/erpnext/stock/print_format/delivery_note_modern/delivery_note_modern.json
new file mode 100644
index 00000000000..8a7c2144d26
--- /dev/null
+++ b/erpnext/stock/print_format/delivery_note_modern/delivery_note_modern.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:10:34.193509",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Delivery Note",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 14,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_jPuFiKyJ\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Delivery Note\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_QbdbRgGE\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Billed To\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"custom_style\":\"flex-direction:column;align-items:flex-start;gap:3px;\"},{\"label\":\"Address\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\"}],\"width\":56},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Customer's PO No\",\"fieldname\":\"po_no\",\"fieldtype\":\"Small Text\",\"align\":\"left\",\"label_justify\":\"space-between\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nTo Bill\\nPartially Billed\\nCompleted\\nReturn\\nReturn Issued\\nCancelled\\nClosed\",\"label_justify\":\"space-between\",\"visible_if\":\"\"}],\"width\":38}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Delivery Note Item\",\"table_columns\":[{\"label\":\"No\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":25,\"merged_fields\":[{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}]},{\"label\":\"Code\",\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"options\":\"Item\",\"width\":12},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":12,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":14},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8,\"table_header_bg\":\"#f3f3f3\",\"table_border_color\":\"#f3f3f3\",\"show_label\":\"hide\",\"custom_style\":\"\"}]}],\"has_fields\":true,\"margin\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":45},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"label_justify\":\"space-between\",\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":60,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"width\":40,\"color\":\"#1f2328\"}],\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\",\"custom_style\":\"\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"custom_style\":\"border-top:1px solid #e5e7eb;margin-top:5px;padding-top:9px;font-weight:700;\",\"label_color\":\"#1f2328\"}],\"width\":49}],\"has_fields\":true,\"field_orientation\":\"left-right\",\"gap\":44,\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\"}]}],\"background\":\"#f8f8f8\",\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_IMskVBrj\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:14:44.535589",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Delivery Note Modern",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/stock/print_format/delivery_note_modern_with_images/__init__.py b/erpnext/stock/print_format/delivery_note_modern_with_images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/stock/print_format/delivery_note_modern_with_images/delivery_note_modern_with_images.json b/erpnext/stock/print_format/delivery_note_modern_with_images/delivery_note_modern_with_images.json
new file mode 100644
index 00000000000..ecce40a2293
--- /dev/null
+++ b/erpnext/stock/print_format/delivery_note_modern_with_images/delivery_note_modern_with_images.json
@@ -0,0 +1,36 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2026-07-24 16:10:34.079390",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Delivery Note",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Inter",
+ "font_size": 13,
+ "format_data": "{\"header\":{\"columns\":[{\"label\":\"\",\"fields\":[]}]},\"sections\":[{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WJGFzvLg\",\"fieldtype\":\"HTML\",\"html\":\"\\n
\\n Delivery Note\\n
\\n
\\n {{ doc.name }}\\n
\\n
\",\"custom\":1}]}],\"margin\":{\"top\":10,\"right\":0,\"bottom\":10,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_hXQEMfIw\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Custom HTML\",\"fieldname\":\"custom_html_WbBDnaVv_lSJFDiWO\",\"fieldtype\":\"HTML\",\"html\":\"\",\"custom_style\":\"\",\"custom\":1},{\"label\":\"Customer\",\"fieldname\":\"customer_name\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"custom_style\":\"font-weight: bold;\\n\"},{\"label\":\"Bill To\",\"fieldname\":\"address_display\",\"fieldtype\":\"Text Editor\",\"show_label\":\"hide\",\"align\":\"left\"}],\"width\":67},{\"label\":\"\",\"fields\":[{\"label\":\"Posting Date\",\"fieldname\":\"posting_date\",\"fieldtype\":\"Date\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"\"},{\"label\":\"Customer's PO No\",\"fieldname\":\"po_no\",\"fieldtype\":\"Small Text\",\"show_label\":\"show\",\"align\":\"right\",\"label_justify\":\"space-between\",\"label_gap\":20,\"custom_style\":\"\"},{\"label\":\"Status\",\"fieldname\":\"status\",\"fieldtype\":\"Select\",\"options\":\"\\nDraft\\nTo Bill\\nPartially Billed\\nCompleted\\nReturn\\nReturn Issued\\nCancelled\\nClosed\",\"align\":\"right\"}],\"width\":33}],\"has_fields\":true,\"label_case\":\"normal\",\"background\":\"\",\"field_orientation\":\"\",\"gap\":0,\"padding\":{\"top\":0,\"right\":0,\"bottom\":0,\"left\":0},\"inner_rows\":true,\"inner_cols\":true,\"cell_padding\":0,\"border\":{\"width\":1,\"color\":\"#e5e7eb\",\"radius\":6},\"show_label\":\"show\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12},\"custom_style\":\"\"},{\"label\":\"Item\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"\",\"fieldname\":\"items\",\"fieldtype\":\"Table\",\"options\":\"Delivery Note Item\",\"table_columns\":[{\"label\":\"No.\",\"fieldname\":\"idx\",\"fieldtype\":\"Data\",\"width\":5},{\"label\":\"Item\",\"fieldname\":\"item_name\",\"fieldtype\":\"Data\",\"width\":30,\"merged_fields\":[{\"fieldname\":\"image\",\"fieldtype\":\"Attach\",\"style\":\"secondary\"},{\"fieldname\":\"item_code\",\"fieldtype\":\"Link\",\"style\":\"secondary\"},{\"fieldname\":\"description\",\"fieldtype\":\"Text Editor\",\"style\":\"muted-sm\"}],\"image_size\":44},{\"label\":\"Quantity\",\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"options\":\"UOM\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity != 1\"},{\"label\":\"Quantity\",\"fieldname\":\"qty\",\"fieldtype\":\"Float\",\"width\":10,\"merged_fields\":[{\"fieldname\":\"uom\",\"fieldtype\":\"Link\",\"style\":\"secondary\"}],\"merge_direction\":\"horizontal\",\"column_condition\":\"print_settings.print_uom_after_quantity\"},{\"label\":\"Rate\",\"fieldname\":\"rate\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15},{\"label\":\"Amount\",\"fieldname\":\"amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"width\":15}],\"table_style\":\"lined\",\"table_bordered\":false,\"table_header\":\"styled\",\"table_cell_padding\":10,\"table_radius\":8}]}],\"has_fields\":true,\"gap\":12,\"show_label\":\"hide\",\"margin\":{\"top\":15,\"right\":0,\"bottom\":0,\"left\":0}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[],\"width\":51},{\"label\":\"\",\"fields\":[{\"label\":\"Sub Total\",\"fieldname\":\"total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null},{\"label\":\"Discount\",\"fieldname\":\"discount_amount\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":0,\"custom_style\":\"\"},{\"label\":\"\",\"fieldname\":\"repeater\",\"fieldtype\":\"Repeater\",\"source\":\"taxes\",\"repeater_columns\":[{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"description\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"left\",\"width\":70,\"color\":\"#6b7280\"},{\"template\":[{\"t\":\"s\",\"v\":\"\"},{\"t\":\"f\",\"v\":\"tax_amount\"},{\"t\":\"s\",\"v\":\"\"}],\"align\":\"right\",\"color\":\"#1f2328\",\"width\":30}],\"custom_style\":\"\",\"row_condition\":\"print_settings.print_taxes_with_zero_amount or row.tax_amount != 0\"},{\"label\":\"Grand Total\",\"fieldname\":\"grand_total\",\"fieldtype\":\"Currency\",\"options\":\"currency\",\"align\":\"left\",\"label_justify\":\"space-between\",\"label_gap\":null,\"custom_style\":\"border-top: 1px solid #e5e7eb;\\nmargin-top:5px;\\nfont-weight: bold;\\npadding-top:9px\\n\"}],\"width\":49}],\"field_orientation\":\"left-right\",\"margin\":{\"top\":15,\"right\":12,\"bottom\":0,\"left\":12}},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"In Words:\",\"fieldname\":\"in_words\",\"fieldtype\":\"Data\",\"show_label\":\"hide\",\"align\":\"left\",\"label_justify\":\"\",\"label_gap\":2,\"custom_style\":\"\"}]}],\"field_orientation\":\"left-right\",\"background\":\"#f8f8f8\",\"margin\":{\"top\":10,\"right\":0,\"bottom\":0,\"left\":0},\"padding\":{\"top\":5,\"right\":12,\"bottom\":5,\"left\":12},\"gap\":0},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Divider\",\"fieldname\":\"divider_cdcIgUdZ\",\"fieldtype\":\"Divider\",\"custom\":1}]}]},{\"label\":\"\",\"columns\":[{\"label\":\"\",\"fields\":[{\"label\":\"Terms and Conditions Details\",\"fieldname\":\"terms\",\"fieldtype\":\"Text Editor\"}]}],\"margin\":{\"top\":5,\"right\":12,\"bottom\":0,\"left\":12}}],\"footer\":{\"columns\":[{\"label\":\"\",\"fields\":[]}],\"show_label\":\"hide\"}}",
+ "idx": 0,
+ "label_color": "#6b7280",
+ "line_breaks": 0,
+ "margin_bottom": 8.0,
+ "margin_left": 8.0,
+ "margin_right": 8.0,
+ "margin_top": 15.0,
+ "modified": "2026-07-24 16:14:44.560506",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Delivery Note Modern with Images",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "pdf_generator": "chrome",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 1,
+ "print_format_for": "DocType",
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_label_colon": 0,
+ "show_section_headings": 0,
+ "standard": "Yes",
+ "value_color": "#1f2328"
+}
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 4c5b549c19a..463d195d38f 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -338,7 +338,7 @@ class FIFOSlots:
self._process_stock_ledger_entry(row, bundle_wise_serial_nos, bundle_wise_batch_nos)
self._recompute_moving_average_slots()
- self._rebalance_negative_batch_slots()
+ self._rebalance_batch_slots()
if not self.filters.get("show_warehouse_wise_stock"):
# (Item 1, WH 1), (Item 1, WH 2) => (Item 1)
@@ -360,14 +360,14 @@ class FIFOSlots:
if is_qty_slot(slot):
slot[FIFO_VALUE_INDEX] = flt(slot[FIFO_QTY_INDEX] * rate)
- def _rebalance_negative_batch_slots(self) -> None:
+ def _rebalance_batch_slots(self) -> None:
for item_dict in self.item_details.values():
if item_dict.get("has_batch_no"):
- self._rebalance_negative_batch_slot_values(item_dict["fifo_queue"])
+ self._rebalance_batch_slot_values(item_dict["fifo_queue"])
- def _rebalance_negative_batch_slot_values(self, fifo_queue: list) -> None:
- """A batch is one valuation pool, so a slot driven negative by consumption
- at the pooled rate is stale detail: spread the pool value over its slots."""
+ def _rebalance_batch_slot_values(self, fifo_queue: list) -> None:
+ """A batch is one valuation pool, so per-slot value differences are stale
+ detail: spread the pool value over its slots in proportion to qty."""
groups = {}
for slot in fifo_queue:
if is_batch_slot(slot):
@@ -375,12 +375,8 @@ class FIFOSlots:
groups.setdefault(key, []).append(slot)
for slots in groups.values():
- has_negative_slot = any(
- flt(slot[BATCH_SLOT_VALUE_INDEX]) < 0 and flt(slot[BATCH_SLOT_QTY_INDEX]) > 0
- for slot in slots
- )
total_qty = sum(flt(slot[BATCH_SLOT_QTY_INDEX]) for slot in slots)
- if not has_negative_slot or total_qty <= 0:
+ if total_qty <= 0:
continue
rate = sum(flt(slot[BATCH_SLOT_VALUE_INDEX]) for slot in slots) / total_qty
diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
index 640c10e35c3..39c046fb689 100644
--- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
@@ -5,7 +5,13 @@ from unittest.mock import patch
import frappe
-from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, format_report_data, get_average_age
+from erpnext.stock.report.stock_ageing.stock_ageing import (
+ BATCH_SLOT_QTY_INDEX,
+ BATCH_SLOT_VALUE_INDEX,
+ FIFOSlots,
+ format_report_data,
+ get_average_age,
+)
from erpnext.tests.utils import ERPNextTestSuite
@@ -565,10 +571,11 @@ class TestStockAgeing(ERPNextTestSuite):
],
)
- def test_partial_batch_reco_keeps_existing_slot_values(self):
+ def test_partial_batch_reco_pools_slot_values(self):
"""Ledger (same wh, batch B): [+10 @ 100, single-SLE reco >> 12]
The reco entry qty (delta 2) does not cover the whole batch, so
- stock_value_difference / qty is not the batch rate: skip the rescale."""
+ stock_value_difference / qty is not the batch rate: skip the rescale.
+ The batch total (1400) is untouched, then pooled across both slots."""
from erpnext.stock.doctype.item.test_item import make_item
item_code = make_item(
@@ -609,12 +616,112 @@ class TestStockAgeing(ERPNextTestSuite):
queue = slots[item_code]["fifo_queue"]
self.assertEqual(
- queue,
+ [slot[:4] for slot in queue],
[
- [batch_no, 1, 10.0, "2021-12-01", 1000.0],
- [batch_no, 1, 2.0, "2021-12-01", 400.0],
+ [batch_no, 1, 10.0, "2021-12-01"],
+ [batch_no, 1, 2.0, "2021-12-01"],
],
)
+ self.assertAlmostEqual(queue[0][4], 1166.67, places=2)
+ self.assertAlmostEqual(queue[1][4], 233.33, places=2)
+
+ def test_batch_receipts_at_differing_rates_pool_slot_values(self):
+ """Ledger (same wh, batch B): [+10 @ 0, +10 @ 10] and no issue.
+ Nothing goes negative, but the batch is one valuation pool, so both
+ age slots carry the pooled rate instead of their receipt value."""
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ item_code = make_item(
+ "Test Stock Ageing Batch Pool Split",
+ {"is_stock_item": 1, "has_batch_no": 1, "valuation_method": "FIFO"},
+ ).name
+
+ batch_no = "SA-POOL-SPLIT-BATCH"
+ if not frappe.db.exists("Batch", batch_no):
+ frappe.get_doc({"doctype": "Batch", "batch_id": batch_no, "item": item_code}).insert(
+ ignore_permissions=True
+ )
+ frappe.db.set_value("Batch", batch_no, "use_batchwise_valuation", 1)
+
+ def make_sle(posting_date, voucher_no, actual_qty, qty_after, stock_value_difference):
+ return frappe._dict(
+ name=item_code,
+ actual_qty=actual_qty,
+ qty_after_transaction=qty_after,
+ stock_value_difference=stock_value_difference,
+ valuation_rate=abs(stock_value_difference / actual_qty) if actual_qty else 0,
+ warehouse="WH 1",
+ posting_date=posting_date,
+ voucher_type="Stock Entry",
+ voucher_no=voucher_no,
+ has_serial_no=False,
+ has_batch_no=True,
+ serial_no=None,
+ batch_no=batch_no,
+ )
+
+ sle = [
+ make_sle("2021-12-01", "001", 10, 10, 0),
+ make_sle("2021-12-02", "002", 10, 20, 100),
+ ]
+
+ slots = FIFOSlots(self.filters, sle).generate()
+ queue = slots[item_code]["fifo_queue"]
+
+ self.assertEqual(
+ queue,
+ [
+ [batch_no, 1, 10.0, "2021-12-01", 50.0],
+ [batch_no, 1, 10.0, "2021-12-01", 50.0],
+ ],
+ )
+
+ def test_batch_pooling_preserves_total_on_repeating_rate(self):
+ """Ledger (same wh, batch B): [+3 @ 100/3, +6 @ 0, +2 @ 0]
+ The pooled rate does not terminate, so assert the redistributed
+ slot values still add back to the batch total."""
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ item_code = make_item(
+ "Test Stock Ageing Batch Pool Residual",
+ {"is_stock_item": 1, "has_batch_no": 1, "valuation_method": "FIFO"},
+ ).name
+
+ batch_no = "SA-POOL-RESIDUAL-BATCH"
+ if not frappe.db.exists("Batch", batch_no):
+ frappe.get_doc({"doctype": "Batch", "batch_id": batch_no, "item": item_code}).insert(
+ ignore_permissions=True
+ )
+ frappe.db.set_value("Batch", batch_no, "use_batchwise_valuation", 1)
+
+ def make_sle(posting_date, voucher_no, actual_qty, qty_after, stock_value_difference):
+ return frappe._dict(
+ name=item_code,
+ actual_qty=actual_qty,
+ qty_after_transaction=qty_after,
+ stock_value_difference=stock_value_difference,
+ valuation_rate=abs(stock_value_difference / actual_qty) if actual_qty else 0,
+ warehouse="WH 1",
+ posting_date=posting_date,
+ voucher_type="Stock Entry",
+ voucher_no=voucher_no,
+ has_serial_no=False,
+ has_batch_no=True,
+ serial_no=None,
+ batch_no=batch_no,
+ )
+
+ sle = [
+ make_sle("2021-12-01", "001", 3, 3, 100),
+ make_sle("2021-12-02", "002", 6, 9, 0),
+ make_sle("2021-12-03", "003", 2, 11, 0),
+ ]
+
+ slots = FIFOSlots(self.filters, sle).generate()
+ queue = slots[item_code]["fifo_queue"]
+
+ self.assertEqual([slot[BATCH_SLOT_QTY_INDEX] for slot in queue], [3.0, 6.0, 2.0])
+ self.assertEqual(sum(slot[BATCH_SLOT_VALUE_INDEX] for slot in queue), 100.0)
def test_batch_issue_at_pooled_rate_keeps_slot_values_positive(self):
"""Ledger (same wh, batch B): [+10 @ 0, +10 @ 10, -4 @ pooled 5]
diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js
index eef79ce6a27..3060042034b 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.js
+++ b/erpnext/stock/report/stock_balance/stock_balance.js
@@ -136,7 +136,7 @@ frappe.query_reports["Stock Balance"] = {
fieldname: "include_zero_stock_items",
label: __("Include Zero Stock Items"),
fieldtype: "Check",
- default: 0,
+ default: 1,
},
{
fieldname: "show_dimension_wise_stock",
diff --git a/erpnext/stock/services/base_stock_gl_composer.py b/erpnext/stock/services/base_stock_gl_composer.py
index 15b81cab5f5..1d5a80ef10d 100644
--- a/erpnext/stock/services/base_stock_gl_composer.py
+++ b/erpnext/stock/services/base_stock_gl_composer.py
@@ -196,13 +196,16 @@ class BaseStockGLComposer(BaseGLComposer):
self.append_expenses_added_to_stock_pair(gl_list, item_code, amount, item_row)
def append_expenses_added_to_stock_pair(self, gl_list, item_code, amount, item_row):
+ # A service item holds no stock value, so there is nothing to book against it - and it must
+ # not make the expense accounts mandatory either. A zero pair would be rejected by GL Entry
+ # anyway, which needs a debit or a credit on every row.
+ if not amount or not frappe.get_cached_value("Item", item_code, "is_stock_item"):
+ return
+
doc = self.doc
fields = ("expenses_added_to_stock_account", "expenses_added_to_stock_contra_account")
details = get_expenses_added_to_stock_accounts(item_code, doc.company)
- if not any(details.get(field) for field in fields):
- return
-
for field in fields:
if not details.get(field):
frappe.throw(
diff --git a/erpnext/stock/tests/test_expenses_added_to_stock.py b/erpnext/stock/tests/test_expenses_added_to_stock.py
index 2fe9e0ba7e4..c1ca84a4e69 100644
--- a/erpnext/stock/tests/test_expenses_added_to_stock.py
+++ b/erpnext/stock/tests/test_expenses_added_to_stock.py
@@ -140,22 +140,6 @@ class TestExpensesAddedToStock(ERPNextTestSuite):
self.assertEqual(debits[self.eats_account], 0)
self.assertEqual(credits[self.eats_contra_account], 0)
- def test_unconfigured_company_skips_booking(self):
- frappe.db.set_value(
- "Company",
- COMPANY,
- {
- "expenses_added_to_stock_account": None,
- "expenses_added_to_stock_contra_account": None,
- },
- )
-
- se = make_stock_entry(item_code=self.item, to_warehouse=WAREHOUSE, qty=10, rate=100, company=COMPANY)
-
- _balances, debits, credits = self.get_gl_balances("Stock Entry", se.name)
- self.assertEqual(debits[self.eats_account], 0)
- self.assertEqual(credits[self.eats_contra_account], 0)
-
def test_missing_contra_account_raises_when_feature_enabled(self):
frappe.db.set_value("Company", COMPANY, "expenses_added_to_stock_contra_account", None)
@@ -168,3 +152,38 @@ class TestExpensesAddedToStock(ERPNextTestSuite):
rate=100,
company=COMPANY,
)
+
+ def test_service_item_books_nothing_on_purchase_invoice_with_update_stock(self):
+ """A service item carries no stock value, so booking it produced a GL row with neither a
+ debit nor a credit, which GL Entry rejects outright."""
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+
+ service_item = make_item(properties={"is_stock_item": 0}).name
+
+ pi = make_purchase_invoice(
+ company=COMPANY,
+ warehouse=WAREHOUSE,
+ item_code=service_item,
+ qty=1,
+ rate=500,
+ update_stock=1,
+ expense_account="Cost of Goods Sold - TCP1",
+ cost_center="Main - TCP1",
+ )
+
+ self.assertEqual(pi.docstatus, 1)
+
+ _balances, debits, credits = self.get_gl_balances("Purchase Invoice", pi.name)
+ self.assertEqual(debits[self.eats_account], 0)
+ self.assertEqual(credits[self.eats_contra_account], 0)
+
+ booked = frappe.get_all(
+ "GL Entry",
+ filters={
+ "voucher_type": "Purchase Invoice",
+ "voucher_no": pi.name,
+ "is_cancelled": 0,
+ "account": self.purchase_expense_account,
+ },
+ )
+ self.assertFalse(booked)
diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json
index e98890a399e..07519124ef9 100644
--- a/erpnext/stock/workspace/stock/stock.json
+++ b/erpnext/stock/workspace/stock/stock.json
@@ -799,7 +799,7 @@
"type": "Link"
}
],
- "modified": "2026-07-05 12:08:07.187999",
+ "modified": "2026-07-30 11:42:33.379243",
"modified_by": "Administrator",
"module": "Stock",
"module_onboarding": "Stock Onboarding",
@@ -1022,7 +1022,7 @@
{
"child": 1,
"collapsible": 1,
- "default_workspace": 1,
+ "default_workspace": 0,
"icon": "",
"indent": 0,
"keep_closed": 0,
diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json
index a0b163f4271..034e98f37b6 100644
--- a/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json
+++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json
@@ -8,7 +8,6 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
- "title",
"naming_series",
"sales_order",
"customer",
@@ -29,6 +28,7 @@
"service_items_section",
"service_items",
"tab_other_info",
+ "title",
"order_status_section",
"status",
"per_raw_material_received",
@@ -43,10 +43,8 @@
"fields": [
{
"allow_on_submit": 1,
- "default": "{customer_name}",
"fieldname": "title",
"fieldtype": "Data",
- "hidden": 1,
"label": "Title",
"no_copy": 1,
"print_hide": 1
@@ -306,7 +304,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2026-02-26 17:16:21.697846",
+ "modified": "2026-07-27 11:20:14.512336",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Inward Order",
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
index d0223a3acd2..317dd3fd71d 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
@@ -8,7 +8,6 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
- "title",
"naming_series",
"purchase_order",
"supplier",
@@ -55,6 +54,7 @@
"additional_costs",
"total_additional_costs",
"tab_other_info",
+ "title",
"order_status_section",
"status",
"column_break_39",
@@ -69,10 +69,8 @@
"fields": [
{
"allow_on_submit": 1,
- "default": "{supplier_name}",
"fieldname": "title",
"fieldtype": "Data",
- "hidden": 1,
"label": "Title",
"no_copy": 1,
"print_hide": 1
@@ -494,7 +492,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
- "modified": "2025-11-14 10:31:40.682892",
+ "modified": "2026-07-27 11:20:14.512336",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order",
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
index 0186549bb4d..9c936c5aff4 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
+++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
@@ -394,6 +394,85 @@ class TestSubcontractingOrder(ERPNextTestSuite):
bin_after_cancel_sco.reserved_qty_for_sub_contract, bin_before_sco.reserved_qty_for_sub_contract
)
+ def test_close_subcontracting_order_releases_reserved_qty(self):
+ # RM in stock at the reserve warehouse for transfer
+ make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item", qty=10, basic_rate=100)
+ make_stock_entry(
+ target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100
+ )
+
+ bin_before_sco = frappe.db.get_value(
+ "Bin",
+ filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+ fieldname="reserved_qty_for_sub_contract",
+ as_dict=1,
+ )
+
+ # Create SCO with a reserve warehouse on the supplied items
+ service_items = [
+ {
+ "warehouse": "_Test Warehouse - _TC",
+ "item_code": "Subcontracted Service Item 1",
+ "qty": 10,
+ "rate": 100,
+ "fg_item": "_Test FG Item",
+ "fg_item_qty": 10,
+ },
+ ]
+ sco = get_subcontracting_order(service_items=service_items)
+
+ # Transfer only 90% of the raw materials to the supplier warehouse
+ ste = frappe.get_doc(make_rm_stock_entry(sco.name))
+ for item in ste.items:
+ item.qty *= 0.9
+ ste.save()
+ ste.submit()
+ sco.load_from_db()
+ self.assertEqual(sco.status, "Partial Material Transferred")
+
+ # Receive only a partial qty so the order stays open (per_received < 100)
+ scr = make_subcontracting_receipt(sco.name)
+ scr.items[0].qty -= 1
+ scr.save()
+ scr.submit()
+ sco.load_from_db()
+ self.assertEqual(sco.status, "Partially Received")
+
+ # Keep another SCO open so transfers from the closed SCO must not reduce its reservation
+ open_sco = get_subcontracting_order(service_items=service_items)
+ self.assertEqual(open_sco.status, "Open")
+
+ bin_before_close = frappe.db.get_value(
+ "Bin",
+ filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+ fieldname=["reserved_qty_for_sub_contract", "projected_qty"],
+ as_dict=1,
+ )
+
+ # One unit remains reserved for the partially transferred SCO, plus ten for the open SCO
+ self.assertEqual(
+ bin_before_close.reserved_qty_for_sub_contract,
+ bin_before_sco.reserved_qty_for_sub_contract + 11,
+ )
+
+ # Close the partially-received order
+ sco.update_status("Closed")
+ self.assertEqual(sco.status, "Closed")
+
+ bin_after_close = frappe.db.get_value(
+ "Bin",
+ filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+ fieldname=["reserved_qty_for_sub_contract", "projected_qty"],
+ as_dict=1,
+ )
+
+ # Closing releases the remaining unit without applying its transfer against the open SCO
+ self.assertEqual(
+ bin_after_close.reserved_qty_for_sub_contract,
+ bin_before_sco.reserved_qty_for_sub_contract + 10,
+ )
+ self.assertEqual(bin_after_close.projected_qty, bin_before_close.projected_qty + 1)
+
def test_send_to_subcontractor_ste_submit_without_sco_write_permission(self):
"""A Stock-only user (can submit Stock Entries but has no Subcontracting Order write) must be
able to submit and cancel a 'Send to Subcontractor' Stock Entry. The SCO status update on the
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
index a284f24fd50..81e347aceaa 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
@@ -7,7 +7,6 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "title",
"naming_series",
"supplier",
"supplier_name",
@@ -63,6 +62,7 @@
"total_additional_costs",
"tab_other_info",
"more_info",
+ "title",
"amended_from",
"range",
"column_break4",
@@ -91,10 +91,8 @@
"fields": [
{
"allow_on_submit": 1,
- "default": "{supplier_name}",
"fieldname": "title",
"fieldtype": "Data",
- "hidden": 1,
"label": "Title",
"no_copy": 1,
"print_hide": 1
@@ -679,7 +677,7 @@
"in_create": 1,
"is_submittable": 1,
"links": [],
- "modified": "2026-02-27 17:59:44.107193",
+ "modified": "2026-07-28 13:04:52.771908",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Receipt",
@@ -747,6 +745,6 @@
"sort_order": "DESC",
"states": [],
"timeline_field": "supplier",
- "title_field": "title",
+ "title_field": "supplier_name",
"track_changes": 1
}
diff --git a/erpnext/tests/test_init.py b/erpnext/tests/test_init.py
index 4be96199ddd..dc7c961c92d 100644
--- a/erpnext/tests/test_init.py
+++ b/erpnext/tests/test_init.py
@@ -44,3 +44,20 @@ class TestInit(ERPNextTestSuite):
from frappe.tests.test_patches import check_patch_files
check_patch_files("erpnext")
+
+ def test_no_unrendered_title_templates(self):
+ import frappe
+
+ modules = frappe.get_all("Module Def", filters={"app_name": "erpnext"}, pluck="name")
+ for doctype in frappe.get_all("DocType", filters={"module": ("in", modules)}, pluck="name"):
+ meta = frappe.get_meta(doctype)
+ field = meta.get_field("title")
+ if not field or not field.default or "{" not in field.default:
+ continue
+
+ self.assertEqual(
+ meta.title_field,
+ "title",
+ f"{doctype}: title default {field.default!r} is stored verbatim because "
+ "Document.set_title_field() only renders it when title_field is 'title'",
+ )
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index aebb7a22650..4b0e8bcbfee 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -246,7 +246,6 @@ class BootStrapTestData:
stock_settings = frappe.get_doc("Stock Settings")
stock_settings.item_naming_by = "Item Code"
stock_settings.valuation_method = "FIFO"
- stock_settings.default_warehouse = frappe.db.get_value("Warehouse", {"warehouse_name": _("Stores")})
stock_settings.stock_uom = "Nos"
stock_settings.auto_indent = 1
stock_settings.auto_insert_price_list_rate_if_missing = 1