diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json index 444a45ff8c1..1bf909117a8 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json @@ -50,6 +50,7 @@ "fieldname": "amount", "fieldtype": "Currency", "label": "Amount", + "options": "currency", "read_only": 1 }, { diff --git a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py index 4de556b7e46..216a9034be4 100644 --- a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py +++ b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py @@ -541,7 +541,7 @@ class FinancialQueryBuilder: .where(acb_table.period_closing_voucher == closing_voucher) ) - query = self._apply_standard_filters(query, acb_table) + query = self._apply_standard_filters(query, acb_table, "Account Closing Balance") results = self._execute_with_permissions(query, "Account Closing Balance") for row in results: @@ -636,12 +636,15 @@ class FinancialQueryBuilder: return self._execute_with_permissions(query, "GL Entry") def _calculate_running_balances(self, balances_data: dict, gl_data: list[dict]) -> dict: - for row in gl_data: - account = row["account"] + gl_dict = {row["account"]: row for row in gl_data} + accounts = set(balances_data.keys()) | set(gl_dict.keys()) + + for account in accounts: if account not in balances_data: balances_data[account] = AccountData(account=account, **self._get_account_meta(account)) account_data: AccountData = balances_data[account] + gl_movement = gl_dict.get(account, {}) if account_data.has_periods(): first_period = account_data.get_period(self.periods[0]["key"]) @@ -651,20 +654,13 @@ class FinancialQueryBuilder: for period in self.periods: period_key = period["key"] - movement = row.get(period_key, 0.0) + movement = gl_movement.get(period_key, 0.0) closing_balance = current_balance + movement account_data.add_period(PeriodValue(period_key, current_balance, closing_balance, movement)) current_balance = closing_balance - # Accounts with no movements - for account_data in balances_data.values(): - for period in self.periods: - period_key = period["key"] - if period_key not in account_data.period_values: - account_data.add_period(PeriodValue(period_key, 0.0, 0.0, 0.0)) - def _handle_balance_accumulation(self, balances_data): for account_data in balances_data.values(): account_data: AccountData @@ -683,12 +679,12 @@ class FinancialQueryBuilder: else: account_data.unaccumulate_values() - def _apply_standard_filters(self, query, table): + def _apply_standard_filters(self, query, table, doctype: str = "GL Entry"): if self.filters.get("ignore_closing_entries"): - if hasattr(table, "is_period_closing_voucher_entry"): - query = query.where(table.is_period_closing_voucher_entry == 0) - else: + if doctype == "GL Entry": query = query.where(table.voucher_type != "Period Closing Voucher") + else: + query = query.where(table.is_period_closing_voucher_entry == 0) if self.filters.get("project"): projects = self.filters.get("project") diff --git a/erpnext/accounts/doctype/financial_report_template/test_financial_report_engine.py b/erpnext/accounts/doctype/financial_report_template/test_financial_report_engine.py index 1a3ffc7ab61..dcfe884bdf3 100644 --- a/erpnext/accounts/doctype/financial_report_template/test_financial_report_engine.py +++ b/erpnext/accounts/doctype/financial_report_template/test_financial_report_engine.py @@ -16,7 +16,8 @@ from erpnext.accounts.doctype.financial_report_template.financial_report_engine from erpnext.accounts.doctype.financial_report_template.test_financial_report_template import ( FinancialReportTemplateTestCase, ) -from erpnext.accounts.utils import get_currency_precision +from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry +from erpnext.accounts.utils import get_currency_precision, get_fiscal_year # On IntegrationTestCase, the doctype test records and all # link-field test record dependencies are recursively loaded @@ -1672,3 +1673,360 @@ class TestFilterExpressionParser(FinancialReportTemplateTestCase): mock_row_invalid = self._create_mock_report_row(invalid_formula) condition = parser.build_condition(mock_row_invalid, account_table) self.assertIsNone(condition) + + +class TestFinancialQueryBuilder(FinancialReportTemplateTestCase): + def test_fetch_balances_with_journal_entries(self): + company = "_Test Company" + cash_account = "_Test Cash - _TC" + bank_account = "_Test Bank - _TC" + + # Create journal entries in different periods + # October: Transfer 1000 from Bank to Cash + jv_oct = make_journal_entry( + account1=cash_account, + account2=bank_account, + amount=1000, + posting_date="2024-10-15", + company=company, + submit=True, + ) + + # November: Transfer 500 from Bank to Cash + jv_nov = make_journal_entry( + account1=cash_account, + account2=bank_account, + amount=500, + posting_date="2024-11-20", + company=company, + submit=True, + ) + + # December: No transactions (test zero movement period) + + try: + # Set up filters and periods for Q4 2024 + filters = { + "company": company, + "from_fiscal_year": "2024", + "to_fiscal_year": "2024", + "period_start_date": "2024-10-01", + "period_end_date": "2024-12-31", + "filter_based_on": "Date Range", + "periodicity": "Monthly", + } + + periods = [ + {"key": "2024_oct", "from_date": "2024-10-01", "to_date": "2024-10-31"}, + {"key": "2024_nov", "from_date": "2024-11-01", "to_date": "2024-11-30"}, + {"key": "2024_dec", "from_date": "2024-12-01", "to_date": "2024-12-31"}, + ] + + query_builder = FinancialQueryBuilder(filters, periods) + + # Create account objects as expected by fetch_account_balances + accounts = [ + frappe._dict({"name": cash_account, "account_name": "Cash", "account_number": "1001"}), + frappe._dict({"name": bank_account, "account_name": "Bank", "account_number": "1002"}), + ] + + # Fetch balances using the full workflow + balances_data = query_builder.fetch_account_balances(accounts) + + # Verify Cash account balances + cash_data = balances_data.get(cash_account) + self.assertIsNotNone(cash_data, "Cash account should exist in results") + + # October: movement = +1000 (debit) + oct_cash = cash_data.get_period("2024_oct") + self.assertIsNotNone(oct_cash, "October period should exist for cash") + self.assertEqual(oct_cash.movement, 1000.0, "October cash movement should be 1000") + + # November: movement = +500 + nov_cash = cash_data.get_period("2024_nov") + self.assertIsNotNone(nov_cash, "November period should exist for cash") + self.assertEqual(nov_cash.movement, 500.0, "November cash movement should be 500") + self.assertEqual( + nov_cash.opening, oct_cash.closing, "November opening should equal October closing" + ) + + # December: movement = 0 (no transactions) + dec_cash = cash_data.get_period("2024_dec") + self.assertIsNotNone(dec_cash, "December period should exist for cash") + self.assertEqual(dec_cash.movement, 0.0, "December cash movement should be 0") + self.assertEqual( + dec_cash.closing, + nov_cash.closing, + "December closing should equal November closing when no movement", + ) + + # Verify Bank account balances (opposite direction) + bank_data = balances_data.get(bank_account) + self.assertIsNotNone(bank_data, "Bank account should exist in results") + + oct_bank = bank_data.get_period("2024_oct") + self.assertEqual(oct_bank.movement, -1000.0, "October bank movement should be -1000") + + nov_bank = bank_data.get_period("2024_nov") + self.assertEqual(nov_bank.movement, -500.0, "November bank movement should be -500") + + finally: + # Clean up: cancel journal entries + jv_nov.cancel() + jv_oct.cancel() + + def test_opening_balance_from_previous_period_closing(self): + company = "_Test Company" + cash_account = "_Test Cash - _TC" + sales_account = "Sales - _TC" + posting_date_2023 = "2023-06-15" + + # Create journal entry in prior period (2023) + # Cash Dr 5000, Sales Cr 5000 + jv_2023 = make_journal_entry( + account1=cash_account, + account2=sales_account, + amount=5000, + posting_date=posting_date_2023, + company=company, + submit=True, + ) + + pcv = None + jv_2024 = None + original_pcv_setting = frappe.db.get_single_value( + "Accounts Settings", "use_legacy_controller_for_pcv" + ) + + try: + # Create Period Closing Voucher for 2023 + # This will create Account Closing Balance entries + closing_account = frappe.db.get_value( + "Account", + { + "company": company, + "root_type": "Liability", + "is_group": 0, + "account_type": ["not in", ["Payable", "Receivable"]], + }, + "name", + ) + + fy_2023 = get_fiscal_year(posting_date_2023, company=company) + + frappe.db.set_single_value("Accounts Settings", "use_legacy_controller_for_pcv", 1) + + pcv = frappe.get_doc( + { + "doctype": "Period Closing Voucher", + "transaction_date": "2023-12-31", + "period_start_date": fy_2023[1], + "period_end_date": fy_2023[2], + "company": company, + "fiscal_year": fy_2023[0], + "cost_center": "_Test Cost Center - _TC", + "closing_account_head": closing_account, + "remarks": "Test Period Closing", + } + ) + pcv.insert() + pcv.submit() + pcv.reload() + + # Now create a small transaction in 2024 to ensure the account appears + jv_2024 = make_journal_entry( + account1=cash_account, + account2=sales_account, + amount=100, + posting_date="2024-01-15", + company=company, + submit=True, + ) + + # Set up filters for Q1 2024 (after the period closing) + filters = { + "company": company, + "from_fiscal_year": "2024", + "to_fiscal_year": "2024", + "period_start_date": "2024-01-01", + "period_end_date": "2024-03-31", + "filter_based_on": "Date Range", + "periodicity": "Monthly", + "ignore_closing_entries": True, # Don't include PCV entries in movements + } + + periods = [ + {"key": "2024_jan", "from_date": "2024-01-01", "to_date": "2024-01-31"}, + {"key": "2024_feb", "from_date": "2024-02-01", "to_date": "2024-02-29"}, + {"key": "2024_mar", "from_date": "2024-03-01", "to_date": "2024-03-31"}, + ] + + query_builder = FinancialQueryBuilder(filters, periods) + + accounts = [ + frappe._dict({"name": cash_account, "account_name": "Cash", "account_number": "1001"}), + ] + + balances_data = query_builder.fetch_account_balances(accounts) + + # Verify Cash account has opening balance from 2023 transactions + cash_data = balances_data.get(cash_account) + self.assertIsNotNone(cash_data, "Cash account should exist in results") + + jan_cash = cash_data.get_period("2024_jan") + self.assertIsNotNone(jan_cash, "January period should exist") + + # Opening balance should be from prior period + # Cash had 5000 debit in 2023, so opening in 2024 should be >= 5000 + # (may be higher if there were other test transactions) + self.assertEqual( + jan_cash.opening, + 5000.0, + "January opening should equal to balance from 2023 (5000)", + ) + + # Verify running balance logic + # Movement in January is 100 (from jv_2024) + self.assertEqual(jan_cash.movement, 100.0, "January movement should be 100") + self.assertEqual( + jan_cash.closing, jan_cash.opening + jan_cash.movement, "Closing = Opening + Movement" + ) + + # February and March should have no movement but carry the balance + feb_cash = cash_data.get_period("2024_feb") + self.assertEqual(feb_cash.opening, jan_cash.closing, "Feb opening = Jan closing") + self.assertEqual(feb_cash.movement, 0.0, "February should have no movement") + self.assertEqual(feb_cash.closing, feb_cash.opening, "Feb closing = opening when no movement") + + mar_cash = cash_data.get_period("2024_mar") + self.assertEqual(mar_cash.opening, feb_cash.closing, "Mar opening = Feb closing") + self.assertEqual(mar_cash.movement, 0.0, "March should have no movement") + self.assertEqual(mar_cash.closing, mar_cash.opening, "Mar closing = opening when no movement") + + # Set up filters for Q2 2024 + filters_q2 = { + "company": company, + "from_fiscal_year": "2024", + "to_fiscal_year": "2024", + "period_start_date": "2024-04-01", + "period_end_date": "2024-06-30", + "filter_based_on": "Date Range", + "periodicity": "Monthly", + "ignore_closing_entries": True, + } + + periods_q2 = [ + {"key": "2024_apr", "from_date": "2024-04-01", "to_date": "2024-04-30"}, + {"key": "2024_may", "from_date": "2024-05-01", "to_date": "2024-05-31"}, + {"key": "2024_jun", "from_date": "2024-06-01", "to_date": "2024-06-30"}, + ] + + query_builder_q2 = FinancialQueryBuilder(filters_q2, periods_q2) + + balances_data_q2 = query_builder_q2.fetch_account_balances(accounts) + + # Verify Cash account in Q2 + cash_data_q2 = balances_data_q2.get(cash_account) + self.assertIsNotNone(cash_data_q2, "Cash account should exist in Q2 results") + + apr_cash = cash_data_q2.get_period("2024_apr") + self.assertIsNotNone(apr_cash, "April period should exist") + + # Opening balance in April should equal closing in March + self.assertEqual( + apr_cash.opening, + mar_cash.closing, + "April opening should equal March closing balance", + ) + + self.assertEqual(apr_cash.closing, apr_cash.opening, "April closing = opening when no movement") + + finally: + # Clean up + frappe.db.set_single_value( + "Accounts Settings", "use_legacy_controller_for_pcv", original_pcv_setting or 0 + ) + + if jv_2024: + jv_2024.cancel() + + if pcv: + pcv.reload() + if pcv.docstatus == 1: + pcv.cancel() + + jv_2023.cancel() + + def test_account_with_gl_entries_but_no_prior_closing_balance(self): + company = "_Test Company" + cash_account = "_Test Cash - _TC" + bank_account = "_Test Bank - _TC" + + # Create journal entries WITHOUT any prior Period Closing Voucher + # This ensures the account exists in gl_dict but NOT in balances_data + jv = make_journal_entry( + account1=cash_account, + account2=bank_account, + amount=2500, + posting_date="2024-07-15", + company=company, + submit=True, + ) + + try: + # Set up filters - use a period with no prior PCV + filters = { + "company": company, + "from_fiscal_year": "2024", + "to_fiscal_year": "2024", + "period_start_date": "2024-07-01", + "period_end_date": "2024-09-30", + "filter_based_on": "Date Range", + "periodicity": "Monthly", + } + + periods = [ + {"key": "2024_jul", "from_date": "2024-07-01", "to_date": "2024-07-31"}, + {"key": "2024_aug", "from_date": "2024-08-01", "to_date": "2024-08-31"}, + {"key": "2024_sep", "from_date": "2024-09-01", "to_date": "2024-09-30"}, + ] + + query_builder = FinancialQueryBuilder(filters, periods) + + # Use accounts that have GL entries but may not have Account Closing Balance + accounts = [ + frappe._dict({"name": cash_account, "account_name": "Cash", "account_number": "1001"}), + frappe._dict({"name": bank_account, "account_name": "Bank", "account_number": "1002"}), + ] + + balances_data = query_builder.fetch_account_balances(accounts) + + # Verify accounts are present in results even without prior closing balance + cash_data = balances_data.get(cash_account) + self.assertIsNotNone(cash_data, "Cash account should exist in results") + + bank_data = balances_data.get(bank_account) + self.assertIsNotNone(bank_data, "Bank account should exist in results") + + # Verify July has the movement from journal entry + jul_cash = cash_data.get_period("2024_jul") + self.assertIsNotNone(jul_cash, "July period should exist for cash") + self.assertEqual(jul_cash.movement, 2500.0, "July cash movement should be 2500") + + jul_bank = bank_data.get_period("2024_jul") + self.assertIsNotNone(jul_bank, "July period should exist for bank") + self.assertEqual(jul_bank.movement, -2500.0, "July bank movement should be -2500") + + # Verify subsequent periods exist with zero movement + aug_cash = cash_data.get_period("2024_aug") + self.assertIsNotNone(aug_cash, "August period should exist for cash") + self.assertEqual(aug_cash.movement, 0.0, "August cash movement should be 0") + self.assertEqual(aug_cash.opening, jul_cash.closing, "August opening = July closing") + + sep_cash = cash_data.get_period("2024_sep") + self.assertIsNotNone(sep_cash, "September period should exist for cash") + self.assertEqual(sep_cash.movement, 0.0, "September cash movement should be 0") + self.assertEqual(sep_cash.opening, aug_cash.closing, "September opening = August closing") + + finally: + jv.cancel() diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index c115204c28b..640ad3d9ad2 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -277,7 +277,21 @@ frappe.ui.form.on("Journal Entry", { var update_jv_details = function (doc, r) { $.each(r, function (i, d) { var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts"); - frappe.model.set_value(row.doctype, row.name, "account", d.account); + const { + idx, + name, + owner, + parent, + parenttype, + parentfield, + creation, + modified, + modified_by, + doctype, + docstatus, + ...fields + } = d; + frappe.model.set_value(row.doctype, row.name, fields); }); refresh_field("accounts"); }; diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 2465948c5ef..29e6c1fb638 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -9,6 +9,7 @@ "engine": "InnoDB", "field_order": [ "entry_type_and_date", + "company", "is_system_generated", "title", "voucher_type", @@ -17,7 +18,6 @@ "reversal_of", "column_break1", "from_template", - "company", "posting_date", "finance_book", "apply_tds", @@ -638,7 +638,7 @@ "table_fieldname": "payment_entries" } ], - "modified": "2025-11-13 17:54:14.542903", + "modified": "2026-02-03 14:40:39.944524", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index dd37c82af0e..6428896acae 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -74,8 +74,8 @@ class JournalEntry(AccountsController): mode_of_payment: DF.Link | None multi_currency: DF.Check naming_series: DF.Literal["ACC-JV-.YYYY.-"] - party_not_required: DF.Check override_tax_withholding_entries: DF.Check + party_not_required: DF.Check pay_to_recd_from: DF.Data | None payment_order: DF.Link | None periodic_entry_difference_account: DF.Link | None diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js index cfd6ccfe375..6ef2a80c068 100644 --- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js +++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js @@ -3,6 +3,7 @@ frappe.ui.form.on("Journal Entry Template", { onload: function (frm) { + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); if (frm.is_new()) { frappe.call({ type: "GET", @@ -37,6 +38,31 @@ frappe.ui.form.on("Journal Entry Template", { return { filters: filters }; }); + + frm.set_query("project", "accounts", function (doc, cdt, cdn) { + let row = frappe.get_doc(cdt, cdn); + let filters = { + company: doc.company, + }; + if (row.party_type == "Customer") { + filters.customer = row.party; + } + return { + query: "erpnext.controllers.queries.get_project_name", + filters, + }; + }); + + frm.set_query("party_type", "accounts", function (doc, cdt, cdn) { + const row = locals[cdt][cdn]; + + return { + query: "erpnext.setup.doctype.party_type.party_type.get_party_type", + filters: { + account: row.account, + }, + }; + }); }, voucher_type: function (frm) { var add_accounts = function (doc, r) { diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py index f87efc54e18..f86706774fc 100644 --- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py +++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py @@ -3,6 +3,7 @@ import frappe +from frappe import _ from frappe.model.document import Document @@ -42,7 +43,29 @@ class JournalEntryTemplate(Document): ] # end: auto-generated types - pass + def validate(self): + self.validate_party() + + def validate_party(self): + """ + Loop over all accounts and see if party and party type is set correctly + """ + for account in self.accounts: + if account.party_type: + account_type = frappe.get_cached_value("Account", account.account, "account_type") + if account_type not in ["Receivable", "Payable"]: + frappe.throw( + _( + "Check row {0} for account {1}: Party Type is only allowed for Receivable or Payable accounts" + ).format(account.idx, account.account) + ) + + if account.party and not account.party_type: + frappe.throw( + _("Check row {0} for account {1}: Party is only allowed if Party Type is set").format( + account.idx, account.account + ) + ) @frappe.whitelist() diff --git a/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json index e621fa2557f..0ec8b78884a 100644 --- a/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json +++ b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json @@ -5,7 +5,13 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "account" + "account", + "party_type", + "party", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project" ], "fields": [ { @@ -15,18 +21,55 @@ "label": "Account", "options": "Account", "reqd": 1 + }, + { + "fieldname": "party_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Party Type", + "options": "DocType" + }, + { + "fieldname": "party", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Party", + "options": "party_type" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "istable": 1, "links": [], - "modified": "2024-03-27 13:09:58.986448", + "modified": "2026-01-09 13:16:27.615083", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Template Account", "owner": "Administrator", "permissions": [], + "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/journal_entry_template_account/journal_entry_template_account.py b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py index 2426f577a1d..1ab32d34606 100644 --- a/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py +++ b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py @@ -16,9 +16,13 @@ class JournalEntryTemplateAccount(Document): from frappe.types import DF account: DF.Link + cost_center: DF.Link | None parent: DF.Data parentfield: DF.Data parenttype: DF.Data + party: DF.DynamicLink | None + party_type: DF.Link | None + project: DF.Link | None # end: auto-generated types pass diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py index 002c913a7a2..1f02ba7e0a7 100644 --- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py @@ -99,8 +99,7 @@ def get_customers_list(pos_profile=None): return ( frappe.db.sql( - f""" select name, customer_name, customer_group, - territory, customer_pos_id from tabCustomer where disabled = 0 + f""" select name, customer_name, customer_group, territory from tabCustomer where disabled = 0 and {cond}""", tuple(customer_groups), as_dict=1, diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 980eee2c48c..21cda420f5a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -8,12 +8,12 @@ "email_append_to": 1, "engine": "InnoDB", "field_order": [ + "company", "title", "naming_series", "supplier", "supplier_name", "tax_id", - "company", "column_break_6", "posting_date", "posting_time", @@ -606,6 +606,7 @@ }, { "default": "0", + "depends_on": "eval:doc.items.every((item) => !item.pr_detail)", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", @@ -1668,7 +1669,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2026-01-29 21:21:53.051193", + "modified": "2026-02-05 20:45:16.964500", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index b372da7f74a..b668e13e36c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,19 +1,18 @@ { "actions": [], "allow_import": 1, - "allow_rename": 1, "autoname": "naming_series:", "creation": "2022-01-25 10:29:57.771398", "doctype": "DocType", "engine": "InnoDB", "field_order": [ "customer_section", + "company", + "company_tax_id", "naming_series", "customer", "customer_name", "tax_id", - "company", - "company_tax_id", "column_break1", "posting_date", "posting_time", @@ -704,6 +703,7 @@ }, { "default": "0", + "depends_on": "eval:doc.items.every((item) => !item.dn_detail)", "fieldname": "update_stock", "fieldtype": "Check", "hide_days": 1, @@ -2306,7 +2306,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2026-01-30 16:45:59.682473", + "modified": "2026-02-06 20:43:44.732805", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 3606de08ca3..68f4b7800c1 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -232,11 +232,11 @@ def get_report_summary( def get_chart_data(filters, columns, asset, liability, equity, currency): - labels = [d.get("label") for d in columns[2:]] + labels = [d.get("label") for d in columns[4:]] asset_data, liability_data, equity_data = [], [], [] - for p in columns[2:]: + for p in columns[4:]: if asset: asset_data.append(asset[-2].get(p.get("fieldname"))) if liability: diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index d2fe570fa3b..55ab95ac662 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -5,15 +5,16 @@ from collections import OrderedDict import frappe from frappe import _, qb, scrub -from frappe.query_builder import Order +from frappe.query_builder import Case, Order +from frappe.query_builder.functions import Coalesce from frappe.utils import cint, flt, formatdate +from pypika.terms import ExistsCriterion from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, get_dimension_with_children, ) from erpnext.accounts.report.financial_statements import get_cost_centers_with_children -from erpnext.controllers.queries import get_match_cond from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition from erpnext.stock.utils import get_incoming_rate @@ -176,7 +177,9 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_ column_names = get_column_names() # to display item as Item Code: Item Name - columns[0] = "Sales Invoice:Link/Item:300" + columns[0]["fieldname"] = "sales_invoice" + columns[0]["options"] = "Item" + columns[0]["width"] = 300 # removing Item Code and Item Name columns supplier_master_name = frappe.db.get_single_value("Buying Settings", "supp_master_name") customer_master_name = frappe.db.get_single_value("Selling Settings", "cust_master_name") @@ -203,7 +206,11 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_ data.append(row) - total_gross_profit = total_base_amount - total_buying_amount + total_gross_profit = flt( + total_base_amount + abs(total_buying_amount) + if total_buying_amount < 0 + else total_base_amount - total_buying_amount, + ) data.append( frappe._dict( { @@ -215,7 +222,7 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_ "buying_amount": total_buying_amount, "gross_profit": total_gross_profit, "gross_profit_%": flt( - (total_gross_profit / total_base_amount) * 100.0, + (total_gross_profit / abs(total_base_amount)) * 100.0, cint(frappe.db.get_default("currency_precision")) or 3, ) if total_base_amount @@ -248,9 +255,13 @@ def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_ data.append(row) - total_gross_profit = total_base_amount - total_buying_amount + total_gross_profit = flt( + total_base_amount + abs(total_buying_amount) + if total_buying_amount < 0 + else total_base_amount - total_buying_amount, + ) currency_precision = cint(frappe.db.get_default("currency_precision")) or 3 - gross_profit_percent = (total_gross_profit / total_base_amount * 100.0) if total_base_amount else 0 + gross_profit_percent = (total_gross_profit / abs(total_base_amount) * 100.0) if total_base_amount else 0 total_row = { group_columns[0]: "Total", @@ -581,10 +592,15 @@ class GrossProfitGenerator: base_amount += row.base_amount # calculate gross profit - row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision) + row.gross_profit = flt( + row.base_amount + abs(row.buying_amount) + if row.buying_amount < 0 + else row.base_amount - row.buying_amount, + self.currency_precision, + ) if row.base_amount: row.gross_profit_percent = flt( - (row.gross_profit / row.base_amount) * 100.0, + (row.gross_profit / abs(row.base_amount)) * 100.0, self.currency_precision, ) else: @@ -673,9 +689,14 @@ class GrossProfitGenerator: return new_row def set_average_gross_profit(self, new_row): - new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision) + new_row.gross_profit = flt( + new_row.base_amount + abs(new_row.buying_amount) + if new_row.buying_amount < 0 + else new_row.base_amount - new_row.buying_amount, + self.currency_precision, + ) new_row.gross_profit_percent = ( - flt(((new_row.gross_profit / new_row.base_amount) * 100.0), self.currency_precision) + flt(((new_row.gross_profit / abs(new_row.base_amount)) * 100.0), self.currency_precision) if new_row.base_amount else 0 ) @@ -851,129 +872,173 @@ class GrossProfitGenerator: return flt(last_purchase_rate[0][0]) if last_purchase_rate else 0 def load_invoice_items(self): - conditions = "" - if self.filters.company: - conditions += " and `tabSales Invoice`.company = %(company)s" - if self.filters.from_date: - conditions += " and posting_date >= %(from_date)s" - if self.filters.to_date: - conditions += " and posting_date <= %(to_date)s" + self.si_list = [] + + SalesInvoice = frappe.qb.DocType("Sales Invoice") + base_query = self.prepare_invoice_query() if self.filters.include_returned_invoices: - conditions += " and (is_return = 0 or (is_return=1 and return_against is null))" + invoice_query = base_query.where( + (SalesInvoice.is_return == 0) + | ((SalesInvoice.is_return == 1) & SalesInvoice.return_against.isnull()) + ) else: - conditions += " and is_return = 0" + invoice_query = base_query.where(SalesInvoice.is_return == 0) - if self.filters.item_group: - conditions += f" and {get_item_group_condition(self.filters.item_group)}" + self.si_list += invoice_query.run(as_dict=True) + self.prepare_vouchers_to_ignore() - if self.filters.sales_person: - conditions += """ - and exists(select 1 - from `tabSales Team` st - where st.parent = `tabSales Invoice`.name - and st.sales_person = %(sales_person)s) - """ + ret_invoice_query = base_query.where( + (SalesInvoice.is_return == 1) & SalesInvoice.return_against.isnotnull() + ) + if self.vouchers_to_ignore: + ret_invoice_query = ret_invoice_query.where( + SalesInvoice.return_against.notin(self.vouchers_to_ignore) + ) + + self.si_list += ret_invoice_query.run(as_dict=True) + + def prepare_invoice_query(self): + SalesInvoice = frappe.qb.DocType("Sales Invoice") + SalesInvoiceItem = frappe.qb.DocType("Sales Invoice Item") + Item = frappe.qb.DocType("Item") + SalesTeam = frappe.qb.DocType("Sales Team") + PaymentSchedule = frappe.qb.DocType("Payment Schedule") + + query = ( + frappe.qb.from_(SalesInvoice) + .join(SalesInvoiceItem) + .on(SalesInvoiceItem.parent == SalesInvoice.name) + .join(Item) + .on(Item.name == SalesInvoiceItem.item_code) + .where((SalesInvoice.docstatus == 1) & (SalesInvoice.is_opening != "Yes")) + ) + + query = self.apply_common_filters(query, SalesInvoice, SalesInvoiceItem, SalesTeam, Item) + + query = query.select( + SalesInvoiceItem.parenttype, + SalesInvoiceItem.parent, + SalesInvoice.posting_date, + SalesInvoice.posting_time, + SalesInvoice.project, + SalesInvoice.update_stock, + SalesInvoice.customer, + SalesInvoice.customer_group, + SalesInvoice.customer_name, + SalesInvoice.territory, + SalesInvoiceItem.item_code, + SalesInvoice.base_net_total.as_("invoice_base_net_total"), + SalesInvoiceItem.item_name, + SalesInvoiceItem.description, + SalesInvoiceItem.warehouse, + SalesInvoiceItem.item_group, + SalesInvoiceItem.brand, + SalesInvoiceItem.so_detail, + SalesInvoiceItem.sales_order, + SalesInvoiceItem.dn_detail, + SalesInvoiceItem.delivery_note, + SalesInvoiceItem.stock_qty.as_("qty"), + SalesInvoiceItem.base_net_rate, + SalesInvoiceItem.base_net_amount, + SalesInvoiceItem.name.as_("item_row"), + SalesInvoice.is_return, + SalesInvoiceItem.cost_center, + SalesInvoiceItem.serial_and_batch_bundle, + ) if self.filters.group_by == "Sales Person": - sales_person_cols = """, sales.sales_person, - sales.allocated_percentage * `tabSales Invoice Item`.base_net_amount / 100 as allocated_amount, - sales.incentives - """ - sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name" - else: - sales_person_cols = "" - sales_team_table = "" + query = query.select( + SalesTeam.sales_person, + (SalesTeam.allocated_percentage * SalesInvoiceItem.base_net_amount / 100).as_( + "allocated_amount" + ), + SalesTeam.incentives, + ) + + query = query.left_join(SalesTeam).on(SalesTeam.parent == SalesInvoice.name) if self.filters.group_by == "Payment Term": - payment_term_cols = """,if(`tabSales Invoice`.is_return = 1, - '{}', - coalesce(schedule.payment_term, '{}')) as payment_term, - schedule.invoice_portion, - schedule.payment_amount """.format(_("Sales Return"), _("No Terms")) - payment_term_table = """ left join `tabPayment Schedule` schedule on schedule.parent = `tabSales Invoice`.name and - `tabSales Invoice`.is_return = 0 """ - else: - payment_term_cols = "" - payment_term_table = "" + query = query.select( + Case() + .when(SalesInvoice.is_return == 1, _("Sales Return")) + .else_(Coalesce(PaymentSchedule.payment_term, _("No Terms"))) + .as_("payment_term"), + PaymentSchedule.invoice_portion, + PaymentSchedule.payment_amount, + ) - if self.filters.get("sales_invoice"): - conditions += " and `tabSales Invoice`.name = %(sales_invoice)s" + query = query.left_join(PaymentSchedule).on( + (PaymentSchedule.parent == SalesInvoice.name) & (SalesInvoice.is_return == 0) + ) - if self.filters.get("item_code"): - conditions += " and `tabSales Invoice Item`.item_code = %(item_code)s" + query = query.orderby(SalesInvoice.posting_date, order=Order.desc).orderby( + SalesInvoice.posting_time, order=Order.desc + ) - if self.filters.get("cost_center"): + return query + + def apply_common_filters(self, query, SalesInvoice, SalesInvoiceItem, SalesTeam, Item): + if self.filters.company: + query = query.where(SalesInvoice.company == self.filters.company) + + if self.filters.from_date: + query = query.where(SalesInvoice.posting_date >= self.filters.from_date) + + if self.filters.to_date: + query = query.where(SalesInvoice.posting_date <= self.filters.to_date) + + if self.filters.item_group: + query = query.where(get_item_group_condition(self.filters.item_group, Item)) + + if self.filters.sales_person: + query = query.where( + ExistsCriterion( + frappe.qb.from_(SalesTeam) + .select(1) + .where( + (SalesTeam.parent == SalesInvoice.name) + & (SalesTeam.sales_person == self.filters.sales_person) + ) + ) + ) + + if self.filters.sales_invoice: + query = query.where(SalesInvoice.name == self.filters.sales_invoice) + + if self.filters.item_code: + query = query.where(SalesInvoiceItem.item_code == self.filters.item_code) + + if self.filters.cost_center: self.filters.cost_center = frappe.parse_json(self.filters.get("cost_center")) self.filters.cost_center = get_cost_centers_with_children(self.filters.cost_center) - conditions += " and `tabSales Invoice Item`.cost_center in %(cost_center)s" + query = query.where(SalesInvoiceItem.cost_center.isin(self.filters.cost_center)) - if self.filters.get("project"): + if self.filters.project: self.filters.project = frappe.parse_json(self.filters.get("project")) - conditions += " and `tabSales Invoice Item`.project in %(project)s" + query = query.where(SalesInvoiceItem.project.isin(self.filters.project)) - accounting_dimensions = get_accounting_dimensions(as_list=False) - if accounting_dimensions: - for dimension in accounting_dimensions: - if self.filters.get(dimension.fieldname): - if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"): - self.filters[dimension.fieldname] = get_dimension_with_children( - dimension.document_type, self.filters.get(dimension.fieldname) - ) - conditions += ( - f" and `tabSales Invoice Item`.{dimension.fieldname} in %({dimension.fieldname})s" - ) - else: - conditions += ( - f" and `tabSales Invoice Item`.{dimension.fieldname} in %({dimension.fieldname})s" - ) + for dim in get_accounting_dimensions(as_list=False) or []: + if self.filters.get(dim.fieldname): + if frappe.get_cached_value("DocType", dim.document_type, "is_tree"): + self.filters[dim.fieldname] = get_dimension_with_children( + dim.document_type, self.filters.get(dim.fieldname) + ) + query = query.where(SalesInvoiceItem[dim.fieldname].isin(self.filters[dim.fieldname])) - if self.filters.get("warehouse"): - warehouse_details = frappe.db.get_value( - "Warehouse", self.filters.get("warehouse"), ["lft", "rgt"], as_dict=1 + if self.filters.warehouse: + lft, rgt = frappe.db.get_value("Warehouse", self.filters.warehouse, ["lft", "rgt"]) + WH = frappe.qb.DocType("Warehouse") + query = query.where( + SalesInvoiceItem.warehouse.isin( + frappe.qb.from_(WH).select(WH.name).where((WH.lft >= lft) & (WH.rgt <= rgt)) + ) ) - if warehouse_details: - conditions += f" and `tabSales Invoice Item`.warehouse in (select name from `tabWarehouse` wh where wh.lft >= {warehouse_details.lft} and wh.rgt <= {warehouse_details.rgt} and warehouse = wh.name)" - self.si_list = frappe.db.sql( - """ - select - `tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent, - `tabSales Invoice`.posting_date, `tabSales Invoice`.posting_time, - `tabSales Invoice`.project, `tabSales Invoice`.update_stock, - `tabSales Invoice`.customer, `tabSales Invoice`.customer_group, `tabSales Invoice`.customer_name, - `tabSales Invoice`.territory, `tabSales Invoice Item`.item_code, - `tabSales Invoice`.base_net_total as "invoice_base_net_total", - `tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description, - `tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group, - `tabSales Invoice Item`.brand, `tabSales Invoice Item`.so_detail, - `tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.dn_detail, - `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty, - `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, - `tabSales Invoice Item`.name as "item_row", `tabSales Invoice`.is_return, - `tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.serial_and_batch_bundle - {sales_person_cols} - {payment_term_cols} - from - `tabSales Invoice` inner join `tabSales Invoice Item` - on `tabSales Invoice Item`.parent = `tabSales Invoice`.name - join `tabItem` item on item.name = `tabSales Invoice Item`.item_code - {sales_team_table} - {payment_term_table} - where - `tabSales Invoice`.docstatus=1 and `tabSales Invoice`.is_opening!='Yes' {conditions} {match_cond} - order by - `tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc""".format( - conditions=conditions, - sales_person_cols=sales_person_cols, - sales_team_table=sales_team_table, - payment_term_cols=payment_term_cols, - payment_term_table=payment_term_table, - match_cond=get_match_cond("Sales Invoice"), - ), - self.filters, - as_dict=1, - ) + return query + + def prepare_vouchers_to_ignore(self): + self.vouchers_to_ignore = tuple(row["parent"] for row in self.si_list) def get_delivery_notes(self): self.delivery_notes = frappe._dict({}) diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index 116cdca4d6c..159c1086018 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -470,7 +470,7 @@ class TestGrossProfit(IntegrationTestCase): "selling_amount": -100.0, "buying_amount": 0.0, "gross_profit": -100.0, - "gross_profit_%": 100.0, + "gross_profit_%": -100.0, } gp_entry = [x for x in data if x.parent_invoice == sinv.name] report_output = {k: v for k, v in gp_entry[0].items() if k in expected_entry} @@ -649,21 +649,24 @@ class TestGrossProfit(IntegrationTestCase): def test_profit_for_later_period_return(self): month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) + sales_inv_date = month_start_date + return_inv_date = add_days(month_end_date, 1) + # create sales invoice on month start date sinv = self.create_sales_invoice(qty=1, rate=100, do_not_save=True, do_not_submit=True) sinv.set_posting_time = 1 - sinv.posting_date = month_start_date + sinv.posting_date = sales_inv_date sinv.save().submit() # create credit note on next month start date cr_note = make_sales_return(sinv.name) cr_note.set_posting_time = 1 - cr_note.posting_date = add_days(month_end_date, 1) + cr_note.posting_date = return_inv_date cr_note.save().submit() # apply filters for invoiced period filters = frappe._dict( - company=self.company, from_date=month_start_date, to_date=month_end_date, group_by="Invoice" + company=self.company, from_date=month_start_date, to_date=month_start_date, group_by="Invoice" ) _, data = execute(filters=filters) @@ -675,7 +678,7 @@ class TestGrossProfit(IntegrationTestCase): self.assertEqual(total.get("gross_profit_%"), 100.0) # extend filters upto returned period - filters.update(to_date=add_days(month_end_date, 1)) + filters.update({"to_date": return_inv_date}) _, data = execute(filters=filters) total = data[-1] @@ -684,3 +687,63 @@ class TestGrossProfit(IntegrationTestCase): self.assertEqual(total.buying_amount, 0.0) self.assertEqual(total.gross_profit, 0.0) self.assertEqual(total.get("gross_profit_%"), 0.0) + + # apply filters only on returned period + filters.update({"from_date": return_inv_date, "to_date": return_inv_date}) + _, data = execute(filters=filters) + total = data[-1] + + self.assertEqual(total.selling_amount, -100.0) + self.assertEqual(total.buying_amount, 0.0) + self.assertEqual(total.gross_profit, -100.0) + self.assertEqual(total.get("gross_profit_%"), -100.0) + + def test_sales_person_wise_gross_profit(self): + sales_person = make_sales_person("_Test Sales Person") + + posting_date = get_first_day(nowdate()) + qty = 10 + rate = 100 + + sinv = self.create_sales_invoice(qty=qty, rate=rate, do_not_save=True, do_not_submit=True) + sinv.set_posting_time = 1 + sinv.posting_date = posting_date + sinv.append( + "sales_team", + { + "sales_person": sales_person.name, + "allocated_percentage": 100, + "allocated_amount": 1000.0, + "commission_rate": 5, + "incentives": 5, + }, + ) + sinv.save().submit() + + filters = frappe._dict( + company=self.company, from_date=posting_date, to_date=posting_date, group_by="Sales Person" + ) + + _, data = execute(filters=filters) + total = data[-1] + + self.assertEqual(total[5], 1000.0) + self.assertEqual(total[6], 0.0) + self.assertEqual(total[7], 1000.0) + self.assertEqual(total[8], 100.0) + + +def make_sales_person(sales_person_name="_Test Sales Person"): + if not frappe.db.exists("Sales Person", {"sales_person_name": sales_person_name}): + sales_person_doc = frappe.get_doc( + { + "doctype": "Sales Person", + "is_group": 0, + "parent_sales_person": "Sales Team", + "sales_person_name": sales_person_name, + } + ).insert(ignore_permissions=True) + else: + sales_person_doc = frappe.get_doc("Sales Person", {"sales_person_name": sales_person_name}) + + return sales_person_doc diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index e485dac114b..ed4a223311b 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -154,17 +154,11 @@ def get_columns(filters): "width": 60, }, { - "label": _("Total Amount"), + "label": _("Taxable Amount"), "fieldname": "total_amount", "fieldtype": "Currency", "width": 120, }, - { - "label": _("Base Total"), - "fieldname": "base_total", - "fieldtype": "Currency", - "width": 120, - }, { "label": _("Tax Amount"), "fieldname": "tax_amount", @@ -172,10 +166,16 @@ def get_columns(filters): "width": 120, }, { - "label": _("Grand Total"), + "label": _("Grand Total (Company Currency)"), + "fieldname": "base_total", + "fieldtype": "Currency", + "width": 150, + }, + { + "label": _("Grand Total (Transaction Currency)"), "fieldname": "grand_total", "fieldtype": "Currency", - "width": 120, + "width": 170, }, { "label": _("Reference Date"), diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py index 49bf556da66..59296602b3d 100644 --- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py @@ -106,7 +106,7 @@ def get_columns(filters): "width": 120, }, { - "label": _("Total Amount"), + "label": _("Total Taxable Amount"), "fieldname": "total_amount", "fieldtype": "Float", "width": 120, diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 1c5c483bac4..1397b23f39b 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -513,12 +513,14 @@ frappe.ui.form.on("Asset", { }, is_composite_asset: function (frm) { - if (frm.doc.is_composite_asset) { - frm.set_value("net_purchase_amount", 0); - } else { - frm.set_df_property("net_purchase_amount", "read_only", 0); + if (frm.doc.docstatus == 0) { + if (frm.doc.is_composite_asset) { + frm.set_value("net_purchase_amount", 0); + } else { + frm.set_df_property("net_purchase_amount", "read_only", 0); + } + frm.trigger("toggle_reference_doc"); } - frm.trigger("toggle_reference_doc"); }, make_sales_invoice: function (frm) { diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index 25a06e463bb..c3248563440 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cstr, get_link_to_form +from frappe.utils import cstr, get_datetime, get_link_to_form from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activity @@ -34,6 +34,7 @@ class AssetMovement(Document): for d in self.assets: self.validate_asset(d) self.validate_movement(d) + self.validate_transaction_date(d) def validate_asset(self, d): status, company = frappe.db.get_value("Asset", d.asset, ["status", "company"]) @@ -51,6 +52,18 @@ class AssetMovement(Document): else: self.validate_employee(d) + def validate_transaction_date(self, d): + previous_movement_date = frappe.db.get_value( + "Asset Movement", + [["Asset Movement Item", "asset", "=", d.asset], ["docstatus", "=", 1]], + "transaction_date", + order_by="transaction_date desc", + ) + if previous_movement_date and get_datetime(previous_movement_date) > get_datetime( + self.transaction_date + ): + frappe.throw(_("Transaction date can't be earlier than previous movement date")) + def validate_location_and_employee(self, d): self.validate_location(d) self.validate_employee(d) diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py index 620ea434e24..0cceb011a25 100644 --- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py @@ -4,9 +4,9 @@ import unittest import frappe from frappe.tests import IntegrationTestCase -from frappe.utils import now +from frappe.utils import add_days, now -from erpnext.assets.doctype.asset.test_asset import create_asset_data +from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data from erpnext.setup.doctype.employee.test_employee import make_employee from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt @@ -147,6 +147,33 @@ class TestAssetMovement(IntegrationTestCase): movement1.cancel() self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") + def test_movement_transaction_date(self): + asset = create_asset(item_code="Macbook Pro", do_not_save=1) + asset.save().submit() + + if not frappe.db.exists("Location", "Test Location 2"): + frappe.get_doc({"doctype": "Location", "location_name": "Test Location 2"}).insert() + + asset_creation_date = frappe.db.get_value( + "Asset Movement", + [["Asset Movement Item", "asset", "=", asset.name], ["docstatus", "=", 1]], + "transaction_date", + ) + asset_movement = create_asset_movement( + purpose="Transfer", + company=asset.company, + assets=[ + { + "asset": asset.name, + "source_location": "Test Location", + "target_location": "Test Location 2", + } + ], + transaction_date=add_days(asset_creation_date, -1), + do_not_save=True, + ) + self.assertRaises(frappe.ValidationError, asset_movement.save) + def create_asset_movement(**args): args = frappe._dict(args) @@ -165,9 +192,10 @@ def create_asset_movement(**args): "reference_name": args.reference_name, } ) - - movement.insert() - movement.submit() + if not args.do_not_save: + movement.insert(ignore_if_duplicate=True) + if not args.do_not_submit: + movement.submit() return movement diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 05d03d47676..25a31ea698d 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -9,10 +9,9 @@ "engine": "InnoDB", "field_order": [ "supplier_section", + "company", "title", "naming_series", - "supplier", - "supplier_name", "order_confirmation_no", "order_confirmation_date", "get_items_from_open_material_requests", @@ -21,8 +20,9 @@ "transaction_date", "schedule_date", "column_break1", - "company", + "supplier", "is_subcontracted", + "supplier_name", "has_unit_price_items", "supplier_warehouse", "amended_from", @@ -1310,7 +1310,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2026-01-29 21:22:54.323838", + "modified": "2026-02-03 14:44:55.192192", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 4b46ddc3d80..fb4ef867ade 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -135,14 +135,6 @@ frappe.ui.form.on("Supplier", { // indicators erpnext.utils.set_party_dashboard_indicators(frm); } - - frm.set_query("supplier_group", () => { - return { - filters: { - is_group: 0, - }, - }; - }); }, get_supplier_group_details: function (frm) { frappe.call({ diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index a801a2a601d..78b797d4c2b 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -167,6 +167,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Supplier Group", + "link_filters": "[[\"Supplier Group\",\"is_group\",\"=\",0]]", "oldfieldname": "supplier_type", "oldfieldtype": "Link", "options": "Supplier Group" @@ -500,7 +501,7 @@ "link_fieldname": "party" } ], - "modified": "2026-01-16 15:56:31.139206", + "modified": "2026-02-06 12:58:01.398824", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py index a5a2132dc0c..9e24a26caa8 100644 --- a/erpnext/crm/doctype/email_campaign/email_campaign.py +++ b/erpnext/crm/doctype/email_campaign/email_campaign.py @@ -38,18 +38,18 @@ class EmailCampaign(Document): def set_date(self): if getdate(self.start_date) < getdate(today()): frappe.throw(_("Start Date cannot be before the current date")) + # set the end date as start date + max(send after days) in campaign schedule - send_after_days = [] - campaign = frappe.get_doc("Campaign", self.campaign_name) - for entry in campaign.get("campaign_schedules"): - send_after_days.append(entry.send_after_days) - try: - self.end_date = add_days(getdate(self.start_date), max(send_after_days)) - except ValueError: + campaign = frappe.get_cached_doc("Campaign", self.campaign_name) + send_after_days = [entry.send_after_days for entry in campaign.get("campaign_schedules")] + + if not send_after_days: frappe.throw( _("Please set up the Campaign Schedule in the Campaign {0}").format(self.campaign_name) ) + self.end_date = add_days(getdate(self.start_date), max(send_after_days)) + def validate_lead(self): lead_email_id = frappe.db.get_value("Lead", self.recipient, "email_id") if not lead_email_id: @@ -77,58 +77,128 @@ class EmailCampaign(Document): start_date = getdate(self.start_date) end_date = getdate(self.end_date) today_date = getdate(today()) + if start_date > today_date: - self.db_set("status", "Scheduled", update_modified=False) + new_status = "Scheduled" elif end_date >= today_date: - self.db_set("status", "In Progress", update_modified=False) - elif end_date < today_date: - self.db_set("status", "Completed", update_modified=False) + new_status = "In Progress" + else: + new_status = "Completed" + + if self.status != new_status: + self.db_set("status", new_status, update_modified=False) # called through hooks to send campaign mails to leads def send_email_to_leads_or_contacts(): + today_date = getdate(today()) + + # Get all active email campaigns in a single query email_campaigns = frappe.get_all( - "Email Campaign", filters={"status": ("not in", ["Unsubscribed", "Completed", "Scheduled"])} + "Email Campaign", + filters={"status": "In Progress"}, + fields=["name", "campaign_name", "email_campaign_for", "recipient", "start_date", "sender"], ) - for camp in email_campaigns: - email_campaign = frappe.get_doc("Email Campaign", camp.name) - campaign = frappe.get_cached_doc("Campaign", email_campaign.campaign_name) + + if not email_campaigns: + return + + # Process each email campaign + for email_campaign in email_campaigns: + try: + campaign = frappe.get_cached_doc("Campaign", email_campaign.campaign_name) + except frappe.DoesNotExistError: + frappe.log_error( + title=_("Email Campaign Error"), + message=_("Campaign {0} not found").format(email_campaign.campaign_name), + ) + continue + + # Find schedules that match today for entry in campaign.get("campaign_schedules"): - scheduled_date = add_days(email_campaign.get("start_date"), entry.get("send_after_days")) - if scheduled_date == getdate(today()): - send_mail(entry, email_campaign) + try: + scheduled_date = add_days(getdate(email_campaign.start_date), entry.get("send_after_days")) + if scheduled_date == today_date: + send_mail(entry, email_campaign) + except Exception: + frappe.log_error( + title=_("Email Campaign Send Error"), + message=_("Failed to send email for campaign {0} to {1}").format( + email_campaign.name, email_campaign.recipient + ), + ) def send_mail(entry, email_campaign): - recipient_list = [] - if email_campaign.email_campaign_for == "Email Group": - for member in frappe.db.get_list( - "Email Group Member", filters={"email_group": email_campaign.get("recipient")}, fields=["email"] - ): - recipient_list.append(member["email"]) + campaign_for = email_campaign.get("email_campaign_for") + recipient = email_campaign.get("recipient") + sender_user = email_campaign.get("sender") + campaign_name = email_campaign.get("name") + + # Get recipient emails + if campaign_for == "Email Group": + recipient_list = frappe.get_all( + "Email Group Member", + filters={"email_group": recipient, "unsubscribed": 0}, + pluck="email", + ) else: - recipient_list.append( - frappe.db.get_value( - email_campaign.email_campaign_for, email_campaign.get("recipient"), "email_id" + email_id = frappe.db.get_value(campaign_for, recipient, "email_id") + if not email_id: + frappe.log_error( + title=_("Email Campaign Error"), + message=_("No email found for {0} {1}").format(campaign_for, recipient), ) + return + recipient_list = [email_id] + + if not recipient_list: + frappe.log_error( + title=_("Email Campaign Error"), + message=_("No recipients found for campaign {0}").format(campaign_name), + ) + return + + # Get email template and sender + email_template = frappe.get_cached_doc("Email Template", entry.get("email_template")) + sender = frappe.db.get_value("User", sender_user, "email") if sender_user else None + + # Build context for template rendering + if campaign_for != "Email Group": + context = {"doc": frappe.get_doc(campaign_for, recipient)} + else: + # For email groups, use the email group document as context + context = {"doc": frappe.get_doc("Email Group", recipient)} + + # Render template + subject = frappe.render_template(email_template.get("subject"), context) + content = frappe.render_template(email_template.response_, context) + + try: + comm = make( + doctype="Email Campaign", + name=campaign_name, + subject=subject, + content=content, + sender=sender, + recipients=recipient_list, + communication_medium="Email", + sent_or_received="Sent", + send_email=False, + email_template=email_template.name, ) - email_template = frappe.get_doc("Email Template", entry.get("email_template")) - sender = frappe.db.get_value("User", email_campaign.get("sender"), "email") - context = {"doc": frappe.get_doc(email_campaign.email_campaign_for, email_campaign.recipient)} - # send mail and link communication to document - comm = make( - doctype="Email Campaign", - name=email_campaign.name, - subject=frappe.render_template(email_template.get("subject"), context), - content=frappe.render_template(email_template.response_, context), - sender=sender, - bcc=recipient_list, - communication_medium="Email", - sent_or_received="Sent", - send_email=True, - email_template=email_template.name, - ) + frappe.sendmail( + recipients=recipient_list, + subject=subject, + content=content, + sender=sender, + communication=comm["name"], + queue_separately=True, + ) + except Exception: + frappe.log_error(title="Email Campaign Failed.") + return comm @@ -140,7 +210,12 @@ def unsubscribe_recipient(unsubscribe, method): # called through hooks to update email campaign status daily def set_email_campaign_status(): - email_campaigns = frappe.get_all("Email Campaign", filters={"status": ("!=", "Unsubscribed")}) - for entry in email_campaigns: - email_campaign = frappe.get_doc("Email Campaign", entry.name) + email_campaigns = frappe.get_all( + "Email Campaign", + filters={"status": ("!=", "Unsubscribed")}, + pluck="name", + ) + + for name in email_campaigns: + email_campaign = frappe.get_doc("Email Campaign", name) email_campaign.update_status() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 9440459fef3..bdbe2292139 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -534,6 +534,7 @@ accounting_dimension_doctypes = [ "Purchase Order Item", "Sales Order Item", "Journal Entry Account", + "Journal Entry Template Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", diff --git a/erpnext/locale/main.pot b/erpnext/locale/main.pot index 4a738fff71d..eab1b559dde 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-02-01 09:43+0000\n" -"PO-Revision-Date: 2026-02-01 09:43+0000\n" +"POT-Creation-Date: 2026-02-08 09:43+0000\n" +"PO-Revision-Date: 2026-02-08 09:43+0000\n" "Last-Translator: hello@frappe.io\n" "Language-Team: hello@frappe.io\n" "MIME-Version: 1.0\n" @@ -16,10 +16,10 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.16.0\n" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1456 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1466 msgid "" "\n" -"\t\t\t\t\tThe Batch {0} of an item {1} has negative stock in the warehouse {2}. Please add a stock quantity of {3} to proceed with this entry." +"\t\t\tThe Batch {0} of an item {1} has negative stock in the warehouse {2}. Please add a stock quantity of {3} to proceed with this entry." msgstr "" #. Label of the column_break_32 (Column Break) field in DocType 'Email Digest' @@ -153,7 +153,7 @@ msgstr "" msgid "% Delivered" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:989 +#: erpnext/manufacturing/doctype/bom/bom.js:992 #, python-format msgid "% Finished Item Quantity" msgstr "" @@ -999,14 +999,10 @@ msgstr "" msgid "A Reconciliation Job {0} is running for the same filters. Cannot reconcile now" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1750 +#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1754 msgid "A Reverse Journal Entry {0} already exists for this Journal Entry." msgstr "" -#: erpnext/setup/doctype/company/company.py:1097 -msgid "A Transaction Deletion Document: {0} is triggered for {0}" -msgstr "" - #. Description of a DocType #: erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.json msgid "A condition for a Shipping Rule" @@ -1070,6 +1066,10 @@ msgstr "" msgid "ACC-PINV-.YYYY.-" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:88 +msgid "ALL records will be deleted (entire DocType cleared)" +msgstr "" + #: erpnext/stock/report/serial_no_and_batch_traceability/serial_no_and_batch_traceability.py:552 msgid "AMC Expiry (Serial)" msgstr "" @@ -1164,7 +1164,7 @@ msgstr "" #. Label of the qty (Float) field in DocType 'Purchase Receipt Item' #. Label of the qty (Float) field in DocType 'Subcontracting Receipt Item' -#: erpnext/public/js/controllers/transaction.js:2814 +#: erpnext/public/js/controllers/transaction.js:2807 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json #: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json msgid "Accepted Quantity" @@ -1197,7 +1197,7 @@ msgstr "" msgid "According to CEFACT/ICG/2010/IC013 or CEFACT/ICG/2010/IC010" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:988 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:989 msgid "According to the BOM {0}, the Item '{1}' is missing in the stock entry." msgstr "" @@ -1659,6 +1659,8 @@ msgstr "" #. Label of the accounting_dimensions_section (Section Break) field in DocType #. 'Journal Entry Account' #. Label of the accounting_dimensions_section (Section Break) field in DocType +#. 'Journal Entry Template Account' +#. Label of the accounting_dimensions_section (Section Break) field in DocType #. 'Loyalty Program' #. Label of the accounting_dimensions_section (Section Break) field in DocType #. 'Opening Invoice Creation Tool' @@ -1744,6 +1746,7 @@ msgstr "" #. 'Subcontracting Receipt Supplied Item' #: erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +#: erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json #: erpnext/accounts/doctype/loyalty_program/loyalty_program.json #: erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.json #: erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json @@ -1824,8 +1827,8 @@ msgstr "" msgid "Accounting Entry for Asset" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1959 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1979 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1960 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1980 msgid "Accounting Entry for LCV in Stock Entry {0}" msgstr "" @@ -1849,8 +1852,8 @@ msgstr "" #: erpnext/controllers/stock_controller.py:686 #: erpnext/controllers/stock_controller.py:703 #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:930 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1904 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1918 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1905 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1919 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py:708 msgid "Accounting Entry for Stock" msgstr "" @@ -2550,7 +2553,7 @@ msgid "Add Quote" msgstr "" #. Label of the add_raw_materials (Button) field in DocType 'BOM Operation' -#: erpnext/manufacturing/doctype/bom/bom.js:1017 +#: erpnext/manufacturing/doctype/bom/bom.js:1020 #: erpnext/manufacturing/doctype/bom_operation/bom_operation.json msgid "Add Raw Materials" msgstr "" @@ -2925,7 +2928,7 @@ msgstr "" msgid "Additional Transferred Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:666 +#: erpnext/manufacturing/doctype/work_order/work_order.py:676 msgid "" "Additional Transferred Qty {0}\n" "\t\t\t\t\tcannot be greater than {1}.\n" @@ -3088,7 +3091,7 @@ msgstr "" msgid "Adjust Qty" msgstr "" -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1129 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1130 msgid "Adjustment Against" msgstr "" @@ -3544,7 +3547,7 @@ msgstr "" msgid "All Activities HTML" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:318 +#: erpnext/manufacturing/doctype/bom/bom.py:369 msgid "All BOMs" msgstr "" @@ -3685,15 +3688,15 @@ msgstr "" msgid "All items have already been Invoiced/Returned" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:1208 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:1216 msgid "All items have already been received" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3110 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3112 msgid "All items have already been transferred for this Work Order." msgstr "" -#: erpnext/public/js/controllers/transaction.js:2922 +#: erpnext/public/js/controllers/transaction.js:2915 msgid "All items in this document already have a linked Quality Inspection." msgstr "" @@ -3719,7 +3722,7 @@ msgstr "" msgid "All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table." msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:858 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:866 msgid "All these items have already been Invoiced/Returned" msgstr "" @@ -3906,7 +3909,7 @@ msgstr "" msgid "Allow Item To Be Added Multiple Times in a Transaction" msgstr "" -#: erpnext/controllers/selling_controller.py:853 +#: erpnext/controllers/selling_controller.py:847 msgid "Allow Item to Be Added Multiple Times in a Transaction" msgstr "" @@ -4406,7 +4409,7 @@ msgstr "" #: erpnext/accounts/doctype/bank_guarantee/bank_guarantee.json #: erpnext/accounts/doctype/budget_distribution/budget_distribution.json #: erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.json -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:613 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:627 #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/payment_entry/payment_entry.json #: erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -5728,7 +5731,7 @@ msgstr "" msgid "At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} in the warehouse {4}." msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1350 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1355 msgid "At Row {0}: In Serial and Batch Bundle {1} must have docstatus as 1 and not 0" msgstr "" @@ -5785,7 +5788,7 @@ msgstr "" msgid "At row #{0}: you have selected the Difference Account {1}, which is a Cost of Goods Sold type account. Please select a different account" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1112 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1117 msgid "At row {0}: Batch No is mandatory for Item {1}" msgstr "" @@ -5793,11 +5796,11 @@ msgstr "" msgid "At row {0}: Parent Row No cannot be set for item {1}" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1097 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1102 msgid "At row {0}: Qty is mandatory for the batch {1}" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1104 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1109 msgid "At row {0}: Serial No is mandatory for Item {1}" msgstr "" @@ -6283,7 +6286,7 @@ msgstr "" #: erpnext/stock/report/stock_ageing/stock_ageing.py:170 #: erpnext/stock/report/stock_ageing/stock_ageing.py:204 -#: erpnext/stock/report/stock_balance/stock_balance.py:515 +#: erpnext/stock/report/stock_balance/stock_balance.py:517 msgid "Average Age" msgstr "" @@ -6398,7 +6401,7 @@ msgstr "" #: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:109 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json #: erpnext/selling/doctype/sales_order/sales_order.js:1415 -#: erpnext/stock/doctype/material_request/material_request.js:339 +#: erpnext/stock/doctype/material_request/material_request.js:351 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json #: erpnext/stock/doctype/stock_entry/stock_entry.js:689 #: erpnext/stock/report/bom_search/bom_search.py:38 @@ -6413,7 +6416,7 @@ msgstr "" msgid "BOM 1" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1700 +#: erpnext/manufacturing/doctype/bom/bom.py:1751 msgid "BOM 1 {0} and BOM 2 {1} should not be same" msgstr "" @@ -6639,7 +6642,7 @@ msgstr "" msgid "BOM Website Operation" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2278 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2280 msgid "BOM and Finished Good Quantity is mandatory for Disassembly" msgstr "" @@ -6653,7 +6656,7 @@ msgstr "" msgid "BOM and Production" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:374 +#: erpnext/stock/doctype/material_request/material_request.js:386 #: erpnext/stock/doctype/stock_entry/stock_entry.js:741 msgid "BOM does not contain any stock item" msgstr "" @@ -6662,23 +6665,23 @@ msgstr "" msgid "BOM recursion: {0} cannot be child of {1}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:700 +#: erpnext/manufacturing/doctype/bom/bom.py:751 msgid "BOM recursion: {1} cannot be parent or child of {0}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1435 +#: erpnext/manufacturing/doctype/bom/bom.py:1486 msgid "BOM {0} does not belong to Item {1}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1417 +#: erpnext/manufacturing/doctype/bom/bom.py:1468 msgid "BOM {0} must be active" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1420 +#: erpnext/manufacturing/doctype/bom/bom.py:1471 msgid "BOM {0} must be submitted" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:788 +#: erpnext/manufacturing/doctype/bom/bom.py:839 msgid "BOM {0} not found for the item {1}" msgstr "" @@ -6768,7 +6771,7 @@ msgstr "" #: erpnext/stock/report/available_batch_report/available_batch_report.py:63 #: erpnext/stock/report/available_serial_no/available_serial_no.py:126 #: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:84 -#: erpnext/stock/report/stock_balance/stock_balance.py:443 +#: erpnext/stock/report/stock_balance/stock_balance.py:445 #: erpnext/stock/report/stock_ledger/stock_ledger.py:251 msgid "Balance Qty" msgstr "" @@ -6831,7 +6834,7 @@ msgstr "" #: erpnext/stock/report/available_serial_no/available_serial_no.py:174 #: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:86 -#: erpnext/stock/report/stock_balance/stock_balance.py:450 +#: erpnext/stock/report/stock_balance/stock_balance.py:452 #: erpnext/stock/report/stock_ledger/stock_ledger.py:308 msgid "Balance Value" msgstr "" @@ -7383,7 +7386,7 @@ 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:2840 +#: erpnext/public/js/controllers/transaction.js:2833 #: erpnext/public/js/utils/barcode_scanner.js:281 #: erpnext/public/js/utils/serial_no_batch_selector.js:438 #: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -7412,11 +7415,11 @@ msgstr "" msgid "Batch No" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1115 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1120 msgid "Batch No is mandatory" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3240 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3256 msgid "Batch No {0} does not exists" msgstr "" @@ -7424,7 +7427,7 @@ msgstr "" msgid "Batch No {0} is linked with Item {1} which has serial no. Please scan serial no instead." msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:436 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:437 msgid "Batch No {0} is not present in the original {1} {2}, hence you can't return it against the {1} {2}" msgstr "" @@ -7439,11 +7442,11 @@ msgstr "" msgid "Batch Nos" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1834 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1850 msgid "Batch Nos are created successfully" msgstr "" -#: erpnext/controllers/sales_and_purchase_return.py:1165 +#: erpnext/controllers/sales_and_purchase_return.py:1187 msgid "Batch Not Available for Return" msgstr "" @@ -7493,7 +7496,7 @@ msgstr "" msgid "Batch and Serial No" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:886 +#: erpnext/manufacturing/doctype/work_order/work_order.py:896 msgid "Batch not created for item {} since it does not have a batch series." msgstr "" @@ -7501,16 +7504,16 @@ msgstr "" msgid "Batch {0} and Warehouse" msgstr "" -#: erpnext/controllers/sales_and_purchase_return.py:1164 +#: erpnext/controllers/sales_and_purchase_return.py:1186 msgid "Batch {0} is not available in warehouse {1}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3287 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3289 #: erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py:290 msgid "Batch {0} of Item {1} has expired." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3293 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3295 msgid "Batch {0} of Item {1} is disabled." msgstr "" @@ -7574,9 +7577,9 @@ msgstr "" #. Label of a Card Break in the Manufacturing Workspace #. Label of a Link in the Manufacturing Workspace -#: erpnext/manufacturing/doctype/bom/bom.py:1267 +#: erpnext/manufacturing/doctype/bom/bom.py:1318 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json -#: erpnext/stock/doctype/material_request/material_request.js:127 +#: erpnext/stock/doctype/material_request/material_request.js:139 #: erpnext/stock/doctype/stock_entry/stock_entry.js:675 msgid "Bill of Materials" msgstr "" @@ -8680,7 +8683,7 @@ msgstr "" msgid "Can be approved by {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2460 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2504 msgid "Can not close Work Order. Since {0} Job Cards are in Work In Progress state." msgstr "" @@ -8787,6 +8790,10 @@ msgstr "" msgid "Cannot Resubmit Ledger entries for vouchers in Closed fiscal year." msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:202 +msgid "Cannot add child table {0} to deletion list. Child tables are automatically deleted with their parent DocTypes." +msgstr "" + #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:226 msgid "Cannot amend {0} {1}, please create a new one instead." msgstr "" @@ -8815,7 +8822,7 @@ msgstr "" msgid "Cannot cancel as processing of cancelled documents is pending." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1071 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1081 msgid "Cannot cancel because submitted Stock Entry {0} exists" msgstr "" @@ -8900,7 +8907,7 @@ msgstr "" msgid "Cannot create return for consolidated invoice {0}." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1124 +#: erpnext/manufacturing/doctype/bom/bom.py:1175 msgid "Cannot deactivate or cancel BOM as it is linked with other BOMs" msgstr "" @@ -8925,11 +8932,20 @@ msgstr "" msgid "Cannot delete an item which has been ordered" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:195 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:778 +msgid "Cannot delete protected core DocType: {0}" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:211 +msgid "Cannot delete virtual DocType: {0}. Virtual DocTypes do not have database tables." +msgstr "" + #: erpnext/setup/doctype/company/company.py:561 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/manufacturing/doctype/work_order/work_order.py:683 +#: erpnext/manufacturing/doctype/work_order/work_order.py:693 msgid "Cannot disassemble more than produced quantity." msgstr "" @@ -8937,10 +8953,6 @@ msgstr "" msgid "Cannot enable Item-wise Inventory Account, as there are existing Stock Ledger Entries for the company {0} with Warehouse-wise Inventory Account. Please cancel the stock transactions first and try again." msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:113 -msgid "Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1}" -msgstr "" - #: erpnext/selling/doctype/sales_order/sales_order.py:782 #: erpnext/selling/doctype/sales_order/sales_order.py:805 msgid "Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No." @@ -8958,23 +8970,19 @@ msgstr "" msgid "Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings." msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:519 -msgid "Cannot make any transactions until the deletion job is completed" -msgstr "" - #: erpnext/accounts/party.py:1075 msgid "Cannot merge {0} '{1}' into '{2}' as both have existing accounting entries in different currencies for company '{3}'." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:533 +#: erpnext/manufacturing/doctype/work_order/work_order.py:543 msgid "Cannot produce more Item {0} than Sales Order quantity {1} {2}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1426 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1436 msgid "Cannot produce more item for {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1430 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1440 msgid "Cannot produce more than {0} items for {1}" msgstr "" @@ -9033,6 +9041,10 @@ msgstr "" msgid "Cannot set the field {0} for copying in variants" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:264 +msgid "Cannot start deletion. Another deletion {0} is already queued/running. Please wait for it to complete." +msgstr "" + #: erpnext/accounts/doctype/payment_entry/payment_entry.py:1921 msgid "Cannot {0} from {1} without any negative outstanding invoice" msgstr "" @@ -9068,7 +9080,7 @@ msgstr "" msgid "Capacity Planning" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1057 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1067 msgid "Capacity Planning Error, planned start time can not be same as end time" msgstr "" @@ -9552,6 +9564,14 @@ msgstr "" msgid "Check if material transfer entry is not required" msgstr "" +#: erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py:58 +msgid "Check row {0} for account {1}: Party Type is only allowed for Receivable or Payable accounts" +msgstr "" + +#: erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py:65 +msgid "Check row {0} for account {1}: Party is only allowed if Party Type is set" +msgstr "" + #. Description of the 'Must be Whole Number' (Check) field in DocType 'UOM' #: erpnext/setup/doctype/uom/uom.json msgid "Check this to disallow fractions. (for Nos)" @@ -9619,7 +9639,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:2751 +#: erpnext/public/js/controllers/transaction.js:2744 msgid "Cheque/Reference Date" msgstr "" @@ -9660,6 +9680,12 @@ msgstr "" msgid "Chief Technology Officer" msgstr "" +#. Label of the child_doctypes (Small Text) field in DocType 'Transaction +#. Deletion Record To Delete' +#: erpnext/setup/doctype/transaction_deletion_record_to_delete/transaction_deletion_record_to_delete.json +msgid "Child DocTypes" +msgstr "" + #. Label of the child_docname (Data) field in DocType 'Pricing Rule Detail' #: erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.json msgid "Child Docname" @@ -9667,11 +9693,15 @@ msgstr "" #. Label of the child_row_reference (Data) field in DocType 'Quality #. Inspection' -#: erpnext/public/js/controllers/transaction.js:2846 +#: erpnext/public/js/controllers/transaction.js:2839 #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Child Row Reference" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:205 +msgid "Child Table Not Allowed" +msgstr "" + #: erpnext/projects/doctype/task/task.py:312 msgid "Child Task exists for this Task. You can not delete this Task." msgstr "" @@ -9680,6 +9710,12 @@ msgstr "" msgid "Child nodes can be only created under 'Group' type nodes" msgstr "" +#. Description of the 'Child DocTypes' (Small Text) field in DocType +#. 'Transaction Deletion Record To Delete' +#: erpnext/setup/doctype/transaction_deletion_record_to_delete/transaction_deletion_record_to_delete.json +msgid "Child tables that will also be deleted" +msgstr "" + #: erpnext/stock/doctype/warehouse/warehouse.py:103 msgid "Child warehouse exists for this warehouse. You can not delete this warehouse." msgstr "" @@ -9714,8 +9750,8 @@ msgstr "" msgid "Clear Demo Data" msgstr "" -#. Label of the clear_notifications (Check) field in DocType 'Transaction -#. Deletion Record' +#. Label of the clear_notifications_status (Select) field in DocType +#. 'Transaction Deletion Record' #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json msgid "Clear Notifications" msgstr "" @@ -9825,7 +9861,7 @@ msgstr "" msgid "Closed Documents" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2383 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2427 msgid "Closed Work Order can not be stopped or Re-opened" msgstr "" @@ -10191,6 +10227,7 @@ msgstr "" #. Label of the company (Link) field in DocType 'Transaction Deletion Record' #. Label of the company (Link) field in DocType 'Vehicle' #. Label of a Link in the Home Workspace +#. Label of the company (Link) field in DocType 'Bin' #. Label of the company (Link) field in DocType 'Delivery Note' #. Label of the company (Link) field in DocType 'Delivery Trip' #. Label of the company (Link) field in DocType 'Item Default' @@ -10448,6 +10485,7 @@ msgstr "" #: erpnext/setup/install.py:165 erpnext/setup/workspace/home/home.json #: erpnext/stock/dashboard_chart_source/stock_value_by_item_group/stock_value_by_item_group.js:8 #: erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js:8 +#: erpnext/stock/doctype/bin/bin.json #: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_trip/delivery_trip.json #: erpnext/stock/doctype/item_default/item_default.json @@ -10494,7 +10532,7 @@ msgstr "" #: erpnext/stock/report/stock_analytics/stock_analytics.js:41 #: erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js:7 #: erpnext/stock/report/stock_balance/stock_balance.js:8 -#: erpnext/stock/report/stock_balance/stock_balance.py:504 +#: erpnext/stock/report/stock_balance/stock_balance.py:506 #: erpnext/stock/report/stock_ledger/stock_ledger.js:8 #: erpnext/stock/report/stock_ledger/stock_ledger.py:358 #: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js:18 @@ -10642,6 +10680,12 @@ msgstr "" msgid "Company Email" msgstr "" +#. Label of the company_field (Data) field in DocType 'Transaction Deletion +#. Record To Delete' +#: erpnext/setup/doctype/transaction_deletion_record_to_delete/transaction_deletion_record_to_delete.json +msgid "Company Field" +msgstr "" + #. Label of the company_logo (Attach Image) field in DocType 'Company' #: erpnext/public/js/print.js:64 erpnext/setup/doctype/company/company.json msgid "Company Logo" @@ -10673,7 +10717,7 @@ msgstr "" msgid "Company currencies of both the companies should match for Inter Company Transactions." msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:368 +#: erpnext/stock/doctype/material_request/material_request.js:380 #: erpnext/stock/doctype/stock_entry/stock_entry.js:735 msgid "Company field is required" msgstr "" @@ -10690,6 +10734,12 @@ msgstr "" msgid "Company is mandatory for generating an invoice. Please set a default company in Global Defaults." msgstr "" +#. Description of the 'Company Field' (Data) field in DocType 'Transaction +#. Deletion Record To Delete' +#: erpnext/setup/doctype/transaction_deletion_record_to_delete/transaction_deletion_record_to_delete.json +msgid "Company link field name used for filtering (optional - leave empty to delete all records)" +msgstr "" + #: erpnext/setup/doctype/company/company.js:222 msgid "Company name not same" msgstr "" @@ -10811,7 +10861,7 @@ msgstr "" msgid "Completed Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1340 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1350 msgid "Completed Qty cannot be greater than 'Qty to Manufacture'" msgstr "" @@ -11128,7 +11178,7 @@ msgstr "" msgid "Consumed Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1718 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1729 msgid "Consumed Qty cannot be greater than Reserved Qty for item {0}" msgstr "" @@ -11157,7 +11207,7 @@ msgstr "" msgid "Consumed Stock Total Value" msgstr "" -#: erpnext/stock/doctype/stock_entry_type/stock_entry_type.py:118 +#: erpnext/stock/doctype/stock_entry_type/stock_entry_type.py:127 msgid "Consumed quantity of item {0} exceeds transferred quantity." msgstr "" @@ -11587,6 +11637,8 @@ msgstr "" #. Label of the cost_center (Link) field in DocType 'Dunning Type' #. Label of the cost_center (Link) field in DocType 'GL Entry' #. Label of the cost_center (Link) field in DocType 'Journal Entry Account' +#. Label of the cost_center (Link) field in DocType 'Journal Entry Template +#. Account' #. Label of the cost_center (Link) field in DocType 'Loyalty Program' #. Label of the cost_center (Link) field in DocType 'Opening Invoice Creation #. Tool' @@ -11659,6 +11711,7 @@ msgstr "" #: erpnext/accounts/doctype/dunning_type/dunning_type.json #: erpnext/accounts/doctype/gl_entry/gl_entry.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +#: erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json #: erpnext/accounts/doctype/loyalty_program/loyalty_program.json #: erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.json #: erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json @@ -11927,7 +11980,7 @@ msgstr "" msgid "Could not auto create Customer due to the following missing mandatory field(s):" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:680 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:688 msgid "Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again" msgstr "" @@ -12019,7 +12072,7 @@ msgstr "" msgid "Create Chart Of Accounts Based On" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note_list.js:61 +#: erpnext/stock/doctype/delivery_note/delivery_note_list.js:63 msgid "Create Delivery Trip" msgstr "" @@ -12083,7 +12136,7 @@ msgstr "" msgid "Create Ledger Entries for Change Amount" msgstr "" -#: erpnext/buying/doctype/supplier/supplier.js:226 +#: erpnext/buying/doctype/supplier/supplier.js:218 #: erpnext/selling/doctype/customer/customer.js:285 msgid "Create Link" msgstr "" @@ -12184,7 +12237,7 @@ msgid "Create Sales Orders to help you plan your work and deliver on-time" msgstr "" #: erpnext/stock/dashboard/item_dashboard.js:283 -#: erpnext/stock/doctype/material_request/material_request.js:488 +#: erpnext/stock/doctype/material_request/material_request.js:500 msgid "Create Stock Entry" msgstr "" @@ -12383,7 +12436,7 @@ msgstr "" msgid "Credit ({0})" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:627 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:641 msgid "Credit Account" msgstr "" @@ -12461,7 +12514,7 @@ msgstr "" msgid "Credit Limit" msgstr "" -#: erpnext/selling/doctype/customer/customer.py:603 +#: erpnext/selling/doctype/customer/customer.py:606 msgid "Credit Limit Crossed" msgstr "" @@ -12535,7 +12588,7 @@ msgstr "" msgid "Credit Note will update it's own outstanding amount, even if 'Return Against' is specified." msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:677 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:685 msgid "Credit Note {0} has been created automatically" msgstr "" @@ -12552,8 +12605,8 @@ msgstr "" msgid "Credit in Company Currency" msgstr "" -#: erpnext/selling/doctype/customer/customer.py:569 -#: erpnext/selling/doctype/customer/customer.py:624 +#: erpnext/selling/doctype/customer/customer.py:572 +#: erpnext/selling/doctype/customer/customer.py:627 msgid "Credit limit has been crossed for customer {0} ({1}/{2})" msgstr "" @@ -12561,7 +12614,7 @@ msgstr "" msgid "Credit limit is already defined for the Company {0}" msgstr "" -#: erpnext/selling/doctype/customer/customer.py:623 +#: erpnext/selling/doctype/customer/customer.py:626 msgid "Credit limit reached for customer {0}" msgstr "" @@ -12748,7 +12801,7 @@ msgstr "" msgid "Currency of the Closing Account must be {0}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:634 +#: erpnext/manufacturing/doctype/bom/bom.py:685 msgid "Currency of the price list {0} must be {1} or {2}" msgstr "" @@ -13012,7 +13065,7 @@ msgstr "" #: erpnext/accounts/doctype/pricing_rule/pricing_rule.json #: erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json #: erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:308 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:309 #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json #: erpnext/accounts/doctype/sales_invoice_reference/sales_invoice_reference.json #: erpnext/accounts/doctype/tax_rule/tax_rule.json @@ -13029,7 +13082,7 @@ msgstr "" #: erpnext/accounts/report/sales_register/sales_register.py:187 #: erpnext/assets/doctype/asset/asset.json #: erpnext/buying/doctype/purchase_order/purchase_order.json -#: erpnext/buying/doctype/supplier/supplier.js:194 +#: erpnext/buying/doctype/supplier/supplier.js:186 #: erpnext/crm/doctype/contract/contract.json #: erpnext/crm/doctype/lead/lead.js:32 #: erpnext/crm/doctype/opportunity/opportunity.js:99 @@ -13561,7 +13614,7 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1142 #: erpnext/selling/doctype/sales_order/sales_order.py:432 -#: erpnext/stock/doctype/delivery_note/delivery_note.py:424 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:432 msgid "Customer {0} does not belong to project {1}" msgstr "" @@ -13671,7 +13724,7 @@ msgstr "" msgid "DFS" msgstr "" -#: erpnext/projects/doctype/project/project.py:673 +#: erpnext/projects/doctype/project/project.py:675 msgid "Daily Project Summary for {0}" msgstr "" @@ -13930,7 +13983,7 @@ msgstr "" msgid "Debit / Credit Note Posting Date" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:617 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:631 msgid "Debit Account" msgstr "" @@ -14170,7 +14223,7 @@ msgstr "" msgid "Default BOM ({0}) must be active for this item or its template" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2185 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2229 msgid "Default BOM for {0} not found" msgstr "" @@ -14178,7 +14231,7 @@ msgstr "" msgid "Default BOM not found for FG Item {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2182 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2226 msgid "Default BOM not found for Item {0} and Project {1}" msgstr "" @@ -14710,8 +14763,8 @@ msgstr "" msgid "Delete Accounting and Stock Ledger Entries on deletion of Transaction" msgstr "" -#. Label of the delete_bin_data (Check) field in DocType 'Transaction Deletion -#. Record' +#. Label of the delete_bin_data_status (Select) field in DocType 'Transaction +#. Deletion Record' #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json msgid "Delete Bins" msgstr "" @@ -14726,14 +14779,14 @@ msgstr "" msgid "Delete Dimension" msgstr "" -#. Label of the delete_leads_and_addresses (Check) field in DocType +#. Label of the delete_leads_and_addresses_status (Select) field in DocType #. 'Transaction Deletion Record' #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json msgid "Delete Leads and Addresses" msgstr "" -#. Label of the delete_transactions (Check) field in DocType 'Transaction -#. Deletion Record' +#. Label of the delete_transactions_status (Select) field in DocType +#. 'Transaction Deletion Record' #: erpnext/setup/doctype/company/company.js:168 #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json msgid "Delete Transactions" @@ -14752,7 +14805,8 @@ msgstr "" msgid "Deleting {0} and all associated Common Code documents..." msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:506 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1095 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1114 msgid "Deletion in Progress!" msgstr "" @@ -14760,6 +14814,14 @@ msgstr "" msgid "Deletion is not permitted for country {0}" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:216 +msgid "Deletion process restarted" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:97 +msgid "Deletion will start automatically after submission." +msgstr "" + #. Label of the delimiter_options (Data) field in DocType 'Bank Statement #. Import' #: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json @@ -14918,8 +14980,8 @@ msgstr "" #. Label of the delivery_note (Link) field in DocType 'Shipment Delivery Note' #. Label of a Link in the Stock Workspace #: erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:128 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:331 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:129 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:332 #: erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js:36 #: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json #: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.js:22 @@ -15258,7 +15320,7 @@ msgstr "" msgid "Depreciation Posting Date" msgstr "" -#: erpnext/assets/doctype/asset/asset.js:832 +#: erpnext/assets/doctype/asset/asset.js:834 msgid "Depreciation Posting Date cannot be before Available-for-use Date" msgstr "" @@ -15381,7 +15443,7 @@ msgstr "" msgid "Difference Account must be a Asset/Liability type account (Temporary Opening), since this Stock Entry is an Opening Entry" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:983 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:986 msgid "Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry" msgstr "" @@ -16168,7 +16230,7 @@ msgstr "" msgid "Do not update variants on save" msgstr "" -#: erpnext/assets/doctype/asset/asset.js:870 +#: erpnext/assets/doctype/asset/asset.js:872 msgid "Do you really want to restore this scrapped asset?" msgstr "" @@ -16196,14 +16258,45 @@ msgstr "" msgid "Do you want to submit the stock entry?" msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:81 -msgid "DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it." +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:180 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:441 +msgid "DocType {0} does not exist" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:295 +msgid "DocType {0} with company field '{1}' is already in the list" +msgstr "" + +#. Label of the doctypes_to_delete (Table) field in DocType 'Transaction +#. Deletion Record' +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +msgid "DocTypes To Delete" +msgstr "" + +#. Description of the 'Excluded DocTypes' (Table) field in DocType 'Transaction +#. Deletion Record' +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +msgid "DocTypes that will NOT be deleted." +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:84 +msgid "DocTypes with a company field:" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:88 +msgid "DocTypes without a company field:" msgstr "" #: erpnext/templates/pages/search_help.py:22 msgid "Docs Search" msgstr "" +#. Label of the document_count (Int) field in DocType 'Transaction Deletion +#. Record To Delete' +#: erpnext/setup/doctype/transaction_deletion_record_to_delete/transaction_deletion_record_to_delete.json +msgid "Document Count" +msgstr "" + #. Label of the document_type (Link) field in DocType 'Subscription Invoice' #: erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json msgid "Document Type " @@ -16406,7 +16499,7 @@ msgstr "" #. Name of a DocType #: erpnext/accounts/doctype/dunning/dunning.json -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:155 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:156 msgid "Dunning" msgstr "" @@ -16455,6 +16548,10 @@ msgstr "" msgid "Duplicate Customer Group" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:188 +msgid "Duplicate DocType" +msgstr "" + #: erpnext/setup/doctype/authorization_rule/authorization_rule.py:71 msgid "Duplicate Entry. Please check Authorization Rule {0}" msgstr "" @@ -16509,6 +16606,10 @@ msgstr "" msgid "Duplicate entry against the item code {0} and manufacturer {1}" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:187 +msgid "Duplicate entry: {0}{1}" +msgstr "" + #: erpnext/accounts/doctype/pos_profile/pos_profile.py:164 msgid "Duplicate item group found in the item group table" msgstr "" @@ -16551,12 +16652,12 @@ msgstr "" msgid "Dyne" msgstr "" -#: erpnext/regional/italy/utils.py:221 erpnext/regional/italy/utils.py:241 -#: erpnext/regional/italy/utils.py:251 erpnext/regional/italy/utils.py:259 -#: erpnext/regional/italy/utils.py:266 erpnext/regional/italy/utils.py:270 -#: erpnext/regional/italy/utils.py:277 erpnext/regional/italy/utils.py:286 -#: erpnext/regional/italy/utils.py:311 erpnext/regional/italy/utils.py:318 -#: erpnext/regional/italy/utils.py:423 +#: erpnext/regional/italy/utils.py:228 erpnext/regional/italy/utils.py:248 +#: erpnext/regional/italy/utils.py:258 erpnext/regional/italy/utils.py:266 +#: erpnext/regional/italy/utils.py:273 erpnext/regional/italy/utils.py:277 +#: erpnext/regional/italy/utils.py:284 erpnext/regional/italy/utils.py:293 +#: erpnext/regional/italy/utils.py:318 erpnext/regional/italy/utils.py:325 +#: erpnext/regional/italy/utils.py:430 msgid "E-Invoicing Information Missing" msgstr "" @@ -16608,7 +16709,7 @@ msgstr "" msgid "Earliest" msgstr "" -#: erpnext/stock/report/stock_balance/stock_balance.py:516 +#: erpnext/stock/report/stock_balance/stock_balance.py:518 msgid "Earliest Age" msgstr "" @@ -17005,7 +17106,7 @@ msgstr "" msgid "Employee {0} does not belong to the company {1}" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:334 +#: erpnext/manufacturing/doctype/job_card/job_card.py:359 msgid "Employee {0} is currently working on another workstation. Please assign another employee." msgstr "" @@ -17017,6 +17118,10 @@ msgstr "" msgid "Empty" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:750 +msgid "Empty To Delete List" +msgstr "" + #. Name of a UOM #: erpnext/setup/setup_wizard/data/uom_data.json msgid "Ems(Pica)" @@ -17310,7 +17415,7 @@ msgstr "" msgid "Enter Serial Nos" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:425 +#: erpnext/stock/doctype/material_request/material_request.js:437 msgid "Enter Supplier" msgstr "" @@ -17352,7 +17457,7 @@ msgstr "" msgid "Enter customer's phone number" msgstr "" -#: erpnext/assets/doctype/asset/asset.js:841 +#: erpnext/assets/doctype/asset/asset.js:843 msgid "Enter date to scrap asset" msgstr "" @@ -17391,7 +17496,7 @@ msgstr "" msgid "Enter the opening stock units." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:962 +#: erpnext/manufacturing/doctype/bom/bom.js:965 msgid "Enter the quantity of the Item that will be manufactured from this Bill of Materials." msgstr "" @@ -17468,6 +17573,10 @@ msgstr "" msgid "Error evaluating the criteria formula" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:267 +msgid "Error getting details for {0}: {1}" +msgstr "" + #: erpnext/accounts/doctype/bank_transaction/bank_transaction.py:310 msgid "Error in party matching for Bank Transaction {0}" msgstr "" @@ -17567,7 +17676,7 @@ msgstr "" msgid "Excess Materials Consumed" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1031 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1117 msgid "Excess Transfer" msgstr "" @@ -18067,7 +18176,7 @@ msgstr "" msgid "Extra Consumed Qty" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:231 +#: erpnext/manufacturing/doctype/job_card/job_card.py:256 msgid "Extra Job Card Quantity" msgstr "" @@ -18245,7 +18354,7 @@ msgstr "" msgid "Fetch Subscription Updates" msgstr "" -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1068 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1069 msgid "Fetch Timesheet" msgstr "" @@ -18267,7 +18376,7 @@ msgstr "" msgid "Fetch Value From" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:360 +#: erpnext/stock/doctype/material_request/material_request.js:372 #: erpnext/stock/doctype/stock_entry/stock_entry.js:712 msgid "Fetch exploded BOM (including sub-assemblies)" msgstr "" @@ -18295,7 +18404,7 @@ msgid "Fetching Sales Orders..." msgstr "" #: erpnext/accounts/doctype/dunning/dunning.js:135 -#: erpnext/public/js/controllers/transaction.js:1496 +#: erpnext/public/js/controllers/transaction.js:1489 msgid "Fetching exchange rates ..." msgstr "" @@ -18303,6 +18412,10 @@ msgstr "" msgid "Fetching..." msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:222 +msgid "Field '{0}' is not a valid Company link field for DocType {1}" +msgstr "" + #. Label of the field_mapping_section (Section Break) field in DocType #. 'Inventory Dimension' #: erpnext/stock/doctype/inventory_dimension/inventory_dimension.json @@ -18321,6 +18434,18 @@ msgstr "" msgid "Fields will be copied over only at time of creation." msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1062 +msgid "File does not belong to this Transaction Deletion Record" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1056 +msgid "File not found" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1070 +msgid "File not found on server" +msgstr "" + #. Label of the file_to_rename (Attach) field in DocType 'Rename Tool' #: erpnext/utilities/doctype/rename_tool/rename_tool.json msgid "File to Rename" @@ -18661,7 +18786,7 @@ msgstr "" msgid "Finished Goods based Operating Cost" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1670 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1671 msgid "Finished Item {0} does not match with Work Order {1}" msgstr "" @@ -18715,7 +18840,7 @@ msgstr "" msgid "First Response Time for Opportunity" msgstr "" -#: erpnext/regional/italy/utils.py:229 +#: erpnext/regional/italy/utils.py:236 msgid "Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}" msgstr "" @@ -18825,7 +18950,7 @@ msgstr "" msgid "Fixed Asset Turnover Ratio" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:691 +#: erpnext/manufacturing/doctype/bom/bom.py:742 msgid "Fixed Asset item {0} cannot be used in BOMs." msgstr "" @@ -18903,7 +19028,7 @@ msgstr "" msgid "Following Material Requests have been raised automatically based on Item's re-order level" msgstr "" -#: erpnext/selling/doctype/customer/customer.py:794 +#: erpnext/selling/doctype/customer/customer.py:797 msgid "Following fields are mandatory to create address:" msgstr "" @@ -18955,7 +19080,7 @@ msgstr "" msgid "For Company" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:403 +#: erpnext/stock/doctype/material_request/material_request.js:415 msgid "For Default Supplier (Optional)" msgstr "" @@ -19024,7 +19149,7 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan/production_plan.js:471 #: erpnext/manufacturing/doctype/production_plan/production_plan.json #: erpnext/selling/doctype/sales_order/sales_order.js:1407 -#: erpnext/stock/doctype/material_request/material_request.js:349 +#: erpnext/stock/doctype/material_request/material_request.js:361 #: erpnext/templates/form_grid/material_request_grid.html:36 msgid "For Warehouse" msgstr "" @@ -19033,11 +19158,11 @@ msgstr "" msgid "For Work Order" msgstr "" -#: erpnext/controllers/status_updater.py:281 +#: erpnext/controllers/status_updater.py:282 msgid "For an item {0}, quantity must be negative number" msgstr "" -#: erpnext/controllers/status_updater.py:278 +#: erpnext/controllers/status_updater.py:279 msgid "For an item {0}, quantity must be positive number" msgstr "" @@ -19067,11 +19192,15 @@ msgstr "" msgid "For item {0}, only {1} asset have been created or linked to {2}. Please create or link {3} more asset with the respective document." msgstr "" -#: erpnext/controllers/status_updater.py:286 +#: erpnext/controllers/status_updater.py:287 msgid "For item {0}, rate must be a positive number. To Allow negative rates, enable {1} in {2}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2530 +#: erpnext/manufacturing/doctype/bom/bom.py:346 +msgid "For operation {0} at row {1}, please add raw materials or set a BOM against it." +msgstr "" + +#: erpnext/manufacturing/doctype/work_order/work_order.py:2574 msgid "For operation {0}: Quantity ({1}) can not be greater than pending quantity({2})" msgstr "" @@ -19088,7 +19217,7 @@ msgstr "" msgid "For projected and forecast quantities, the system will consider all child warehouses under the selected parent warehouse." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1702 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1703 msgid "For quantity {0} should not be greater than allowed quantity {1}" msgstr "" @@ -19121,11 +19250,11 @@ msgstr "" msgid "For the convenience of customers, these codes can be used in print formats like Invoices and Delivery Notes" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:977 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:978 msgid "For the item {0}, the consumed quantity should be {1} according to the BOM {2}." msgstr "" -#: erpnext/public/js/controllers/transaction.js:1306 +#: erpnext/public/js/controllers/transaction.js:1299 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 "" @@ -19134,7 +19263,7 @@ msgstr "" msgid "For the {0}, no stock is available for the return in the warehouse {1}." msgstr "" -#: erpnext/controllers/sales_and_purchase_return.py:1216 +#: erpnext/controllers/sales_and_purchase_return.py:1238 msgid "For the {0}, the quantity is required to make the return entry" msgstr "" @@ -19911,6 +20040,14 @@ msgstr "" msgid "Generate Stock Closing Entry" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:112 +msgid "Generate To Delete List" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:468 +msgid "Generate To Delete list first" +msgstr "" + #. Description of a DocType #: erpnext/stock/doctype/packing_slip/packing_slip.json msgid "Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight." @@ -20008,10 +20145,10 @@ msgstr "" #. Label of the get_items_from (Select) field in DocType 'Production Plan' #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js:166 #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js:191 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:292 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:324 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:358 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1124 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:293 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:325 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:359 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1125 #: erpnext/buying/doctype/purchase_order/purchase_order.js:561 #: erpnext/buying/doctype/purchase_order/purchase_order.js:584 #: erpnext/buying/doctype/request_for_quotation/request_for_quotation.js:376 @@ -20030,8 +20167,8 @@ msgstr "" #: erpnext/selling/doctype/sales_order/sales_order.js:1200 #: erpnext/stock/doctype/delivery_note/delivery_note.js:187 #: erpnext/stock/doctype/delivery_note/delivery_note.js:239 -#: erpnext/stock/doctype/material_request/material_request.js:129 -#: erpnext/stock/doctype/material_request/material_request.js:226 +#: erpnext/stock/doctype/material_request/material_request.js:141 +#: erpnext/stock/doctype/material_request/material_request.js:238 #: 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:351 @@ -20053,7 +20190,7 @@ msgstr "" msgid "Get Items for Purchase Only" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:334 +#: erpnext/stock/doctype/material_request/material_request.js:346 #: erpnext/stock/doctype/stock_entry/stock_entry.js:715 #: erpnext/stock/doctype/stock_entry/stock_entry.js:728 msgid "Get Items from BOM" @@ -20168,7 +20305,7 @@ msgstr "" msgid "Get Suppliers By" msgstr "" -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1120 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1121 msgid "Get Timesheets" msgstr "" @@ -20238,7 +20375,7 @@ msgstr "" msgid "Goods Transferred" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2219 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2221 msgid "Goods are already received against the outward entry {0}" msgstr "" @@ -21089,6 +21226,10 @@ msgstr "" msgid "IBAN" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:93 +msgid "IMPORTANT: Create a backup before proceeding!" +msgstr "" + #. Name of a report #: erpnext/regional/report/irs_1099/irs_1099.json msgid "IRS 1099" @@ -21748,6 +21889,10 @@ msgstr "" msgid "Import Successful" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:560 +msgid "Import Summary" +msgstr "" + #. Label of a Link in the Buying Workspace #. Name of a DocType #: erpnext/buying/workspace/buying/buying.json @@ -21768,6 +21913,10 @@ msgstr "" msgid "Import in Bulk" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:192 +msgid "Imported {0} DocTypes" +msgstr "" + #: erpnext/edi/doctype/common_code/common_code.py:109 msgid "Importing Common Codes" msgstr "" @@ -21820,7 +21969,7 @@ msgstr "" #: erpnext/stock/report/available_serial_no/available_serial_no.py:112 #: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:82 -#: erpnext/stock/report/stock_balance/stock_balance.py:471 +#: erpnext/stock/report/stock_balance/stock_balance.py:473 #: erpnext/stock/report/stock_ledger/stock_ledger.py:237 msgid "In Qty" msgstr "" @@ -21844,15 +21993,15 @@ msgstr "" msgid "In Transit" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:487 +#: erpnext/stock/doctype/material_request/material_request.js:499 msgid "In Transit Transfer" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:456 +#: erpnext/stock/doctype/material_request/material_request.js:468 msgid "In Transit Warehouse" msgstr "" -#: erpnext/stock/report/stock_balance/stock_balance.py:477 +#: erpnext/stock/report/stock_balance/stock_balance.py:479 msgid "In Value" msgstr "" @@ -22271,7 +22420,7 @@ msgstr "" msgid "Incorrect Balance Qty After Transaction" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1063 +#: erpnext/controllers/subcontracting_controller.py:1071 msgid "Incorrect Batch Consumed" msgstr "" @@ -22279,7 +22428,7 @@ msgstr "" msgid "Incorrect Check in (group) Warehouse for Reorder" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:984 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:985 msgid "Incorrect Component Quantity" msgstr "" @@ -22305,7 +22454,7 @@ msgstr "" msgid "Incorrect Serial No Valuation" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1076 +#: erpnext/controllers/subcontracting_controller.py:1084 msgid "Incorrect Serial Number Consumed" msgstr "" @@ -22456,8 +22605,8 @@ msgstr "" msgid "Initial Email Notification Sent" msgstr "" -#. Label of the initialize_doctypes_table (Check) field in DocType 'Transaction -#. Deletion Record' +#. Label of the initialize_doctypes_table_status (Select) field in DocType +#. 'Transaction Deletion Record' #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json msgid "Initialize Summary Table" msgstr "" @@ -22481,6 +22630,7 @@ msgid "Inspected By" msgstr "" #: erpnext/controllers/stock_controller.py:1453 +#: erpnext/manufacturing/doctype/job_card/job_card.py:817 msgid "Inspection Rejected" msgstr "" @@ -22504,6 +22654,7 @@ msgid "Inspection Required before Purchase" msgstr "" #: erpnext/controllers/stock_controller.py:1438 +#: erpnext/manufacturing/doctype/job_card/job_card.py:798 msgid "Inspection Submission" msgstr "" @@ -22533,7 +22684,7 @@ msgstr "" msgid "Installation Note Item" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:631 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:639 msgid "Installation Note {0} has already been submitted" msgstr "" @@ -22854,14 +23005,22 @@ msgstr "" msgid "Invalid Barcode. There is no Item attached to this barcode." msgstr "" -#: erpnext/public/js/controllers/transaction.js:3106 +#: erpnext/public/js/controllers/transaction.js:3099 msgid "Invalid Blanket Order for the selected Customer and Item" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:494 +msgid "Invalid CSV format. Expected column: doctype_name" +msgstr "" + #: erpnext/quality_management/doctype/quality_procedure/quality_procedure.py:72 msgid "Invalid Child Procedure" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:225 +msgid "Invalid Company Field" +msgstr "" + #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:2307 msgid "Invalid Company for Inter Company Transaction." msgstr "" @@ -22954,7 +23113,7 @@ msgstr "" msgid "Invalid Priority" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1173 +#: erpnext/manufacturing/doctype/bom/bom.py:1224 msgid "Invalid Process Loss Configuration" msgstr "" @@ -22987,16 +23146,16 @@ msgstr "" msgid "Invalid Schedule" msgstr "" -#: erpnext/controllers/selling_controller.py:309 +#: erpnext/controllers/selling_controller.py:310 msgid "Invalid Selling Price" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1745 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1746 msgid "Invalid Serial and Batch Bundle" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1018 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1040 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1019 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1041 msgid "Invalid Source and Target Warehouse" msgstr "" @@ -23017,6 +23176,10 @@ msgstr "" msgid "Invalid condition expression" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1051 +msgid "Invalid file URL" +msgstr "" + #: erpnext/accounts/doctype/financial_report_template/financial_report_template.js:93 msgid "Invalid filter formula. Please check the syntax." msgstr "" @@ -23145,7 +23308,7 @@ msgstr "" #. Account' #: erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:145 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:146 msgid "Invoice Discounting" msgstr "" @@ -23873,7 +24036,7 @@ msgstr "" msgid "Issue Date" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:168 +#: erpnext/stock/doctype/material_request/material_request.js:180 msgid "Issue Material" msgstr "" @@ -23947,7 +24110,7 @@ msgstr "" msgid "It can take upto few hours for accurate stock values to be visible after merging items." msgstr "" -#: erpnext/public/js/controllers/transaction.js:2508 +#: erpnext/public/js/controllers/transaction.js:2501 msgid "It is needed to fetch Item Details." msgstr "" @@ -24007,7 +24170,7 @@ msgstr "" #: erpnext/buying/workspace/buying/buying.json #: erpnext/controllers/taxes_and_totals.py:1207 #: erpnext/manufacturing/doctype/blanket_order/blanket_order.json -#: erpnext/manufacturing/doctype/bom/bom.js:1055 +#: erpnext/manufacturing/doctype/bom/bom.js:1058 #: erpnext/manufacturing/doctype/bom/bom.json #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:109 #: erpnext/manufacturing/doctype/workstation/workstation_job_card.html:25 @@ -24063,7 +24226,7 @@ msgstr "" #: erpnext/stock/report/stock_ageing/stock_ageing.js:46 #: erpnext/stock/report/stock_analytics/stock_analytics.js:15 #: erpnext/stock/report/stock_analytics/stock_analytics.py:28 -#: erpnext/stock/report/stock_balance/stock_balance.py:398 +#: erpnext/stock/report/stock_balance/stock_balance.py:400 #: erpnext/stock/report/stock_ledger/stock_ledger.py:207 #: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js:27 #: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:51 @@ -24246,7 +24409,7 @@ msgstr "" #: erpnext/accounts/doctype/pricing_rule_item_code/pricing_rule_item_code.json #: erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json #: erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1077 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1078 #: erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py:68 #: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py:37 #: erpnext/accounts/report/gross_profit/gross_profit.py:301 @@ -24301,7 +24464,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:119 #: erpnext/projects/doctype/timesheet/timesheet.js:214 -#: erpnext/public/js/controllers/transaction.js:2802 +#: erpnext/public/js/controllers/transaction.js:2795 #: erpnext/public/js/stock_reservation.js:112 #: erpnext/public/js/stock_reservation.js:318 erpnext/public/js/utils.js:488 #: erpnext/public/js/utils.js:645 @@ -24562,7 +24725,7 @@ msgstr "" #: erpnext/stock/report/stock_analytics/stock_analytics.js:8 #: erpnext/stock/report/stock_analytics/stock_analytics.py:37 #: erpnext/stock/report/stock_balance/stock_balance.js:32 -#: erpnext/stock/report/stock_balance/stock_balance.py:406 +#: erpnext/stock/report/stock_balance/stock_balance.py:408 #: erpnext/stock/report/stock_ledger/stock_ledger.js:71 #: erpnext/stock/report/stock_ledger/stock_ledger.py:265 #: erpnext/stock/report/stock_projected_qty/stock_projected_qty.js:39 @@ -24777,7 +24940,7 @@ msgstr "" #: erpnext/manufacturing/report/production_planning_report/production_planning_report.py:371 #: 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:2808 +#: erpnext/public/js/controllers/transaction.js:2801 #: erpnext/public/js/utils.js:736 #: erpnext/selling/doctype/quotation_item/quotation_item.json #: erpnext/selling/doctype/sales_order/sales_order.js:1252 @@ -24818,7 +24981,7 @@ msgstr "" #: erpnext/stock/report/serial_no_and_batch_traceability/serial_no_and_batch_traceability.py:440 #: erpnext/stock/report/stock_ageing/stock_ageing.py:138 #: erpnext/stock/report/stock_analytics/stock_analytics.py:30 -#: erpnext/stock/report/stock_balance/stock_balance.py:404 +#: erpnext/stock/report/stock_balance/stock_balance.py:406 #: erpnext/stock/report/stock_ledger/stock_ledger.py:213 #: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:110 #: erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.py:31 @@ -25155,7 +25318,7 @@ msgstr "" msgid "Item and Warranty Details" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3266 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3268 msgid "Item for row {0} does not match Material Request" msgstr "" @@ -25189,7 +25352,7 @@ msgstr "" msgid "Item qty can not be updated as raw materials are already processed." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1148 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1149 msgid "Item rate has been updated to zero as Allow Zero Valuation Rate is checked for item {0}" msgstr "" @@ -25232,7 +25395,7 @@ msgstr "" msgid "Item {0} does not exist" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:619 +#: erpnext/manufacturing/doctype/bom/bom.py:670 msgid "Item {0} does not exist in the system or has expired" msgstr "" @@ -25240,7 +25403,7 @@ msgstr "" msgid "Item {0} does not exist." msgstr "" -#: erpnext/controllers/selling_controller.py:850 +#: erpnext/controllers/selling_controller.py:844 msgid "Item {0} entered multiple times." msgstr "" @@ -25288,7 +25451,7 @@ msgstr "" msgid "Item {0} is not a subcontracted item" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2131 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2133 msgid "Item {0} is not active or end of life has been reached" msgstr "" @@ -25308,7 +25471,7 @@ msgstr "" msgid "Item {0} must be a non-stock item" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1481 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1482 msgid "Item {0} not found in 'Raw Materials Supplied' table in {1} {2}" msgstr "" @@ -25361,7 +25524,7 @@ msgstr "" msgid "Item/Item Code required to get Item Tax Template." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:361 +#: erpnext/manufacturing/doctype/bom/bom.py:412 msgid "Item: {0} does not exist in the system" msgstr "" @@ -25412,7 +25575,7 @@ msgstr "" msgid "Items not found." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1144 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1145 msgid "Items rate has been updated to zero as Allow Zero Valuation Rate is checked for the following items: {0}" msgstr "" @@ -25484,7 +25647,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:896 +#: erpnext/manufacturing/doctype/job_card/job_card.py:982 #: erpnext/manufacturing/doctype/operation/operation.json #: erpnext/manufacturing/doctype/work_order/work_order.js:396 #: erpnext/manufacturing/doctype/work_order/work_order.json @@ -25545,7 +25708,7 @@ msgstr "" msgid "Job Card and Capacity Planning" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1356 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1458 msgid "Job Card {0} has been completed" msgstr "" @@ -25621,7 +25784,7 @@ msgstr "" msgid "Job Worker Warehouse" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2583 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2627 msgid "Job card {0} created" msgstr "" @@ -25832,7 +25995,7 @@ msgstr "" msgid "Kilowatt-Hour" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:898 +#: erpnext/manufacturing/doctype/job_card/job_card.py:984 msgid "Kindly cancel the Manufacturing Entries first against the work order {0}." msgstr "" @@ -26036,7 +26199,7 @@ msgstr "" msgid "Latest" msgstr "" -#: erpnext/stock/report/stock_balance/stock_balance.py:517 +#: erpnext/stock/report/stock_balance/stock_balance.py:519 msgid "Latest Age" msgstr "" @@ -26330,7 +26493,7 @@ msgstr "" msgid "License Plate" msgstr "" -#: erpnext/controllers/status_updater.py:470 +#: erpnext/controllers/status_updater.py:471 msgid "Limit Crossed" msgstr "" @@ -26416,12 +26579,12 @@ msgstr "" msgid "Linked with submitted documents" msgstr "" -#: erpnext/buying/doctype/supplier/supplier.js:220 +#: erpnext/buying/doctype/supplier/supplier.js:212 #: erpnext/selling/doctype/customer/customer.js:279 msgid "Linking Failed" msgstr "" -#: erpnext/buying/doctype/supplier/supplier.js:219 +#: erpnext/buying/doctype/supplier/supplier.js:211 msgid "Linking to Customer Failed. Please try again." msgstr "" @@ -26690,7 +26853,7 @@ msgstr "" #: erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json #: erpnext/accounts/doctype/loyalty_program/loyalty_program.json #: erpnext/accounts/doctype/pos_invoice/pos_invoice.json -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1175 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1176 #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json #: erpnext/selling/doctype/customer/customer.json #: erpnext/selling/page/point_of_sale/pos_item_cart.js:952 @@ -26878,7 +27041,7 @@ msgstr "" #. Label of the maintenance_schedule (Link) field in DocType 'Maintenance #. Visit' #. Label of a Link in the Support Workspace -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:161 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:162 #: erpnext/crm/workspace/crm/crm.json #: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json #: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js:81 @@ -27237,8 +27400,8 @@ msgstr "" #: erpnext/stock/doctype/material_request_item/material_request_item.json #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json #: erpnext/stock/doctype/stock_entry/stock_entry.json -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1225 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1241 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1226 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1242 #: erpnext/stock/doctype/stock_entry_type/stock_entry_type.json #: erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json #: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -27382,7 +27545,7 @@ msgstr "" msgid "Manufacturing Manager" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2384 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2386 msgid "Manufacturing Quantity is mandatory" msgstr "" @@ -27604,7 +27767,7 @@ msgstr "" #. Option for the 'Purpose' (Select) field in DocType 'Stock Entry Type' #: erpnext/setup/setup_wizard/operations/install_fixtures.py:114 #: erpnext/stock/doctype/stock_entry/stock_entry.json -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1226 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1227 #: erpnext/stock/doctype/stock_entry_type/stock_entry_type.json msgid "Material Consumption for Manufacture" msgstr "" @@ -27634,7 +27797,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:176 +#: erpnext/stock/doctype/material_request/material_request.js:188 #: erpnext/stock/doctype/stock_entry/stock_entry.json #: erpnext/stock/doctype/stock_entry_type/stock_entry_type.json msgid "Material Receipt" @@ -27788,7 +27951,7 @@ msgstr "" msgid "Material Request used to make this Stock Entry" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1329 +#: erpnext/controllers/subcontracting_controller.py:1337 msgid "Material Request {0} is cancelled or stopped" msgstr "" @@ -27846,7 +28009,7 @@ msgstr "" #: 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:154 +#: erpnext/stock/doctype/material_request/material_request.js:166 #: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/pick_list/pick_list.json #: erpnext/stock/doctype/stock_entry/stock_entry.json @@ -27854,7 +28017,7 @@ msgstr "" msgid "Material Transfer" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:160 +#: erpnext/stock/doctype/material_request/material_request.js:172 msgid "Material Transfer (In Transit)" msgstr "" @@ -27902,11 +28065,12 @@ msgstr "" msgid "Material to Supplier" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1562 +#: erpnext/controllers/subcontracting_controller.py:1570 msgid "Materials are already received against the {0} {1}" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:752 +#: erpnext/manufacturing/doctype/job_card/job_card.py:183 +#: erpnext/manufacturing/doctype/job_card/job_card.py:838 msgid "Materials needs to be transferred to the work in progress warehouse for the job card {0}" msgstr "" @@ -27997,11 +28161,11 @@ msgstr "" msgid "Maximum Payment Amount" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3869 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3871 msgid "Maximum Samples - {0} can be retained for Batch {1} and Item {2}." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3860 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3862 msgid "Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}." msgstr "" @@ -28456,7 +28620,7 @@ msgstr "" msgid "Missing Finance Book" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1680 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1681 msgid "Missing Finished Good" msgstr "" @@ -28464,7 +28628,7 @@ msgstr "" msgid "Missing Formula" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:991 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:992 msgid "Missing Item" msgstr "" @@ -28484,8 +28648,8 @@ msgstr "" msgid "Missing required filter: {0}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1132 -#: erpnext/manufacturing/doctype/work_order/work_order.py:1452 +#: erpnext/manufacturing/doctype/bom/bom.py:1183 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1462 msgid "Missing value" msgstr "" @@ -28751,11 +28915,15 @@ msgstr "" msgid "Multiple Warehouse Accounts" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:244 +msgid "Multiple company fields available: {0}. Please select manually." +msgstr "" + #: erpnext/controllers/accounts_controller.py:1302 msgid "Multiple fiscal years exist for the date {0}. Please set company in Fiscal Year" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1687 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1688 msgid "Multiple items cannot be marked as finished item" msgstr "" @@ -28764,7 +28932,7 @@ msgid "Music" msgstr "" #. Label of the must_be_whole_number (Check) field in DocType 'UOM' -#: erpnext/manufacturing/doctype/work_order/work_order.py:1399 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1409 #: erpnext/setup/doctype/uom/uom.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:267 #: erpnext/utilities/transaction_base.py:566 @@ -28840,10 +29008,14 @@ msgstr "" msgid "Naming Series and Price Defaults" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:94 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:95 msgid "Naming Series is mandatory" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:932 +msgid "Naming series '{0}' for DocType '{1}' does not contain standard '.' or '{{' separator. Using fallback extraction." +msgstr "" + #. Name of a UOM #: erpnext/setup/setup_wizard/data/uom_data.json msgid "Nanocoulomb" @@ -28879,16 +29051,16 @@ msgstr "" msgid "Needs Analysis" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:620 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:623 msgid "Negative Quantity is not allowed" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1465 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1475 #: erpnext/stock/serial_batch_bundle.py:1520 msgid "Negative Stock Error" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:625 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:628 msgid "Negative Valuation Rate is not allowed" msgstr "" @@ -29447,6 +29619,10 @@ msgstr "" msgid "No Delivery Note selected for Customer {}" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:749 +msgid "No DocTypes in To Delete list. Please generate or import the list before submitting." +msgstr "" + #: erpnext/public/js/utils/ledger_preview.js:64 msgid "No Impact on Accounting Ledger" msgstr "" @@ -29459,7 +29635,7 @@ msgstr "" msgid "No Item with Serial No {0}" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1476 +#: erpnext/controllers/subcontracting_controller.py:1484 msgid "No Items selected for transfer." msgstr "" @@ -29512,7 +29688,7 @@ msgstr "" msgid "No Selection" msgstr "" -#: erpnext/controllers/sales_and_purchase_return.py:951 +#: erpnext/controllers/sales_and_purchase_return.py:973 msgid "No Serial / Batches are available for return" msgstr "" @@ -29598,7 +29774,7 @@ msgstr "" msgid "No employee was scheduled for call popup" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1371 +#: erpnext/controllers/subcontracting_controller.py:1379 msgid "No item available for transfer." msgstr "" @@ -29771,7 +29947,11 @@ msgstr "" msgid "No reserved stock to unreserve." msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:802 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:152 +msgid "No rows with zero document count found" +msgstr "" + +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:805 msgid "No stock ledger entries were created. Please set the quantity or valuation rate for the items properly and try again." msgstr "" @@ -29825,7 +30005,7 @@ msgstr "" msgid "Non Profit" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1533 +#: erpnext/manufacturing/doctype/bom/bom.py:1584 msgid "Non stock items" msgstr "" @@ -29838,7 +30018,7 @@ msgstr "" msgid "Non-Zeros" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:554 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:557 msgid "None of the items have any change in quantity or value." msgstr "" @@ -29899,7 +30079,6 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan/production_plan_list.js:7 #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/doctype/work_order/work_order_list.js:15 -#: erpnext/manufacturing/report/production_analytics/production_analytics.py:124 #: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/material_request/material_request_list.js:9 msgid "Not Started" @@ -29955,7 +30134,7 @@ msgstr "" msgid "Note: Email will not be sent to disabled users" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:703 +#: erpnext/manufacturing/doctype/bom/bom.py:754 msgid "Note: If you want to use the finished good {0} as a raw material, then enable the 'Do Not Explode' checkbox in the Items table against the same raw material." msgstr "" @@ -30372,6 +30551,10 @@ msgstr "" msgid "Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1065 +msgid "Only CSV files are allowed" +msgstr "" + #. Label of the tax_on_excess_amount (Check) field in DocType 'Tax Withholding #. Category' #: erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json @@ -30420,7 +30603,11 @@ msgstr "" msgid "Only one of Deposit or Withdrawal should be non-zero when applying an Excluded Fee." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1240 +#: erpnext/manufacturing/doctype/bom/bom.py:324 +msgid "Only one operation can have 'Is Final Finished Good' checked when 'Track Semi Finished Goods' is enabled." +msgstr "" + +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1241 msgid "Only one {0} entry can be created against the Work Order {1}" msgstr "" @@ -30705,7 +30892,7 @@ msgid "Opening Purchase Invoices have been created." msgstr "" #: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:81 -#: erpnext/stock/report/stock_balance/stock_balance.py:457 +#: erpnext/stock/report/stock_balance/stock_balance.py:459 msgid "Opening Qty" msgstr "" @@ -30725,7 +30912,7 @@ msgstr "" msgid "Opening Time" msgstr "" -#: erpnext/stock/report/stock_balance/stock_balance.py:464 +#: erpnext/stock/report/stock_balance/stock_balance.py:466 msgid "Opening Value" msgstr "" @@ -30767,7 +30954,7 @@ msgstr "" msgid "Operating Cost Per BOM Quantity" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1620 +#: erpnext/manufacturing/doctype/bom/bom.py:1671 msgid "Operating Cost as per Work Order / BOM" msgstr "" @@ -30843,7 +31030,7 @@ msgstr "" msgid "Operation Time" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1458 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1468 msgid "Operation Time must be greater than 0 for Operation {0}" msgstr "" @@ -30862,7 +31049,7 @@ msgstr "" msgid "Operation {0} added multiple times in the work order {1}" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1145 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1231 msgid "Operation {0} does not belong to the work order {1}" msgstr "" @@ -30892,7 +31079,7 @@ msgstr "" msgid "Operations Routing" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1141 +#: erpnext/manufacturing/doctype/bom/bom.py:1192 msgid "Operations cannot be left blank" msgstr "" @@ -31301,12 +31488,12 @@ msgstr "" #: erpnext/stock/report/available_serial_no/available_serial_no.py:119 #: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:83 -#: erpnext/stock/report/stock_balance/stock_balance.py:479 +#: erpnext/stock/report/stock_balance/stock_balance.py:481 #: erpnext/stock/report/stock_ledger/stock_ledger.py:244 msgid "Out Qty" msgstr "" -#: erpnext/stock/report/stock_balance/stock_balance.py:485 +#: erpnext/stock/report/stock_balance/stock_balance.py:487 msgid "Out Value" msgstr "" @@ -31463,7 +31650,7 @@ msgstr "" msgid "Over Receipt" msgstr "" -#: erpnext/controllers/status_updater.py:475 +#: erpnext/controllers/status_updater.py:476 msgid "Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role." msgstr "" @@ -31483,7 +31670,7 @@ msgstr "" msgid "Over Withheld" msgstr "" -#: erpnext/controllers/status_updater.py:477 +#: erpnext/controllers/status_updater.py:478 msgid "Overbilling of {0} {1} ignored for item {2} because you have {3} role." msgstr "" @@ -31506,7 +31693,6 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:281 #: erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json #: erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json -#: erpnext/manufacturing/report/production_analytics/production_analytics.py:125 #: erpnext/projects/doctype/task/task.json #: erpnext/projects/report/project_summary/project_summary.py:100 #: erpnext/projects/web_form/tasks/tasks.json @@ -31910,7 +32096,7 @@ msgstr "" msgid "Package Weight Details" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note_list.js:71 +#: erpnext/stock/doctype/delivery_note/delivery_note_list.js:73 msgid "Packaging Slip From Delivery Note" msgstr "" @@ -31967,7 +32153,7 @@ msgstr "" msgid "Packing Slip Item" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:647 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:655 msgid "Packing Slip(s) cancelled" msgstr "" @@ -32286,8 +32472,11 @@ msgid "Partial stock can be reserved. For example, If you have a Sales Order of msgstr "" #. Option for the 'Status' (Select) field in DocType 'Timesheet' +#. Option for the 'Status' (Select) field in DocType 'Delivery Note' #: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/doctype/timesheet/timesheet_list.js:5 +#: erpnext/stock/doctype/delivery_note/delivery_note.json +#: erpnext/stock/doctype/delivery_note/delivery_note_list.js:24 msgid "Partially Billed" msgstr "" @@ -32433,6 +32622,8 @@ msgstr "" #. Revaluation Account' #. Label of the party (Dynamic Link) field in DocType 'GL Entry' #. Label of the party (Dynamic Link) field in DocType 'Journal Entry Account' +#. Label of the party (Dynamic Link) field in DocType 'Journal Entry Template +#. Account' #. Label of the party (Dynamic Link) field in DocType 'Opening Invoice Creation #. Tool Item' #. Label of the party (Dynamic Link) field in DocType 'Payment Entry' @@ -32452,6 +32643,7 @@ msgstr "" #: erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json #: erpnext/accounts/doctype/gl_entry/gl_entry.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +#: erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json #: erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json #: erpnext/accounts/doctype/payment_entry/payment_entry.json #: erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -32608,6 +32800,8 @@ msgstr "" #. Account' #. Label of the party_type (Link) field in DocType 'GL Entry' #. Label of the party_type (Link) field in DocType 'Journal Entry Account' +#. Label of the party_type (Link) field in DocType 'Journal Entry Template +#. Account' #. Label of the party_type (Link) field in DocType 'Opening Invoice Creation #. Tool Item' #. Label of the party_type (Link) field in DocType 'Payment Entry' @@ -32629,6 +32823,7 @@ msgstr "" #: erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json #: erpnext/accounts/doctype/gl_entry/gl_entry.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +#: erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json #: erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json #: erpnext/accounts/doctype/payment_entry/payment_entry.json #: erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -32811,7 +33006,7 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js:119 #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py:20 #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js:55 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:97 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:98 #: erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py:25 #: erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js:42 #: erpnext/buying/doctype/purchase_order/purchase_order.js:421 @@ -33140,7 +33335,7 @@ msgstr "" #: erpnext/accounts/doctype/payment_order_reference/payment_order_reference.json #: erpnext/accounts/doctype/payment_request/payment_request.json #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js:135 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:138 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:139 #: erpnext/buying/doctype/purchase_order/purchase_order.js:429 #: erpnext/selling/doctype/sales_order/sales_order.js:1152 msgid "Payment Request" @@ -33813,7 +34008,7 @@ msgstr "" #. Label of a Link in the Stock Workspace #: erpnext/selling/doctype/sales_order/sales_order.js:1022 #: erpnext/stock/doctype/delivery_note/delivery_note.js:199 -#: erpnext/stock/doctype/material_request/material_request.js:144 +#: erpnext/stock/doctype/material_request/material_request.js:156 #: 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 @@ -34278,7 +34473,7 @@ msgstr "" msgid "Please click on 'Generate Schedule' to get schedule" msgstr "" -#: erpnext/selling/doctype/customer/customer.py:595 +#: erpnext/selling/doctype/customer/customer.py:598 msgid "Please contact any of the following users to extend the credit limits for {0}: {1}" msgstr "" @@ -34286,7 +34481,7 @@ msgstr "" msgid "Please contact any of the following users to {} this transaction." msgstr "" -#: erpnext/selling/doctype/customer/customer.py:588 +#: erpnext/selling/doctype/customer/customer.py:591 msgid "Please contact your administrator to extend the credit limits for {0}." msgstr "" @@ -34350,7 +34545,7 @@ msgstr "" msgid "Please enable {0} in the {1}." msgstr "" -#: erpnext/controllers/selling_controller.py:852 +#: erpnext/controllers/selling_controller.py:846 msgid "Please enable {} in {} to allow same item in multiple rows" msgstr "" @@ -34383,11 +34578,11 @@ msgstr "" msgid "Please enter Approving Role or Approving User" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:675 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:678 msgid "Please enter Batch No" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:968 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:971 msgid "Please enter Cost Center" msgstr "" @@ -34399,7 +34594,7 @@ msgstr "" msgid "Please enter Employee Id of this sales person" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:977 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:980 msgid "Please enter Expense Account" msgstr "" @@ -34408,7 +34603,7 @@ msgstr "" msgid "Please enter Item Code to get Batch Number" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2963 +#: erpnext/public/js/controllers/transaction.js:2956 msgid "Please enter Item Code to get batch no" msgstr "" @@ -34448,7 +34643,7 @@ msgstr "" msgid "Please enter Root Type for account- {0}" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:677 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:680 msgid "Please enter Serial No" msgstr "" @@ -34561,6 +34756,14 @@ msgstr "" msgid "Please fix overlapping time slots for {0}." msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:270 +msgid "Please generate To Delete list before submitting" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:70 +msgid "Please generate the To Delete list before submitting" +msgstr "" + #: erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py:67 msgid "Please import accounts against parent company or enable {} in company master." msgstr "" @@ -34654,7 +34857,7 @@ msgstr "" msgid "Please select Charge Type first" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:480 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:494 msgid "Please select Company" msgstr "" @@ -34663,7 +34866,7 @@ msgstr "" msgid "Please select Company and Posting Date to getting entries" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:722 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:736 #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:28 msgid "Please select Company first" msgstr "" @@ -34683,12 +34886,12 @@ msgid "Please select Existing Company for creating Chart of Accounts" msgstr "" #: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.py:210 -#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:322 +#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:299 msgid "Please select Finished Good Item for Service Item {0}" msgstr "" -#: erpnext/assets/doctype/asset/asset.js:667 -#: erpnext/assets/doctype/asset/asset.js:682 +#: erpnext/assets/doctype/asset/asset.js:669 +#: erpnext/assets/doctype/asset/asset.js:684 msgid "Please select Item Code first" msgstr "" @@ -34712,11 +34915,11 @@ msgstr "" msgid "Please select Posting Date before selecting Party" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:723 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:737 msgid "Please select Posting Date first" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1186 +#: erpnext/manufacturing/doctype/bom/bom.py:1237 msgid "Please select Price List" msgstr "" @@ -34740,7 +34943,7 @@ msgstr "" msgid "Please select Stock Asset Account" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1604 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1605 msgid "Please select Subcontracting Order instead of Purchase Order {0}" msgstr "" @@ -34748,7 +34951,7 @@ msgstr "" msgid "Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1441 +#: erpnext/manufacturing/doctype/bom/bom.py:1492 msgid "Please select a BOM" msgstr "" @@ -34758,10 +34961,10 @@ msgid "Please select a Company" msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.js:268 -#: erpnext/manufacturing/doctype/bom/bom.js:677 +#: erpnext/manufacturing/doctype/bom/bom.js:680 #: erpnext/manufacturing/doctype/bom/bom.py:276 #: erpnext/public/js/controllers/accounts.js:277 -#: erpnext/public/js/controllers/transaction.js:3262 +#: erpnext/public/js/controllers/transaction.js:3255 msgid "Please select a Company first." msgstr "" @@ -34785,7 +34988,7 @@ msgstr "" msgid "Please select a Warehouse" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1470 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1572 msgid "Please select a Work Order first." msgstr "" @@ -34862,7 +35065,7 @@ msgstr "" msgid "Please select atleast one operation to create Job Card" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1699 +#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1703 msgid "Please select correct account" msgstr "" @@ -34982,7 +35185,7 @@ msgstr "" #: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js:58 #: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js:68 #: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js:78 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:788 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:789 msgid "Please set Company" msgstr "" @@ -34998,12 +35201,12 @@ msgstr "" msgid "Please set Email/Phone for the contact" msgstr "" -#: erpnext/regional/italy/utils.py:250 +#: erpnext/regional/italy/utils.py:257 #, python-format msgid "Please set Fiscal Code for the customer '%s'" msgstr "" -#: erpnext/regional/italy/utils.py:258 +#: erpnext/regional/italy/utils.py:265 #, python-format msgid "Please set Fiscal Code for the public administration '%s'" msgstr "" @@ -35029,7 +35232,7 @@ msgstr "" msgid "Please set Root Type" msgstr "" -#: erpnext/regional/italy/utils.py:265 +#: erpnext/regional/italy/utils.py:272 #, python-format msgid "Please set Tax ID for the customer '%s'" msgstr "" @@ -35054,7 +35257,7 @@ msgstr "" msgid "Please set a Cost Center for the Asset or set an Asset Depreciation Cost Center for the Company {}" msgstr "" -#: erpnext/projects/doctype/project/project.py:729 +#: erpnext/projects/doctype/project/project.py:731 msgid "Please set a default Holiday List for Company {0}" msgstr "" @@ -35070,7 +35273,7 @@ msgstr "" msgid "Please set actual demand or sales forecast to generate Material Requirements Planning Report." msgstr "" -#: erpnext/regional/italy/utils.py:220 +#: erpnext/regional/italy/utils.py:227 #, python-format msgid "Please set an Address on the Company '%s'" msgstr "" @@ -35083,11 +35286,11 @@ msgstr "" msgid "Please set an email id for the Lead {0}" msgstr "" -#: erpnext/regional/italy/utils.py:276 +#: erpnext/regional/italy/utils.py:283 msgid "Please set at least one row in the Taxes and Charges Table" msgstr "" -#: erpnext/regional/italy/utils.py:240 +#: erpnext/regional/italy/utils.py:247 msgid "Please set both the Tax ID and Fiscal Code on Company {0}" msgstr "" @@ -35144,11 +35347,11 @@ msgstr "" msgid "Please set opening number of booked depreciations" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2651 +#: erpnext/public/js/controllers/transaction.js:2644 msgid "Please set recurring after saving" msgstr "" -#: erpnext/regional/italy/utils.py:270 +#: erpnext/regional/italy/utils.py:277 msgid "Please set the Customer Address" msgstr "" @@ -35160,11 +35363,11 @@ msgstr "" msgid "Please set the Item Code first" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1533 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1635 msgid "Please set the Target Warehouse in the Job Card" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1537 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1639 msgid "Please set the WIP Warehouse in the Job Card" msgstr "" @@ -35191,7 +35394,7 @@ msgstr "" msgid "Please set {0} for Batched Item {1}, which is used to set {2} on Submit." msgstr "" -#: erpnext/regional/italy/utils.py:422 +#: erpnext/regional/italy/utils.py:429 msgid "Please set {0} for address {1}" msgstr "" @@ -35221,7 +35424,7 @@ msgstr "" #: erpnext/accounts/doctype/pos_invoice/pos_invoice.js:120 #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js:419 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:525 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:526 msgid "Please specify Company to proceed" msgstr "" @@ -35238,7 +35441,7 @@ msgstr "" msgid "Please specify at least one attribute in the Attributes table" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:615 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:618 msgid "Please specify either Quantity or Valuation Rate or both" msgstr "" @@ -35471,7 +35674,7 @@ msgstr "" msgid "Posting Date cannot be future date" msgstr "" -#: erpnext/public/js/controllers/transaction.js:1012 +#: erpnext/public/js/controllers/transaction.js:1005 msgid "Posting Date will change to today's date as Edit Posting Date and Time is unchecked. Are you sure want to proceed?" msgstr "" @@ -35533,7 +35736,7 @@ msgstr "" msgid "Posting Time" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2332 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2334 msgid "Posting date and posting time is mandatory" msgstr "" @@ -35928,7 +36131,7 @@ msgstr "" msgid "Price is not set for the item." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:515 +#: erpnext/manufacturing/doctype/bom/bom.py:566 msgid "Price not found for item {0} in price list {1}" msgstr "" @@ -36301,7 +36504,7 @@ msgstr "" msgid "Process Loss" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1169 +#: erpnext/manufacturing/doctype/bom/bom.py:1220 msgid "Process Loss Percentage cannot be greater than 100" msgstr "" @@ -36420,6 +36623,10 @@ msgstr "" msgid "Processing XML Files" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:188 +msgid "Processing import..." +msgstr "" + #: erpnext/buying/doctype/supplier/supplier_dashboard.py:10 msgid "Procurement" msgstr "" @@ -36748,7 +36955,7 @@ msgstr "" msgid "Progress (%)" msgstr "" -#: erpnext/projects/doctype/project/project.py:368 +#: erpnext/projects/doctype/project/project.py:370 msgid "Project Collaboration Invitation" msgstr "" @@ -36790,7 +36997,7 @@ msgstr "" msgid "Project Summary" msgstr "" -#: erpnext/projects/doctype/project/project.py:667 +#: erpnext/projects/doctype/project/project.py:669 msgid "Project Summary for {0}" msgstr "" @@ -36910,7 +37117,7 @@ msgstr "" #. Name of a Workspace #. Label of a Card Break in the Projects Workspace -#: erpnext/config/projects.py:7 erpnext/projects/doctype/project/project.py:445 +#: erpnext/config/projects.py:7 erpnext/projects/doctype/project/project.py:447 #: erpnext/projects/workspace/projects/projects.json #: erpnext/selling/doctype/customer/customer_dashboard.py:26 #: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:28 @@ -37040,6 +37247,11 @@ msgstr "" msgid "Prospects Engaged But Not Converted" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:196 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:779 +msgid "Protected DocType" +msgstr "" + #. Description of the 'Company Email' (Data) field in DocType 'Employee' #: erpnext/setup/doctype/employee/employee.json msgid "Provide Email Address registered in company" @@ -37316,7 +37528,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:184 +#: erpnext/stock/doctype/material_request/material_request.js:196 #: 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 @@ -37745,7 +37957,7 @@ msgstr "" #: erpnext/controllers/trends.py:268 erpnext/controllers/trends.py:280 #: erpnext/controllers/trends.py:285 #: erpnext/crm/doctype/opportunity_item/opportunity_item.json -#: erpnext/manufacturing/doctype/bom/bom.js:1075 +#: erpnext/manufacturing/doctype/bom/bom.js:1078 #: erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json #: erpnext/manufacturing/doctype/bom_item/bom_item.json #: erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json @@ -37871,11 +38083,11 @@ msgstr "" msgid "Qty To Manufacture" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1395 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1405 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:228 +#: erpnext/manufacturing/doctype/job_card/job_card.py:253 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 "" @@ -37978,7 +38190,7 @@ msgid "Qty to Fetch" msgstr "" #: erpnext/manufacturing/doctype/job_card/job_card.js:304 -#: erpnext/manufacturing/doctype/job_card/job_card.py:788 +#: erpnext/manufacturing/doctype/job_card/job_card.py:874 msgid "Qty to Manufacture" msgstr "" @@ -38192,6 +38404,20 @@ msgstr "" msgid "Quality Inspection Template Name" msgstr "" +#: erpnext/manufacturing/doctype/job_card/job_card.py:783 +msgid "Quality Inspection is required for the item {0} before completing the job card {1}" +msgstr "" + +#: erpnext/manufacturing/doctype/job_card/job_card.py:794 +#: erpnext/manufacturing/doctype/job_card/job_card.py:803 +msgid "Quality Inspection {0} is not submitted for the item: {1}" +msgstr "" + +#: erpnext/manufacturing/doctype/job_card/job_card.py:813 +#: erpnext/manufacturing/doctype/job_card/job_card.py:822 +msgid "Quality Inspection {0} is rejected for the item: {1}" +msgstr "" + #: erpnext/public/js/controllers/transaction.js:384 #: erpnext/stock/doctype/stock_entry/stock_entry.js:197 msgid "Quality Inspection(s)" @@ -38336,7 +38562,7 @@ msgstr "" #: erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py:67 #: 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:356 +#: erpnext/stock/doctype/material_request/material_request.js:368 #: 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 @@ -38464,11 +38690,11 @@ msgstr "" msgid "Quantity of item obtained after manufacturing / repacking from given quantities of raw materials" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:683 +#: erpnext/manufacturing/doctype/bom/bom.py:734 msgid "Quantity required for Item {0} in row {1}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:627 +#: erpnext/manufacturing/doctype/bom/bom.py:678 #: erpnext/manufacturing/doctype/job_card/job_card.js:385 #: erpnext/manufacturing/doctype/job_card/job_card.js:455 #: erpnext/manufacturing/doctype/workstation/workstation.js:303 @@ -38483,11 +38709,11 @@ msgstr "" msgid "Quantity to Manufacture" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2523 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2567 msgid "Quantity to Manufacture can not be zero for the operation {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1387 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1397 msgid "Quantity to Manufacture must be greater than 0." msgstr "" @@ -38532,7 +38758,7 @@ msgstr "" msgid "Queue Size should be between 5 and 100" msgstr "" -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:611 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:625 msgid "Quick Journal Entry" msgstr "" @@ -38571,7 +38797,7 @@ msgstr "" #. Label of the prevdoc_docname (Link) field in DocType 'Sales Order Item' #. Label of a Link in the Selling Workspace #. Option for the 'Transaction' (Select) field in DocType 'Authorization Rule' -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:299 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:300 #: erpnext/buying/doctype/supplier_quotation/supplier_quotation.js:51 #: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html:20 #: erpnext/crm/doctype/contract/contract.json @@ -39083,7 +39309,7 @@ msgstr "" #. Label of the section_break_8 (Section Break) field in DocType 'Job Card' #. Label of the mr_items (Table) field in DocType 'Production Plan' #: erpnext/manufacturing/doctype/bom/bom.js:406 -#: erpnext/manufacturing/doctype/bom/bom.js:1048 +#: erpnext/manufacturing/doctype/bom/bom.js:1051 #: erpnext/manufacturing/doctype/bom/bom.json #: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -39147,7 +39373,7 @@ msgstr "" msgid "Raw Materials Supplied Cost" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:675 +#: erpnext/manufacturing/doctype/bom/bom.py:726 msgid "Raw Materials cannot be blank." msgstr "" @@ -39172,7 +39398,7 @@ msgstr "" #: erpnext/manufacturing/doctype/work_order/work_order.js:733 #: erpnext/selling/doctype/sales_order/sales_order.js:968 #: erpnext/selling/doctype/sales_order/sales_order_list.js:70 -#: erpnext/stock/doctype/material_request/material_request.js:231 +#: erpnext/stock/doctype/material_request/material_request.js:243 #: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.js:116 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:164 msgid "Re-open" @@ -39712,7 +39938,7 @@ msgstr "" msgid "Reference #{0} dated {1}" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2764 +#: erpnext/public/js/controllers/transaction.js:2757 msgid "Reference Date for Early Payment Discount" msgstr "" @@ -39846,11 +40072,11 @@ msgstr "" msgid "References" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:391 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:399 msgid "References to Sales Invoices are Incomplete" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:386 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:394 msgid "References to Sales Orders are Incomplete" msgstr "" @@ -40088,14 +40314,22 @@ msgstr "" msgid "Remove Parent Row No in Items Table" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:140 +msgid "Remove Zero Counts" +msgstr "" + #: erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js:9 msgid "Remove item if charges is not applicable to that item" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:561 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:564 msgid "Removed items with no change in quantity or value." msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:161 +msgid "Removed {0} rows with zero document count. Please save to persist changes." +msgstr "" + #: erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py:87 msgid "Removing rows without exchange gain or loss" msgstr "" @@ -40474,7 +40708,7 @@ msgstr "" #: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js:70 #: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py:272 #: erpnext/buying/workspace/buying/buying.json -#: erpnext/stock/doctype/material_request/material_request.js:190 +#: erpnext/stock/doctype/material_request/material_request.js:202 msgid "Request for Quotation" msgstr "" @@ -40815,7 +41049,7 @@ msgstr "" #: erpnext/stock/doctype/bin/bin.json #: erpnext/stock/doctype/pick_list/pick_list.js:168 #: erpnext/stock/report/reserved_stock/reserved_stock.json -#: erpnext/stock/report/stock_balance/stock_balance.py:497 +#: erpnext/stock/report/stock_balance/stock_balance.py:499 #: erpnext/stock/stock_ledger.py:2269 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:205 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:333 @@ -40873,7 +41107,7 @@ msgstr "" msgid "Reserving Stock..." msgstr "" -#. Label of the reset_company_default_values (Check) field in DocType +#. Label of the reset_company_default_values_status (Select) field in DocType #. 'Transaction Deletion Record' #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json msgid "Reset Company Default Values" @@ -41149,7 +41383,7 @@ msgstr "" msgid "Return" msgstr "" -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:110 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:111 msgid "Return / Credit Note" msgstr "" @@ -41715,7 +41949,7 @@ msgstr "" msgid "Row # {0}: Returned Item {1} does not exist in {2} {3}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:260 +#: erpnext/manufacturing/doctype/work_order/work_order.py:270 msgid "Row #1: Sequence ID must be 1 for Operation {0}." msgstr "" @@ -41839,7 +42073,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:1026 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1112 msgid "Row #{0}: Cannot transfer more than Required Qty {1} for Item {2} against Job Card {3}" msgstr "" @@ -41889,11 +42123,11 @@ msgstr "" msgid "Row #{0}: Customer Provided Item {1} cannot be added multiple times in the Subcontracting Inward process." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:337 +#: erpnext/manufacturing/doctype/work_order/work_order.py:347 msgid "Row #{0}: Customer Provided Item {1} cannot be added multiple times." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:362 +#: erpnext/manufacturing/doctype/work_order/work_order.py:372 msgid "Row #{0}: Customer Provided Item {1} does not exist in the Required Items table linked to the Subcontracting Inward Order." msgstr "" @@ -41901,7 +42135,7 @@ msgstr "" msgid "Row #{0}: Customer Provided Item {1} exceeds quantity available through Subcontracting Inward Order" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:350 +#: erpnext/manufacturing/doctype/work_order/work_order.py:360 msgid "Row #{0}: Customer Provided Item {1} has insufficient quantity in the Subcontracting Inward Order. Available quantity is {2}." msgstr "" @@ -41986,7 +42220,7 @@ msgstr "" msgid "Row #{0}: From Date cannot be before To Date" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:778 +#: erpnext/manufacturing/doctype/job_card/job_card.py:864 msgid "Row #{0}: From Time and To Time fields are required" msgstr "" @@ -41994,7 +42228,7 @@ msgstr "" msgid "Row #{0}: Item added" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1535 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1536 msgid "Row #{0}: Item {1} cannot be transferred more than {2} against {3} {4}" msgstr "" @@ -42022,7 +42256,7 @@ msgstr "" msgid "Row #{0}: Item {1} is not a Customer Provided Item." msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:758 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:761 msgid "Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it." msgstr "" @@ -42193,11 +42427,11 @@ msgstr "" msgid "" "Row #{0}: Selling rate for item {1} is lower than its {2}.\n" "\t\t\t\t\tSelling {3} should be atleast {4}.

Alternatively,\n" -"\t\t\t\t\tyou can disable selling price validation in {5} to bypass\n" +"\t\t\t\t\tyou can disable '{5}' in {6} to bypass\n" "\t\t\t\t\tthis validation." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:266 +#: erpnext/manufacturing/doctype/work_order/work_order.py:276 msgid "Row #{0}: Sequence ID must be {1} or {2} for Operation {3}." msgstr "" @@ -42241,19 +42475,19 @@ msgstr "" msgid "Row #{0}: Source Warehouse must be same as Customer Warehouse {1} from the linked Subcontracting Inward Order" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:371 +#: erpnext/manufacturing/doctype/work_order/work_order.py:381 msgid "Row #{0}: Source Warehouse {1} for item {2} cannot be a customer warehouse." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:326 +#: erpnext/manufacturing/doctype/work_order/work_order.py:336 msgid "Row #{0}: Source Warehouse {1} for item {2} must be same as Source Warehouse {3} in the Work Order." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1015 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1016 msgid "Row #{0}: Source and Target Warehouse cannot be the same for Material Transfer" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1037 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1038 msgid "Row #{0}: Source, Target Warehouse and Inventory Dimensions cannot be the exact same for Material Transfer" msgstr "" @@ -42289,7 +42523,7 @@ msgstr "" msgid "Row #{0}: Stock is already reserved for the Item {1}." msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:545 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:553 msgid "Row #{0}: Stock is reserved for item {1} in warehouse {2}." msgstr "" @@ -42459,7 +42693,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:706 +#: erpnext/manufacturing/doctype/job_card/job_card.py:731 msgid "Row {0} : Operation is required against the raw material item {1}" msgstr "" @@ -42467,7 +42701,7 @@ msgstr "" msgid "Row {0} picked quantity is less than the required quantity, additional {1} {2} required." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1559 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1560 msgid "Row {0}# Item {1} not found in 'Raw Materials Supplied' table in {2} {3}" msgstr "" @@ -42499,7 +42733,7 @@ msgstr "" msgid "Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1220 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1221 msgid "Row {0}: As {1} is enabled, raw materials cannot be added to {2} entry. Use {3} entry to consume raw materials." msgstr "" @@ -42533,7 +42767,7 @@ msgstr "" msgid "Row {0}: Credit entry can not be linked with a {1}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:487 +#: erpnext/manufacturing/doctype/bom/bom.py:538 msgid "Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2}" msgstr "" @@ -42541,7 +42775,7 @@ msgstr "" msgid "Row {0}: Debit entry can not be linked with a {1}" msgstr "" -#: erpnext/controllers/selling_controller.py:874 +#: erpnext/controllers/selling_controller.py:868 msgid "Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same" msgstr "" @@ -42590,7 +42824,7 @@ msgstr "" msgid "Row {0}: From Time and To Time is mandatory." msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:283 +#: erpnext/manufacturing/doctype/job_card/job_card.py:308 #: erpnext/projects/doctype/timesheet/timesheet.py:225 msgid "Row {0}: From Time and To Time of {1} is overlapping with {2}" msgstr "" @@ -42599,7 +42833,7 @@ msgstr "" msgid "Row {0}: From Warehouse is mandatory for internal transfers" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:274 +#: erpnext/manufacturing/doctype/job_card/job_card.py:299 msgid "Row {0}: From time must be less than to time" msgstr "" @@ -42615,7 +42849,7 @@ msgstr "" msgid "Row {0}: Item Tax template updated as per validity and rate applied" msgstr "" -#: erpnext/controllers/selling_controller.py:639 +#: erpnext/controllers/selling_controller.py:633 msgid "Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer" msgstr "" @@ -42635,7 +42869,7 @@ msgstr "" msgid "Row {0}: Item {1}'s quantity cannot be higher than the available quantity." msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:602 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:610 msgid "Row {0}: Packed Qty must be equal to {1} Qty." msgstr "" @@ -42679,15 +42913,15 @@ msgstr "" msgid "Row {0}: Please select an valid BOM for Item {1}." msgstr "" -#: erpnext/regional/italy/utils.py:283 +#: erpnext/regional/italy/utils.py:290 msgid "Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges" msgstr "" -#: erpnext/regional/italy/utils.py:310 +#: erpnext/regional/italy/utils.py:317 msgid "Row {0}: Please set the Mode of Payment in Payment Schedule" msgstr "" -#: erpnext/regional/italy/utils.py:315 +#: erpnext/regional/italy/utils.py:322 msgid "Row {0}: Please set the correct code on Mode of Payment {1}" msgstr "" @@ -42727,7 +42961,7 @@ msgstr "" msgid "Row {0}: Shift cannot be changed since the depreciation has already been processed" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1572 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1573 msgid "Row {0}: Subcontracted Item is mandatory for the raw material {1}" msgstr "" @@ -42755,7 +42989,7 @@ msgstr "" msgid "Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3361 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3363 msgid "Row {0}: Transferred quantity cannot be greater than the requested quantity." msgstr "" @@ -42763,8 +42997,8 @@ msgstr "" msgid "Row {0}: UOM Conversion Factor is mandatory" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1152 -#: erpnext/manufacturing/doctype/work_order/work_order.py:400 +#: erpnext/manufacturing/doctype/bom/bom.py:1203 +#: erpnext/manufacturing/doctype/work_order/work_order.py:410 msgid "Row {0}: Workstation or Workstation Type is mandatory for an operation {1}" msgstr "" @@ -43108,7 +43342,7 @@ msgstr "" #: erpnext/setup/doctype/authorization_rule/authorization_rule.json #: erpnext/setup/workspace/home/home.json #: erpnext/stock/doctype/delivery_note/delivery_note.js:347 -#: erpnext/stock/doctype/delivery_note/delivery_note_list.js:65 +#: erpnext/stock/doctype/delivery_note/delivery_note_list.js:67 #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Sales Invoice" msgstr "" @@ -43189,7 +43423,7 @@ msgstr "" msgid "Sales Invoice mode is activated in POS. Please create Sales Invoice instead." msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:622 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:630 msgid "Sales Invoice {0} has already been submitted" msgstr "" @@ -43242,13 +43476,13 @@ msgstr "" #. Label of a Link in the Subcontracting Workspace #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:277 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:278 #: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:276 #: erpnext/accounts/report/sales_register/sales_register.py:238 #: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json #: erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json -#: erpnext/controllers/selling_controller.py:526 +#: erpnext/controllers/selling_controller.py:494 #: erpnext/crm/doctype/contract/contract.json #: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js:65 #: erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.json @@ -43276,7 +43510,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:224 +#: erpnext/stock/doctype/material_request/material_request.js:236 #: 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 @@ -43374,7 +43608,7 @@ msgstr "" msgid "Sales Order Trends" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:274 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:282 msgid "Sales Order required for Item {0}" msgstr "" @@ -43386,12 +43620,12 @@ msgstr "" msgid "Sales Order {0} is not submitted" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:451 +#: erpnext/manufacturing/doctype/work_order/work_order.py:461 msgid "Sales Order {0} is not valid" msgstr "" -#: erpnext/controllers/selling_controller.py:507 -#: erpnext/manufacturing/doctype/work_order/work_order.py:456 +#: erpnext/controllers/selling_controller.py:475 +#: erpnext/manufacturing/doctype/work_order/work_order.py:466 msgid "Sales Order {0} is {1}" msgstr "" @@ -43762,7 +43996,7 @@ msgstr "" msgid "Same Item" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:597 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:600 msgid "Same item and warehouse combination already entered." msgstr "" @@ -43794,12 +44028,12 @@ 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:2821 +#: erpnext/public/js/controllers/transaction.js:2814 #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Sample Size" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3851 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3853 msgid "Sample quantity {0} cannot be more than received quantity {1}" msgstr "" @@ -44269,7 +44503,7 @@ msgstr "" msgid "Select Items based on Delivery Date" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2860 +#: erpnext/public/js/controllers/transaction.js:2853 msgid "Select Items for Quality Inspection" msgstr "" @@ -44294,7 +44528,7 @@ msgstr "" msgid "Select Job Worker Address" msgstr "" -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1172 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1173 #: erpnext/selling/page/point_of_sale/pos_item_cart.js:955 msgid "Select Loyalty Program" msgstr "" @@ -44366,7 +44600,7 @@ msgstr "" msgid "Select a Company this Employee belongs to." msgstr "" -#: erpnext/buying/doctype/supplier/supplier.js:190 +#: erpnext/buying/doctype/supplier/supplier.js:182 msgid "Select a Customer" msgstr "" @@ -44382,7 +44616,7 @@ msgstr "" msgid "Select a Supplier" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:407 +#: erpnext/stock/doctype/material_request/material_request.js:419 msgid "Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only." msgstr "" @@ -44445,7 +44679,7 @@ msgstr "" msgid "Select the Item to be manufactured." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:955 +#: erpnext/manufacturing/doctype/bom/bom.js:958 msgid "Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically." msgstr "" @@ -44458,7 +44692,7 @@ msgstr "" msgid "Select the customer or supplier." msgstr "" -#: erpnext/assets/doctype/asset/asset.js:844 +#: erpnext/assets/doctype/asset/asset.js:846 msgid "Select the date" msgstr "" @@ -44466,7 +44700,7 @@ msgstr "" msgid "Select the date and your timezone" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:974 +#: erpnext/manufacturing/doctype/bom/bom.js:977 msgid "Select the raw materials (Items) required to manufacture the Item" msgstr "" @@ -44760,14 +44994,14 @@ 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:2834 +#: erpnext/public/js/controllers/transaction.js:2827 #: erpnext/public/js/utils/serial_no_batch_selector.js:421 #: erpnext/selling/doctype/installation_note_item/installation_note_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/serial_and_batch_bundle/serial_and_batch_bundle.py:158 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:159 #: erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json #: erpnext/stock/doctype/serial_no/serial_no.json #: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -44818,7 +45052,7 @@ msgstr "" msgid "Serial No Range" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2497 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2513 msgid "Serial No Reserved" msgstr "" @@ -44869,7 +45103,7 @@ msgstr "" msgid "Serial No and Batch Traceability" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1107 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1112 msgid "Serial No is mandatory" msgstr "" @@ -44898,11 +45132,11 @@ msgstr "" msgid "Serial No {0} does not exist" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3234 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:3250 msgid "Serial No {0} does not exists" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:357 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:358 msgid "Serial No {0} is already Delivered. You cannot use them again in Manufacture / Repack entry." msgstr "" @@ -44914,7 +45148,7 @@ msgstr "" msgid "Serial No {0} is already assigned to customer {1}. Can only be returned against the customer {1}" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:429 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:430 msgid "Serial No {0} is not present in the {1} {2}, hence you can't return it against the {1} {2}" msgstr "" @@ -44938,7 +45172,7 @@ msgstr "" #: erpnext/public/js/utils/serial_no_batch_selector.js:16 #: erpnext/public/js/utils/serial_no_batch_selector.js:190 #: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js:50 -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:158 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:159 msgid "Serial Nos" msgstr "" @@ -44952,7 +45186,7 @@ msgstr "" msgid "Serial Nos and Batches" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1783 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1799 msgid "Serial Nos are created successfully" msgstr "" @@ -44960,7 +45194,7 @@ msgstr "" msgid "Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding." msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:363 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:364 msgid "Serial Nos {0} are already Delivered. You cannot use them again in Manufacture / Repack entry." msgstr "" @@ -45034,11 +45268,11 @@ msgstr "" msgid "Serial and Batch Bundle" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2005 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2021 msgid "Serial and Batch Bundle created" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2077 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2093 msgid "Serial and Batch Bundle updated" msgstr "" @@ -45160,7 +45394,7 @@ msgstr "" #: erpnext/accounts/doctype/budget/budget.json #: erpnext/accounts/doctype/cashier_closing/cashier_closing.json #: erpnext/accounts/doctype/dunning/dunning.json -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:645 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:659 #: erpnext/accounts/doctype/journal_entry/journal_entry.json #: erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json #: erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -45403,12 +45637,12 @@ msgid "Service Stop Date" msgstr "" #: erpnext/accounts/deferred_revenue.py:45 -#: erpnext/public/js/controllers/transaction.js:1678 +#: erpnext/public/js/controllers/transaction.js:1671 msgid "Service Stop Date cannot be after Service End Date" msgstr "" #: erpnext/accounts/deferred_revenue.py:42 -#: erpnext/public/js/controllers/transaction.js:1675 +#: erpnext/public/js/controllers/transaction.js:1668 msgid "Service Stop Date cannot be before Service Start Date" msgstr "" @@ -45485,7 +45719,7 @@ msgstr "" msgid "Set Landed Cost Based on Purchase Invoice Rate" msgstr "" -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1184 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1185 msgid "Set Loyalty Program" msgstr "" @@ -45514,7 +45748,7 @@ msgstr "" msgid "Set Posting Date" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:1001 +#: erpnext/manufacturing/doctype/bom/bom.js:1004 msgid "Set Process Loss Item Quantity" msgstr "" @@ -45637,7 +45871,7 @@ msgstr "" msgid "Set fieldname from which you want to fetch the data from the parent form." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:991 +#: erpnext/manufacturing/doctype/bom/bom.js:994 msgid "Set quantity of process loss item:" msgstr "" @@ -45742,8 +45976,8 @@ msgstr "" msgid "Setting up company" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1131 -#: erpnext/manufacturing/doctype/work_order/work_order.py:1451 +#: erpnext/manufacturing/doctype/bom/bom.py:1182 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1461 msgid "Setting {0} is required" msgstr "" @@ -45905,7 +46139,7 @@ msgstr "" msgid "Shipment details" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:793 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:801 msgid "Shipments" msgstr "" @@ -46406,6 +46640,10 @@ msgstr "" msgid "Since there is a process loss of {0} units for the finished good {1}, you should reduce the quantity by {0} units for the finished good {1} in the Items Table." msgstr "" +#: erpnext/manufacturing/doctype/bom/bom.py:317 +msgid "Since you have enabled 'Track Semi Finished Goods', at least one operation must have 'Is Final Finished Good' checked. For that set the FG / Semi FG Item as {0} against an operation." +msgstr "" + #: erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py:111 msgid "Since {0} are Serial No/Batch No items, you cannot enable 'Recreate Stock Ledgers' in Repost Item Valuation." msgstr "" @@ -46448,6 +46686,10 @@ msgstr "" msgid "Skip Material Transfer to WIP Warehouse" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:559 +msgid "Skipped {0} DocType(s):
{1}" +msgstr "" + #. Label of the customer_skype (Data) field in DocType 'Appointment' #: erpnext/crm/doctype/appointment/appointment.json msgid "Skype ID" @@ -46611,7 +46853,7 @@ msgstr "" msgid "Source Warehouse is mandatory for the Item {0}." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:285 +#: erpnext/manufacturing/doctype/work_order/work_order.py:295 msgid "Source Warehouse {0} must be same as Customer Warehouse {1} in the Subcontracting Inward Order." msgstr "" @@ -46672,7 +46914,7 @@ msgstr "" msgid "Spending for Account {0} ({1}) between {2} and {3} has already exceeded the new allocated budget. Spent: {4}, Budget: {5}" msgstr "" -#: erpnext/assets/doctype/asset/asset.js:605 +#: erpnext/assets/doctype/asset/asset.js:607 #: erpnext/stock/doctype/batch/batch.js:91 #: erpnext/stock/doctype/batch/batch.js:183 #: erpnext/support/doctype/issue/issue.js:114 @@ -46680,7 +46922,7 @@ msgid "Split" msgstr "" #: erpnext/assets/doctype/asset/asset.js:120 -#: erpnext/assets/doctype/asset/asset.js:589 +#: erpnext/assets/doctype/asset/asset.js:591 msgid "Split Asset" msgstr "" @@ -46703,7 +46945,7 @@ msgstr "" msgid "Split Issue" msgstr "" -#: erpnext/assets/doctype/asset/asset.js:595 +#: erpnext/assets/doctype/asset/asset.js:597 msgid "Split Qty" msgstr "" @@ -46844,10 +47086,6 @@ msgstr "" msgid "Start Date should be lower than End Date" msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:21 -msgid "Start Deletion" -msgstr "" - #: erpnext/manufacturing/doctype/job_card/job_card.js:215 #: erpnext/manufacturing/doctype/workstation/workstation.js:124 msgid "Start Job" @@ -46942,7 +47180,7 @@ msgstr "" msgid "Status Illustration" msgstr "" -#: erpnext/projects/doctype/project/project.py:710 +#: erpnext/projects/doctype/project/project.py:712 msgid "Status must be Cancelled or Completed" msgstr "" @@ -47143,7 +47381,7 @@ msgstr "" msgid "Stock Entry {0} created" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1396 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1498 msgid "Stock Entry {0} has created" msgstr "" @@ -47380,7 +47618,7 @@ msgstr "" #: erpnext/stock/doctype/pick_list/pick_list.js:165 #: erpnext/stock/doctype/pick_list/pick_list.js:170 #: erpnext/stock/doctype/stock_entry/stock_entry_dashboard.py:12 -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:735 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:738 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:674 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1237 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1644 @@ -47407,12 +47645,12 @@ msgstr "" #: erpnext/controllers/subcontracting_inward_controller.py:1003 #: erpnext/manufacturing/doctype/production_plan/production_plan.py:2234 -#: erpnext/manufacturing/doctype/work_order/work_order.py:2065 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2109 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1777 msgid "Stock Reservation Entries Created" msgstr "" -#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:453 +#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:430 msgid "Stock Reservation Entries created" msgstr "" @@ -47435,7 +47673,7 @@ msgstr "" msgid "Stock Reservation Entry created against a Pick List cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one." msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:555 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:563 msgid "Stock Reservation Warehouse Mismatch" msgstr "" @@ -47580,7 +47818,7 @@ msgstr "" #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json #: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:35 #: erpnext/stock/report/reserved_stock/reserved_stock.py:110 -#: erpnext/stock/report/stock_balance/stock_balance.py:436 +#: erpnext/stock/report/stock_balance/stock_balance.py:438 #: erpnext/stock/report/stock_ledger/stock_ledger.py:215 #: erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json #: erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.json @@ -47761,7 +47999,7 @@ msgstr "" msgid "Stop Reason" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1061 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1071 msgid "Stopped Work Order cannot be cancelled, Unstop it first to cancel" msgstr "" @@ -47915,7 +48153,7 @@ msgstr "" msgid "Subcontracted Item To Be Received" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:212 +#: erpnext/stock/doctype/material_request/material_request.js:224 msgid "Subcontracted Purchase Order" msgstr "" @@ -48053,7 +48291,7 @@ msgstr "" #. Label of the subcontracting_order (Link) field in DocType 'Subcontracting #. Receipt Supplied Item' #: erpnext/buying/doctype/purchase_order/purchase_order.js:399 -#: erpnext/controllers/subcontracting_controller.py:1159 +#: erpnext/controllers/subcontracting_controller.py:1167 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json #: erpnext/stock/doctype/stock_entry/stock_entry.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json @@ -48369,7 +48607,7 @@ msgstr "" msgid "Successfully imported {0} records." msgstr "" -#: erpnext/buying/doctype/supplier/supplier.js:212 +#: erpnext/buying/doctype/supplier/supplier.js:204 msgid "Successfully linked to Customer" msgstr "" @@ -48809,7 +49047,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:196 +#: erpnext/stock/doctype/material_request/material_request.js:208 msgid "Supplier Quotation" msgstr "" @@ -49279,15 +49517,15 @@ msgstr "" msgid "Target Warehouse for Finished Good must be same as Finished Good Warehouse {1} in Work Order {2} linked to the Subcontracting Inward Order." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:743 +#: erpnext/manufacturing/doctype/work_order/work_order.py:753 msgid "Target Warehouse is required before Submit" msgstr "" -#: erpnext/controllers/selling_controller.py:880 +#: erpnext/controllers/selling_controller.py:874 msgid "Target Warehouse is set for some items but the customer is not an internal customer." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:301 +#: erpnext/manufacturing/doctype/work_order/work_order.py:311 msgid "Target Warehouse {0} must be same as Delivery Warehouse {1} in the Subcontracting Inward Order Item." msgstr "" @@ -50319,7 +50557,7 @@ msgstr "" msgid "The Pick List having Stock Reservation Entries cannot be updated. If you need to make changes, we recommend canceling the existing Stock Reservation Entries before updating the Pick List." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2608 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2610 msgid "The Process Loss Qty has reset as per job cards Process Loss Qty" msgstr "" @@ -50331,11 +50569,11 @@ msgstr "" msgid "The Serial No at Row #{0}: {1} is not available in warehouse {2}." msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2494 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2510 msgid "The Serial No {0} is reserved against the {1} {2} and cannot be used for any other transaction." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1742 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1743 msgid "The Serial and Batch Bundle {0} is not valid for this transaction. The 'Type of Transaction' should be 'Outward' instead of 'Inward' in Serial and Batch Bundle {0}" msgstr "" @@ -50361,7 +50599,7 @@ msgstr "" msgid "The batch {0} is already reserved in {1} {2}. So, cannot proceed with the {3} {4}, which is created against the {5} {6}." msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1218 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1304 msgid "The completed quantity {0} of an operation {1} cannot be greater than the completed quantity {2} of a previous operation {3}." msgstr "" @@ -50398,7 +50636,7 @@ msgstr "" msgid "The field To Shareholder cannot be blank" msgstr "" -#: erpnext/stock/doctype/delivery_note/delivery_note.py:405 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:413 msgid "The field {0} in row {1} is not set" msgstr "" @@ -50577,8 +50815,8 @@ msgstr "" msgid "The seller and the buyer cannot be the same" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:175 -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:187 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:176 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:188 msgid "The serial and batch bundle {0} not linked to {1} {2}" msgstr "" @@ -50602,7 +50840,7 @@ msgstr "" 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 "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:729 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:732 msgid "The stock has been reserved for the following Items and Warehouses, un-reserve the same to {0} the Stock Reconciliation:

{1}" msgstr "" @@ -50616,11 +50854,11 @@ msgstr "" msgid "The system will create a Sales Invoice or a POS Invoice from the POS interface based on this setting. For high-volume transactions, it is recommended to use POS Invoice." msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:1024 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:1027 msgid "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:1035 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:1038 msgid "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage" msgstr "" @@ -50676,11 +50914,11 @@ msgstr "" msgid "The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse." msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:791 +#: erpnext/manufacturing/doctype/job_card/job_card.py:877 msgid "The {0} ({1}) must be equal to {2} ({3})" msgstr "" -#: erpnext/public/js/controllers/transaction.js:3301 +#: erpnext/public/js/controllers/transaction.js:3294 msgid "The {0} contains Unit Price Items." msgstr "" @@ -50696,7 +50934,7 @@ msgstr "" msgid "The {0} {1} does not match with the {0} {2} in the {3} {4}" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:894 +#: erpnext/manufacturing/doctype/job_card/job_card.py:980 msgid "The {0} {1} is used to calculate the valuation cost for the finished good {2}." msgstr "" @@ -50760,7 +50998,7 @@ msgstr "" msgid "There is no batch found against the {0}: {1}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1679 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1680 msgid "There must be atleast 1 Finished Good in this Stock Entry" msgstr "" @@ -50831,7 +51069,7 @@ msgstr "" msgid "This covers all scorecards tied to this Setup" msgstr "" -#: erpnext/controllers/status_updater.py:459 +#: erpnext/controllers/status_updater.py:460 msgid "This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?" msgstr "" @@ -51032,7 +51270,7 @@ msgstr "" msgid "This will restrict user access to other employee records" msgstr "" -#: erpnext/controllers/selling_controller.py:881 +#: erpnext/controllers/selling_controller.py:875 msgid "This {} will be treated as material transfer." msgstr "" @@ -51143,7 +51381,7 @@ msgstr "" msgid "Time in mins." msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:770 +#: erpnext/manufacturing/doctype/job_card/job_card.py:856 msgid "Time logs are required for {0} {1}" msgstr "" @@ -51166,7 +51404,7 @@ msgstr "" #. Name of a DocType #. Label of a Link in the Projects Workspace -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1065 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1066 #: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py:26 #: erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py:59 @@ -51274,6 +51512,10 @@ msgstr "" msgid "To Datetime" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:118 +msgid "To Delete list generated with {0} DocTypes" +msgstr "" + #. Option for the 'Sales Order Status' (Select) field in DocType 'Production #. Plan' #. Option for the 'Status' (Select) field in DocType 'Sales Order' @@ -51456,7 +51698,7 @@ msgstr "" msgid "To Warehouse (Optional)" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:969 +#: erpnext/manufacturing/doctype/bom/bom.js:972 msgid "To add Operations tick the 'With Operations' checkbox." msgstr "" @@ -51464,11 +51706,11 @@ msgstr "" msgid "To add subcontracted Item's raw materials if include exploded items is disabled." msgstr "" -#: erpnext/controllers/status_updater.py:452 +#: erpnext/controllers/status_updater.py:453 msgid "To allow over billing, update \"Over Billing Allowance\" in Accounts Settings or the Item." msgstr "" -#: erpnext/controllers/status_updater.py:448 +#: erpnext/controllers/status_updater.py:449 msgid "To allow over receipt / delivery, update \"Over Receipt/Delivery Allowance\" in Stock Settings or the Item." msgstr "" @@ -51815,11 +52057,15 @@ 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:787 +#: erpnext/manufacturing/doctype/job_card/job_card.py:873 #: erpnext/manufacturing/report/job_card_summary/job_card_summary.py:174 msgid "Total Completed Qty" msgstr "" +#: erpnext/manufacturing/doctype/job_card/job_card.py:190 +msgid "Total Completed Qty is required for Job Card {0}, please start and complete the job card before submission" +msgstr "" + #. Label of the total_consumed_material_cost (Currency) field in DocType #. 'Project' #: erpnext/projects/doctype/project/project.json @@ -52514,8 +52760,8 @@ msgstr "" msgid "Transaction Date" msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:507 -msgid "Transaction Deletion Document: {0} is running for this Company. {1}" +#: erpnext/setup/doctype/company/company.py:1104 +msgid "Transaction Deletion Document {0} has been triggered for company {1}" msgstr "" #. Name of a DocType @@ -52533,6 +52779,19 @@ msgstr "" msgid "Transaction Deletion Record Item" msgstr "" +#. Name of a DocType +#: erpnext/setup/doctype/transaction_deletion_record_to_delete/transaction_deletion_record_to_delete.json +msgid "Transaction Deletion Record To Delete" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1096 +msgid "Transaction Deletion Record {0} is already running. {1}" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:1115 +msgid "Transaction Deletion Record {0} is currently deleting {1}. Cannot save documents until deletion completes." +msgstr "" + #. Label of the transaction_details_section (Section Break) field in DocType #. 'GL Entry' #. Label of the transaction_details (Section Break) field in DocType 'Payment @@ -52607,7 +52866,7 @@ msgstr "" msgid "Transaction from which tax is withheld" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:763 +#: erpnext/manufacturing/doctype/job_card/job_card.py:849 msgid "Transaction not allowed against stopped Work Order {0}" msgstr "" @@ -53106,7 +53365,7 @@ msgstr "" msgid "UOM Name" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3773 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3775 msgid "UOM conversion factor required for UOM: {0} in Item: {1}" msgstr "" @@ -53148,6 +53407,10 @@ msgstr "" msgid "UnReconcile Allocations" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:462 +msgid "Unable to fetch DocType details. Please contact system administrator." +msgstr "" + #: erpnext/setup/utils.py:182 msgid "Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually" msgstr "" @@ -53161,7 +53424,7 @@ msgstr "" msgid "Unable to find score starting at {0}. You need to have standing scores covering 0 to 100" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1019 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1029 msgid "Unable to find the time slot in the next {0} days for the operation {1}. Please increase the 'Capacity Planning For (Days)' in the {2}." msgstr "" @@ -53233,6 +53496,10 @@ msgstr "" msgid "Under Working Hours table, you can add start and end times for a Workstation. For example, a Workstation may be active from 9 am to 1 pm, then 2 pm to 5 pm. You can also specify the working hours based on shifts. While scheduling a Work Order, the system will check for the availability of the Workstation based on the working hours specified." msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:931 +msgid "Unexpected Naming Series Pattern" +msgstr "" + #. Option for the 'Fulfilment Status' (Select) field in DocType 'Contract' #: erpnext/crm/doctype/contract/contract.json msgid "Unfulfilled" @@ -53890,7 +54157,7 @@ msgstr "" msgid "Use Transaction Date Exchange Rate" msgstr "" -#: erpnext/projects/doctype/project/project.py:561 +#: erpnext/projects/doctype/project/project.py:563 msgid "Use a name that is different from previous project name" msgstr "" @@ -53926,7 +54193,7 @@ msgstr "" #. Label of the user_remark (Small Text) field in DocType 'Journal Entry' #. Label of the user_remark (Small Text) field in DocType 'Journal Entry #. Account' -#: erpnext/accounts/doctype/journal_entry/journal_entry.js:641 +#: erpnext/accounts/doctype/journal_entry/journal_entry.js:655 #: erpnext/accounts/doctype/journal_entry/journal_entry.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json msgid "User Remark" @@ -54241,7 +54508,7 @@ msgstr "" #: 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:67 -#: erpnext/stock/report/stock_balance/stock_balance.py:487 +#: erpnext/stock/report/stock_balance/stock_balance.py:489 #: erpnext/stock/report/stock_ledger/stock_ledger.py:298 msgid "Valuation Rate" msgstr "" @@ -54262,7 +54529,7 @@ msgstr "" msgid "Valuation Rate is mandatory if Opening Stock entered" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:781 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:784 msgid "Valuation Rate required for Item {0} at row {1}" msgstr "" @@ -54272,7 +54539,7 @@ msgstr "" msgid "Valuation and Total" msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:1001 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:1004 msgid "Valuation rate for customer provided items has been set to zero." msgstr "" @@ -54642,6 +54909,10 @@ msgstr "" msgid "Vimeo" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:214 +msgid "Virtual DocType" +msgstr "" + #: erpnext/templates/pages/help.html:46 msgid "Visit the forums" msgstr "" @@ -54770,7 +55041,7 @@ msgstr "" msgid "Voucher No" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1333 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1338 msgid "Voucher No is mandatory" msgstr "" @@ -55013,7 +55284,7 @@ msgid "Warehouse not found against the account {0}" msgstr "" #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1215 -#: erpnext/stock/doctype/delivery_note/delivery_note.py:432 +#: erpnext/stock/doctype/delivery_note/delivery_note.py:440 msgid "Warehouse required for stock Item {0}" msgstr "" @@ -55159,11 +55430,11 @@ msgstr "" msgid "Warning: Another {0} # {1} exists against stock entry {2}" msgstr "" -#: erpnext/stock/doctype/material_request/material_request.js:544 +#: erpnext/stock/doctype/material_request/material_request.js:556 msgid "Warning: Material Requested Qty is less than Minimum Order Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1436 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1446 msgid "Warning: Quantity exceeds maximum producible quantity based on quantity of raw materials received through the Subcontracting Inward Order {0}." msgstr "" @@ -55171,6 +55442,10 @@ msgstr "" msgid "Warning: Sales Order {0} already exists against Customer's Purchase Order {1}" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:75 +msgid "Warning: This action cannot be undone!" +msgstr "" + #: erpnext/accounts/doctype/financial_report_template/financial_report_validation.py:77 msgid "Warnings" msgstr "" @@ -55387,6 +55662,10 @@ msgstr "" msgid "What do you need help with?" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js:82 +msgid "What will be deleted:" +msgstr "" + #. Label of the whatsapp_no (Data) field in DocType 'Lead' #. Label of the whatsapp (Data) field in DocType 'Opportunity' #: erpnext/crm/doctype/lead/lead.json @@ -55579,7 +55858,7 @@ msgstr "" #: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:104 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json #: erpnext/selling/doctype/sales_order/sales_order.js:1050 -#: erpnext/stock/doctype/material_request/material_request.js:204 +#: erpnext/stock/doctype/material_request/material_request.js:216 #: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/material_request/material_request.py:899 #: erpnext/stock/doctype/pick_list/pick_list.json @@ -55645,12 +55924,12 @@ msgstr "" msgid "Work Order cannot be created for following reason:
{0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1380 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1390 msgid "Work Order cannot be raised against a Item Template" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2387 -#: erpnext/manufacturing/doctype/work_order/work_order.py:2467 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2431 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2511 msgid "Work Order has been {0}" msgstr "" @@ -55692,7 +55971,7 @@ msgstr "" msgid "Work-in-Progress Warehouse" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:741 +#: erpnext/manufacturing/doctype/work_order/work_order.py:751 msgid "Work-in-Progress Warehouse is required before Submit" msgstr "" @@ -56026,7 +56305,7 @@ msgid "You can only have Plans with the same billing cycle in a Subscription" msgstr "" #: erpnext/accounts/doctype/pos_invoice/pos_invoice.js:423 -#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:927 +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:928 msgid "You can only redeem max {0} points in this order." msgstr "" @@ -56046,11 +56325,11 @@ msgstr "" msgid "You can use {0} to reconcile against {1} later." msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1230 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1316 msgid "You can't make any changes to Job Card since Work Order is closed." msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:218 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:219 msgid "You can't process the serial number {0} as it has already been used in the SABB {1}. {2} if you want to inward same serial number multiple times then enabled 'Allow existing Serial No to be Manufactured/Received again' in the {3}" msgstr "" @@ -56058,7 +56337,7 @@ msgstr "" msgid "You can't redeem Loyalty Points having more value than the Total Amount." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:725 +#: erpnext/manufacturing/doctype/bom/bom.js:728 msgid "You cannot change the rate if BOM is mentioned against any Item." msgstr "" @@ -56090,7 +56369,7 @@ msgstr "" msgid "You cannot enable both the settings '{0}' and '{1}'." msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:156 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:157 msgid "You cannot outward following {0} as either they are Delivered, Inactive or located in a different warehouse." msgstr "" @@ -56138,7 +56417,7 @@ msgstr "" msgid "You have already selected items from {0} {1}" msgstr "" -#: erpnext/projects/doctype/project/project.py:361 +#: erpnext/projects/doctype/project/project.py:363 msgid "You have been invited to collaborate on the project {0}." msgstr "" @@ -56234,7 +56513,7 @@ msgstr "" msgid "[Important] [ERPNext] Auto Reorder Errors" msgstr "" -#: erpnext/controllers/status_updater.py:290 +#: erpnext/controllers/status_updater.py:291 msgid "`Allow Negative rates for Items`" msgstr "" @@ -56254,7 +56533,7 @@ msgstr "" msgid "as Title" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.js:993 +#: erpnext/manufacturing/doctype/bom/bom.js:996 msgid "as a percentage of finished item quantity" msgstr "" @@ -56496,8 +56775,8 @@ msgstr "" msgid "subscription is already cancelled." msgstr "" -#: erpnext/controllers/status_updater.py:462 -#: erpnext/controllers/status_updater.py:481 +#: erpnext/controllers/status_updater.py:463 +#: erpnext/controllers/status_updater.py:482 msgid "target_ref_field" msgstr "" @@ -56550,7 +56829,7 @@ msgstr "" msgid "{0} '{1}' not in Fiscal Year {2}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:633 +#: erpnext/manufacturing/doctype/work_order/work_order.py:643 msgid "{0} ({1}) cannot be greater than planned quantity ({2}) in Work Order {3}" msgstr "" @@ -56586,7 +56865,7 @@ msgstr "" msgid "{0} Number {1} is already used in {2} {3}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1578 +#: erpnext/manufacturing/doctype/bom/bom.py:1629 msgid "{0} Operating Cost for operation {1}" msgstr "" @@ -56728,6 +57007,10 @@ msgstr "" msgid "{0} in row {1}" msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:448 +msgid "{0} is a child table and will be deleted automatically with its parent" +msgstr "" + #: erpnext/accounts/doctype/pos_profile/pos_profile.py:94 msgid "{0} is a mandatory Accounting Dimension.
Please set a value for {0} in Accounting Dimensions section." msgstr "" @@ -56738,7 +57021,7 @@ msgstr "" msgid "{0} is added multiple times on rows: {1}" msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:201 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:624 msgid "{0} is already running for {1}" msgstr "" @@ -56791,7 +57074,7 @@ msgstr "" msgid "{0} is not enabled in {1}" msgstr "" -#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:209 +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:632 msgid "{0} is not running. Cannot trigger events for this Document" msgstr "" @@ -56827,7 +57110,7 @@ msgstr "" msgid "{0} not allowed to transact with {1}. Please change the Company or add the Company in the 'Allowed To Transact With'-Section in the Customer record." msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:522 +#: erpnext/manufacturing/doctype/bom/bom.py:573 msgid "{0} not found for item {1}" msgstr "" @@ -56843,7 +57126,7 @@ msgstr "" msgid "{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}." msgstr "" -#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:719 +#: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:722 msgid "{0} units are reserved for Item {1} in Warehouse {2}, please un-reserve the same to {3} the Stock Reconciliation." msgstr "" @@ -56896,7 +57179,7 @@ msgstr "" msgid "{0} will be set as the {1} in subsequently scanned items" msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:903 +#: erpnext/manufacturing/doctype/job_card/job_card.py:989 msgid "{0} {1}" msgstr "" @@ -56956,8 +57239,8 @@ msgstr "" msgid "{0} {1} is associated with {2}, but Party Account is {3}" msgstr "" -#: erpnext/controllers/selling_controller.py:526 -#: erpnext/controllers/subcontracting_controller.py:1159 +#: erpnext/controllers/selling_controller.py:494 +#: erpnext/controllers/subcontracting_controller.py:1167 msgid "{0} {1} is cancelled or closed" msgstr "" @@ -57094,11 +57377,27 @@ msgstr "" msgid "{0}'s {1} cannot be after {2}'s Expected End Date." msgstr "" -#: erpnext/manufacturing/doctype/job_card/job_card.py:1202 -#: erpnext/manufacturing/doctype/job_card/job_card.py:1210 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1288 +#: erpnext/manufacturing/doctype/job_card/job_card.py:1296 msgid "{0}, complete the operation {1} before the operation {2}." msgstr "" +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:519 +msgid "{0}: Child table (auto-deleted with parent)" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:514 +msgid "{0}: Not found" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:510 +msgid "{0}: Protected DocType" +msgstr "" + +#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:524 +msgid "{0}: Virtual DocType (no database table)" +msgstr "" + #: erpnext/controllers/accounts_controller.py:539 msgid "{0}: {1} does not belong to the Company: {2}" msgstr "" diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 7ba01e07851..44cddac46d6 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -580,9 +580,12 @@ frappe.ui.form.on("BOM", { frappe.ui.form.on("BOM Operation", { finished_good(frm, cdt, cdn) { let row = locals[cdt][cdn]; - if (row.finished_good === frm.doc.item) { - frappe.model.set_value(row.doctype, row.name, "is_final_finished_good", 1); - } + frappe.model.set_value( + row.doctype, + row.name, + "is_final_finished_good", + row.finished_good === frm.doc.item + ); }, bom_no(frm, cdt, cdn) { diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index a58a37b92a0..ceb2e9d0ffd 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -296,6 +296,57 @@ class BOM(WebsiteGenerator): self.set_process_loss_qty() self.validate_scrap_items() self.set_default_uom() + self.validate_semi_finished_goods() + + if self.docstatus == 1: + self.validate_raw_materials_of_operation() + + def validate_semi_finished_goods(self): + if not self.track_semi_finished_goods or not self.operations: + return + + fg_items = [] + for row in self.operations: + if not row.is_final_finished_good: + continue + + fg_items.append(row.finished_good) + + if not fg_items: + frappe.throw( + _( + "Since you have enabled 'Track Semi Finished Goods', at least one operation must have 'Is Final Finished Good' checked. For that set the FG / Semi FG Item as {0} against an operation." + ).format(bold(self.item)), + ) + + if fg_items and len(fg_items) > 1: + frappe.throw( + _( + "Only one operation can have 'Is Final Finished Good' checked when 'Track Semi Finished Goods' is enabled." + ), + ) + + def validate_raw_materials_of_operation(self): + if not self.track_semi_finished_goods or not self.operations: + return + + operation_idx_with_no_rm = {} + for row in self.operations: + if row.bom_no: + continue + + operation_idx_with_no_rm[row.idx] = row + + for row in self.items: + if row.operation_row_id and row.operation_row_id in operation_idx_with_no_rm: + del operation_idx_with_no_rm[row.operation_row_id] + + for idx, row in operation_idx_with_no_rm.items(): + frappe.throw( + _("For operation {0} at row {1}, please add raw materials or set a BOM against it.").format( + bold(row.operation), idx + ), + ) def set_default_uom(self): if not self.get("items"): diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index c9010ff54cc..f46db6e6d3a 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -23,6 +23,10 @@ from frappe.utils import ( time_diff_in_hours, ) +from erpnext.controllers.stock_controller import ( + QualityInspectionNotSubmittedError, + QualityInspectionRejectedError, +) from erpnext.manufacturing.doctype.bom.bom import add_additional_cost, get_bom_items_as_dict from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import ( get_mins_between_operations, @@ -167,6 +171,27 @@ class JobCard(Document): self.validate_work_order() self.set_employees() + if self.docstatus == 1: + self.validate_semi_finished_goods() + + def validate_semi_finished_goods(self): + if not self.track_semi_finished_goods: + return + + if self.items and not self.transferred_qty and not self.skip_material_transfer: + frappe.throw( + _( + "Materials needs to be transferred to the work in progress warehouse for the job card {0}" + ).format(self.name) + ) + + if self.docstatus == 1 and not self.total_completed_qty: + frappe.throw( + _( + "Total Completed Qty is required for Job Card {0}, please start and complete the job card before submission" + ).format(self.name) + ) + def on_update(self): self.validate_job_card_qty() @@ -732,6 +757,7 @@ class JobCard(Document): self.set_process_loss() def on_submit(self): + self.validate_inspection() self.validate_transfer_qty() self.validate_job_card() self.update_work_order() @@ -741,6 +767,66 @@ class JobCard(Document): self.update_work_order() self.set_transferred_qty() + def validate_inspection(self): + action_submit, action_reject = frappe.get_single_value( + "Stock Settings", + ["action_if_quality_inspection_is_not_submitted", "action_if_quality_inspection_is_rejected"], + ) + + item = self.finished_good or self.production_item + bom_inspection_required = frappe.db.get_value( + "BOM", self.semi_fg_bom or self.bom_no, "inspection_required" + ) + if bom_inspection_required: + if not self.quality_inspection: + frappe.throw( + _( + "Quality Inspection is required for the item {0} before completing the job card {1}" + ).format(get_link_to_form("Item", item), bold(self.name)) + ) + qa_status, docstatus = frappe.db.get_value( + "Quality Inspection", self.quality_inspection, ["status", "docstatus"] + ) + + if docstatus != 1: + if action_submit == "Stop": + frappe.throw( + _("Quality Inspection {0} is not submitted for the item: {1}").format( + get_link_to_form("Quality Inspection", self.quality_inspection), + get_link_to_form("Item", item), + ), + title=_("Inspection Submission"), + exc=QualityInspectionNotSubmittedError, + ) + else: + frappe.msgprint( + _("Quality Inspection {0} is not submitted for the item: {1}").format( + get_link_to_form("Quality Inspection", self.quality_inspection), + get_link_to_form("Item", item), + ), + alert=True, + indicator="orange", + ) + elif qa_status == "Rejected": + if action_reject == "Stop": + frappe.throw( + _("Quality Inspection {0} is rejected for the item: {1}").format( + get_link_to_form("Quality Inspection", self.quality_inspection), + get_link_to_form("Item", item), + ), + title=_("Inspection Rejected"), + exc=QualityInspectionRejectedError, + ) + else: + frappe.msgprint( + _("Quality Inspection {0} is rejected for the item: {1}").format( + get_link_to_form("Quality Inspection", self.quality_inspection), + get_link_to_form("Item", item), + ), + alert=True, + indicator="orange", + ) + def validate_transfer_qty(self): if ( not self.finished_good @@ -1251,10 +1337,26 @@ class JobCard(Document): frappe.db.set_value("Workstation", self.workstation, "status", status) def add_time_logs(self, **kwargs): - row = None kwargs = frappe._dict(kwargs) + if not kwargs.employees and kwargs.to_time: + for row in self.time_logs: + if not row.to_time and row.from_time: + row.to_time = kwargs.to_time + row.time_in_mins = time_diff_in_minutes(row.to_time, row.from_time) + if kwargs.completed_qty: + row.completed_qty = kwargs.completed_qty + row.db_update() + else: + self.add_time_logs_for_employess(kwargs) + + self.validate_time_logs(save=True) + self.save() + + def add_time_logs_for_employess(self, kwargs): + row = None update_status = False + for employee in kwargs.employees: kwargs.employee = employee.get("employee") if kwargs.from_time and not kwargs.to_time: @@ -1290,9 +1392,6 @@ class JobCard(Document): self.set_status(update_status=update_status) - self.validate_time_logs(save=True) - self.save() - def update_workstation_status(self): status_map = { "Open": "Off", @@ -1341,6 +1440,9 @@ class JobCard(Document): employees=self.employee, sub_operation=kwargs.get("sub_operation"), ) + + if self.docstatus == 1: + self.update_work_order() else: self.add_time_logs(completed_qty=kwargs.qty, employees=self.employee) self.save() diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index e6eaea39578..bcda8913238 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -20,8 +20,9 @@ from erpnext.manufacturing.doctype.job_card.job_card import ( make_stock_entry as make_stock_entry_from_jc, ) from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record -from erpnext.manufacturing.doctype.work_order.work_order import WorkOrder +from erpnext.manufacturing.doctype.work_order.work_order import WorkOrder, make_work_order from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation +from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.tests.utils import ERPNextTestSuite @@ -73,6 +74,68 @@ class TestJobCard(ERPNextTestSuite): def tearDown(self): frappe.db.rollback() + def test_quality_inspection_mandatory_check(self): + from erpnext.manufacturing.doctype.operation.test_operation import make_operation + + raw = create_item("Fabric-Raw") + cut_fg = create_item("Cut-Fabric-SFG") + stitch_fg = create_item("Stitched-TShirt-SFG") + final = create_item("Finished-TShirt") + + row = {"operation": "Cutting", "workstation": "_Test Workstation 1"} + + cutting = make_operation(row) + stitching = make_operation({"operation": "Stitching", "workstation": "_Test Workstation 1"}) + ironing = make_operation({"operation": "Ironing", "workstation": "_Test Workstation 1"}) + + cut_bom = create_semi_fg_bom(cut_fg.name, raw.name, inspection_required=1) + stitch_bom = create_semi_fg_bom(stitch_fg.name, cut_fg.name, inspection_required=0) + final_bom = frappe.new_doc( + "BOM", item=final.name, quantity=1, with_operations=1, track_semi_finished_goods=1 + ) + final_bom.append("items", {"item_code": raw.name, "qty": 1}) + final_bom.append( + "operations", + { + "operation": cutting.name, + "workstation": "_Test Workstation 1", + "bom_no": cut_bom, + "skip_material_transfer": 1, + }, + ) + final_bom.append( + "operations", + { + "operation": stitching.name, + "workstation": "_Test Workstation 1", + "bom_no": stitch_bom, + "skip_material_transfer": 1, + }, + ) + final_bom.append( + "operations", + { + "operation": ironing.name, + "workstation": "_Test Workstation 1", + "is_final_finished_good": 1, + "bom_no": final_bom.name, + "skip_material_transfer": 1, + }, + ) + final_bom.append("items", {"item_code": stitch_fg.name, "qty": 1, "operation_row_id": 3}) + final_bom.submit() + work_order = make_work_order(final_bom.name, final.name, 1, variant_items=[], use_multi_level_bom=0) + work_order.wip_warehouse = "Work In Progress - WP" + work_order.fg_warehouse = "Finished Goods - WP" + work_order.scrap_warehouse = "All Warehouses - WP" + for operation in work_order.operations: + operation.time_in_mins = 60 + + work_order.submit() + job_card = frappe.get_all("Job Card", filters={"work_order": work_order.name, "operation": "Cutting"}) + job_card_doc = frappe.get_doc("Job Card", job_card[0].name) + self.assertRaises(frappe.ValidationError, job_card_doc.submit) + def test_job_card_operations(self): job_cards = frappe.get_all( "Job Card", filters={"work_order": self.work_order.name}, fields=["operation_id", "name"] @@ -871,3 +934,13 @@ def make_wo_with_transfer_against_jc(): work_order.submit() return work_order + + +def create_semi_fg_bom(semi_fg_item, raw_item, inspection_required): + bom = frappe.new_doc("BOM") + bom.item = semi_fg_item + bom.quantity = 1 + bom.inspection_required = inspection_required + bom.append("items", {"item_code": raw_item, "qty": 1}) + bom.submit() + return bom.name diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 9ae36795fec..ab989185b73 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -497,7 +497,7 @@ class ProductionPlan(Document): item_details = get_item_details(data.item_code, throw=False) if self.combine_items: - bom_no = item_details.bom_no + bom_no = item_details.get("bom_no") if data.get("bom_no"): bom_no = data.get("bom_no") diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 78f2ba090cc..a61f95812b1 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -809,7 +809,7 @@ erpnext.work_order = { } } - if (frm.doc.status != "Stopped") { + if (frm.doc.status != "Stopped" && !frm.doc.track_semi_finished_goods) { // If "Material Consumption is check in Manufacturing Settings, allow Material Consumption if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) { if (flt(doc.material_transferred_for_manufacturing) > 0 || frm.doc.skip_transfer) { diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 80cfc0c2a6e..912131e2825 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -248,6 +248,16 @@ class WorkOrder(Document): if self.is_new() and frappe.db.get_single_value("Stock Settings", "auto_reserve_stock"): self.reserve_stock = 1 + def before_save(self): + self.set_skip_transfer_for_operations() + + def set_skip_transfer_for_operations(self): + if not self.track_semi_finished_goods: + return + + for op in self.operations: + op.skip_material_transfer = self.skip_transfer + def validate_operations_sequence(self): if all([not op.sequence_id for op in self.operations]): for op in self.operations: @@ -1599,6 +1609,7 @@ class WorkOrder(Document): "item_code": row.item_code, "voucher_detail_no": row.name, "warehouse": row.source_warehouse, + "status": ("not in", ["Closed", "Cancelled", "Completed"]), }, pluck="name", ): @@ -1807,24 +1818,10 @@ class WorkOrder(Document): elif stock_entry.job_card: # Reserve the final product for the job card. finished_good = frappe.db.get_value("Job Card", stock_entry.job_card, "finished_good") + if finished_good == self.production_item: + return - for row in stock_entry.items: - if row.item_code == finished_good: - item_details = [ - frappe._dict( - { - "item_code": row.item_code, - "stock_qty": row.qty, - "stock_reserved_qty": 0, - "warehouse": row.t_warehouse, - "voucher_no": stock_entry.work_order, - "voucher_type": "Work Order", - "name": row.name, - "delivered_qty": 0, - } - ) - ] - break + item_details = self.get_items_to_reserve_for_job_card(stock_entry, finished_good) else: # Reserve the final product for the sales order. item_details = self.get_so_details() @@ -1878,6 +1875,53 @@ class WorkOrder(Document): return items + def get_items_to_reserve_for_job_card(self, stock_entry, finished_good): + item_details = [] + for row in stock_entry.items: + if row.item_code == finished_good: + name = frappe.db.get_value( + "Work Order Item", + {"item_code": finished_good, "parent": self.name}, + "name", + ) + + sres = frappe.get_all( + "Stock Reservation Entry", + fields=["reserved_qty"], + filters={ + "voucher_no": self.name, + "item_code": finished_good, + "voucher_detail_no": name, + "warehouse": row.t_warehouse, + "docstatus": 1, + "status": "Reserved", + }, + ) + + pending_qty = row.qty + for d in sres: + pending_qty -= d.reserved_qty + + if pending_qty > 0: + item_details = [ + frappe._dict( + { + "item_code": row.item_code, + "stock_qty": pending_qty, + "stock_reserved_qty": 0, + "warehouse": row.t_warehouse, + "voucher_no": stock_entry.work_order, + "voucher_type": "Work Order", + "name": name, + "delivered_qty": 0, + } + ) + ] + + break + + return item_details + def get_wo_details(self): doctype = frappe.qb.DocType("Work Order") child_doctype = frappe.qb.DocType("Work Order Item") diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 5c84a2dc8d1..41fd4dd0e82 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -8,6 +8,8 @@ from frappe.utils import getdate, today from erpnext.stock.report.stock_analytics.stock_analytics import get_period, get_period_date_ranges +WORK_ORDER_STATUS_LIST = ["Not Started", "Overdue", "Pending", "Completed", "Closed", "Stopped"] + def execute(filters=None): columns = get_columns(filters) @@ -16,119 +18,98 @@ def execute(filters=None): def get_columns(filters): - columns = [{"label": _("Status"), "fieldname": "Status", "fieldtype": "Data", "width": 140}] - + columns = [{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 140}] ranges = get_period_date_ranges(filters) for _dummy, end_date in ranges: period = get_period(end_date, filters) - columns.append({"label": _(period), "fieldname": scrub(period), "fieldtype": "Float", "width": 120}) return columns -def get_periodic_data(filters, entry): - periodic_data = { - "Not Started": {}, - "Overdue": {}, - "Pending": {}, - "Completed": {}, - "Closed": {}, - "Stopped": {}, - } +def get_work_orders(filters): + from_date = filters.get("from_date") + to_date = filters.get("to_date") - ranges = get_period_date_ranges(filters) + WorkOrder = frappe.qb.DocType("Work Order") - for from_date, end_date in ranges: - period = get_period(end_date, filters) - for d in entry: - if getdate(from_date) <= getdate(d.creation) <= getdate(end_date) and d.status not in [ - "Draft", - "Submitted", - "Completed", - "Cancelled", - ]: - if d.status in ["Not Started", "Closed", "Stopped"]: - periodic_data = update_periodic_data(periodic_data, d.status, period) - elif getdate(today()) > getdate(d.planned_end_date): - periodic_data = update_periodic_data(periodic_data, "Overdue", period) - elif getdate(today()) < getdate(d.planned_end_date): - periodic_data = update_periodic_data(periodic_data, "Pending", period) - - if ( - getdate(from_date) <= getdate(d.actual_end_date) <= getdate(end_date) - and d.status == "Completed" - ): - periodic_data = update_periodic_data(periodic_data, "Completed", period) - - return periodic_data - - -def update_periodic_data(periodic_data, status, period): - if periodic_data.get(status).get(period): - periodic_data[status][period] += 1 - else: - periodic_data[status][period] = 1 - - return periodic_data + return ( + frappe.qb.from_(WorkOrder) + .select(WorkOrder.creation, WorkOrder.actual_end_date, WorkOrder.planned_end_date, WorkOrder.status) + .where( + (WorkOrder.docstatus == 1) + & (WorkOrder.company == filters.get("company")) + & ( + (WorkOrder.creation.between(from_date, to_date)) + | (WorkOrder.actual_end_date.between(from_date, to_date)) + ) + ) + .run(as_dict=True) + ) def get_data(filters, columns): - data = [] - entry = frappe.get_all( - "Work Order", - fields=[ - "creation", - "actual_end_date", - "planned_end_date", - "status", - ], - filters={"docstatus": 1, "company": filters["company"]}, - ) + ranges = build_ranges(filters) + period_labels = [scrub(pd) for _fd, _td, pd in ranges] + periodic_data = {status: {pd: 0 for pd in period_labels} for status in WORK_ORDER_STATUS_LIST} + entries = get_work_orders(filters) - periodic_data = get_periodic_data(filters, entry) + for d in entries: + if d.status == "Completed": + if not d.actual_end_date: + continue - labels = ["Not Started", "Overdue", "Pending", "Completed", "Closed", "Stopped"] - chart_data = get_chart_data(periodic_data, columns) - ranges = get_period_date_ranges(filters) + if period := scrub(get_period_for_date(getdate(d.actual_end_date), ranges)): + periodic_data["Completed"][period] += 1 + continue - for label in labels: - work = {} - work["Status"] = _(label) - for _dummy, end_date in ranges: - period = get_period(end_date, filters) - if periodic_data.get(label).get(period): - work[scrub(period)] = periodic_data.get(label).get(period) + creation_date = getdate(d.creation) + period = scrub(get_period_for_date(creation_date, ranges)) + if not period: + continue + + if d.status in ("Not Started", "Closed", "Stopped"): + periodic_data[d.status][period] += 1 + else: + if d.planned_end_date and getdate(today()) > getdate(d.planned_end_date): + periodic_data["Overdue"][period] += 1 else: - work[scrub(period)] = 0.0 - data.append(work) + periodic_data["Pending"][period] += 1 - return data, chart_data + data = [] + for status in WORK_ORDER_STATUS_LIST: + row = {"status": _(status)} + for _fd, _td, period in ranges: + row[scrub(period)] = periodic_data[status].get(scrub(period), 0) + data.append(row) + + chart = get_chart_data(periodic_data, columns) + return data, chart + + +def get_period_for_date(date, ranges): + for from_date, to_date, period in ranges: + if from_date <= date <= to_date: + return period + return None + + +def build_ranges(filters): + ranges = [] + for from_date, end_date in get_period_date_ranges(filters): + period = get_period(end_date, filters) + ranges.append((getdate(from_date), getdate(end_date), period)) + return ranges def get_chart_data(periodic_data, columns): - labels = [d.get("label") for d in columns[1:]] + period_labels = [d.get("label") for d in columns[1:]] + period_fieldnames = [d.get("fieldname") for d in columns[1:]] - not_start, overdue, pending, completed, closed, stopped = [], [], [], [], [], [] datasets = [] + for status in WORK_ORDER_STATUS_LIST: + values = [periodic_data.get(status, {}).get(fieldname, 0) for fieldname in period_fieldnames] + datasets.append({"name": _(status), "values": values}) - for d in labels: - not_start.append(periodic_data.get("Not Started").get(d)) - overdue.append(periodic_data.get("Overdue").get(d)) - pending.append(periodic_data.get("Pending").get(d)) - completed.append(periodic_data.get("Completed").get(d)) - closed.append(periodic_data.get("Closed").get(d)) - stopped.append(periodic_data.get("Stopped").get(d)) - - datasets.append({"name": _("Not Started"), "values": not_start}) - datasets.append({"name": _("Overdue"), "values": overdue}) - datasets.append({"name": _("Pending"), "values": pending}) - datasets.append({"name": _("Completed"), "values": completed}) - datasets.append({"name": _("Closed"), "values": closed}) - datasets.append({"name": _("Stopped"), "values": stopped}) - - chart = {"data": {"labels": labels, "datasets": datasets}} - chart["type"] = "line" - - return chart + return {"data": {"labels": period_labels, "datasets": datasets}, "type": "line"} diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 12058846096..838c837321e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -456,9 +456,11 @@ erpnext.patches.v16_0.update_tax_withholding_field_in_payment_entry erpnext.patches.v16_0.migrate_tax_withholding_data erpnext.patches.v16_0.update_corrected_cancelled_status erpnext.patches.v16_0.fix_barcode_typo +erpnext.patches.v16_0.add_accounting_dimensions_to_journal_template_accounts erpnext.patches.v16_0.set_post_change_gl_entries_on_pos_settings execute:frappe.delete_doc_if_exists("Workspace Sidebar", "Opening & Closing") erpnext.patches.v15_0.create_accounting_dimensions_in_advance_taxes_and_charges erpnext.patches.v16_0.set_ordered_qty_in_quotation_item erpnext.patches.v16_0.migrate_transaction_deletion_task_flags_to_status # 2 erpnext.patches.v16_0.update_company_custom_field_in_bin +erpnext.patches.v15_0.replace_http_with_https_in_sales_partner diff --git a/erpnext/patches/v15_0/replace_http_with_https_in_sales_partner.py b/erpnext/patches/v15_0/replace_http_with_https_in_sales_partner.py new file mode 100644 index 00000000000..80bc418920a --- /dev/null +++ b/erpnext/patches/v15_0/replace_http_with_https_in_sales_partner.py @@ -0,0 +1,10 @@ +import frappe +from frappe import qb +from pypika.functions import Replace + + +def execute(): + sp = frappe.qb.DocType("Sales Partner") + qb.update(sp).set(sp.partner_website, Replace(sp.partner_website, "http://", "https://")).where( + sp.partner_website.rlike("^http://.*") + ).run() diff --git a/erpnext/patches/v16_0/add_accounting_dimensions_to_journal_template_accounts.py b/erpnext/patches/v16_0/add_accounting_dimensions_to_journal_template_accounts.py new file mode 100644 index 00000000000..afdcbaeb706 --- /dev/null +++ b/erpnext/patches/v16_0/add_accounting_dimensions_to_journal_template_accounts.py @@ -0,0 +1,11 @@ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_dimensions, + make_dimension_in_accounting_doctypes, +) + + +def execute(): + dimensions_and_defaults = get_dimensions() + if dimensions_and_defaults: + for dimension in dimensions_and_defaults[0]: + make_dimension_in_accounting_doctypes(dimension, ["Journal Entry Template Account"]) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 355dadbc534..08b745d2c32 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -989,7 +989,7 @@ erpnext.utils.map_current_doc = function (opts) { } if (query_args.filters || query_args.query) { - opts.get_query = () => query_args; + opts.get_query = () => JSON.parse(JSON.stringify(query_args)); } if (opts.source_doctype) { diff --git a/erpnext/public/js/utils/contact_address_quick_entry.js b/erpnext/public/js/utils/contact_address_quick_entry.js index 1a1d67b475a..f6ce5d0d28b 100644 --- a/erpnext/public/js/utils/contact_address_quick_entry.js +++ b/erpnext/public/js/utils/contact_address_quick_entry.js @@ -110,12 +110,6 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm options: "Country", mandatory_depends_on: "eval:doc.city || doc.address_line1", }, - { - label: __("Customer POS Id"), - fieldname: "customer_pos_id", - fieldtype: "Data", - hidden: 1, - }, ]; return variant_fields; diff --git a/erpnext/public/js/utils/demo.js b/erpnext/public/js/utils/demo.js index e0da8985e9b..db15834ae95 100644 --- a/erpnext/public/js/utils/demo.js +++ b/erpnext/public/js/utils/demo.js @@ -28,4 +28,4 @@ erpnext.demo.clear_demo = function () { }, }); }); -}; \ No newline at end of file +}; diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index fd6f6ec812a..3db03a18513 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -621,6 +621,9 @@ def handle_mandatory_error(e, customer, lead_name): def get_ordered_items(quotation: str): return frappe._dict( frappe.get_all( - "Quotation Item", {"docstatus": 1, "parent": quotation}, ["name", "ordered_qty"], as_list=True + "Quotation Item", + {"docstatus": 1, "parent": quotation, "ordered_qty": (">", 0)}, + ["name", "ordered_qty"], + as_list=True, ) ) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index de17b04c450..609b093bae3 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -11,18 +11,18 @@ "field_order": [ "customer_section", "column_break0", + "company", "naming_series", - "customer", - "customer_name", - "tax_id", "order_type", "column_break_7", "transaction_date", "delivery_date", "column_break1", + "customer", + "customer_name", + "tax_id", "po_no", "po_date", - "company", "skip_delivery_note", "has_unit_price_items", "is_subcontracted", @@ -1458,9 +1458,9 @@ }, { "default": "0", + "depends_on": "eval:doc.order_type == 'Maintenance';", "fieldname": "skip_delivery_note", "fieldtype": "Check", - "hidden": 1, "hide_days": 1, "hide_seconds": 1, "label": "Skip Delivery Note", @@ -1713,7 +1713,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2026-01-29 21:23:48.362401", + "modified": "2026-02-06 11:06:16.092658", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index b3cb42906cc..3cb326cbcc3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -57,6 +57,28 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): frappe.db.rollback() frappe.set_user("Administrator") + def test_sales_order_skip_delivery_note(self): + so = make_sales_order(do_not_submit=True) + so.order_type = "Maintenance" + so.skip_delivery_note = 1 + so.append( + "items", + { + "item_code": "_Test Item 2", + "qty": 2, + "rate": 100, + }, + ) + so.save() + so.submit() + + so.reload() + si = make_sales_invoice(so.name) + si.insert() + si.submit() + so.reload() + self.assertEqual(so.status, "Completed") + @IntegrationTestCase.change_settings("Selling Settings", {"allow_negative_rates_for_items": 1}) def test_sales_order_with_negative_rate(self): """ diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 3777e330e75..7f087768fe7 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -162,8 +162,6 @@ class EmailDigest(Document): context.purchase_order_list, context.purchase_orders_items_overdue_list, ) = self.get_purchase_orders_items_overdue_list() - if not context.purchase_order_list: - frappe.throw(_("No items to be received are overdue")) if not context: return None diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py index 36c24c0f37e..6ef5b6dc646 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.py +++ b/erpnext/setup/doctype/sales_partner/sales_partner.py @@ -50,8 +50,17 @@ class SalesPartner(WebsiteGenerator): if not self.route: self.route = "partners/" + self.scrub(self.partner_name) super().validate() - if self.partner_website and not self.partner_website.startswith("http"): - self.partner_website = "http://" + self.partner_website + if self.partner_website: + from urllib.parse import urlsplit, urlunsplit + + # scrub http + parts = urlsplit(self.partner_website) + if not parts.netloc and parts.path: + parts = parts._replace(netloc=parts.path, path="") + if not parts.scheme or parts.scheme == "http": + parts = parts._replace(scheme="https") + + self.partner_website = urlunsplit(parts) def get_context(self, context): address_names = frappe.db.get_all( diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py index 3d7c21639e0..d77ed7a6212 100644 --- a/erpnext/stock/dashboard/item_dashboard.py +++ b/erpnext/stock/dashboard/item_dashboard.py @@ -1,5 +1,5 @@ import frappe -from frappe.model.db_query import DatabaseQuery +from frappe.desk.reportview import build_match_conditions from frappe.utils import cint, flt from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( @@ -30,7 +30,7 @@ def get_data( filters.append(["item_code", "in", items]) try: # check if user has any restrictions based on user permissions on warehouse - if DatabaseQuery("Warehouse", user=frappe.session.user).build_match_conditions(): + if build_match_conditions("Warehouse", user=frappe.session.user): filters.append(["warehouse", "in", [w.name for w in frappe.get_list("Warehouse")]]) except frappe.PermissionError: # user does not have access on warehouse diff --git a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py index 24e0ef11ffa..75b2951e30b 100644 --- a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py +++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py @@ -1,5 +1,5 @@ import frappe -from frappe.model.db_query import DatabaseQuery +from frappe.desk.reportview import build_match_conditions from frappe.utils import flt, nowdate from erpnext.stock.utils import get_stock_balance @@ -54,7 +54,7 @@ def get_filters(item_code=None, warehouse=None, parent_warehouse=None, company=N def get_warehouse_filter_based_on_permissions(filters): try: # check if user has any restrictions based on user permissions on warehouse - if DatabaseQuery("Warehouse", user=frappe.session.user).build_match_conditions(): + if build_match_conditions("Warehouse", user=frappe.session.user): filters.append(["warehouse", "in", [w.name for w in frappe.get_list("Warehouse")]]) return False, filters except frappe.PermissionError: diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index 6154a982c98..31d6cc1f241 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -123,6 +123,80 @@ class TestBatch(IntegrationTestCase): for d in batches: self.assertEqual(d.qty, batchwise_qty[(d.batch_no, d.warehouse)]) + def test_batch_qty_on_pos_creation(self): + from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import ( + init_user_and_profile, + ) + from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice + from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry + from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import ( + get_auto_batch_nos, + ) + from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_batch_item_with_batch, + ) + + invoice_type = frappe.db.get_single_value("POS Settings", "invoice_type") + session_user = frappe.session.user + + try: + # Set invoice type to POS Invoice + frappe.db.set_single_value("POS Settings", "invoice_type", "POS Invoice") + + # Create batch item + create_batch_item_with_batch("_Test BATCH ITEM", "TestBatch-RS 02") + + # Create stock entry + se = make_stock_entry( + target="_Test Warehouse - _TC", + item_code="_Test BATCH ITEM", + qty=30, + basic_rate=100, + ) + + se.reload() + + batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle) + + # Create opening entry + session_user = frappe.session.user + test_user, pos_profile = init_user_and_profile() + create_opening_entry(pos_profile, test_user.name) + + # POS Invoice 1, for the batch without bundle + pos_inv1 = create_pos_invoice(item="_Test BATCH ITEM", rate=300, qty=15, do_not_save=1) + pos_inv1.append( + "payments", + {"mode_of_payment": "Cash", "amount": 4500}, + ) + pos_inv1.items[0].batch_no = batch_no + pos_inv1.save() + pos_inv1.submit() + pos_inv1.reload() + + # Get auto batch nos after pos invoice + batches = get_auto_batch_nos( + frappe._dict( + { + "item_code": "_Test BATCH ITEM", + "warehouse": "_Test Warehouse - _TC", + "for_stock_levels": True, + "ignore_reserved_stock": True, + } + ) + ) + + # Check batch qty after pos invoice + row = _find_batch_row(batches, batch_no, "_Test Warehouse - _TC") + self.assertIsNotNone(row) + self.assertEqual(row.qty, 30) + + finally: + # Set invoice type to Sales Invoice + frappe.db.set_single_value("POS Settings", "invoice_type", invoice_type) + # Set user to session user + frappe.set_user(session_user) + def test_stock_entry_incoming(self): """Test batch creation via Stock Entry (Work Order)""" @@ -610,6 +684,10 @@ def create_price_list_for_batch(item_code, batch, rate): ).insert() +def _find_batch_row(batches, batch_no, warehouse): + return next((b for b in batches if b.batch_no == batch_no and b.warehouse == warehouse), None) + + def make_new_batch(**args): args = frappe._dict(args) diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 0c50df0c23b..469e4d5e53a 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -139,7 +139,7 @@ class InventoryDimension(Document): self.source_fieldname = scrub(self.dimension_name) if not self.target_fieldname: - self.target_fieldname = scrub(self.reference_document) + self.target_fieldname = scrub(self.dimension_name) def on_update(self): self.add_custom_fields() diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index fd9f12ddd46..1d7c475d1ae 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -117,12 +117,12 @@ class TestInventoryDimension(IntegrationTestCase): inward.load_from_db() sle_data = frappe.db.get_value( - "Stock Ledger Entry", {"voucher_no": inward.name}, ["shelf", "warehouse"], as_dict=1 + "Stock Ledger Entry", {"voucher_no": inward.name}, ["to_shelf", "warehouse"], as_dict=1 ) self.assertEqual(inward.items[0].to_shelf, "Shelf 1") self.assertEqual(sle_data.warehouse, warehouse) - self.assertEqual(sle_data.shelf, "Shelf 1") + self.assertEqual(sle_data.to_shelf, "Shelf 1") outward = make_stock_entry( item_code=item_code, diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 1975343c90b..b730080ea35 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -30,7 +30,10 @@ frappe.ui.form.on("Material Request", { frm.set_query("from_warehouse", "items", function (doc) { return { - filters: { company: doc.company }, + filters: { + company: doc.company, + is_group: 0, + }, }; }); @@ -70,19 +73,28 @@ frappe.ui.form.on("Material Request", { frm.set_query("warehouse", "items", function (doc) { return { - filters: { company: doc.company }, + filters: { + company: doc.company, + is_group: 0, + }, }; }); frm.set_query("set_warehouse", function (doc) { return { - filters: { company: doc.company }, + filters: { + company: doc.company, + is_group: 0, + }, }; }); frm.set_query("set_from_warehouse", function (doc) { return { - filters: { company: doc.company }, + filters: { + company: doc.company, + is_group: 0, + }, }; }); diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index b98870788a5..1868730ffd3 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -777,6 +777,9 @@ def make_stock_entry(source_name, target_doc=None): target.purpose = source.material_request_type target.from_warehouse = source.set_from_warehouse target.to_warehouse = source.set_warehouse + if source.material_request_type == "Material Issue": + target.from_warehouse = source.set_warehouse + target.to_warehouse = None if source.job_card: target.purpose = "Material Transfer for Manufacture" diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 24a30581188..da5bfd270df 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -1017,15 +1017,27 @@ class TestMaterialRequest(IntegrationTestCase): import json from erpnext.stock.doctype.pick_list.pick_list import create_stock_entry + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry - mr = make_material_request(material_request_type="Material Transfer") + new_item = create_item("_Test Pick List Item", is_stock_item=1) + item_code = new_item.name + + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=10, + do_not_save=False, + do_not_submit=False, + ) + + mr = make_material_request(item_code=item_code, material_request_type="Material Transfer") pl = create_pick_list(mr.name) pl.save() pl.locations[0].qty = 5 pl.locations[0].stock_qty = 5 pl.submit() - to_warehouse = create_warehouse("Test To Warehouse") + to_warehouse = create_warehouse("_Test Warehouse - _TC") se_data = create_stock_entry(json.dumps(pl.as_dict())) se = frappe.get_doc(se_data) @@ -1044,6 +1056,15 @@ class TestMaterialRequest(IntegrationTestCase): def test_mr_pick_list_qty_validation(self): """Test for checking pick list qty validation from Material Request""" + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + make_stock_entry( + item_code="_Test Item", + target="_Test Warehouse - _TC", + qty=10, + do_not_save=False, + do_not_submit=False, + ) mr = make_material_request(material_request_type="Material Transfer") pl = create_pick_list(mr.name) @@ -1104,6 +1125,19 @@ class TestMaterialRequest(IntegrationTestCase): self.assertRaises(frappe.ValidationError, end_transit_2.submit) + def test_make_stock_entry_material_issue_warehouse_mapping(self): + """Test to ensure while making stock entry from material request of type Material Issue, warehouse is mapped correctly""" + mr = make_material_request(material_request_type="Material Issue", do_not_submit=True) + mr.set_warehouse = "_Test Warehouse - _TC" + mr.save() + mr.submit() + + se = make_stock_entry(mr.name) + self.assertEqual(se.from_warehouse, "_Test Warehouse - _TC") + self.assertIsNone(se.to_warehouse) + se.save() + se.submit() + def get_in_transit_warehouse(company): if not frappe.db.exists("Warehouse Type", "Transit"): 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 7ac65775b23..6376f6b9f4e 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 @@ -1462,6 +1462,9 @@ class SerialandBatchBundle(Document): def throw_negative_batch(self, batch_no, available_qty, precision): from erpnext.stock.stock_ledger import NegativeStockError + if frappe.db.get_single_value("Stock Settings", "allow_negative_stock_for_batch"): + return + frappe.throw( _( """ @@ -2690,7 +2693,10 @@ def get_auto_batch_nos(kwargs): available_batches = get_available_batches(kwargs) stock_ledgers_batches = get_stock_ledgers_batches(kwargs) - pos_invoice_batches = get_reserved_batches_for_pos(kwargs) + + pos_invoice_batches = frappe._dict() + if not kwargs.for_stock_levels: + pos_invoice_batches = get_reserved_batches_for_pos(kwargs) sre_reserved_batches = frappe._dict() if not kwargs.ignore_reserved_stock: diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index f5f724efe42..2e2639080cf 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -449,12 +449,12 @@ class StockEntry(StockController, SubcontractingInwardController): def set_job_card_data(self): if self.job_card and not self.work_order: data = frappe.db.get_value( - "Job Card", self.job_card, ["for_quantity", "work_order", "bom_no"], as_dict=1 + "Job Card", self.job_card, ["for_quantity", "work_order", "bom_no", "semi_fg_bom"], as_dict=1 ) self.fg_completed_qty = data.for_quantity self.work_order = data.work_order self.from_bom = 1 - self.bom_no = data.bom_no + self.bom_no = data.semi_fg_bom or data.bom_no def validate_job_card_fg_item(self): if not self.job_card: @@ -971,6 +971,7 @@ class StockEntry(StockController, SubcontractingInwardController): precision = frappe.get_precision("Stock Entry Detail", "qty") for item_code, details in raw_materials.items(): + item_code = item_code[0] if type(item_code) == tuple else item_code if matched_item := self.get_matched_items(item_code): if flt(details.get("qty"), precision) != flt(matched_item.qty, precision): frappe.throw( @@ -2005,6 +2006,7 @@ class StockEntry(StockController, SubcontractingInwardController): else: job_doc.set_consumed_qty_in_job_card_item(self) job_doc.set_manufactured_qty() + job_doc.update_work_order() if self.work_order: pro_doc = frappe.get_doc("Work Order", self.work_order) diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py index 29c0cd7e05c..4a768ee94fd 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py @@ -2,12 +2,16 @@ # For license information, please see license.txt +from collections import defaultdict + import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import flt +from frappe.utils import cint, flt from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict +from erpnext.stock.serial_batch_bundle import SerialBatchCreation +from erpnext.stock.utils import get_combine_datetime class StockEntryType(Document): @@ -77,7 +81,6 @@ class ManufactureEntry: self.stock_entry.fg_completed_qty = self.for_quantity self.stock_entry.project = self.project self.stock_entry.job_card = self.job_card - self.stock_entry.work_order = self.work_order self.stock_entry.set_stock_entry_type() self.prepare_source_warehouse() @@ -107,11 +110,17 @@ class ManufactureEntry: "Manufacturing Settings", "backflush_raw_materials_based_on" ) + available_serial_batches = frappe._dict({}) + if backflush_based_on != "BOM": + available_serial_batches = self.get_transferred_serial_batches() + for item_code, _dict in item_dict.items(): _dict.from_warehouse = self.source_wh.get(item_code) or self.wip_warehouse _dict.to_warehouse = "" - if backflush_based_on != "BOM": + if backflush_based_on != "BOM" and not frappe.db.get_value( + "Job Card", self.job_card, "skip_material_transfer" + ): calculated_qty = flt(_dict.transferred_qty) - flt(_dict.consumed_qty) if calculated_qty < 0: frappe.throw( @@ -119,9 +128,131 @@ class ManufactureEntry: ) _dict.qty = calculated_qty + self.update_available_serial_batches(_dict, available_serial_batches) self.stock_entry.add_to_stock_entry_detail(item_dict) + def parse_available_serial_batches(self, item_dict, available_serial_batches): + key = (item_dict.item_code, item_dict.from_warehouse) + if key not in available_serial_batches: + return [], {} + + _avl_dict = available_serial_batches[key] + + qty = item_dict.qty + serial_nos = [] + batches = frappe._dict() + + if _avl_dict.serial_nos: + serial_nos = _avl_dict.serial_nos[: cint(qty)] + qty -= len(serial_nos) + for sn in serial_nos: + _avl_dict.serial_nos.remove(sn) + + elif _avl_dict.batches: + batches = frappe._dict() + for batch_no, batch_qty in _avl_dict.batches.items(): + if qty <= 0: + break + if batch_qty <= qty: + batches[batch_no] = batch_qty + qty -= batch_qty + else: + batches[batch_no] = qty + qty = 0 + + for _used_batch_no in batches: + _avl_dict.batches[_used_batch_no] -= batches[_used_batch_no] + if _avl_dict.batches[_used_batch_no] <= 0: + del _avl_dict.batches[_used_batch_no] + + return serial_nos, batches + + def update_available_serial_batches(self, item_dict, available_serial_batches): + serial_nos, batches = self.parse_available_serial_batches(item_dict, available_serial_batches) + if serial_nos or batches: + sabb = SerialBatchCreation( + { + "item_code": item_dict.item_code, + "warehouse": item_dict.from_warehouse, + "posting_datetime": get_combine_datetime( + self.stock_entry.posting_date, self.stock_entry.posting_time + ), + "voucher_type": self.stock_entry.doctype, + "company": self.stock_entry.company, + "type_of_transaction": "Outward", + "qty": item_dict.qty, + "serial_nos": serial_nos, + "batches": batches, + "do_not_submit": True, + } + ).make_serial_and_batch_bundle() + + item_dict.serial_and_batch_bundle = sabb.name + + def get_stock_entry_data(self): + stock_entry = frappe.qb.DocType("Stock Entry") + stock_entry_detail = frappe.qb.DocType("Stock Entry Detail") + + return ( + frappe.qb.from_(stock_entry) + .inner_join(stock_entry_detail) + .on(stock_entry.name == stock_entry_detail.parent) + .select( + stock_entry_detail.item_code, + stock_entry_detail.qty, + stock_entry_detail.serial_and_batch_bundle, + stock_entry_detail.s_warehouse, + stock_entry_detail.t_warehouse, + stock_entry.purpose, + ) + .where( + (stock_entry.job_card == self.job_card) + & (stock_entry_detail.serial_and_batch_bundle.isnotnull()) + & (stock_entry.docstatus == 1) + & (stock_entry.purpose.isin(["Material Transfer for Manufacture", "Manufacture"])) + ) + .orderby(stock_entry.posting_date, stock_entry.posting_time) + ).run(as_dict=True) + + def get_transferred_serial_batches(self): + available_serial_batches = frappe._dict({}) + + stock_entry_data = self.get_stock_entry_data() + + for row in stock_entry_data: + warehouse = ( + row.t_warehouse if row.purpose == "Material Transfer for Manufacture" else row.s_warehouse + ) + key = (row.item_code, warehouse) + if key not in available_serial_batches: + available_serial_batches[key] = frappe._dict( + { + "batches": defaultdict(float), + "serial_nos": [], + } + ) + + _avl_dict = available_serial_batches[key] + + sabb_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + fields=["serial_no", "batch_no", "qty"], + ) + for entry in sabb_data: + if entry.serial_no: + if entry.qty > 0: + _avl_dict.serial_nos.append(entry.serial_no) + else: + _avl_dict.serial_nos.remove(entry.serial_no) + if entry.batch_no: + _avl_dict.batches[entry.batch_no] += flt(entry.qty) * ( + -1 if row.purpose == "Material Transfer for Manufacture" else 1 + ) + + return available_serial_batches + def get_items_from_job_card(self): item_dict = {} items = frappe.get_all( diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index c7d8461704f..ac9e94f1e6a 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -523,6 +523,9 @@ class StockReconciliation(StockController): if abs(difference_amount) > 0: return True + float_precision = frappe.db.get_default("float_precision") or 3 + item_dict["rate"] = flt(item_dict.get("rate"), float_precision) + item.valuation_rate = flt(item.valuation_rate, float_precision) if item.valuation_rate else None if ( (item.qty is None or item.qty == item_dict.get("qty")) and (item.valuation_rate is None or item.valuation_rate == item_dict.get("rate")) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 40f372f2bc7..4b3d9099afc 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1713,6 +1713,101 @@ class TestStockReconciliation(IntegrationTestCase, StockTestMixin): self.assertEqual(docstatus, 2) + def test_stock_reco_with_opening_stock_with_diff_inventory(self): + from erpnext.stock.doctype.inventory_dimension.test_inventory_dimension import ( + create_inventory_dimension, + ) + + if frappe.db.exists("DocType", "Plant"): + return + + doctype = frappe.get_doc( + { + "doctype": "DocType", + "name": "Plant", + "module": "Stock", + "custom": 1, + "fields": [ + { + "fieldname": "plant_name", + "fieldtype": "Data", + "label": "Plant Name", + "reqd": 1, + } + ], + "autoname": "field:plant_name", + } + ) + doctype.insert(ignore_permissions=True) + create_inventory_dimension(dimension_name="ID-Plant", reference_document="Plant") + + plant_a = frappe.get_doc( + { + "doctype": "Plant", + "plant_name": "Plant A", + } + ).insert(ignore_permissions=True) + + plant_b = frappe.get_doc( + { + "doctype": "Plant", + "plant_name": "Plant B", + } + ).insert(ignore_permissions=True) + + warehouse = "_Test Warehouse - _TC" + + item_code = "Item-Test" + item = self.make_item(item_code, {"is_stock_item": 1}) + + sr = frappe.new_doc("Stock Reconciliation") + sr.purpose = "Opening Stock" + sr.posting_date = nowdate() + sr.posting_time = nowtime() + sr.company = "_Test Company" + + sr.append( + "items", + { + "item_code": item.name, + "warehouse": warehouse, + "qty": 5, + "valuation_rate": 100, + "id_plant": plant_a.name, + }, + ) + + sr.append( + "items", + { + "item_code": item.name, + "warehouse": warehouse, + "qty": 3, + "valuation_rate": 110, + "id_plant": plant_b.name, + }, + ) + + sr.insert() + sr.submit() + + self.assertEqual(len(sr.items), 2) + sle_count = frappe.db.count( + "Stock Ledger Entry", + {"voucher_type": "Stock Reconciliation", "voucher_no": sr.name, "is_cancelled": 0}, + ) + self.assertEqual(sle_count, 2) + sle = frappe.get_all( + "Stock Ledger Entry", + {"voucher_type": "Stock Reconciliation", "voucher_no": sr.name, "is_cancelled": 0}, + ["item_code", "id_plant", "actual_qty", "valuation_rate"], + ) + for s in sle: + if s.id_plant == plant_a.name: + self.assertEqual(s.actual_qty, 5) + elif s.id_plant == plant_b.name: + self.assertEqual(s.actual_qty, 3) + def create_batch_item_with_batch(item_name, batch_id): batch_item_doc = create_item(item_name, is_stock_item=1) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index bc53daa2ec4..e3851b5b41f 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -47,6 +47,7 @@ "disable_serial_no_and_batch_selector", "use_serial_batch_fields", "do_not_update_serial_batch_on_creation_of_auto_bundle", + "allow_negative_stock_for_batch", "serial_and_batch_bundle_section", "set_serial_and_batch_bundle_naming_based_on_naming_series", "section_break_gnhq", @@ -546,6 +547,13 @@ "fieldname": "validate_material_transfer_warehouses", "fieldtype": "Check", "label": "Validate Material Transfer Warehouses" + }, + { + "default": "0", + "description": "If enabled, the system will allow negative stock entries for the batch, but this could calculate the valuation rate incorrectly, so avoid using this option.", + "fieldname": "allow_negative_stock_for_batch", + "fieldtype": "Check", + "label": "Allow Negative Stock for Batch" } ], "hide_toolbar": 1, @@ -554,7 +562,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-01-02 18:19:59.034785", + "modified": "2026-02-09 15:01:12.466175", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index a112cd089e2..2ab8c93bcb7 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -30,6 +30,7 @@ class StockSettings(Document): allow_from_pr: DF.Check allow_internal_transfer_at_arms_length_price: DF.Check allow_negative_stock: DF.Check + allow_negative_stock_for_batch: DF.Check allow_partial_reservation: DF.Check allow_to_edit_stock_uom_qty_for_purchase: DF.Check allow_to_edit_stock_uom_qty_for_sales: DF.Check diff --git a/erpnext/workspace_sidebar/invoicing.json b/erpnext/workspace_sidebar/invoicing.json index 99a6b367953..9a80f8237a6 100644 --- a/erpnext/workspace_sidebar/invoicing.json +++ b/erpnext/workspace_sidebar/invoicing.json @@ -314,7 +314,7 @@ "type": "Link" } ], - "modified": "2026-01-26 21:23:15.665712", + "modified": "2026-01-27 21:23:15.665712", "modified_by": "Administrator", "module": "Accounts", "name": "Invoicing",