diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 1cf9a5bd9b5..9f040b4e62e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,7 +6,7 @@ Feature requests are also a great way to take the product forward. New ideas can When you are raising an Issue, you should keep a few things in mind. Remember that the developer does not have access to your machine so you must give all the information you can while raising an Issue. If you are suggesting a feature, you should be very clear about what you want. -The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum [https://discuss.erpnext.com](https://discuss.erpnext.com). +The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum [https://discuss.frappe.io](https://discuss.frappe.io/c/erpnext/6). ### Reply and Closing Policy diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 4d61f1fb943..8b13b00c042 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -9,7 +9,7 @@ body: Welcome to ERPNext issue tracker! Before creating an issue, please heed the following: 1. This tracker should only be used to report bugs and request features / enhancements to ERPNext - - For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.erpnext.com) + - For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.frappe.io/c/erpnext/6) - For documentation issues, propose edit on [documentation site](https://docs.erpnext.com/) directly. 2. When making a bug report, make sure you provide all required information. The easier it is for maintainers to reproduce, the faster it'll be fixed. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 26bb7ab280c..7b58c0d3a3a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Community Forum - url: https://discuss.erpnext.com/ + url: https://discuss.frappe.io/c/erpnext/6 about: For general QnA, discussions and community help. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 607e42d1b49..38d881e24ae 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -11,7 +11,7 @@ assignees: '' Welcome to ERPNext issue tracker! Before creating an issue, please heed the following: 1. This tracker should only be used to report bugs and request features / enhancements to ERPNext - - For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com + - For questions and general support, checkout the manual https://docs.erpnext.com or use https://discuss.frappe.io/c/erpnext/6 2. Use the search function before creating a new issue. Duplicates will be closed and directed to the original discussion. 3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen. @@ -21,7 +21,7 @@ Please keep in mind that we get many many requests and we can't possibly work on If you're in urgent need to a feature, please try the following channels to get paid developments done quickly: 1. Certified ERPNext partners: https://erpnext.com/partners -2. Developer community on ERPNext forums: https://discuss.erpnext.com/c/developers/5 +2. Developer community on ERPNext forums: https://discuss.frappe.io/c/framework/5 3. Telegram group for ERPNext/Frappe development work: https://t.me/erpnext_opps --> diff --git a/README.md b/README.md index a3b1ae1f0ef..96ac64c035f 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ To setup the repository locally follow the steps mentioned below: 1. [Frappe School](https://school.frappe.io) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community. 2. [Official documentation](https://docs.erpnext.com/) - Extensive documentation for ERPNext. -3. [Discussion Forum](https://discuss.erpnext.com/) - Engage with community of ERPNext users and service providers. +3. [Discussion Forum](https://discuss.frappe.io/c/erpnext/6) - Engage with community of ERPNext users and service providers. 4. [Telegram Group](https://erpnext_public.t.me) - Get instant help from huge community of users. diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 0ccf997e66f..cb34b172ab4 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -46,7 +46,8 @@ def validate_service_stop_date(doc): if ( old_stop_dates and old_stop_dates.get(item.name) - and item.service_stop_date != old_stop_dates.get(item.name) + and item.service_stop_date + and getdate(item.service_stop_date) != getdate(old_stop_dates.get(item.name)) ): frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx)) 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 5ad2479e858..35a3196c140 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 @@ -82,7 +82,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-07-29 11:37:42.678556", + "modified": "2025-10-13 15:11:58.300836", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Payment Ledger Entry", diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py index fa863741d51..599bd2c5e4c 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py @@ -34,3 +34,15 @@ class AdvancePaymentLedgerEntry(Document): and not frappe.flags.is_reverse_depr_entry ): update_voucher_outstanding(self.against_voucher_type, self.against_voucher_no, None, None, None) + + +def on_doctype_update(): + frappe.db.add_index( + "Advance Payment Ledger Entry", + ["against_voucher_type", "against_voucher_no"], + ) + + frappe.db.add_index( + "Advance Payment Ledger Entry", + ["voucher_type", "voucher_no"], + ) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html index c3603290f47..5aac9d902e1 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html @@ -13,7 +13,7 @@ {% endif %} -

{{ _("STATEMENT OF ACCOUNTS") }}

+

{{ _("GENERAL LEDGER") }}

{% if filters.party[0] == filters.party_name[0] %}
{{ _("Customer: ") }} {{ filters.party_name[0] }}
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index ea1b61a31d4..dd021edd7f2 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_rename": 1, "autoname": "Prompt", "creation": "2020-05-22 16:46:18.712954", "doctype": "DocType", @@ -69,7 +70,7 @@ "fieldname": "frequency", "fieldtype": "Select", "label": "Frequency", - "options": "Weekly\nMonthly\nQuarterly" + "options": "Daily\nWeekly\nBiweekly\nMonthly\nQuarterly" }, { "fieldname": "company", @@ -416,7 +417,7 @@ } ], "links": [], - "modified": "2025-09-03 14:24:43.608565", + "modified": "2025-10-07 12:19:20.719898", "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 96389f67e1e..9cf27216b1e 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -8,7 +8,7 @@ import frappe from frappe import _ from frappe.desk.reportview import get_match_cond from frappe.model.document import Document -from frappe.utils import add_days, add_months, format_date, getdate, today +from frappe.utils import add_days, add_months, add_to_date, format_date, getdate, today from frappe.utils.jinja import validate_template from frappe.utils.pdf import get_pdf from frappe.www.printview import get_print_style @@ -55,7 +55,7 @@ class ProcessStatementOfAccounts(Document): enable_auto_email: DF.Check filter_duration: DF.Int finance_book: DF.Link | None - frequency: DF.Literal["Weekly", "Monthly", "Quarterly"] + frequency: DF.Literal["Daily", "Weekly", "Biweekly", "Monthly", "Quarterly"] from_date: DF.Date | None ignore_cr_dr_notes: DF.Check ignore_exchange_rate_revaluation_journals: DF.Check @@ -555,8 +555,9 @@ def send_emails(document_name, from_scheduler=False, posting_date=None): if doc.enable_auto_email and from_scheduler: new_to_date = getdate(posting_date or today()) - if doc.frequency == "Weekly": - new_to_date = add_days(new_to_date, 7) + if doc.frequency in ("Daily", "Weekly", "Biweekly"): + frequency = {"Daily": 1, "Weekly": 7, "Biweekly": 14} + new_to_date = add_days(new_to_date, frequency[doc.frequency]) else: new_to_date = add_months(new_to_date, 1 if doc.frequency == "Monthly" else 3) new_from_date = add_months(new_to_date, -1 * doc.filter_duration) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html index 441a4b3da43..89ea90f6eb7 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html @@ -23,7 +23,7 @@ {% endif %}
-

{{ _(report.report_name) }}

+

{{ _("STATEMENT OF ACCOUNTS") }}

{{ filters.customer_name }}

diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py index 4f9dd9a590d..7d5cfb90af8 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py @@ -81,6 +81,7 @@ class TestProcessStatementOfAccounts(AccountsTestMixin, IntegrationTestCase): process_soa = create_process_soa( name="_Test Process SOA", enable_auto_email=1, report="Accounts Receivable" ) + send_emails(process_soa.name, from_scheduler=True) process_soa.load_from_db() self.assertEqual(process_soa.posting_date, getdate(add_days(today(), 7))) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 280d338d56d..cd63a3f757d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2629,6 +2629,38 @@ class TestPurchaseInvoice(IntegrationTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) + @IntegrationTestCase.change_settings( + "Buying Settings", {"maintain_same_rate": 0, "set_landed_cost_based_on_purchase_invoice_rate": 1} + ) + def test_pr_status_rate_adjusted_from_pi(self): + pr = make_purchase_receipt(qty=5, rate=100) + pi = create_purchase_invoice_from_receipt(pr.name) + pi.submit() + pr.reload() + + # Inital check + self.assertEqual(pr.status, "Completed") + + pi.reload() + pi.cancel() + pi = create_purchase_invoice_from_receipt(pr.name) + pi.items[0].rate = 80 + pi.submit() + pr.reload() + + # Test 1 : Adjustment amount is negative + self.assertEqual(pr.status, "Completed") + + pi.reload() + pi.cancel() + pi = create_purchase_invoice_from_receipt(pr.name) + pi.items[0].rate = 120 + pi.submit() + pr.reload() + + # Test 2 : Adjustment amount is positive + self.assertEqual(pr.status, "Completed") + def test_opening_invoice_rounding_adjustment_validation(self): pi = make_purchase_invoice(do_not_save=1) pi.items[0].rate = 99.98 diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 05000375037..19990719d9e 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -912,7 +912,8 @@ "label": "Rejected Serial and Batch Bundle", "no_copy": 1, "options": "Serial and Batch Bundle", - "print_hide": 1 + "print_hide": 1, + "search_index": 1 }, { "fieldname": "wip_composite_asset", @@ -984,7 +985,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2025-03-07 10:21:59.960021", + "modified": "2025-10-14 13:00:54.441511", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", @@ -995,4 +996,4 @@ "sort_field": "creation", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py index 6dc1fd18ad1..cde007d14f6 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py @@ -232,7 +232,7 @@ class TestRepostAccountingLedger(AccountsTestMixin, IntegrationTestCase): company.save() test_cc = company.cost_center - default_expense_account = company.default_expense_account + default_expense_account = company.service_expense_account item = make_item(properties={"is_stock_item": 0}) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index b55e94a1dce..2c86981a8a4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -256,6 +256,18 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( sales_order_btn() { var me = this; + + let filters = { + docstatus: 1, + status: ["not in", ["Closed", "On Hold"]], + per_billed: ["<", 99.99], + company: me.frm.doc.company, + }; + + if (me.frm.doc.has_subcontracted) { + filters.is_subcontracted = 1; + } + this.$sales_order_btn = this.frm.add_custom_button( __("Sales Order"), function () { @@ -266,12 +278,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( setters: { customer: me.frm.doc.customer || undefined, }, - get_query_filters: { - docstatus: 1, - status: ["not in", ["Closed", "On Hold"]], - per_billed: ["<", 99.99], - company: me.frm.doc.company, - }, + get_query_filters: filters, allow_child_item_selection: true, child_fieldname: "items", child_columns: ["item_code", "item_name", "qty", "amount", "billed_amt"], @@ -798,6 +805,15 @@ frappe.ui.form.on("Sales Invoice", { }, }; }); + + frm.set_query("sales_person", "sales_team", function () { + return { + filters: { + is_group: 0, + enabled: 1, + }, + }; + }); }, onload: function (frm) { frm.redemption_conversion_factor = null; @@ -1094,6 +1110,9 @@ frappe.ui.form.on("Sales Invoice", { if (frm.doc.is_debit_note) { frm.set_df_property("return_against", "label", __("Adjustment Against")); } + + frm.set_df_property("update_stock", "read_only", frm.doc.has_subcontracted); + frm.toggle_display("update_stock", !frm.doc.has_subcontracted); }, }); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 4a1758a9e1b..221ff6c610c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -31,6 +31,7 @@ "amended_from", "is_created_using_pos", "pos_closing_entry", + "has_subcontracted", "accounting_dimensions_section", "cost_center", "dimension_col_break", @@ -2229,6 +2230,14 @@ "fieldtype": "Data", "is_virtual": 1, "label": "Last Scanned Warehouse" + }, + { + "default": "0", + "fieldname": "has_subcontracted", + "fieldtype": "Check", + "hidden": 1, + "label": "Has Subcontracted", + "read_only": 1 } ], "grid_page_length": 50, @@ -2242,7 +2251,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2025-09-09 14:48:59.472826", + "modified": "2025-10-09 14:48:59.472826", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 929db4895ad..5eee1c9f289 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -8,6 +8,7 @@ from frappe import _, msgprint, throw from frappe.contacts.doctype.address.address import get_address_display from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values +from frappe.query_builder import Case from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate from frappe.utils.data import comma_and @@ -31,7 +32,6 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center from erpnext.accounts.party import get_due_date, get_party_account, get_party_details from erpnext.accounts.utils import ( - cancel_exchange_gain_loss_journal, get_account_currency, update_voucher_outstanding, ) @@ -131,6 +131,7 @@ class SalesInvoice(SellingController): from_date: DF.Date | None grand_total: DF.Currency group_same_items: DF.Check + has_subcontracted: DF.Check ignore_default_payment_terms_template: DF.Check ignore_pricing_rule: DF.Check in_words: DF.SmallText | None @@ -279,10 +280,65 @@ class SalesInvoice(SellingController): self.indicator_color = "green" self.indicator_title = _("Paid") + def before_print(self, settings=None): + from frappe.contacts.doctype.address.address import get_address_display_list + + super().before_print(settings) + + company_details = frappe.get_value( + "Company", self.company, ["company_logo", "website", "phone_no", "email"], as_dict=True + ) + + required_fields = [ + company_details.get("company_logo"), + company_details.get("phone_no"), + company_details.get("email"), + ] + + if not all(required_fields) and not frappe.has_permission("Company", "write", throw=False): + frappe.msgprint( + _( + "Some required Company details are missing. You don't have permission to update them. Please contact your System Manager." + ) + ) + return + + if not self.company_address and not frappe.has_permission("Sales Invoice", "write", throw=False): + frappe.msgprint( + _( + "Company Address is missing. You don't have permission to update it. Please contact your System Manager." + ) + ) + return + + address_display_list = get_address_display_list("Company", self.company) + address_line = address_display_list[0].get("address_line1") if address_display_list else "" + + required_fields.append(self.company_address) + required_fields.append(address_line) + + if not all(required_fields): + frappe.publish_realtime( + "sales_invoice_before_print", + { + "company_logo": company_details.get("company_logo"), + "website": company_details.get("website"), + "phone_no": company_details.get("phone_no"), + "email": company_details.get("email"), + "address_line": address_line, + "company": self.company, + "company_address": self.company_address, + "name": self.name, + }, + user=frappe.session.user, + ) + def validate(self): self.validate_auto_set_posting_time() super().validate() + self.is_subcontracted() + if not (self.is_pos or self.is_debit_note): self.so_dn_required() @@ -355,6 +411,8 @@ class SalesInvoice(SellingController): self.allow_write_off_only_on_pos() self.reset_default_field_value("set_warehouse", "items", "warehouse") + self.validate_subcontracted_sales_order() + self.validate_scio_self_rm_qty() def validate_accounts(self): self.validate_write_off_account() @@ -521,6 +579,7 @@ class SalesInvoice(SellingController): self.apply_loyalty_points() self.process_common_party_accounting() + self.update_billed_qty_in_scio() def validate_pos_return(self): if self.is_consolidated: @@ -651,6 +710,8 @@ class SalesInvoice(SellingController): ): self.cancel_pos_invoice_credit_note_generated_during_sales_invoice_mode() + self.update_billed_qty_in_scio() + def update_status_updater_args(self): if not cint(self.update_stock): return @@ -785,6 +846,26 @@ class SalesInvoice(SellingController): timesheet.set_status() timesheet.db_update_all() + def update_billed_qty_in_scio(self): + table = frappe.qb.DocType("Subcontracting Inward Order Received Item") + fieldname = table.returned_qty if self.is_return else table.billed_qty + + data = frappe._dict( + { + item.scio_detail: item.stock_qty if self._action == "submit" else -item.stock_qty + for item in self.items + if item.scio_detail + } + ) + + if data: + case_expr = Case() + for name, qty in data.items(): + case_expr = case_expr.when(table.name == name, fieldname + qty) + frappe.qb.update(table).set(fieldname, case_expr).where( + (table.name.isin(list(data.keys()))) & (table.docstatus == 1) + ).run() + def update_time_sheet_detail(self, timesheet, args, sales_invoice): for data in timesheet.time_logs: if ( @@ -1177,6 +1258,50 @@ class SalesInvoice(SellingController): if not self.is_pos and self.write_off_account: self.write_off_account = None + def validate_subcontracted_sales_order(self): + if self.has_subcontracted: + if [item for item in self.items if not item.sales_order and not item.scio_detail]: + frappe.throw( + _( + "All items must be linked to a Sales Order or Subcontracting Inward Order for this Sales Invoice." + ) + ) + if not all( + frappe.get_all( + "Sales Order", + {"name": ["in", [item.sales_order for item in self.items if item.sales_order]]}, + pluck="is_subcontracted", + ) + ): + frappe.throw(_("All linked Sales Orders must be subcontracted.")) + + def validate_scio_self_rm_qty(self): + self_rms = [item for item in self.items if item.scio_detail] + if self_rms: + table = frappe.qb.DocType("Subcontracting Inward Order Received Item") + query = ( + frappe.qb.from_(table) + .select( + table.required_qty, table.consumed_qty, table.billed_qty, table.returned_qty, table.name + ) + .where((table.docstatus == 1) & (table.name.isin([item.scio_detail for item in self_rms]))) + ) + result = query.run(as_dict=True) + data = {item.name: item for item in result} + for item in self_rms: + row = data.get(item.scio_detail) + max_qty = max(row.required_qty, row.consumed_qty) - row.billed_qty - row.returned_qty + if item.stock_qty > max_qty: + frappe.throw( + _("Row #{0}: Stock quantity {1} ({2}) for item {3} cannot exceed {4}").format( + item.idx, + item.stock_qty, + item.stock_uom, + frappe.bold(item.item_code), + frappe.bold(max_qty), + ) + ) + def validate_write_off_account(self): if flt(self.write_off_amount) and not self.write_off_account: self.write_off_account = frappe.get_cached_value("Company", self.company, "write_off_account") @@ -2098,6 +2223,23 @@ class SalesInvoice(SellingController): if update: self.db_set("status", self.status, update_modified=update_modified) + @frappe.whitelist() + def is_subcontracted(self): + if not self.has_subcontracted: + self.has_subcontracted = bool( + frappe.get_cached_value( + "Sales Order", + { + "name": ["in", [item.sales_order for item in self.items if item.sales_order]], + "is_subcontracted": 1, + }, + "name", + ) + ) + if self.has_subcontracted: + self.update_stock = 0 + return self.has_subcontracted + def get_total_in_party_account_currency(doc): total_fieldname = "grand_total" if doc.disable_rounded_total else "rounded_total" @@ -2299,7 +2441,7 @@ def make_delivery_note(source_name, target_doc=None): "cost_center": "cost_center", }, "postprocess": update_item, - "condition": lambda doc: doc.delivered_by_supplier != 1, + "condition": lambda doc: doc.delivered_by_supplier != 1 and not doc.scio_detail, }, "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True}, "Sales Team": { @@ -2802,6 +2944,59 @@ def get_loyalty_programs(customer): return lp_details +@frappe.whitelist() +def save_company_master_details(name, company, details): + from frappe.utils import validate_email_address + + if isinstance(details, str): + details = frappe.parse_json(details) + + if details.get("email"): + validate_email_address(details.get("email"), throw=True) + + company_fields = ["company_logo", "website", "phone_no", "email"] + company_fields_to_update = {field: details.get(field) for field in company_fields if details.get(field)} + + if company_fields_to_update: + frappe.db.set_value("Company", company, company_fields_to_update) + + company_address = details.get("company_address") + if details.get("address_line1"): + address_doc = frappe.get_doc( + { + "doctype": "Address", + "address_title": details.get("address_title"), + "address_type": details.get("address_type"), + "address_line1": details.get("address_line1"), + "address_line2": details.get("address_line2"), + "city": details.get("city"), + "state": details.get("state"), + "pincode": details.get("pincode"), + "country": details.get("country"), + "is_your_company_address": 1, + "links": [{"link_doctype": "Company", "link_name": company}], + } + ) + address_doc.insert() + company_address = address_doc.name + + if company_address: + company_address_display = frappe.db.get_value("Sales Invoice", name, "company_address_display") + if not company_address_display or details.get("address_line1"): + from frappe.query_builder import DocType + + SalesInvoice = DocType("Sales Invoice") + + ( + frappe.qb.update(SalesInvoice) + .set(SalesInvoice.company_address, company_address) + .set(SalesInvoice.company_address_display, get_address_display(company_address)) + .where(SalesInvoice.name == name) + ).run() + + return True + + @frappe.whitelist() def create_invoice_discounting(source_name, target_doc=None): invoice = frappe.get_doc("Sales Invoice", source_name) diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 6336d9e6c62..e5283ea8103 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -109,6 +109,7 @@ "column_break_vwhb", "pos_invoice", "pos_invoice_item", + "scio_detail", "internal_transfer_section", "purchase_order", "column_break_92", @@ -978,13 +979,20 @@ "options": "POS Invoice", "print_hide": 1, "search_index": 1 + }, + { + "fieldname": "scio_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "SCIO Detail", + "read_only": 1 } ], "grid_page_length": 50, "idx": 1, "istable": 1, "links": [], - "modified": "2025-03-07 10:25:30.275246", + "modified": "2025-09-04 11:08:25.583561", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", @@ -995,4 +1003,4 @@ "sort_field": "creation", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py index e708bdadfaa..3d3e92f3fd4 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py @@ -84,6 +84,7 @@ class SalesInvoiceItem(Document): rate_with_margin: DF.Currency sales_invoice_item: DF.Data | None sales_order: DF.Link | None + scio_detail: DF.Data | None serial_and_batch_bundle: DF.Link | None serial_no: DF.Text | None service_end_date: DF.Date | None diff --git a/erpnext/accounts/letterhead/company_letterhead.html b/erpnext/accounts/letterhead/company_letterhead.html new file mode 100644 index 00000000000..f4b8db863f6 --- /dev/null +++ b/erpnext/accounts/letterhead/company_letterhead.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + +
+
+ {% set company_logo = frappe.db.get_value("Company", doc.company, "company_logo") %} + {% if company_logo %} + Company Logo + {% endif %} +
+
+
+ {{ doc.company }} +
+ {% if doc.company_address %} + {% set company_address = frappe.db.get_value("Address", doc.company_address, ["address_line1", "address_line2", "city", "state", "pincode", "country"], as_dict=True) %} + + {{ company_address.get("address_line1") or "" }}
+ {% if company_address.get("address_line2") %}{{ company_address.get("address_line2") }}
{% endif %} + {{ company_address.get("city") or "" }}, {{ company_address.get("state") or "" }} {{ company_address.get("pincode") or "" }}, {{ company_address.get("country") or "" }}
+ {% endif %} +
+ {% set company_details = frappe.db.get_value("Company", doc.company, ["website", "email", "phone_no"], as_dict=True) %} + +
+ {{ _("Invoice:") }} + {{ doc.name }} +
+ {% if company_details.website %} +
+ {{ _("Website:") }} + {{ company_details.website }} +
+ {% endif %} + {% if company_details.email %} +
+ {{ _("Email:") }} + {{ company_details.email }} +
+ {% endif %} + {% if company_details.phone_no %} +
+ {{ _("Contact:") }} + {{ company_details.phone_no }} +
+ {% endif %} +
diff --git a/erpnext/accounts/letterhead/company_letterhead_grey.html b/erpnext/accounts/letterhead/company_letterhead_grey.html new file mode 100644 index 00000000000..3736c19084f --- /dev/null +++ b/erpnext/accounts/letterhead/company_letterhead_grey.html @@ -0,0 +1,125 @@ + + + + + + + + + + +
+ {% set company_logo = frappe.db.get_value("Company", doc.company, "company_logo") %} {% if + company_logo %} + + {% endif %} +
{{ doc.company }}
+
+ {% if doc.company_address %} + {% set company_address = frappe.db.get_value("Address", doc.company_address, ["address_line1", "address_line2", "city", "state", "pincode", "country"], as_dict=True) %} + {{ company_address.address_line1 or "" }}
+ {% if company_address.address_line2 %} {{ company_address.address_line2 }}
{% endif %} + {{ company_address.city or "" }}, {{ company_address.state or "" }} + {{ company_address.pincode or "" }}, {{ company_address.country or ""}}
+ {% endif %} +
+
+
+
{{ _("Sales Invoice") }}
+
{{ doc.name }}
+
+
+
+ {% set company_details = frappe.db.get_value("Company", doc.company, ["website", "email", "phone_no"], as_dict=True) %} + {% if company_details.website %} +
+ {{ _("Website:") }}{{ company_details.website }} +
+ {% endif %} + {% if company_details.email %} +
+ {{ _("Email:") }}{{ company_details.email }} +
+ {% endif %} + {% if company_details.phone_no %} +
+ {{ _("Contact:") }}{{ company_details.phone_no }} +
+ {% endif %} +
+
diff --git a/erpnext/accounts/print_format/sales_invoice_standard/__init__.py b/erpnext/accounts/print_format/sales_invoice_standard/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/print_format/sales_invoice_standard/sales_invoice_standard.json b/erpnext/accounts/print_format/sales_invoice_standard/sales_invoice_standard.json new file mode 100644 index 00000000000..f66861078d3 --- /dev/null +++ b/erpnext/accounts/print_format/sales_invoice_standard/sales_invoice_standard.json @@ -0,0 +1,33 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2025-10-10 02:08:36.763108", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font_size": 14, + "html": "{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead, footer, print_settings=None, print_heading_template=None) -%}\n\t{% if letter_head and not no_letterhead %}\n\t\t
{{ letter_head }}
\n\t{% endif %}\n\t{% if print_heading_template %}\n\t\t{{ frappe.render_template(print_heading_template, {\"doc\":doc}) }}\n\t{% endif %}\n{%- endmacro -%}\n\n{% for page in layout %}\n
\n\t
\n\t\t{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}\n\t
\n\t{%- if doc.meta.is_submittable and doc.docstatus==2-%}\n\t\t
\n\t\t\t

{{ _(\"CANCELLED\") }}

\n\t\t
\n\t{%- endif -%}\n\t{%- if doc.meta.is_submittable and doc.docstatus==0 and (print_settings==None or print_settings.add_draft_heading) -%}\n\t\t
\n\t\t\t

{{ _(\"DRAFT\") }}

\n\t\t
\n\t{%- endif -%}\n\n\t\n\t
\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t
\n\t\t\t\t\t{{ _(\"Customer Name\") }}: {{doc.customer_name }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Payment Due Date\") }}: {{\n\t\t\t\t\tfrappe.utils.format_date(doc.due_date) }}\n\t\t\t\t
{{ _(\"Invoice Number\") }}: {{ doc.name }}\n\t\t\t\t\t{{ _(\"Invoice Date\") }}: {{\n\t\t\t\t\tfrappe.utils.format_date(doc.posting_date) }}\n\t\t\t\t
{{ _(\"Bill From\") }}:
\n\t\t\t\t\t{% if doc.company_address %}\n {% set company_address = frappe.db.get_value(\"Address\", doc.company_address, [\"address_line1\", \"address_line2\", \"city\", \"state\", \"pincode\", \"country\"], as_dict=True) %}\n {{ doc.company }}
\n {{ company_address.get(\"address_line1\") or \"\" }}
\n {% if company_address.get(\"address_line2\") %}{{ company_address.get(\"address_line2\") }}
{% endif %}\n {{ company_address.get(\"city\") or \"\" }}, {{ company_address.get(\"state\") or \"\" }} {{ company_address.get(\"pincode\") or \"\" }}, {{ company_address.get(\"country\") or \"\" }}
\n {% endif %}\n\t\t\t\t
{{ _(\"Bill To\") }}:
\n\t\t\t\t {% if doc.customer_address %}\n\t\t\t\t\t\t{% set customer_address = frappe.db.get_value(\"Address\", doc.customer_address, [\"address_line1\", \"address_line2\", \"city\", \"state\", \"pincode\", \"country\"], as_dict=True) %}\n {{ doc.customer_name }}
\n\t\t\t\t\t\t{{ customer_address.address_line1 or \"\" }}
\n\t\t\t\t\t\t{% if customer_address.address_line2 %}{{ customer_address.address_line2 }}
{% endif %}\n\t\t\t\t\t\t{{ customer_address.city or \"\" }} {{ customer_address.state or \"\" }} {{ customer_address.pincode or \"\" }} {{ customer_address.country or \"\" }}
\n\t\t\t\t\t{% endif %}\n\t\t\t\t
\n\n\t\t\n\t\t{% set item_naming_by = frappe.db.get_single_value(\"Stock Settings\", \"item_naming_by\") %}\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{% if item_naming_by != \"Item Code\" %}\n\t\t\t\t\t\t\n\t\t\t\t\t{% endif %}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t{% for item in doc.items %}\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{% if item_naming_by != \"Item Code\" %}\n\t\t\t\t\t\t\n\t\t\t\t\t{% endif %}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t{% endfor %}\n\t\t\t\n\t\t
{{ _(\"No\") }}{{ _(\"Item\") }}{{ _(\"Item Code\") }}{{ _(\"Quantity\") }}{{ _(\"Rate\") }}{{ _(\"Amount\") }}
{{ loop.index }}{{ item.item_name }}{{ item.item_code }}{{ item.get_formatted(\"qty\", 0) }} {{ item.uom }}{{ item.get_formatted(\"net_rate\", doc) }}\n\t\t\t\t\t\t{{ item.get_formatted(\"net_amount\", doc) }}\n\t\t\t\t\t
\n\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
\n\t\t\t\t

{{ _(\"Total in words\") }}

\n\t\t\t\t
{{ doc.in_words }}
\n\t\t\t
\n\t\t\t\t \n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{%- if doc.apply_discount_on == \"Net Total\" -%}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t{%- endif -%}\n\t\t\t\t\t{%- for tax in doc.taxes -%}\n\t\t\t\t\t\t{%- if (tax.tax_amount or print_settings.print_taxes_with_zero_amount) and (not tax.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{%- endif -%}\n\t\t\t\t\t{%- endfor -%}\n\t\t\t\t\t{%- if doc.apply_discount_on == \"Grand Total\" -%}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t{%- endif -%}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
{{ _(\"Sub Total:\") }}
{{ doc.get_formatted(\"total\", doc) }}
\n\t\t\t\t\t\t\t\t
{{ _(\"Discount\") }} ({{ doc.additional_discount_percentage }}%):\n\t\t\t\t\t\t\t
{{ doc.get_formatted(\"discount_amount\", doc) }}
{{ tax.get_formatted(\"description\") }} ({{ tax.get_formatted(\"rate\") }}%):
{{ tax.get_formatted(\"tax_amount\") }}
\n\t\t\t\t\t\t\t\t
{{ _(\"Discount\") }} ({{ doc.additional_discount_percentage }}%):\n\t\t\t\t\t\t\t
{{ doc.get_formatted(\"discount_amount\", doc) }}
{{ _(\"Grand Total:\") }}{{ doc.get_formatted(\"grand_total\", doc) }}
\n\t\t\t
\n\n\t\t\n\t\t
\n\t\t\t{% if doc.terms %}\n\t\t\t
\n\t\t\t\t
{{ _(\"Terms and Conditions\") }}
\n\t\t\t\t{{ doc.terms}}\n\t\t\t
\n\t\t\t{% endif %}\n\t
\n
\n{% endfor %}\n", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-10-12 17:18:38.613066", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Invoice Standard", + "owner": "Administrator", + "page_number": "Hide", + "pdf_generator": "wkhtmltopdf", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_for": "DocType", + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} diff --git a/erpnext/accounts/print_format/sales_invoice_with_item_image/__init__.py b/erpnext/accounts/print_format/sales_invoice_with_item_image/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/print_format/sales_invoice_with_item_image/sales_invoice_with_item_image.json b/erpnext/accounts/print_format/sales_invoice_with_item_image/sales_invoice_with_item_image.json new file mode 100644 index 00000000000..1d3b4dac309 --- /dev/null +++ b/erpnext/accounts/print_format/sales_invoice_with_item_image/sales_invoice_with_item_image.json @@ -0,0 +1,33 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2025-10-10 02:16:45.078776", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font_size": 14, + "html": "{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead, footer, print_settings=None, print_heading_template=None) -%}\n\n{% if letter_head and not no_letterhead %}\n
{{ letter_head }}
\n{% endif %}\n{% if print_heading_template %}\n{{ frappe.render_template(print_heading_template, {\"doc\":doc}) }}\n{% endif %}\n{%- endmacro -%}\n\n{% for page in layout %}\n
\n\t
\n\t\t{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}\n\t
\n\t{%- if doc.meta.is_submittable and doc.docstatus==2-%}\n\t\t
\n\t\t\t

{{ _(\"CANCELLED\") }}

\n\t\t
\n\t{%- endif -%}\n\t{%- if doc.meta.is_submittable and doc.docstatus==0 and (print_settings==None or print_settings.add_draft_heading) -%}\n\t\t
\n\t\t\t

{{ _(\"DRAFT\") }}

\n\t\t
\n\t{%- endif -%}\n\n\t\n\n\t
\n\t\t\n\t\t\t\n\t\t\t\t\n\n\t\t\t\t\n\t\t\t\n\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
Customer Name:
\n\t\t\t\t\t\t
Bill to:
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
{{ doc.customer_name }}
\n\t\t\t\t\t\t
\n \t\t\t\t\t{% if doc.customer_address %}\n \t\t\t\t\t\t{% set customer_address = frappe.db.get_value(\"Address\", doc.customer_address, [\"address_line1\", \"address_line2\", \"city\", \"state\", \"pincode\", \"country\"], as_dict=True) %}\n \t\t\t\t\t\t{{ customer_address.address_line1 or \"\" }}
\n \t\t\t\t\t\t{% if customer_address.address_line2 %}{{ customer_address.address_line2 }}
{% endif %}\n \t\t\t\t\t\t{{ customer_address.city or \"\" }} {{ customer_address.state or \"\" }} {{ customer_address.pincode or \"\" }} {{ customer_address.country or \"\" }}
\n \t\t\t\t\t{% endif %}\n\t\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
Invoice Number:
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
{{ doc.name }}
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
Invoice Date:
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
{{ frappe.utils.format_date(doc.posting_date) }}
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
Payment Due Date:
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
{{ frappe.utils.format_date(doc.due_date) }}
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\n\t\t{% set item_naming_by = frappe.db.get_single_value(\"Stock Settings\", \"item_naming_by\") %}\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{% if item_naming_by != \"Item Code\" %}\n\t\t\t\t\t\t\n\t\t\t\t\t{% endif %}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t{% for item in doc.items %}\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{% if item_naming_by != \"Item Code\" %}\n\t\t\t\t\t\t\n\t\t\t\t\t{% endif %}\n\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t{% endfor %}\n\t\t\t\n\t\t
{{ _(\"No\") }}{{ _(\"Item\") }}{{ _(\"Item Code\") }}{{ _(\"Quantity\") }}{{ _(\"Rate\") }}{{ _(\"Amount\") }}
{{ loop.index }}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{% if item.image %}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{% endif %}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{{ item.item_name }}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t
{{ item.item_code }}{{ item.get_formatted(\"qty\", 0) }} {{ item.uom }}{{ item.get_formatted(\"net_rate\", doc) }}{{ item.get_formatted(\"net_amount\", doc) }}
\n\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\n\t\t\t\t{%- if doc.apply_discount_on == \"Net Total\" -%}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- for tax in doc.taxes -%}\n\t\t\t\t\t{%- if (tax.tax_amount or print_settings.print_taxes_with_zero_amount) and (not tax.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t{%- endif -%}\n\t\t\t\t{%- endfor -%}\n\t\t\t\t{%- if doc.apply_discount_on == \"Grand Total\" -%}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t{%- endif -%}\n\t\t\t
{{ _(\"Sub Total:\") }}{{ doc.get_formatted(\"total\", doc) }}
\n\t\t\t\t\t\t\t{{ _(\"Discount\") }} ({{ doc.additional_discount_percentage }}%):\n\t\t\t\t\t\t{{ doc.get_formatted(\"discount_amount\", doc) }}
{{ tax.get_formatted(\"description\") }} ({{ tax.get_formatted(\"rate\") }}%):{{ tax.get_formatted(\"tax_amount\") }}
\n\t\t\t\t\t\t\t{{ _(\"Discount\") }} ({{ doc.additional_discount_percentage }}%):\n\t\t\t\t\t\t{{ doc.get_formatted(\"discount_amount\", doc) }}
\n\t\t
\n\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t {{ _(\"In Words: \") }}{{ doc.in_words }}\n\t\t\t\t\t\t
\n\t\t\t\t\t
{{ _(\"Grand Total:\") }}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{{ doc.get_formatted(\"grand_total\", doc) }}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t
\n\n\n\t\t\n\t\t{% if doc.terms %}\n\t\t
\n\t\t\t
{{ _(\"Terms and Conditions\") }}
\n\t\t\t{{ doc.terms}}\n\t\t
\n\t\t{% endif %}\n\t
\n
\n{% endfor %}\n", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-10-10 18:20:55.546151", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Invoice with Item Image", + "owner": "Administrator", + "page_number": "Hide", + "pdf_generator": "wkhtmltopdf", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_for": "DocType", + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index e2b989a4bea..839801d580d 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -354,7 +354,7 @@ def get_asset_details_for_grouped_by_category(filters): # nosemgrep return frappe.db.sql( f""" - SELECT a.name, + SELECT a.name, a.asset_name, ifnull(sum(case when a.purchase_date < %(from_date)s then case when ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s then a.net_purchase_amount @@ -583,6 +583,14 @@ def get_columns(filters): "width": 120, } ) + columns.append( + { + "label": _("Asset Name"), + "fieldname": "asset_name", + "fieldtype": "Data", + "width": 140, + } + ) columns += [ { diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 7238b13e9f0..2103379df93 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -319,6 +319,7 @@ def get_asset_value_adjustment_map(filters, finance_book): .select(asset.name.as_("asset"), Sum(gle.debit - gle.credit).as_("adjustment_amount")) .where(gle.account == aca.fixed_asset_account) .where(gle.is_cancelled == 0) + .where(gle.is_opening == "No") .where(company.name == filters.company) .where(asset.docstatus == 1) ) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 20303559c00..f339f503a6b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -435,7 +435,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( ); } } else { - if (!doc.items.every((item) => item.qty == item.subcontracted_quantity)) { + if (!doc.items.every((item) => item.qty == item.subcontracted_qty)) { this.frm.add_custom_button( __("Subcontracting Order"), () => { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index a097e4abecf..81e06548bfe 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -972,7 +972,7 @@ def make_subcontracting_order(source_name, target_doc=None, save=False, submit=F return target_doc else: - frappe.throw(_("This PO has been fully subcontracted.")) + frappe.throw(_("This Purchase Order has been fully subcontracted.")) def is_po_fully_subcontracted(po_name): @@ -980,7 +980,7 @@ def is_po_fully_subcontracted(po_name): query = ( frappe.qb.from_(table) .select(table.name) - .where((table.parent == po_name) & (table.qty != table.subcontracted_quantity)) + .where((table.parent == po_name) & (table.qty != table.subcontracted_qty)) ) return not query.run(as_dict=True) @@ -1035,7 +1035,7 @@ def get_mapped_subcontracting_order(source_name, target_doc=None): "material_request_item": "material_request_item", }, "field_no_map": ["qty", "fg_item_qty", "amount"], - "condition": lambda item: item.qty != item.subcontracted_quantity, + "condition": lambda item: item.qty != item.subcontracted_qty, }, }, target_doc, diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 6da863cb040..aa4668923b8 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -1095,9 +1095,9 @@ class TestPurchaseOrder(IntegrationTestCase): # Test - 2: Subcontracted Quantity for the PO Items of each line item should be updated accordingly po.reload() - self.assertEqual(po.items[0].subcontracted_quantity, 5) - self.assertEqual(po.items[1].subcontracted_quantity, 0) - self.assertEqual(po.items[2].subcontracted_quantity, 12.5) + self.assertEqual(po.items[0].subcontracted_qty, 5) + self.assertEqual(po.items[1].subcontracted_qty, 0) + self.assertEqual(po.items[2].subcontracted_qty, 12.5) # Test - 3: Amount for both FG Item and its Service Item should be updated correctly based on change in Quantity self.assertEqual(sco.items[0].amount, 2000) @@ -1133,10 +1133,10 @@ class TestPurchaseOrder(IntegrationTestCase): # Test - 8: Subcontracted Quantity for each PO Item should be subtracted if SCO gets cancelled po.reload() - self.assertEqual(po.items[2].subcontracted_quantity, 25) + self.assertEqual(po.items[2].subcontracted_qty, 25) sco.cancel() po.reload() - self.assertEqual(po.items[2].subcontracted_quantity, 12.5) + self.assertEqual(po.items[2].subcontracted_qty, 12.5) sco = make_subcontracting_order(po.name) sco.save() diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 912e2934364..719fac80bf4 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -26,7 +26,7 @@ "quantity_and_rate", "qty", "stock_uom", - "subcontracted_quantity", + "subcontracted_qty", "col_break2", "uom", "conversion_factor", @@ -933,8 +933,9 @@ }, { "allow_on_submit": 1, + "default": "0", "depends_on": "eval:parent.is_subcontracted && !parent.is_old_subcontracting_flow", - "fieldname": "subcontracted_quantity", + "fieldname": "subcontracted_qty", "fieldtype": "Float", "label": "Subcontracted Quantity", "no_copy": 1, @@ -947,7 +948,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-03-13 17:27:43.468602", + "modified": "2025-10-12 10:57:31.552812", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py index aebe6e1299e..c0747a614bc 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py @@ -85,7 +85,7 @@ class PurchaseOrderItem(Document): stock_qty: DF.Float stock_uom: DF.Link stock_uom_rate: DF.Currency - subcontracted_quantity: DF.Float + subcontracted_qty: DF.Float supplier_part_no: DF.Data | None supplier_quotation: DF.Link | None supplier_quotation_item: DF.Link | None diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py index 20267e9ae10..db93a3d7e79 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py @@ -284,15 +284,15 @@ def get_columns(filters): def get_message(): - return """ - Valid till :    + return f""" + {_("Valid Till")}:   - Expires in a week or less + {_("Expires in a week or less")}    - Expires today / Already Expired + {_("Expires today or already expired")} """ diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 883f6595c6a..178fa4227ec 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3804,9 +3804,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil any_qty_changed = True if ( - parent.doctype == "Purchase Order" + parent.doctype in ["Sales Order", "Purchase Order"] and parent.is_subcontracted - and not parent.is_old_subcontracting_flow + and not parent.get("is_old_subcontracting_flow") ): validate_fg_item_for_subcontracting(d, new_child_flag) child_item.fg_item_qty = flt(d["fg_item_qty"]) @@ -3891,7 +3891,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.set_qty_as_per_stock_uom() parent.calculate_taxes_and_totals() parent.set_total_in_words() - if parent_doctype == "Sales Order": + if parent_doctype == "Sales Order" and not parent.is_subcontracted: make_packing_list(parent) parent.set_gross_profit() frappe.get_cached_doc("Authorization Control").validate_approving_authority( @@ -3938,6 +3938,12 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil ).format(frappe.bold(parent.name)) ) else: # Sales Order + if parent.is_subcontracted and not parent.can_update_items(): + frappe.throw( + _( + "Items cannot be updated as Subcontracting Inward Order is created against the Sales Order {0}." + ).format(frappe.bold(parent.name)) + ) parent.validate_selling_price() parent.validate_for_duplicate_items() parent.validate_warehouse() @@ -3957,7 +3963,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.validate_uom_is_integer("stock_uom", "stock_qty") # Cancel and Recreate Stock Reservation Entries. - if parent_doctype == "Sales Order": + if parent_doctype == "Sales Order" and not parent.is_subcontracted: from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( cancel_stock_reservation_entries, has_reserved_stock, diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index ce7b4da1ed2..16b86eeb525 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -854,13 +854,14 @@ def available_serial_batch_for_return(field, doctype, reference_ids, is_rejected def get_available_serial_batches(field, doctype, reference_ids, is_rejected=False): _bundle_ids = get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=is_rejected) + if not _bundle_ids: return frappe._dict({}) - return get_serial_batches_based_on_bundle(field, _bundle_ids) + return get_serial_batches_based_on_bundle(doctype, field, _bundle_ids) -def get_serial_batches_based_on_bundle(field, _bundle_ids): +def get_serial_batches_based_on_bundle(doctype, field, _bundle_ids): available_dict = frappe._dict({}) batch_serial_nos = frappe.get_all( "Serial and Batch Bundle", @@ -872,6 +873,7 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): "`tabSerial and Batch Bundle`.`voucher_detail_no`", "`tabSerial and Batch Bundle`.`voucher_type`", "`tabSerial and Batch Bundle`.`voucher_no`", + "`tabSerial and Batch Bundle`.`item_code`", ], filters=[ ["Serial and Batch Bundle", "name", "in", _bundle_ids], @@ -885,6 +887,16 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): if frappe.get_cached_value(row.voucher_type, row.voucher_no, "is_return"): key = frappe.get_cached_value(row.voucher_type + " Item", row.voucher_detail_no, field) + if doctype == "Packed Item": + if key is None: + key = frappe.get_cached_value("Packed Item", row.voucher_detail_no, field) + if row.voucher_type == "Delivery Note": + key = frappe.get_cached_value("Delivery Note Item", key, "dn_detail") + elif row.voucher_type == "Sales Invoice": + key = frappe.get_cached_value("Sales Invoice Item", key, "sales_invoice_item") + + key = (row.item_code, key) + if row.voucher_type in ["Sales Invoice", "Delivery Note"]: row.qty = -1 * row.qty @@ -913,6 +925,8 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False): filters = {"docstatus": 1, "name": ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")} + if doctype == "Packed Item": + filters = get_filters_for_packed_item(field, reference_ids) pluck_field = "serial_and_batch_bundle" if is_rejected: @@ -926,10 +940,14 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False pluck=pluck_field, ) + if _bundle_ids and doctype == "Packed Item": + return _bundle_ids + if not _bundle_ids: return {} - del filters["name"] + if "name" in filters: + del filters["name"] filters[field] = ("in", reference_ids) @@ -972,10 +990,29 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False return _bundle_ids +def get_filters_for_packed_item(field, reference_ids): + names = [] + filters = {"docstatus": 1, "dn_detail": ("in", reference_ids)} + if dns := frappe.get_all("Delivery Note Item", filters=filters, pluck="name"): + names.extend(dns) + + filters = {"docstatus": 1, "sales_invoice_item": ("in", reference_ids)} + if sis := frappe.get_all("Sales Invoice Item", filters=filters, pluck="name"): + names.extend(sis) + + if names: + reference_ids.extend(names) + + return {"docstatus": 1, field: ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")} + + def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field=None): if not qty_field: qty_field = "stock_qty" + if not hasattr(row, qty_field): + qty_field = "qty" + if not warehouse_field: warehouse_field = "warehouse" @@ -1065,6 +1102,9 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f if not qty_field: qty_field = "stock_qty" + if not hasattr(child_doc, qty_field): + qty_field = "qty" + warehouse = child_doc.get(warehouse_field) if parent_doc.get("is_internal_customer"): warehouse = child_doc.get("target_warehouse") diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index a0c53ad0ece..8b0a0f19f9b 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -519,8 +519,15 @@ class SellingController(StockController): if not frappe.get_cached_value("Item", d.item_code, "is_stock_item"): continue + item_details = frappe.get_cached_value( + "Item", d.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 + ) + if not self.get("return_against") or ( - get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return") + get_valuation_method(d.item_code) == "Moving Average" + and self.get("is_return") + and not item_details.has_serial_no + and not item_details.has_batch_no ): # Get incoming rate based on original item cost based on valuation method qty = flt(d.get("stock_qty") or d.get("actual_qty") or d.get("qty")) @@ -999,6 +1006,9 @@ def set_default_income_account_for_item(obj): def get_serial_and_batch_bundle(child, parent, delivery_note_child=None): from erpnext.stock.serial_batch_bundle import SerialBatchCreation + if parent.get("is_return") and parent.get("packed_items"): + return + if child.get("use_serial_batch_fields"): return diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index fe0a273b6ed..538887977d3 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -265,6 +265,8 @@ class StatusUpdater(Document): # if target_ref_field is not specified, the programmer does not want to validate qty / amount continue + items_to_validate = [] + # get unique transactions to update for d in self.get_all_children(): if hasattr(d, "qty") and d.qty < 0 and not self.get("is_return"): @@ -286,31 +288,63 @@ class StatusUpdater(Document): ) if d.doctype == args["source_dt"] and d.get(args["join_field"]): - args["name"] = d.get(args["join_field"]) - - is_from_pp = ( - hasattr(d, "production_plan_sub_assembly_item") - and frappe.db.get_value( - "Production Plan Sub Assembly Item", - d.production_plan_sub_assembly_item, - "type_of_manufacturing", + items_to_validate.append( + frappe._dict( + { + "name": d.get(args["join_field"]), + "production_plan_sub_assembly_item": d.get( + "production_plan_sub_assembly_item" + ), + "idx": d.idx, + "child_doc": d, + } ) - == "Subcontract" ) - args["item_code"] = "production_item" if is_from_pp else "item_code" - # get all qty where qty > target_field - item = frappe.db.sql( - """select `{item_code}` as item_code, `{target_ref_field}`, - `{target_field}`, parenttype, parent from `tab{target_dt}` - where `{target_ref_field}` < `{target_field}` - and name=%s and docstatus=1""".format(**args), - args["name"], - as_dict=1, + if items_to_validate: + pp_sub_assembly_items = [ + item.production_plan_sub_assembly_item + for item in items_to_validate + if item.production_plan_sub_assembly_item + ] + + pp_subcontract_items = [] + if pp_sub_assembly_items: + pp_subcontract_items = frappe.db.get_all( + "Production Plan Sub Assembly Item", + filters={ + "name": ("in", pp_sub_assembly_items), + "type_of_manufacturing": "Subcontract", + }, + pluck="name", ) + + regular_items = [] + pp_items = [] + + for item in items_to_validate: + if item.production_plan_sub_assembly_item in pp_subcontract_items: + pp_items.append(item.name) + else: + regular_items.append(item.name) + + item_details = [] + + # Query regular items with item_code field + if regular_items: + item_details.extend(self.fetch_items_with_pending_qty(args, "item_code", regular_items)) + + # Query production plan items with production_item field + if pp_items: + item_details.extend(self.fetch_items_with_pending_qty(args, "production_item", pp_items)) + + item_lookup = {item.name: item for item in item_details} + + for child_item in items_to_validate: + item = item_lookup.get(child_item.name) + if item: - item = item[0] - item["idx"] = d.idx + item["idx"] = child_item.idx item["target_ref_field"] = args["target_ref_field"].replace("_", " ") # if not item[args['target_ref_field']]: @@ -323,6 +357,28 @@ class StatusUpdater(Document): elif item[args["target_ref_field"]]: self.check_overflow_with_allowance(item, args) + def fetch_items_with_pending_qty(self, args, item_field, items): + doctype = frappe.qb.DocType(args["target_dt"]) + item_field = doctype[item_field] + target_ref_field = doctype[args["target_ref_field"]] + target_field = doctype[args["target_field"]] + + return ( + frappe.qb.from_(doctype) + .select( + doctype.name, + item_field.as_("item_code"), + target_ref_field, + target_field, + doctype.parenttype, + doctype.parent, + ) + .where(target_ref_field < target_field) + .where(doctype.name.isin(items)) + .where(doctype.docstatus == 1) + .run(as_dict=True) + ) + def check_overflow_with_allowance(self, item, args): """ Checks if there is overflow condering a relaxation allowance diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 84d22c54f8c..10674250ffb 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -95,9 +95,11 @@ class StockController(AccountsController): "Stock Reconciliation", ]: for item in self.get("items"): - if (item.get("valuation_rate") == 0 or item.get("incoming_rate") == 0) and item.get( - "allow_zero_valuation_rate" - ) == 0: + if ( + (item.get("valuation_rate") == 0 or item.get("incoming_rate") == 0) + and item.get("allow_zero_valuation_rate") == 0 + and frappe.get_cached_value("Item", item.item_code, "is_stock_item") + ): frappe.toast( _( "Row #{0}: Item {1} has zero rate but 'Allow Zero Valuation Rate' is not enabled." @@ -360,10 +362,20 @@ class StockController(AccountsController): return child_doctype = self.doctype + " Item" + if table_name == "packed_items": + field = "parent_detail_docname" + child_doctype = "Packed Item" + available_dict = available_serial_batch_for_return(field, child_doctype, reference_ids) for row in self.get(table_name): - if data := available_dict.get(row.get(field)): + value = row.get(field) + if table_name == "packed_items" and row.get("parent_detail_docname"): + value = self.get_value_for_packed_item(row) + if not value: + continue + + if data := available_dict.get(value): data = filter_serial_batches(self, data, row) bundle = make_serial_batch_bundle_for_return(data, row, self) row.db_set( @@ -379,6 +391,14 @@ class StockController(AccountsController): "incoming_rate", frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate") ) + def get_value_for_packed_item(self, row): + parent_items = self.get("items", {"name": row.parent_detail_docname}) + if parent_items: + ref = parent_items[0].get("dn_detail") + return (row.item_code, ref) + + return None + def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]: field = { "Sales Invoice": "sales_invoice_item", @@ -413,6 +433,12 @@ class StockController(AccountsController): ): reference_ids.append(row.get(field)) + if table_name == "packed_items" and row.get("parent_detail_docname"): + parent_rows = self.get("items", {"name": row.parent_detail_docname}) or [] + for d in parent_rows: + if d.get(field) and not d.get(bundle_field): + reference_ids.append(d.get(field)) + return field, reference_ids @frappe.request_cache @@ -520,10 +546,14 @@ class StockController(AccountsController): break elif row.batch_no: - batches = frappe.get_all( - "Serial and Batch Entry", fields=["batch_no"], filters={"parent": row.serial_and_batch_bundle} + batches = sorted( + frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle, "batch_no": ("is", "set")}, + pluck="batch_no", + distinct=True, + ) ) - batches = sorted([d.batch_no for d in batches]) if batches != [row.batch_no]: throw_error = True diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index cf3782c7e4b..0b84d697732 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -35,6 +35,14 @@ class SubcontractingController(StockController): "order_supplied_items_field": "Purchase Order Item Supplied", } ) + elif self.doctype == "Subcontracting Inward Order": + self.subcontract_data = frappe._dict( + { + "order_doctype": "Subcontracting Inward Order", + "order_field": "subcontracting_inward_order", + "rm_detail_field": "scio_detail", + } + ) else: self.subcontract_data = frappe._dict( { @@ -47,14 +55,22 @@ class SubcontractingController(StockController): ) def before_validate(self): - if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]: + if self.doctype in [ + "Subcontracting Order", + "Subcontracting Inward Order", + "Subcontracting Receipt", + ]: self.remove_empty_rows() self.set_items_conversion_factor() def validate(self): - if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]: + if self.doctype in ["Subcontracting Order", "Subcontracting Receipt", "Subcontracting Inward Order"]: self.validate_items() - self.create_raw_materials_supplied() + self.create_raw_materials_supplied_or_received( + raw_material_table="supplied_items" + if self.doctype != "Subcontracting Inward Order" + else "received_items" + ) self.set_valuation_rate_for_rm() else: super().validate() @@ -109,7 +125,7 @@ class SubcontractingController(StockController): ) def remove_empty_rows(self): - for key in ["service_items", "items", "supplied_items"]: + for key in ["service_items", "items", "supplied_items", "received_items"]: if self.get(key): idx = 1 for item in self.get(key)[:]: @@ -133,33 +149,47 @@ class SubcontractingController(StockController): if not is_stock_item: frappe.throw(_("Row {0}: Item {1} must be a stock item.").format(item.idx, item.item_name)) + if ( + self.doctype == "Subcontracting Inward Order" + and item.delivery_warehouse == self.customer_warehouse + ): + frappe.throw( + _( + "Row {0}: Delivery Warehouse cannot be same as Customer Warehouse for Item {1}." + ).format(item.idx, frappe.bold(item.item_name)) + ) + if not item.get("is_scrap_item"): if not is_sub_contracted_item: frappe.throw( _("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name) ) - if ( - self.doctype == "Subcontracting Order" and not item.subcontracting_conversion_factor - ): # this condition will only be true if user has recently updated from develop branch - service_item_qty = frappe.get_value( - "Subcontracting Order Service Item", - filters={"purchase_order_item": item.purchase_order_item, "parent": self.name}, - fieldname=["qty"], + if self.doctype != "Subcontracting Receipt" and item.qty > flt( + get_pending_subcontracted_quantity( + self.doctype, + self.purchase_order if self.doctype == "Subcontracting Order" else self.sales_order, + ).get( + item.purchase_order_item + if self.doctype == "Subcontracting Order" + else item.sales_order_item ) - item.subcontracting_conversion_factor = service_item_qty / item.qty - - if self.doctype not in "Subcontracting Receipt" and item.qty > flt( - get_pending_subcontracted_quantity(self.purchase_order).get(item.purchase_order_item) / item.subcontracting_conversion_factor, - frappe.get_precision("Purchase Order Item", "qty"), + frappe.get_precision( + "Purchase Order Item" + if self.doctype == "Subcontracting Order" + else "Sales Order Item", + "qty", + ), ): frappe.throw( _( "Row {0}: Item {1}'s quantity cannot be higher than the available quantity." ).format(item.idx, item.item_name) ) - item.amount = item.qty * item.rate + + if self.doctype != "Subcontracting Inward Order": + item.amount = item.qty * item.rate if item.bom: is_active, bom_item = frappe.get_value("BOM", item.bom, ["is_active", "item"]) @@ -198,10 +228,16 @@ class SubcontractingController(StockController): self.__changed_name = [] self.__reference_name = [] - if self.doctype in ["Purchase Order", "Subcontracting Order"] or self.is_new(): + if ( + self.doctype in ["Purchase Order", "Subcontracting Order", "Subcontracting Inward Order"] + or self.is_new() + ): self.set(self.raw_material_table, []) return + if not self.get(self.raw_material_table): + return + item_dict = self.__get_data_before_save() if not item_dict: return True @@ -217,8 +253,13 @@ class SubcontractingController(StockController): self.__changed_name.extend(item_dict.keys()) def __get_backflush_based_on(self): - self.backflush_based_on = frappe.db.get_single_value( - "Buying Settings", "backflush_raw_materials_of_subcontract_based_on" + self.backflush_based_on = ( + frappe.db.get_single_value( + "Buying Settings", + "backflush_raw_materials_of_subcontract_based_on", + ) + if self.subcontract_data.order_doctype == "Subcontracting Order" + else "Material Transferred for Subcontract" ) def initialized_fields(self): @@ -230,7 +271,7 @@ class SubcontractingController(StockController): def __get_subcontract_orders(self): self.subcontract_orders = [] - if self.doctype in ["Purchase Order", "Subcontracting Order"]: + if self.doctype in ["Purchase Order", "Subcontracting Order", "Subcontracting Inward Order"]: return self.subcontract_orders = [ @@ -540,8 +581,13 @@ class SubcontractingController(StockController): return frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or [] def __update_reserve_warehouse(self, row, item): - if self.doctype == self.subcontract_data.order_doctype: + if ( + self.doctype == self.subcontract_data.order_doctype + and self.doctype != "Subcontracting Inward Order" + ): row.reserve_warehouse = self.set_reserve_warehouse or item.warehouse + elif frappe.get_cached_value("Item", row.rm_item_code, "is_customer_provided_item"): + row.warehouse = self.customer_warehouse def __set_alternative_item(self, bom_item): if self.alternative_item_details.get(bom_item.rm_item_code): @@ -616,7 +662,7 @@ class SubcontractingController(StockController): return serial_nos - def __add_supplied_item(self, item_row, bom_item, qty): + def __add_supplied_or_received_item(self, item_row, bom_item, qty): bom_item.conversion_factor = item_row.conversion_factor rm_obj = self.append(self.raw_material_table, bom_item) if rm_obj.get("qty"): @@ -629,7 +675,8 @@ class SubcontractingController(StockController): if self.doctype == self.subcontract_data.order_doctype: rm_obj.required_qty = flt(qty, rm_obj.precision("required_qty")) - rm_obj.amount = flt(rm_obj.required_qty * rm_obj.rate, rm_obj.precision("amount")) + if self.doctype != "Subcontracting Inward Order": + rm_obj.amount = flt(rm_obj.required_qty * rm_obj.rate, rm_obj.precision("amount")) else: rm_obj.consumed_qty = flt(qty, rm_obj.precision("consumed_qty")) rm_obj.required_qty = flt(bom_item.required_qty or qty, rm_obj.precision("required_qty")) @@ -640,7 +687,8 @@ class SubcontractingController(StockController): if use_serial_batch_fields: rm_obj.use_serial_batch_fields = 1 - self.__set_batch_nos(bom_item, item_row, rm_obj, qty) + if not self.flags.get("reset_raw_materials"): + self.__set_batch_nos(bom_item, item_row, rm_obj, qty) if self.doctype == "Subcontracting Receipt": if not use_serial_batch_fields: @@ -657,6 +705,9 @@ class SubcontractingController(StockController): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos_for_outward from erpnext.stock.get_item_details import get_filtered_serial_nos + if self.is_return: + return + for row in self.supplied_items: item_details = frappe.get_cached_value( "Item", row.rm_item_code, ["has_batch_no", "has_serial_no"], as_dict=1 @@ -834,14 +885,14 @@ class SubcontractingController(StockController): return qty - def __set_supplied_items(self): + def __set_supplied_or_received_items(self): self.bom_items = {} - has_supplied_items = True if self.get(self.raw_material_table) else False + has_items = True if self.get(self.raw_material_table) else False for row in self.items: if self.doctype != self.subcontract_data.order_doctype and ( (self.__changed_name and row.name not in self.__changed_name) - or (has_supplied_items and not self.__changed_name) + or (has_items and not self.__changed_name) ): continue @@ -855,7 +906,7 @@ class SubcontractingController(StockController): bom_item.main_item_code = row.item_code self.__update_reserve_warehouse(bom_item, row) self.__set_alternative_item(bom_item) - self.__add_supplied_item(row, bom_item, qty) + self.__add_supplied_or_received_item(row, bom_item, qty) elif self.backflush_based_on != "BOM": for key, transfer_item in self.available_materials.items(): @@ -865,7 +916,7 @@ class SubcontractingController(StockController): ) and transfer_item.qty > 0: qty = flt(self.__get_qty_based_on_material_transfer(row, transfer_item)) transfer_item.qty -= qty - self.__add_supplied_item(row, transfer_item.get("item_details"), qty) + self.__add_supplied_or_received_item(row, transfer_item.get("item_details"), qty) if self.qty_to_be_received: self.qty_to_be_received[ @@ -933,13 +984,13 @@ class SubcontractingController(StockController): ): return row - def __prepare_supplied_items(self): + def __prepare_supplied_or_received_items(self): self.initialized_fields() self.__get_subcontract_orders() self.__get_pending_qty_to_receive() self.get_available_materials() self.__remove_changed_rows() - self.__set_supplied_items() + self.__set_supplied_or_received_items() self.__modify_serial_and_batch_bundle() self.__set_rate_for_serial_and_batch_bundle() @@ -966,7 +1017,7 @@ class SubcontractingController(StockController): msg = f"The Serial Nos {incorrect_sn} has not supplied against the {self.subcontract_data.order_doctype} {link}" frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed")) - def __validate_supplied_items(self): + def __validate_supplied_or_received_items(self): if self.doctype not in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: return @@ -984,10 +1035,10 @@ class SubcontractingController(StockController): self.raw_material_table = raw_material_table self.__identify_change_in_item_table() - self.__prepare_supplied_items() - self.__validate_supplied_items() + self.__prepare_supplied_or_received_items() + self.__validate_supplied_or_received_items() - def create_raw_materials_supplied(self, raw_material_table="supplied_items"): + def create_raw_materials_supplied_or_received(self, raw_material_table="supplied_items"): self.set_materials_for_subcontracted_items(raw_material_table) if self.doctype in ["Subcontracting Receipt", "Purchase Receipt", "Purchase Invoice"]: @@ -1240,14 +1291,16 @@ def get_item_details(items): return item_details -def get_pending_subcontracted_quantity(po_name): - table = frappe.qb.DocType("Purchase Order Item") +def get_pending_subcontracted_quantity(doctype, name): + table = frappe.qb.DocType( + "Purchase Order Item" if doctype == "Subcontracting Order" else "Sales Order Item" + ) query = ( frappe.qb.from_(table) - .select(table.name, table.qty, table.subcontracted_quantity) - .where(table.parent == po_name) + .select(table.name, table.stock_qty, table.subcontracted_qty) + .where(table.parent == name) ) - return {item.name: item.qty - item.subcontracted_quantity for item in query.run(as_dict=True)} + return {item.name: item.stock_qty - item.subcontracted_qty for item in query.run(as_dict=True)} @frappe.whitelist() diff --git a/erpnext/controllers/subcontracting_inward_controller.py b/erpnext/controllers/subcontracting_inward_controller.py new file mode 100644 index 00000000000..73256afd852 --- /dev/null +++ b/erpnext/controllers/subcontracting_inward_controller.py @@ -0,0 +1,1021 @@ +import frappe +from frappe import _, bold +from frappe.query_builder import Case +from frappe.utils import flt, get_link_to_form + +from erpnext.stock.serial_batch_bundle import get_serial_batch_list_from_item + + +class SubcontractingInwardController: + def validate_subcontracting_inward(self): + self.validate_inward_order() + self.validate_customer_provided_item_for_inward() + self.validate_warehouse_() + self.validate_serial_batch_for_return_or_delivery() + self.validate_delivery() + self.update_customer_provided_item_cost() + + def on_submit_subcontracting_inward(self): + self.update_inward_order_item() + self.update_inward_order_received_items() + self.update_inward_order_scrap_items() + self.create_stock_reservation_entries_for_inward() + self.update_inward_order_status() + + def on_cancel_subcontracting_inward(self): + self.update_inward_order_item() + self.validate_manufacture_entry_cancel() + self.validate_delivery() + self.validate_receive_from_customer_cancel() + self.update_inward_order_received_items() + self.update_inward_order_scrap_items() + self.remove_reference_for_additional_items() + self.update_inward_order_status() + + def validate_purpose(self): + if self.subcontracting_inward_order and self.purpose not in [ + "Receive from Customer", + "Return Raw Material to Customer", + "Manufacture", + "Subcontracting Delivery", + "Subcontracting Return", + "Material Transfer for Manufacture", + ]: + self.subcontracting_inward_order = None + + def validate_inward_order(self): + if self.subcontracting_inward_order: + match self.purpose: + case "Receive from Customer": + self.validate_material_receipt() + case purpose if purpose in ["Return Raw Material to Customer", "Subcontracting Return"]: + self.validate_returns() + case "Manufacture": + self.validate_manufacture() + + def validate_material_receipt(self): + for item in self.items: + if ( + item.scio_detail + and frappe.get_cached_value( + "Subcontracting Inward Order Received Item", item.scio_detail, "rm_item_code" + ) + != item.item_code + ): + frappe.throw( + _( + "Row #{0}: Item {1} mismatch. Changing of item code is not permitted, add another row instead." + ).format(item.idx, bold(item.item_code)) + ) + + def validate_returns(self): + for item in self.items: + if not item.scio_detail: + frappe.throw( + _("Row #{0}: Item {1} is not a part of Subcontracting Inward Order {2}").format( + item.idx, + bold(item.item_code), + bold(self.subcontracting_inward_order), + ) + ) + elif item.item_code != ( + frappe.get_cached_value( + "Subcontracting Inward Order Received Item", item.scio_detail, "rm_item_code" + ) + or frappe.get_cached_value("Subcontracting Inward Order Item", item.scio_detail, "item_code") + ): + frappe.throw( + _("Row #{0}: Item {1} mismatch. Changing of item code is not permitted.").format( + item.idx, bold(item.item_code) + ) + ) + + if self.purpose == "Return Raw Material to Customer": + data = frappe.get_value( + "Subcontracting Inward Order Received Item", + item.scio_detail, + ["received_qty", "returned_qty", "work_order_qty"], + as_dict=True, + ) + if data.returned_qty + item.transfer_qty > data.received_qty - data.work_order_qty: + frappe.throw( + _( + "Row #{0}: Returned quantity cannot be greater than available quantity for Item {1}" + ).format(item.idx, bold(item.item_code)) + ) + else: + data = frappe.get_value( + "Subcontracting Inward Order Item", + item.scio_detail, + ["returned_qty", "delivered_qty"], + as_dict=True, + ) + if item.transfer_qty > data.delivered_qty - data.returned_qty: + frappe.throw( + _( + "Row #{0}: Returned quantity cannot be greater than available quantity to return for Item {1}" + ).format(item.idx, bold(item.item_code)) + ) + + def validate_manufacture(self): + warehouse = frappe.get_cached_value( + "Subcontracting Inward Order", self.subcontracting_inward_order, "customer_warehouse" + ) + + items = [ + item + for item in self.get("items") + if not item.is_finished_item + and not item.is_scrap_item + and frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item") + ] + + table = frappe.qb.DocType("Subcontracting Inward Order Received Item") + query = ( + frappe.qb.from_(table) + .select( + table.rm_item_code, + (table.received_qty - table.returned_qty).as_("total_qty"), + table.consumed_qty, + table.name, + ) + .where( + (table.docstatus == 1) + & (table.parent == self.subcontracting_inward_order) + & (table.main_item_code == frappe.get_cached_value("BOM", self.bom_no, "item")) + & (table.warehouse == warehouse) + & (table.rm_item_code.isin([item.item_code for item in items])) + ) + ) + rm_item_dict = frappe._dict( + { + d.rm_item_code: frappe._dict( + {"name": d.name, "total_qty": d.total_qty, "qty": d.consumed_qty} + ) + for d in query.run(as_dict=True) + } + ) + + for item in items: + if rm := rm_item_dict.get(item.item_code): + if rm.qty + item.transfer_qty > rm.total_qty: + frappe.throw( + _( + "Row #{0}: Customer Provided Item {1} exceeds quantity available through Subcontracting Inward Order" + ).format(item.idx, bold(item.item_code), item.transfer_qty) + ) + elif item.s_warehouse != warehouse: + frappe.throw( + _("Row #{0}: For Customer Provided Item {1}, Source Warehouse must be {2}").format( + item.idx, + bold(item.item_code), + bold(warehouse), + ) + ) + else: + frappe.throw( + _( + "Row #{0}: Customer Provided Item {1} is not a part of Subcontracting Inward Order {2}" + ).format( + item.idx, + bold(item.item_code), + bold(self.subcontracting_inward_order), + ) + ) + + def validate_customer_provided_item_for_inward(self): + if self.subcontracting_inward_order: + if self.purpose in ["Subcontracting Delivery", "Subcontracting Return"]: + for item in self.items: + if (item.is_finished_item or item.is_scrap_item) and item.valuation_rate == 0: + item.allow_zero_valuation_rate = 1 + elif self.purpose == "Receive from Customer": + for item in self.items: + if not frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item"): + frappe.throw( + _("Row #{0}: Item {1} is not a customer provided item.").format( + item.idx, + get_link_to_form("Item", item.item_code), + ) + ) + + def validate_warehouse_(self): + if self.subcontracting_inward_order and self.purpose in [ + "Receive from Customer", + "Return Raw Material to Customer", + "Material Transfer for Manufacture", + ]: + customer_warehouse = frappe.get_cached_value( + "Subcontracting Inward Order", self.subcontracting_inward_order, "customer_warehouse" + ) + for item in self.items: + if self.purpose == "Material Transfer for Manufacture" and not frappe.get_cached_value( + "Item", item.item_code, "is_customer_provided_item" + ): + continue + + if (item.s_warehouse or item.t_warehouse) != customer_warehouse: + if item.t_warehouse: + frappe.throw( + _( + "Row #{0}: Target Warehouse must be same as Customer Warehouse {1} from the linked Subcontracting Inward Order" + ).format(item.idx, bold(customer_warehouse)) + ) + else: + frappe.throw( + _( + "Row #{0}: Source Warehouse must be same as Customer Warehouse {1} from the linked Subcontracting Inward Order" + ).format(item.idx, bold(customer_warehouse)) + ) + + def validate_serial_batch_for_return_or_delivery(self): + if self.subcontracting_inward_order and self.purpose in [ + "Return Raw Material to Customer", + "Subcontracting Delivery", + "Subcontracting Return", + ]: + for item in self.items: + serial_nos, batch_nos = self.get_serial_nos_and_batches_from_sres( + item.scio_detail, only_pending=self.purpose != "Subcontracting Return" + ) + serial_list, batch_list = get_serial_batch_list_from_item(item) + + if serial_list and ( + incorrect_serial_nos := [sn for sn in serial_list if sn not in serial_nos] + ): + frappe.throw( + _( + "Row #{0}: Serial No(s) {1} are not a part of the linked Subcontracting Inward Order. Please select valid Serial No(s)." + ).format(item.idx, ", ".join([bold(sn) for sn in incorrect_serial_nos])) + ) + if batch_list and ( + incorrect_batch_nos := [bn for bn in batch_list if bn not in list(batch_nos.keys())] + ): + frappe.throw( + _( + "Row #{0}: Batch No(s) {1} is not a part of the linked Subcontracting Inward Order. Please select valid Batch No(s)." + ).format(item.idx, ", ".join([bold(bn) for bn in incorrect_batch_nos])) + ) + + def get_serial_nos_and_batches_from_sres(self, scio_detail, only_pending=True): + serial_nos, batch_nos = [], frappe._dict() + + table = frappe.qb.DocType("Stock Reservation Entry") + child_table = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(table) + .join(child_table) + .on(table.name == child_table.parent) + .select(child_table.serial_no, child_table.batch_no, child_table.qty) + .where((table.docstatus == 1) & (table.voucher_detail_no == scio_detail)) + ) + + if only_pending: + query = query.where(child_table.qty != child_table.delivered_qty) + else: + query = query.where(child_table.delivered_qty > 0) + + for d in query.run(as_dict=True): + if d.serial_no and d.serial_no not in serial_nos: + serial_nos.append(d.serial_no) + if d.batch_no and d.batch_no not in batch_nos: + batch_nos[d.batch_no] = d.qty + + return serial_nos, batch_nos + + def validate_delivery(self): + if self.purpose == "Subcontracting Delivery": + if self._action in ["save", "submit"]: + self.validate_delivery_on_save() + else: + for item in self.items: + if not item.is_scrap_item: + delivered_qty, returned_qty = frappe.get_value( + "Subcontracting Inward Order Item", + item.scio_detail, + ["delivered_qty", "returned_qty"], + ) + if returned_qty > delivered_qty: + frappe.throw( + _( + "Row #{0}: Cannot cancel this Stock Entry as returned quantity cannot be greater than delivered quantity for Item {1} in the linked Subcontracting Inward Order" + ).format(item.idx, bold(item.item_code)) + ) + + def validate_delivery_on_save(self): + allow_delivery_of_overproduced_qty = frappe.get_single_value( + "Selling Settings", "allow_delivery_of_overproduced_qty" + ) + + for item in self.items: + if not item.scio_detail: + frappe.throw( + _("Row #{0}: Item {1} is not a part of Subcontracting Inward Order {2}").format( + item.idx, + bold(item.item_code), + bold(self.subcontracting_inward_order), + ) + ) + + from pypika.terms import ValueWrapper + + table = frappe.qb.DocType("Subcontracting Inward Order Item") + query = ( + frappe.qb.from_(table) + .select( + ( + Case() + .when( + (table.produced_qty < table.qty) + | ValueWrapper(allow_delivery_of_overproduced_qty), + table.produced_qty, + ) + .else_(table.qty) + - table.delivered_qty + - table.returned_qty + ).as_("max_allowed_qty") + ) + .where((table.name == item.scio_detail) & (table.docstatus == 1)) + ) + max_allowed_qty = query.run(pluck="max_allowed_qty") + + if max_allowed_qty: + max_allowed_qty = max_allowed_qty[0] + else: + table = frappe.qb.DocType("Subcontracting Inward Order Scrap Item") + query = ( + frappe.qb.from_(table) + .select((table.produced_qty - table.delivered_qty).as_("max_allowed_qty")) + .where((table.name == item.scio_detail) & (table.docstatus == 1)) + ) + max_allowed_qty = query.run(pluck="max_allowed_qty")[0] + + if item.transfer_qty > max_allowed_qty: + frappe.throw( + _( + "Row #{0}: Quantity of Item {1} cannot be more than {2} {3} against Subcontracting Inward Order {4}" + ).format( + item.idx, + bold(item.item_code), + bold(max_allowed_qty), + bold( + frappe.get_cached_value( + "Subcontracting Inward Order Item" + if not item.is_scrap_item + else "Subcontracting Inward Order Scrap Item", + item.scio_detail, + "stock_uom", + ) + ), + bold(self.subcontracting_inward_order), + ) + ) + + def update_customer_provided_item_cost(self): + if self.purpose == "Receive from Customer": + for item in self.items: + item.valuation_rate = 0 + item.customer_provided_item_cost = flt( + item.basic_rate + (item.additional_cost / item.transfer_qty), item.precision("basic_rate") + ) + + def update_sre_for_subcontracting_delivery(self) -> None: + if self.purpose == "Subcontracting Delivery": + if self._action == "submit": + self.update_sre_for_subcontracting_delivery_submit() + elif self._action == "cancel": + self.update_sre_for_subcontracting_delivery_cancel() + + def update_sre_for_subcontracting_delivery_submit(self): + for item in self.get("items"): + table = frappe.qb.DocType("Stock Reservation Entry") + query = ( + frappe.qb.from_(table) + .select(table.name) + .where( + (table.docstatus == 1) + & (table.voucher_type == "Subcontracting Inward Order") + & (table.voucher_no == self.subcontracting_inward_order) + & (table.voucher_detail_no == item.scio_detail) + & (table.warehouse == item.s_warehouse) + ) + .orderby(table.creation) + ) + sre_list = query.run(pluck="name") + + if not sre_list: + continue + + qty_to_deliver = item.transfer_qty + for sre in sre_list: + if qty_to_deliver <= 0: + break + + sre_doc = frappe.get_doc("Stock Reservation Entry", sre) + + qty_can_be_deliver = 0 + if sre_doc.reservation_based_on == "Serial and Batch": + sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle) + if sre_doc.has_serial_no: + delivered_serial_nos = [d.serial_no for d in sbb.entries] + for entry in sre_doc.sb_entries: + if entry.serial_no in delivered_serial_nos: + entry.delivered_qty = 1 + entry.db_update() + qty_can_be_deliver += 1 + delivered_serial_nos.remove(entry.serial_no) + else: + delivered_batch_qty = {d.batch_no: -1 * d.qty for d in sbb.entries} + for entry in sre_doc.sb_entries: + if entry.batch_no in delivered_batch_qty: + delivered_qty = min( + (entry.qty - entry.delivered_qty), + delivered_batch_qty[entry.batch_no], + ) + entry.delivered_qty += delivered_qty + entry.db_update() + qty_can_be_deliver += delivered_qty + delivered_batch_qty[entry.batch_no] -= delivered_qty + else: + qty_can_be_deliver = min((sre_doc.reserved_qty - sre_doc.delivered_qty), qty_to_deliver) + + sre_doc.delivered_qty += qty_can_be_deliver + sre_doc.db_update() + sre_doc.update_status() + sre_doc.update_reserved_stock_in_bin() + + qty_to_deliver -= qty_can_be_deliver + + def update_sre_for_subcontracting_delivery_cancel(self): + for item in self.get("items"): + table = frappe.qb.DocType("Stock Reservation Entry") + query = ( + frappe.qb.from_(table) + .select(table.name) + .where( + (table.docstatus == 1) + & (table.voucher_type == "Subcontracting Inward Order") + & (table.voucher_no == self.subcontracting_inward_order) + & (table.voucher_detail_no == item.scio_detail) + & (table.warehouse == item.s_warehouse) + ) + .orderby(table.creation) + ) + sre_list = query.run(pluck="name") + + if not sre_list: + continue + + qty_to_undelivered = item.transfer_qty + for sre in sre_list: + if qty_to_undelivered <= 0: + break + + sre_doc = frappe.get_doc("Stock Reservation Entry", sre) + + qty_can_be_undelivered = 0 + if sre_doc.reservation_based_on == "Serial and Batch": + sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle) + if sre_doc.has_serial_no: + serial_nos_to_undelivered = [d.serial_no for d in sbb.entries] + for entry in sre_doc.sb_entries: + if entry.serial_no in serial_nos_to_undelivered: + entry.delivered_qty = 0 + entry.db_update() + qty_can_be_undelivered += 1 + serial_nos_to_undelivered.remove(entry.serial_no) + else: + batch_qty_to_undelivered = {d.batch_no: -1 * d.qty for d in sbb.entries} + for entry in sre_doc.sb_entries: + if entry.batch_no in batch_qty_to_undelivered: + undelivered_qty = min( + entry.delivered_qty, batch_qty_to_undelivered[entry.batch_no] + ) + entry.delivered_qty -= undelivered_qty + entry.db_update() + qty_can_be_undelivered += undelivered_qty + batch_qty_to_undelivered[entry.batch_no] -= undelivered_qty + else: + qty_can_be_undelivered = min(sre_doc.delivered_qty, qty_to_undelivered) + + sre_doc.delivered_qty -= qty_can_be_undelivered + sre_doc.db_update() + sre_doc.update_status() + sre_doc.update_reserved_stock_in_bin() + + qty_to_undelivered -= qty_can_be_undelivered + + def validate_receive_from_customer_cancel(self): + if self.purpose == "Receive from Customer": + for item in self.items: + scio_rm_item = frappe.get_value( + "Subcontracting Inward Order Received Item", + item.scio_detail, + ["received_qty", "returned_qty", "work_order_qty"], + as_dict=True, + ) + if ( + scio_rm_item.received_qty - scio_rm_item.returned_qty - item.transfer_qty + ) < scio_rm_item.work_order_qty: + frappe.throw( + _("Row #{0}: Work Order exists against full or partial quantity of Item {1}").format( + item.idx, bold(item.item_code) + ) + ) + + def validate_manufacture_entry_cancel(self): + if self.subcontracting_inward_order and self.purpose == "Manufacture": + fg_item_name = frappe.get_cached_value( + "Work Order", self.work_order, "subcontracting_inward_order_item" + ) + produced_qty, delivered_qty = frappe.get_value( + "Subcontracting Inward Order Item", fg_item_name, ["produced_qty", "delivered_qty"] + ) + if produced_qty < delivered_qty: + frappe.throw( + _( + "Cannot cancel this Manufacturing Stock Entry as quantity of Finished Good produced cannot be less than quantity delivered in the linked Subcontracting Inward Order." + ) + ) + + for item in [item for item in self.items if not item.is_finished_item]: + if item.is_scrap_item: + scio_scrap_item = frappe.get_value( + "Subcontracting Inward Order Scrap Item", + { + "docstatus": 1, + "item_code": item.item_code, + "warehouse": item.t_warehouse, + "reference_name": fg_item_name, + }, + ["produced_qty", "delivered_qty"], + as_dict=True, + ) + if ( + scio_scrap_item + and scio_scrap_item.delivered_qty > scio_scrap_item.produced_qty - item.transfer_qty + ): + frappe.throw( + _( + "Row #{0}: Cannot cancel this Manufacturing Stock Entry as quantity of Scrap Item {1} produced cannot be less than quantity delivered." + ).format(item.idx, bold(item.item_code)) + ) + else: + scio_rm_item = frappe.get_value( + "Subcontracting Inward Order Received Item", + { + "docstatus": 1, + "rm_item_code": item.item_code, + "warehouse": item.s_warehouse, + "reference_name": fg_item_name, # if this field is set then the additional item is NOT customer provided + "is_additional_item": 1, + }, + ["consumed_qty", "billed_qty", "returned_qty"], + as_dict=True, + ) + if scio_rm_item and (scio_rm_item.billed_qty - scio_rm_item.returned_qty) > ( + scio_rm_item.consumed_qty - item.transfer_qty + ): + frappe.throw( + _( + "Row #{0}: Cannot cancel this Manufacturing Stock Entry as billed quantity of Item {1} cannot be greater than consumed quantity." + ).format(item.idx, bold(item.item_code)) + ) + + def update_inward_order_item(self): + if self.purpose == "Manufacture" and ( + scio_item_name := frappe.get_cached_value( + "Work Order", self.work_order, "subcontracting_inward_order_item" + ) + ): + if scio_item_name: + frappe.get_doc( + "Subcontracting Inward Order Item", scio_item_name + ).update_manufacturing_qty_fields() + elif self.purpose in ["Subcontracting Delivery", "Subcontracting Return"]: + fieldname = "delivered_qty" if self.purpose == "Subcontracting Delivery" else "returned_qty" + for item in self.items: + doctype = ( + "Subcontracting Inward Order Item" + if not item.is_scrap_item + else "Subcontracting Inward Order Scrap Item" + ) + frappe.db.set_value( + doctype, + item.scio_detail, + fieldname, + frappe.get_value(doctype, item.scio_detail, fieldname) + + (item.transfer_qty if self._action == "submit" else -item.transfer_qty), + ) + + def update_inward_order_received_items(self): + if self.subcontracting_inward_order: + match self.purpose: + case "Receive from Customer": + self.update_inward_order_received_items_for_raw_materials_receipt() + case "Manufacture": + self.update_inward_order_received_items_for_manufacture() + case "Return Raw Material to Customer": + scio_rm_names = { + item.scio_detail: item.transfer_qty + if self._action == "submit" + else -item.transfer_qty + for item in self.items + } + case_expr = Case() + table = frappe.qb.DocType("Subcontracting Inward Order Received Item") + for scio_rm_name, qty in scio_rm_names.items(): + case_expr = case_expr.when(table.name == scio_rm_name, table.returned_qty + qty) + + frappe.qb.update(table).set(table.returned_qty, case_expr).where( + (table.name.isin(list(scio_rm_names.keys()))) & (table.docstatus == 1) + ).run() + + def update_inward_order_received_items_for_raw_materials_receipt(self): + data = frappe._dict() + for item in self.items: + if item.scio_detail: + data[item.scio_detail] = item.transfer_qty if self._action == "submit" else -item.transfer_qty + else: + scio_rm = frappe.new_doc( + "Subcontracting Inward Order Received Item", + parent=self.subcontracting_inward_order, + parenttype="Subcontracting Inward Order", + parentfield="received_items", + idx=frappe.db.count( + "Subcontracting Inward Order Received Item", + {"parent": self.subcontracting_inward_order}, + ) + + 1, + rm_item_code=item.item_code, + stock_uom=item.stock_uom, + warehouse=item.t_warehouse, + received_qty=item.transfer_qty, + consumed_qty=0, + work_order_qty=0, + returned_qty=0, + is_additional_item=True, + ) + scio_rm.insert() + item.db_set("scio_detail", scio_rm.name) + + if data: + result = frappe.get_all( + "Subcontracting Inward Order Received Item", + filters={ + "parent": self.subcontracting_inward_order, + "name": ["in", list(data.keys())], + "docstatus": 1, + }, + fields=["name", "required_qty", "received_qty"], + ) + + deleted_docs = [] + table = frappe.qb.DocType("Subcontracting Inward Order Received Item") + case_expr = Case() + for d in result: + d.received_qty += data[d.name] + + if not d.required_qty and not d.received_qty: + deleted_docs.append(d.name) + frappe.delete_doc("Subcontracting Inward Order Received Item", d.name) + else: + case_expr = case_expr.when(table.name == d.name, d.received_qty) + + if len(list(set(data.keys()) - set(deleted_docs))) > 0: + frappe.qb.update(table).set(table.received_qty, case_expr).where( + (table.name.isin(list(set(data.keys()) - set(deleted_docs)))) & (table.docstatus == 1) + ).run() + + def update_inward_order_received_items_for_manufacture(self): + items = [item for item in self.items if not item.is_finished_item and not item.is_scrap_item] + item_code_wh = frappe._dict( + { + (item.item_code, item.s_warehouse): item.transfer_qty + if self._action == "submit" + else -item.transfer_qty + for item in items + } + ) + item_codes, warehouses = zip(*list(item_code_wh.keys()), strict=True) + item_codes = list(item_codes) + warehouses = list(warehouses) + + table = frappe.qb.DocType("Subcontracting Inward Order Received Item") + data = ( + frappe.qb.from_(table) + .select( + table.name, + table.rm_item_code, + table.is_customer_provided_item, + table.consumed_qty, + table.required_qty, + table.warehouse, + ) + .where( + (table.docstatus == 1) + & (table.rm_item_code.isin(item_codes)) + & ((table.warehouse.isin(warehouses)) | (table.warehouse.isnull())) + & (table.parent == self.subcontracting_inward_order) + & ( + ( + table.reference_name + == frappe.get_cached_value( + "Work Order", self.work_order, "subcontracting_inward_order_item" + ) + ) + | (table.reference_name.isnull()) + ) + ) + ) + + if data := data.run(as_dict=True): + deleted_docs, used_item_wh = [], [] + case_expr = Case() + for d in data: + if not d.warehouse: + d.warehouse = next( + key[1] + for key in item_code_wh.keys() + if key[0] == d.rm_item_code and key not in used_item_wh + ) + used_item_wh.append((d.rm_item_code, d.warehouse)) + + qty = d.consumed_qty + item_code_wh[(d.rm_item_code, d.warehouse)] + if qty or d.is_customer_provided_item: + case_expr = case_expr.when((table.name == d.name), qty) + else: + deleted_docs.append(d.name) + frappe.delete_doc("Subcontracting Inward Order Received Item", d.name) + + final_name_list = list(set([d.name for d in data]) - set(deleted_docs)) + if len(final_name_list) > 0: + frappe.qb.update(table).set(table.consumed_qty, case_expr).where( + (table.name.isin(final_name_list)) & (table.docstatus == 1) + ).run() + + main_item_code = next(fg for fg in self.items if fg.is_finished_item).item_code + for extra_item in [ + item + for item in items + if (item.item_code, item.s_warehouse) not in [(d.rm_item_code, d.warehouse) for d in data] + ]: + frappe.new_doc( + "Subcontracting Inward Order Received Item", + parent=self.subcontracting_inward_order, + parenttype="Subcontracting Inward Order", + parentfield="received_items", + idx=frappe.db.count( + "Subcontracting Inward Order Received Item", + {"parent": self.subcontracting_inward_order}, + ) + + 1, + main_item_code=main_item_code, + rm_item_code=extra_item.item_code, + stock_uom=extra_item.stock_uom, + reference_name=frappe.get_cached_value( + "Work Order", self.work_order, "subcontracting_inward_order_item" + ), + required_qty=0, + consumed_qty=extra_item.transfer_qty, + warehouse=extra_item.s_warehouse, + is_additional_item=True, + ).insert() + + def update_inward_order_scrap_items(self): + if (scio := self.subcontracting_inward_order) and self.purpose == "Manufacture": + scrap_items_list = [item for item in self.items if item.is_scrap_item] + scrap_items = frappe._dict( + { + (item.item_code, item.t_warehouse): item.transfer_qty + if self._action == "submit" + else -item.transfer_qty + for item in scrap_items_list + } + ) + if scrap_items: + item_codes, warehouses = zip(*list(scrap_items.keys()), strict=True) + item_codes = list(item_codes) + warehouses = list(warehouses) + + result = frappe.get_all( + "Subcontracting Inward Order Scrap Item", + filters={ + "item_code": ["in", item_codes], + "warehouse": ["in", warehouses], + "reference_name": frappe.get_cached_value( + "Work Order", self.work_order, "subcontracting_inward_order_item" + ), + "docstatus": 1, + }, + fields=["name", "item_code", "warehouse", "produced_qty"], + ) + + if result: + scrap_item_dict = frappe._dict( + { + (d.item_code, d.warehouse): frappe._dict( + {"name": d.name, "produced_qty": d.produced_qty} + ) + for d in result + } + ) + deleted_docs = [] + case_expr = Case() + table = frappe.qb.DocType("Subcontracting Inward Order Scrap Item") + for key, value in scrap_item_dict.items(): + if self._action == "cancel" and value.produced_qty - abs(scrap_items.get(key)) == 0: + deleted_docs.append(value.name) + frappe.delete_doc("Subcontracting Inward Order Scrap Item", value.name) + else: + case_expr = case_expr.when( + table.name == value.name, value.produced_qty + scrap_items.get(key) + ) + + final_list = list(set([v.name for v in scrap_item_dict.values()]) - set(deleted_docs)) + if len(final_list) > 0: + frappe.qb.update(table).set(table.produced_qty, case_expr).where( + (table.name.isin(final_list)) & (table.docstatus == 1) + ).run() + + fg_item_code = next(fg for fg in self.items if fg.is_finished_item).item_code + for scrap_item in [ + item + for item in scrap_items_list + if (item.item_code, item.t_warehouse) not in [(d.item_code, d.warehouse) for d in result] + ]: + frappe.new_doc( + "Subcontracting Inward Order Scrap Item", + parent=scio, + parenttype="Subcontracting Inward Order", + parentfield="scrap_items", + idx=frappe.db.count("Subcontracting Inward Order Scrap Item", {"parent": scio}) + 1, + item_code=scrap_item.item_code, + fg_item_code=fg_item_code, + stock_uom=scrap_item.stock_uom, + warehouse=scrap_item.t_warehouse, + produced_qty=scrap_item.transfer_qty, + delivered_qty=0, + reference_name=frappe.get_value( + "Work Order", self.work_order, "subcontracting_inward_order_item" + ), + ).insert() + + def cancel_stock_reservation_entries_for_inward(self): + if self.purpose == "Receive from Customer": + table = frappe.qb.DocType("Stock Reservation Entry") + query = ( + frappe.qb.from_(table) + .select(table.name) + .where( + (table.docstatus == 1) + & (table.voucher_detail_no.isin([item.scio_detail for item in self.items])) + ) + ) + for sre in query.run(pluck="name"): + frappe.get_doc("Stock Reservation Entry", sre).cancel() + + def remove_reference_for_additional_items(self): + if self.subcontracting_inward_order: + items = [ + item + for item in self.items + if item.scio_detail + and ( + not frappe.db.exists("Subcontracting Inward Order Received Item", item.scio_detail) + and not frappe.db.exists("Subcontracting Inward Order Item", item.scio_detail) + and not frappe.db.exists("Subcontracting Inward Order Scrap Item", item.scio_detail) + ) + ] + for item in items: + item.db_set("scio_detail", None) + + def create_stock_reservation_entries_for_inward(self): + if self.purpose == "Receive from Customer": + for item in self.items: + item.reload() + sre = frappe.new_doc("Stock Reservation Entry") + sre.company = self.company + sre.voucher_type = "Subcontracting Inward Order" + sre.voucher_qty = sre.reserved_qty = sre.available_qty = item.transfer_qty + sre.voucher_no = self.subcontracting_inward_order + sre.voucher_detail_no = item.scio_detail + sre.item_code = item.item_code + sre.stock_uom = item.stock_uom + sre.warehouse = item.t_warehouse or item.s_warehouse + sre.has_serial_no = frappe.get_cached_value("Item", item.item_code, "has_serial_no") + sre.has_batch_no = frappe.get_cached_value("Item", item.item_code, "has_batch_no") + sre.reservation_based_on = "Qty" if not item.serial_and_batch_bundle else "Serial and Batch" + if item.serial_and_batch_bundle: + sabb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle) + for entry in sabb.entries: + sre.append( + "sb_entries", + { + "serial_no": entry.serial_no, + "batch_no": entry.batch_no, + "qty": entry.qty, + "warehouse": entry.warehouse, + }, + ) + sre.submit() + frappe.msgprint(_("Stock Reservation Entries Created"), alert=True, indicator="green") + + def adjust_stock_reservation_entries_for_return(self): + if self.purpose == "Return Raw Material to Customer": + for item in self.items: + serial_list, batch_list = get_serial_batch_list_from_item(item) + + if serial_list or batch_list: + table = frappe.qb.DocType("Stock Reservation Entry") + child_table = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(table) + .join(child_table) + .on(table.name == child_table.parent) + .select( + table.name.as_("sre_name"), + child_table.name.as_("sbe_name"), + child_table.batch_no, + child_table.qty, + ) + .where((table.docstatus == 1) & (table.voucher_detail_no == item.scio_detail)) + ) + if serial_list: + query = query.where(child_table.serial_no.isin(serial_list)) + if batch_list: + query = query.where(child_table.batch_no.isin(batch_list)) + result = query.run(as_dict=True) + + qty_to_deliver = {row.sre_name: 0 for row in result} + consumed_qty = {batch: 0 for batch in batch_list} + for row in result: + if serial_list: + frappe.get_doc("Serial and Batch Entry", row.sbe_name).db_set( + "delivered_qty", 1 if self._action == "submit" else 0 + ) + qty_to_deliver[row.sre_name] += row.qty + elif batch_list and not serial_list: + sabe_qty = abs( + frappe.get_value( + "Serial and Batch Entry", + {"parent": item.serial_and_batch_bundle, "batch_no": row.batch_no}, + "qty", + ) + ) + + qty = min(row.qty, sabe_qty) + sbe_doc = frappe.get_doc("Serial and Batch Entry", row.sbe_name) + sbe_doc.db_set( + "delivered_qty", + sbe_doc.delivered_qty + (qty if self._action == "submit" else -qty), + ) + qty_to_deliver[row.sre_name] += qty + consumed_qty[row.batch_no] += qty + + for sre_name, qty in qty_to_deliver.items(): + sre_doc = frappe.get_doc("Stock Reservation Entry", sre_name) + sre_doc.db_set( + "delivered_qty", + sre_doc.delivered_qty + (qty if self._action == "submit" else -qty), + ) + sre_doc.update_status() + sre_doc.update_reserved_stock_in_bin() + else: + table = frappe.qb.DocType("Stock Reservation Entry") + query = ( + frappe.qb.from_(table) + .select( + table.name, + (table.reserved_qty - table.delivered_qty).as_("qty"), + ) + .where( + (table.docstatus == 1) + & (table.voucher_detail_no == item.scio_detail) + & (table.delivered_qty < table.reserved_qty) + ) + .orderby(table.creation) + ) + sre_list = query.run(as_dict=True) + + voucher_qty = item.transfer_qty + for sre in sre_list: + qty = min(sre.qty, voucher_qty) + sre_doc = frappe.get_doc("Stock Reservation Entry", sre.name) + sre_doc.db_set( + "delivered_qty", + sre_doc.delivered_qty + (qty if self._action == "submit" else -qty), + ) + sre_doc.update_status() + sre_doc.update_reserved_stock_in_bin() + voucher_qty -= qty + if voucher_qty <= 0: + break + + def update_inward_order_status(self): + if self.subcontracting_inward_order: + from erpnext.subcontracting.doctype.subcontracting_inward_order.subcontracting_inward_order import ( + update_subcontracting_inward_order_status, + ) + + update_subcontracting_inward_order_status(self.subcontracting_inward_order) diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index 94ec192779e..b3e06d4db6c 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -72,7 +72,7 @@ class TestSubcontractingController(IntegrationTestCase): def test_create_raw_materials_supplied(self): sco = get_subcontracting_order() sco.supplied_items = None - sco.create_raw_materials_supplied() + sco.create_raw_materials_supplied_or_received() self.assertIsNotNone(sco.supplied_items) def test_sco_with_bom(self): diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 22bff524dea..3582eb7c30d 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -51,6 +51,8 @@ doctype_list_js = { ], } +page_js = {"print": "public/js/print.js"} + extend_doctype_class = {"Address": "erpnext.accounts.custom.address.ERPNextAddress"} override_whitelisted_methods = {"frappe.www.contact.send_message": "erpnext.templates.utils.send_message"} @@ -600,6 +602,7 @@ user_privacy_documents = [ }, ] + # ERPNext doctypes for Global Search global_search_doctypes = { "Default": [ diff --git a/erpnext/locale/fa.po b/erpnext/locale/fa.po index 5d0b8c730b7..12d1e1e4411 100644 --- a/erpnext/locale/fa.po +++ b/erpnext/locale/fa.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: hello@frappe.io\n" "POT-Creation-Date: 2025-10-05 09:35+0000\n" -"PO-Revision-Date: 2025-10-06 10:13\n" +"PO-Revision-Date: 2025-10-09 11:15\n" "Last-Translator: hello@frappe.io\n" "Language-Team: Persian\n" "MIME-Version: 1.0\n" @@ -4127,7 +4127,7 @@ msgstr "همه این آیتم‌ها قبلاً صورتحساب/بازگردا #: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:85 #: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:92 msgid "Allocate" -msgstr "اختصاص دهید" +msgstr "" #. Label of the allocate_advances_automatically (Check) field in DocType 'POS #. Invoice' @@ -5628,7 +5628,7 @@ msgstr "اسلات رزرو قرار" #: erpnext/crm/doctype/appointment/appointment.py:95 msgid "Appointment Confirmation" -msgstr "تایید قرار ملاقات" +msgstr "تأیید قرار ملاقات" #: erpnext/www/book_appointment/index.js:237 msgid "Appointment Created Successfully" @@ -5661,7 +5661,7 @@ msgstr "ملاقات با" #: erpnext/crm/doctype/appointment/appointment.py:101 msgid "Appointment was created. But no lead was found. Please check the email to confirm" -msgstr "قرار ملاقات ایجاد شد. اما سرنخی پیدا نشد. لطفا برای تایید ایمیل را بررسی کنید" +msgstr "قرار ملاقات ایجاد شد. اما سرنخی پیدا نشد. لطفا برای تأیید ایمیل را بررسی کنید" #. Label of the approving_role (Link) field in DocType 'Authorization Rule' #: erpnext/setup/doctype/authorization_rule/authorization_rule.json @@ -5675,7 +5675,7 @@ msgstr "نقش تأیید نمی‌تواند با نقشی که قانون بر #. Label of the approving_user (Link) field in DocType 'Authorization Rule' #: erpnext/setup/doctype/authorization_rule/authorization_rule.json msgid "Approving User (above authorized value)" -msgstr "تایید کاربر (بالاتر از مقدار مجاز)" +msgstr "تأیید کاربر (بالاتر از مقدار مجاز)" #: erpnext/setup/doctype/authorization_rule/authorization_rule.py:77 msgid "Approving User cannot be same as user the rule is Applicable To" @@ -9434,7 +9434,7 @@ msgstr "برنامه های کمپین" #: erpnext/setup/doctype/authorization_control/authorization_control.py:60 msgid "Can be approved by {0}" -msgstr "قابل تایید توسط {0}" +msgstr "قابل تأیید توسط {0}" #: erpnext/manufacturing/doctype/work_order/work_order.py:2172 msgid "Can not close Work Order. Since {0} Job Cards are in Work In Progress state." @@ -10655,7 +10655,7 @@ msgstr "هنگامی که فایل فشرده به سند پیوست شد، رو #: erpnext/templates/emails/confirm_appointment.html:3 msgid "Click on the link below to verify your email and confirm the appointment" -msgstr "برای تایید ایمیل خود و تایید قرار ملاقات روی لینک زیر کلیک کنید" +msgstr "برای تأیید ایمیل خود و تأیید قرار ملاقات روی لینک زیر کلیک کنید" #: erpnext/selling/page/point_of_sale/pos_item_cart.js:485 msgid "Click to add email / phone" @@ -12000,7 +12000,7 @@ msgstr "" #. Label of the final_confirmation_date (Date) field in DocType 'Employee' #: erpnext/setup/doctype/employee/employee.json msgid "Confirmation Date" -msgstr "تاریخ تایید" +msgstr "تاریخ تأیید" #. Label of the connections_tab (Tab Break) field in DocType 'Purchase Invoice' #. Label of the connections_tab (Tab Break) field in DocType 'Sales Invoice' @@ -23223,7 +23223,7 @@ msgstr "درصد سود ناخالص" #: erpnext/accounts/report/financial_ratios/financial_ratios.py:171 msgid "Gross Profit Ratio" -msgstr "" +msgstr "نسبت سود ناخالص" #. Label of the gross_purchase_amount (Currency) field in DocType 'Asset #. Depreciation Schedule' @@ -24763,7 +24763,7 @@ msgstr "در تولید" #: erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.json #: erpnext/telephony/doctype/call_log/call_log.json msgid "In Progress" -msgstr "در حال پیش رفت" +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 @@ -26030,7 +26030,7 @@ msgstr "تنظیمات موجودی" #: erpnext/accounts/report/financial_ratios/financial_ratios.py:214 msgid "Inventory Turnover Ratio" -msgstr "" +msgstr "نسبت گردش موجودی" #: erpnext/setup/setup_wizard/data/industry_type.txt:29 msgid "Investment Banking" @@ -32558,7 +32558,7 @@ msgstr "سود خالص" #: erpnext/accounts/report/financial_ratios/financial_ratios.py:172 msgid "Net Profit Ratio" -msgstr "" +msgstr "نسبت سود خالص" #: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:180 msgid "Net Profit/Loss" @@ -34796,12 +34796,12 @@ msgstr "سفارش توسط" #. Order' #: erpnext/buying/doctype/purchase_order/purchase_order.json msgid "Order Confirmation Date" -msgstr "تاریخ تایید سفارش" +msgstr "تاریخ تأیید سفارش" #. Label of the order_confirmation_no (Data) field in DocType 'Purchase Order' #: erpnext/buying/doctype/purchase_order/purchase_order.json msgid "Order Confirmation No" -msgstr "شماره تایید سفارش" +msgstr "شماره تأیید سفارش" #: erpnext/crm/report/campaign_efficiency/campaign_efficiency.py:23 #: erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py:29 @@ -38140,7 +38140,7 @@ msgstr "لطفاً شناسه مشتری Plaid و مقادیر مخفی خود #: erpnext/crm/doctype/appointment/appointment.py:98 #: erpnext/www/book_appointment/index.js:235 msgid "Please check your email to confirm the appointment" -msgstr "لطفا ایمیل خود را برای تایید قرار ملاقات بررسی کنید" +msgstr "لطفا ایمیل خود را برای تأیید قرار ملاقات بررسی کنید" #: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py:374 msgid "Please click on 'Generate Schedule'" @@ -38263,7 +38263,7 @@ msgstr "لطفاً حساب را برای تغییر مبلغ وارد کنید" #: erpnext/setup/doctype/authorization_rule/authorization_rule.py:75 msgid "Please enter Approving Role or Approving User" -msgstr "لطفاً نقش تأیید یا تأیید کاربر را وارد کنید" +msgstr "لطفاً نقش تأیید یا کاربر تأیید را وارد کنید" #: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:945 msgid "Please enter Cost Center" @@ -59037,11 +59037,11 @@ msgstr "تا زمان" #. Option for the 'Status' (Select) field in DocType 'Appointment' #: erpnext/crm/doctype/appointment/appointment.json msgid "Unverified" -msgstr "تایید نشده" +msgstr "تأیید نشده" #: erpnext/erpnext_integrations/utils.py:22 msgid "Unverified Webhook Data" -msgstr "داده های وب هوک تایید نشده" +msgstr "داده های وب هوک تأیید نشده" #: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js:17 msgid "Up" @@ -60173,7 +60173,7 @@ msgstr "تأیید انجام نشد لطفاً پیوند را بررسی کن #. Label of the verified_by (Data) field in DocType 'Quality Inspection' #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Verified By" -msgstr "تایید شده توسط" +msgstr "تأیید شده توسط" #: erpnext/templates/emails/confirm_appointment.html:6 #: erpnext/www/book_appointment/verify/index.html:4 @@ -62198,7 +62198,7 @@ msgstr "نام شما (الزامی)" #: erpnext/www/book_appointment/verify/index.html:11 msgid "Your email has been verified and your appointment has been scheduled" -msgstr "ایمیل شما تایید شده و قرار ملاقات شما تعیین شده است" +msgstr "ایمیل شما تأیید شده و قرار ملاقات شما تعیین شده است" #: erpnext/patches/v11_0/add_default_dispatch_notification_template.py:22 #: erpnext/setup/setup_wizard/operations/install_fixtures.py:320 diff --git a/erpnext/locale/fr.po b/erpnext/locale/fr.po index 8fbb59fb0ee..9c02c275f08 100644 --- a/erpnext/locale/fr.po +++ b/erpnext/locale/fr.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: hello@frappe.io\n" "POT-Creation-Date: 2025-10-05 09:35+0000\n" -"PO-Revision-Date: 2025-10-06 10:14\n" +"PO-Revision-Date: 2025-10-08 10:44\n" "Last-Translator: hello@frappe.io\n" "Language-Team: French\n" "MIME-Version: 1.0\n" @@ -41601,7 +41601,7 @@ msgstr "Analyse des bons de commande" #: erpnext/buying/report/procurement_tracker/procurement_tracker.py:76 msgid "Purchase Order Date" -msgstr "Date du de la Commande d'Achat" +msgstr "Date de la commande d'achat" #. Label of the po_detail (Data) field in DocType 'Purchase Invoice Item' #. Label of the purchase_order_item (Data) field in DocType 'Sales Invoice diff --git a/erpnext/locale/main.pot b/erpnext/locale/main.pot index d87a87ccd9e..1a7f8d53681 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: 2025-10-05 09:35+0000\n" -"PO-Revision-Date: 2025-10-05 09:35+0000\n" +"POT-Creation-Date: 2025-10-12 09:34+0000\n" +"PO-Revision-Date: 2025-10-12 09:34+0000\n" "Last-Translator: hello@frappe.io\n" "Language-Team: hello@frappe.io\n" "MIME-Version: 1.0\n" @@ -299,8 +299,8 @@ msgstr "" msgid "'{0}' has been already added." msgstr "" -#: erpnext/setup/doctype/company/company.py:210 -#: erpnext/setup/doctype/company/company.py:221 +#: erpnext/setup/doctype/company/company.py:213 +#: erpnext/setup/doctype/company/company.py:224 msgid "'{0}' should be in company currency {1}." msgstr "" @@ -974,7 +974,7 @@ msgstr "" msgid "A Reverse Journal Entry {0} already exists for this Journal Entry." msgstr "" -#: erpnext/setup/doctype/company/company.py:956 +#: erpnext/setup/doctype/company/company.py:974 msgid "A Transaction Deletion Document: {0} is triggered for {0}" msgstr "" @@ -1098,11 +1098,11 @@ msgstr "" msgid "Abbreviation" msgstr "" -#: erpnext/setup/doctype/company/company.py:169 +#: erpnext/setup/doctype/company/company.py:172 msgid "Abbreviation already used for another company" msgstr "" -#: erpnext/setup/doctype/company/company.py:166 +#: erpnext/setup/doctype/company/company.py:169 msgid "Abbreviation is mandatory" msgstr "" @@ -1181,7 +1181,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:2764 +#: erpnext/public/js/controllers/transaction.js:2789 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json #: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json msgid "Accepted Quantity" @@ -1214,7 +1214,7 @@ msgstr "" msgid "According to CEFACT/ICG/2010/IC013 or CEFACT/ICG/2010/IC010" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:818 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:819 msgid "According to the BOM {0}, the Item '{1}' is missing in the stock entry." msgstr "" @@ -1273,8 +1273,9 @@ msgstr "" #: erpnext/accounts/report/account_balance/account_balance.py:21 #: erpnext/accounts/report/budget_variance_report/budget_variance_report.py:83 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py:287 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:402 #: erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py:202 -#: erpnext/accounts/report/financial_statements.py:650 +#: erpnext/accounts/report/financial_statements.py:670 #: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js:30 #: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py:190 #: erpnext/accounts/report/general_ledger/general_ledger.js:38 @@ -1282,7 +1283,7 @@ msgstr "" #: erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js:30 #: erpnext/accounts/report/payment_ledger/payment_ledger.js:30 #: erpnext/accounts/report/payment_ledger/payment_ledger.py:152 -#: erpnext/accounts/report/trial_balance/trial_balance.py:433 +#: erpnext/accounts/report/trial_balance/trial_balance.py:482 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js:70 #: erpnext/regional/doctype/uae_vat_account/uae_vat_account.json #: erpnext/stock/doctype/warehouse/warehouse.json @@ -1391,8 +1392,9 @@ msgstr "" #: erpnext/accounts/doctype/bank_account/bank_account.json #: erpnext/accounts/doctype/ledger_merge/ledger_merge.json #: erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json -#: erpnext/accounts/report/financial_statements.py:661 -#: erpnext/accounts/report/trial_balance/trial_balance.py:440 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:408 +#: erpnext/accounts/report/financial_statements.py:681 +#: erpnext/accounts/report/trial_balance/trial_balance.py:489 msgid "Account Name" msgstr "" @@ -1403,8 +1405,9 @@ msgstr "" #. Label of the account_number (Data) field in DocType 'Account' #: erpnext/accounts/doctype/account/account.json #: erpnext/accounts/doctype/account/account_tree.js:132 -#: erpnext/accounts/report/financial_statements.py:668 -#: erpnext/accounts/report/trial_balance/trial_balance.py:447 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:415 +#: erpnext/accounts/report/financial_statements.py:688 +#: erpnext/accounts/report/trial_balance/trial_balance.py:496 msgid "Account Number" msgstr "" @@ -1489,7 +1492,7 @@ msgstr "" msgid "Account is not set for the dashboard chart {0}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:774 +#: erpnext/assets/doctype/asset/asset.py:772 msgid "Account not Found" msgstr "" @@ -1518,7 +1521,7 @@ msgstr "" msgid "Account {0} added multiple times" msgstr "" -#: erpnext/setup/doctype/company/company.py:192 +#: erpnext/setup/doctype/company/company.py:195 msgid "Account {0} does not belong to company: {1}" msgstr "" @@ -1586,7 +1589,7 @@ msgstr "" msgid "Account {0}: You can not assign itself as parent account" msgstr "" -#: erpnext/accounts/general_ledger.py:451 +#: erpnext/accounts/general_ledger.py:452 msgid "Account: {0} is capital Work in progress and can not be updated by Journal Entry" msgstr "" @@ -1598,7 +1601,7 @@ msgstr "" msgid "Account: {0} is not permitted under Payment Entry" msgstr "" -#: erpnext/controllers/accounts_controller.py:3076 +#: erpnext/controllers/accounts_controller.py:3088 msgid "Account: {0} with currency: {1} can not be selected" msgstr "" @@ -1871,14 +1874,14 @@ msgstr "" msgid "Accounting Entries" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:808 -#: erpnext/assets/doctype/asset/asset.py:823 +#: erpnext/assets/doctype/asset/asset.py:806 +#: erpnext/assets/doctype/asset/asset.py:821 #: erpnext/assets/doctype/asset_capitalization/asset_capitalization.py:559 msgid "Accounting Entry for Asset" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1675 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1695 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1676 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1696 msgid "Accounting Entry for LCV in Stock Entry {0}" msgstr "" @@ -1886,29 +1889,29 @@ msgstr "" msgid "Accounting Entry for Landed Cost Voucher for SCR {0}" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:816 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:817 msgid "Accounting Entry for Service" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1006 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1027 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1045 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1066 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1087 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1111 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1218 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1454 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1476 -#: erpnext/controllers/stock_controller.py:579 -#: erpnext/controllers/stock_controller.py:596 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:909 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1621 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1635 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1007 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1028 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1046 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1067 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1088 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1112 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1219 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1455 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1477 +#: erpnext/controllers/stock_controller.py:603 +#: erpnext/controllers/stock_controller.py:620 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:910 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1622 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1636 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py:644 msgid "Accounting Entry for Stock" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:718 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:719 msgid "Accounting Entry for {0}" msgstr "" @@ -1937,7 +1940,7 @@ msgstr "" msgid "Accounting Period" msgstr "" -#: erpnext/accounts/doctype/accounting_period/accounting_period.py:66 +#: erpnext/accounts/doctype/accounting_period/accounting_period.py:67 msgid "Accounting Period overlaps with {0}" msgstr "" @@ -1971,7 +1974,7 @@ msgstr "" #: erpnext/buying/doctype/supplier/supplier.json #: erpnext/selling/doctype/customer/customer.json #: erpnext/setup/doctype/company/company.json -#: erpnext/setup/doctype/company/company.py:350 +#: erpnext/setup/doctype/company/company.py:353 #: erpnext/setup/doctype/customer_group/customer_group.json #: erpnext/setup/doctype/email_digest/email_digest.json #: erpnext/setup/doctype/incoterm/incoterm.json @@ -2296,8 +2299,8 @@ msgstr "" msgid "Accumulated Depreciation Amount" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:625 -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:643 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:633 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:651 msgid "Accumulated Depreciation as on" msgstr "" @@ -2464,8 +2467,8 @@ msgstr "" #: erpnext/quality_management/doctype/quality_procedure/quality_procedure.json #: erpnext/selling/doctype/customer/customer.js:190 #: erpnext/selling/doctype/customer/customer.js:202 -#: erpnext/stock/doctype/item/item.js:518 -#: erpnext/stock/doctype/item/item.js:528 erpnext/templates/pages/order.html:20 +#: erpnext/stock/doctype/item/item.js:533 +#: erpnext/stock/doctype/item/item.js:543 erpnext/templates/pages/order.html:20 msgid "Actions" msgstr "" @@ -2604,7 +2607,7 @@ msgstr "" #: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/report/work_order_summary/work_order_summary.py:254 -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:107 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:115 msgid "Actual End Date" msgstr "" @@ -2765,7 +2768,7 @@ msgstr "" msgid "Add" msgstr "" -#: erpnext/stock/doctype/item/item.js:514 +#: erpnext/stock/doctype/item/item.js:529 #: erpnext/stock/doctype/price_list/price_list.js:8 msgid "Add / Edit Prices" msgstr "" @@ -3497,7 +3500,7 @@ msgstr "" msgid "Adjustment Against" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:646 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:647 msgid "Adjustment based on Purchase Invoice rate" msgstr "" @@ -3583,7 +3586,7 @@ msgstr "" #: erpnext/accounts/doctype/pos_invoice/pos_invoice.json #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json -#: erpnext/controllers/accounts_controller.py:272 +#: erpnext/controllers/accounts_controller.py:277 #: erpnext/setup/doctype/company/company.json msgid "Advance Payments" msgstr "" @@ -3626,7 +3629,7 @@ msgstr "" msgid "Advance amount" msgstr "" -#: erpnext/controllers/taxes_and_totals.py:843 +#: erpnext/controllers/taxes_and_totals.py:849 msgid "Advance amount cannot be greater than {0} {1}" msgstr "" @@ -4011,21 +4014,21 @@ msgstr "" #: erpnext/patches/v11_0/update_department_lft_rgt.py:9 #: erpnext/patches/v11_0/update_department_lft_rgt.py:11 #: erpnext/patches/v11_0/update_department_lft_rgt.py:16 -#: erpnext/setup/doctype/company/company.py:343 #: erpnext/setup/doctype/company/company.py:346 -#: erpnext/setup/doctype/company/company.py:351 -#: erpnext/setup/doctype/company/company.py:357 -#: erpnext/setup/doctype/company/company.py:363 -#: erpnext/setup/doctype/company/company.py:369 -#: erpnext/setup/doctype/company/company.py:375 -#: erpnext/setup/doctype/company/company.py:381 -#: erpnext/setup/doctype/company/company.py:387 -#: erpnext/setup/doctype/company/company.py:393 -#: erpnext/setup/doctype/company/company.py:399 -#: erpnext/setup/doctype/company/company.py:405 -#: erpnext/setup/doctype/company/company.py:411 -#: erpnext/setup/doctype/company/company.py:417 -#: erpnext/setup/doctype/company/company.py:423 +#: erpnext/setup/doctype/company/company.py:349 +#: erpnext/setup/doctype/company/company.py:354 +#: erpnext/setup/doctype/company/company.py:360 +#: erpnext/setup/doctype/company/company.py:366 +#: erpnext/setup/doctype/company/company.py:372 +#: erpnext/setup/doctype/company/company.py:378 +#: erpnext/setup/doctype/company/company.py:384 +#: erpnext/setup/doctype/company/company.py:390 +#: erpnext/setup/doctype/company/company.py:396 +#: erpnext/setup/doctype/company/company.py:402 +#: erpnext/setup/doctype/company/company.py:408 +#: erpnext/setup/doctype/company/company.py:414 +#: erpnext/setup/doctype/company/company.py:420 +#: erpnext/setup/doctype/company/company.py:426 msgid "All Departments" msgstr "" @@ -4101,7 +4104,7 @@ msgstr "" msgid "All Territories" msgstr "" -#: erpnext/setup/doctype/company/company.py:288 +#: erpnext/setup/doctype/company/company.py:291 msgid "All Warehouses" msgstr "" @@ -4119,7 +4122,7 @@ msgstr "" msgid "All items are already requested" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:1342 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:1347 msgid "All items have already been Invoiced/Returned" msgstr "" @@ -4127,11 +4130,11 @@ msgstr "" msgid "All items have already been received" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2689 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2744 msgid "All items have already been transferred for this Work Order." msgstr "" -#: erpnext/public/js/controllers/transaction.js:2872 +#: erpnext/public/js/controllers/transaction.js:2897 msgid "All items in this document already have a linked Quality Inspection." msgstr "" @@ -4214,7 +4217,7 @@ msgstr "" #: erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json #: erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json #: erpnext/accounts/doctype/unreconcile_payment_entries/unreconcile_payment_entries.json -#: erpnext/accounts/report/gross_profit/gross_profit.py:384 +#: erpnext/accounts/report/gross_profit/gross_profit.py:389 #: erpnext/public/js/utils/unreconcile.js:87 msgid "Allocated Amount" msgstr "" @@ -4335,7 +4338,7 @@ msgstr "" msgid "Allow Item To Be Added Multiple Times in a Transaction" msgstr "" -#: erpnext/controllers/selling_controller.py:806 +#: erpnext/controllers/selling_controller.py:813 msgid "Allow Item to Be Added Multiple Times in a Transaction" msgstr "" @@ -5263,7 +5266,7 @@ msgstr "" msgid "An error has been appeared while reposting item valuation via {0}" msgstr "" -#: erpnext/public/js/controllers/buying.js:377 +#: erpnext/public/js/controllers/buying.js:382 #: erpnext/public/js/utils/sales_common.js:463 msgid "An error occurred during the update process" msgstr "" @@ -5851,7 +5854,7 @@ msgstr "" #: erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.json #: erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json #: erpnext/assets/workspace/assets/assets.json -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:225 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:226 #: erpnext/stock/doctype/serial_no/serial_no.json msgid "Asset" msgstr "" @@ -5914,7 +5917,7 @@ msgstr "" #: erpnext/assets/doctype/asset_maintenance/asset_maintenance.json #: erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:23 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:483 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:485 #: erpnext/assets/workspace/assets/assets.json #: erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -5995,7 +5998,7 @@ msgstr "" msgid "Asset Finance Book" msgstr "" -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:475 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:477 msgid "Asset ID" msgstr "" @@ -6042,7 +6045,7 @@ msgstr "" #. Label of a Link in the Assets Workspace #: erpnext/assets/doctype/asset_movement/asset_movement.json #: erpnext/assets/workspace/assets/assets.json -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:236 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:237 msgid "Asset Movement" msgstr "" @@ -6051,7 +6054,7 @@ msgstr "" msgid "Asset Movement Item" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1054 +#: erpnext/assets/doctype/asset/asset.py:1052 msgid "Asset Movement record {0} created" msgstr "" @@ -6064,6 +6067,7 @@ msgstr "" #. Label of the asset_name (Read Only) field in DocType 'Asset Maintenance Log' #. Label of the asset_name (Data) field in DocType 'Asset Movement Item' #. Label of the asset_name (Read Only) field in DocType 'Asset Repair' +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:588 #: erpnext/assets/doctype/asset/asset.json #: erpnext/assets/doctype/asset_capitalization/asset_capitalization.json #: erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json @@ -6071,7 +6075,7 @@ msgstr "" #: erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json #: erpnext/assets/doctype/asset_movement_item/asset_movement_item.json #: erpnext/assets/doctype/asset_repair/asset_repair.json -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:481 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:483 msgid "Asset Name" msgstr "" @@ -6158,8 +6162,8 @@ msgstr "" #: erpnext/assets/doctype/asset/asset.js:421 #: erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:209 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:458 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:505 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:460 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:507 msgid "Asset Value" msgstr "" @@ -6184,7 +6188,7 @@ msgstr "" msgid "Asset cancelled" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:603 +#: erpnext/assets/doctype/asset/asset.py:601 msgid "Asset cannot be cancelled, as it is already {0}" msgstr "" @@ -6200,7 +6204,7 @@ msgstr "" msgid "Asset created" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1294 +#: erpnext/assets/doctype/asset/asset.py:1292 msgid "Asset created after being split from Asset {0}" msgstr "" @@ -6253,7 +6257,7 @@ msgstr "" msgid "Asset transferred to Location {0}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1303 +#: erpnext/assets/doctype/asset/asset.py:1301 msgid "Asset updated after being split into Asset {0}" msgstr "" @@ -6306,7 +6310,7 @@ msgstr "" msgid "Asset {0} must be submitted" msgstr "" -#: erpnext/controllers/buying_controller.py:946 +#: erpnext/controllers/buying_controller.py:1000 msgid "Asset {assets_link} created for {item_code}" msgstr "" @@ -6336,11 +6340,11 @@ msgstr "" msgid "Assets" msgstr "" -#: erpnext/controllers/buying_controller.py:964 +#: erpnext/controllers/buying_controller.py:1018 msgid "Assets not created for {item_code}. You will have to create asset manually." msgstr "" -#: erpnext/controllers/buying_controller.py:951 +#: erpnext/controllers/buying_controller.py:1005 msgid "Assets {assets_link} created for {item_code}" msgstr "" @@ -6394,7 +6398,7 @@ msgstr "" msgid "At least one account with exchange gain or loss is required" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1160 +#: erpnext/assets/doctype/asset/asset.py:1158 msgid "At least one asset has to be selected." msgstr "" @@ -6419,11 +6423,11 @@ msgstr "" msgid "At least one of the Selling or Buying must be selected" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:652 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:653 msgid "At least one warehouse is mandatory" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:572 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:573 msgid "At row #{0}: the Difference Account must not be a Stock type account, please change the Account Type for the account {1} or select a different account" msgstr "" @@ -6431,11 +6435,11 @@ msgstr "" msgid "At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:580 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:581 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:1030 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1044 msgid "At row {0}: Batch No is mandatory for Item {1}" msgstr "" @@ -6443,15 +6447,15 @@ 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:1015 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1029 msgid "At row {0}: Qty is mandatory for the batch {1}" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1022 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1036 msgid "At row {0}: Serial No is mandatory for Item {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:533 +#: erpnext/controllers/stock_controller.py:557 msgid "At row {0}: Serial and Batch Bundle {1} has already created. Please remove the values from the serial no or batch no fields." msgstr "" @@ -6464,16 +6468,16 @@ msgstr "" msgid "Atmosphere" msgstr "" -#. Description of the 'File to Rename' (Attach) field in DocType 'Rename Tool' -#: erpnext/utilities/doctype/rename_tool/rename_tool.json -msgid "Attach .csv file with two columns, one for the old name and one for the new name" -msgstr "" - #: erpnext/public/js/utils/serial_no_batch_selector.js:244 #: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js:73 msgid "Attach CSV File" msgstr "" +#. Description of the 'File to Rename' (Attach) field in DocType 'Rename Tool' +#: erpnext/utilities/doctype/rename_tool/rename_tool.json +msgid "Attach a comma separated .csv file with two columns, one for the old name and one for the new name." +msgstr "" + #. Label of the import_file (Attach) field in DocType 'Chart of Accounts #. Importer' #: erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json @@ -6785,7 +6789,7 @@ msgstr "" msgid "Auto re-order" msgstr "" -#: erpnext/public/js/controllers/buying.js:372 +#: erpnext/public/js/controllers/buying.js:377 #: erpnext/public/js/utils/sales_common.js:458 msgid "Auto repeat document updated" msgstr "" @@ -6870,7 +6874,7 @@ msgstr "" msgid "Available Batch Report" msgstr "" -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:492 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:494 msgid "Available For Use Date" msgstr "" @@ -6965,7 +6969,7 @@ msgstr "" msgid "Available for use date is required" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:785 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:786 msgid "Available quantity is {0}, you need {1}" msgstr "" @@ -6978,7 +6982,7 @@ msgstr "" msgid "Available-for-use Date" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:422 +#: erpnext/assets/doctype/asset/asset.py:420 msgid "Available-for-use Date should be after purchase date" msgstr "" @@ -7033,7 +7037,7 @@ msgstr "" msgid "Avg. Selling Price List Rate" msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:322 +#: erpnext/accounts/report/gross_profit/gross_profit.py:327 msgid "Avg. Selling Rate" msgstr "" @@ -7369,11 +7373,11 @@ msgstr "" msgid "BOMs Updated" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:267 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:274 msgid "BOMs created successfully" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:277 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:284 msgid "BOMs creation failed" msgstr "" @@ -8041,7 +8045,7 @@ msgstr "" msgid "Batch Details" msgstr "" -#: erpnext/stock/doctype/batch/batch.py:197 +#: erpnext/stock/doctype/batch/batch.py:210 #: erpnext/stock/report/serial_no_and_batch_traceability/serial_no_and_batch_traceability.py:461 msgid "Batch Expiry Date" msgstr "" @@ -8094,7 +8098,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:2790 +#: erpnext/public/js/controllers/transaction.js:2815 #: 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 @@ -8123,11 +8127,11 @@ msgstr "" msgid "Batch No" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1033 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1047 msgid "Batch No is mandatory" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2877 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2891 msgid "Batch No {0} does not exists" msgstr "" @@ -8150,11 +8154,11 @@ msgstr "" msgid "Batch Nos" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1597 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1611 msgid "Batch Nos are created successfully" msgstr "" -#: erpnext/controllers/sales_and_purchase_return.py:1027 +#: erpnext/controllers/sales_and_purchase_return.py:1064 msgid "Batch Not Available for Return" msgstr "" @@ -8167,6 +8171,10 @@ msgstr "" msgid "Batch Qty" msgstr "" +#: erpnext/stock/doctype/batch/batch.py:170 +msgid "Batch Qty updated to {0}" +msgstr "" + #. Label of the batch_qty (Float) field in DocType 'Batch' #: erpnext/stock/doctype/batch/batch.json msgid "Batch Quantity" @@ -8203,16 +8211,16 @@ msgstr "" msgid "Batch {0} and Warehouse" msgstr "" -#: erpnext/controllers/sales_and_purchase_return.py:1026 +#: erpnext/controllers/sales_and_purchase_return.py:1063 msgid "Batch {0} is not available in warehouse {1}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2863 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2918 #: erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py:286 msgid "Batch {0} of Item {1} has expired." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2869 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2924 msgid "Batch {0} of Item {1} is disabled." msgstr "" @@ -8552,6 +8560,12 @@ msgstr "" msgid "Bisecting To" msgstr "" +#. Option for the 'Frequency' (Select) field in DocType 'Process Statement Of +#. Accounts' +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +msgid "Biweekly" +msgstr "" + #: erpnext/setup/setup_wizard/operations/install_fixtures.py:268 msgid "Black" msgstr "" @@ -8711,7 +8725,7 @@ msgstr "" msgid "Booking stock value across multiple accounts will make it harder to track stock and account value." msgstr "" -#: erpnext/accounts/general_ledger.py:800 +#: erpnext/accounts/general_ledger.py:801 msgid "Books have been closed till the period ending on {0}" msgstr "" @@ -8793,7 +8807,7 @@ msgstr "" #: erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.json #: erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json -#: erpnext/accounts/report/gross_profit/gross_profit.py:306 +#: erpnext/accounts/report/gross_profit/gross_profit.py:311 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js:53 #: erpnext/accounts/report/sales_register/sales_register.js:64 #: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -9097,7 +9111,7 @@ msgstr "" msgid "Buying & Selling Settings" msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:343 +#: erpnext/accounts/report/gross_profit/gross_profit.py:348 msgid "Buying Amount" msgstr "" @@ -9500,7 +9514,7 @@ msgid "Can only make payment against unbilled {0}" msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.js:1461 -#: erpnext/controllers/accounts_controller.py:2985 +#: erpnext/controllers/accounts_controller.py:2997 #: erpnext/public/js/controllers/accounts.js:103 msgid "Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'" msgstr "" @@ -9718,11 +9732,11 @@ msgstr "" msgid "Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet." msgstr "" -#: erpnext/controllers/buying_controller.py:1054 +#: erpnext/controllers/buying_controller.py:1108 msgid "Cannot cancel this document as it is linked with the submitted asset {asset_link}. Please cancel the asset to continue." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:358 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:359 msgid "Cannot cancel transaction for Completed Work Order." msgstr "" @@ -9746,7 +9760,7 @@ msgstr "" msgid "Cannot change Variant properties after stock transaction. You will have to make a new Item to do this." msgstr "" -#: erpnext/setup/doctype/company/company.py:237 +#: erpnext/setup/doctype/company/company.py:240 msgid "Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency." msgstr "" @@ -9770,7 +9784,7 @@ msgstr "" msgid "Cannot covert to Group because Account Type is selected." msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:990 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:991 msgid "Cannot create Stock Reservation Entries for future dated Purchase Receipts." msgstr "" @@ -9829,7 +9843,7 @@ msgstr "" msgid "Cannot find Item with this Barcode" msgstr "" -#: erpnext/controllers/accounts_controller.py:3522 +#: erpnext/controllers/accounts_controller.py:3547 msgid "Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings." msgstr "" @@ -9854,7 +9868,7 @@ msgid "Cannot receive from customer against negative outstanding" msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.js:1478 -#: erpnext/controllers/accounts_controller.py:3000 +#: erpnext/controllers/accounts_controller.py:3012 #: erpnext/public/js/controllers/accounts.js:120 msgid "Cannot refer row number greater than or equal to current row number for this Charge type" msgstr "" @@ -9870,9 +9884,9 @@ msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.js:1470 #: erpnext/accounts/doctype/payment_entry/payment_entry.js:1649 #: erpnext/accounts/doctype/payment_entry/payment_entry.py:1888 -#: erpnext/controllers/accounts_controller.py:2990 +#: erpnext/controllers/accounts_controller.py:3002 #: erpnext/public/js/controllers/accounts.js:112 -#: erpnext/public/js/controllers/taxes_and_totals.js:520 +#: erpnext/public/js/controllers/taxes_and_totals.js:521 msgid "Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row" msgstr "" @@ -9888,11 +9902,11 @@ msgstr "" msgid "Cannot set multiple Item Defaults for a company." msgstr "" -#: erpnext/controllers/accounts_controller.py:3670 +#: erpnext/controllers/accounts_controller.py:3695 msgid "Cannot set quantity less than delivered quantity" msgstr "" -#: erpnext/controllers/accounts_controller.py:3673 +#: erpnext/controllers/accounts_controller.py:3698 msgid "Cannot set quantity less than received quantity" msgstr "" @@ -10327,7 +10341,7 @@ msgid "Channel Partner" msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.py:2317 -#: erpnext/controllers/accounts_controller.py:3053 +#: erpnext/controllers/accounts_controller.py:3065 msgid "Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount" msgstr "" @@ -10510,7 +10524,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:2700 +#: erpnext/public/js/controllers/transaction.js:2725 msgid "Cheque/Reference Date" msgstr "" @@ -10558,7 +10572,7 @@ msgstr "" #. Label of the child_row_reference (Data) field in DocType 'Quality #. Inspection' -#: erpnext/public/js/controllers/transaction.js:2796 +#: erpnext/public/js/controllers/transaction.js:2821 #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Child Row Reference" msgstr "" @@ -10687,6 +10701,12 @@ msgstr "" msgid "Click on the link below to verify your email and confirm the appointment" msgstr "" +#. Description of the 'Reset Raw Materials Table' (Button) field in DocType +#. 'Subcontracting Receipt' +#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +msgid "Click this button if you encounter a negative stock error for a serial or batch item. The system will fetch the available serials or batches automatically." +msgstr "" + #: erpnext/selling/page/point_of_sale/pos_item_cart.js:485 msgid "Click to add email / phone" msgstr "" @@ -10706,7 +10726,7 @@ msgstr "" #: erpnext/selling/doctype/sales_order/sales_order.js:843 #: erpnext/selling/doctype/sales_order/sales_order_list.js:66 #: erpnext/stock/doctype/delivery_note/delivery_note.js:325 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:283 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:284 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:170 #: erpnext/support/doctype/issue/issue.js:23 msgid "Close" @@ -10805,12 +10825,14 @@ msgstr "" msgid "Closing" msgstr "" -#: erpnext/accounts/report/trial_balance/trial_balance.py:496 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:464 +#: erpnext/accounts/report/trial_balance/trial_balance.py:545 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py:226 msgid "Closing (Cr)" msgstr "" -#: erpnext/accounts/report/trial_balance/trial_balance.py:489 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:457 +#: erpnext/accounts/report/trial_balance/trial_balance.py:538 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py:219 msgid "Closing (Dr)" msgstr "" @@ -11054,7 +11076,7 @@ msgstr "" msgid "Communication Medium Type" msgstr "" -#: erpnext/setup/install.py:94 +#: erpnext/setup/install.py:95 msgid "Compact Item Print" msgstr "" @@ -11292,6 +11314,7 @@ msgstr "" #: erpnext/accounts/report/budget_variance_report/budget_variance_report.js:72 #: erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js:8 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js:8 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:8 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:8 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:50 #: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js:8 @@ -11337,8 +11360,8 @@ msgstr "" #: erpnext/assets/doctype/asset_repair/asset_repair.json #: erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:8 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:465 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:548 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:467 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:550 #: erpnext/buying/doctype/customer_number_at_supplier/customer_number_at_supplier.json #: erpnext/buying/doctype/purchase_order/purchase_order.json #: erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -11437,8 +11460,8 @@ msgstr "" #: erpnext/setup/doctype/employee/employee_tree.js:8 #: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json -#: erpnext/setup/doctype/vehicle/vehicle.json -#: erpnext/setup/workspace/home/home.json +#: erpnext/setup/doctype/vehicle/vehicle.json erpnext/setup/install.py:154 +#: erpnext/setup/install.py:163 erpnext/setup/workspace/home/home.json #: erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js:8 #: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_trip/delivery_trip.json @@ -12174,6 +12197,19 @@ msgstr "" msgid "Consolidated Sales Invoice" msgstr "" +#. Name of a report +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.json +msgid "Consolidated Trial Balance" +msgstr "" + +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:71 +msgid "Consolidated Trial Balance can be generated for Companies having same root Company." +msgstr "" + +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:153 +msgid "Consolidated Trial balance could not be generated as Exchange Rate from {0} to {1} is not available for {2}." +msgstr "" + #. Option for the 'Lead Type' (Select) field in DocType 'Lead' #: erpnext/crm/doctype/lead/lead.json #: erpnext/setup/setup_wizard/data/designation.txt:8 @@ -12549,7 +12585,7 @@ msgid "Content Type" msgstr "" #: erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js:162 -#: erpnext/public/js/controllers/transaction.js:2714 +#: erpnext/public/js/controllers/transaction.js:2739 #: erpnext/selling/doctype/quotation/quotation.js:357 msgid "Continue" msgstr "" @@ -12725,15 +12761,15 @@ msgstr "" msgid "Conversion factor for item {0} has been reset to 1.0 as the uom {1} is same as stock uom {2}." msgstr "" -#: erpnext/controllers/accounts_controller.py:2866 +#: erpnext/controllers/accounts_controller.py:2878 msgid "Conversion rate cannot be 0" msgstr "" -#: erpnext/controllers/accounts_controller.py:2873 +#: erpnext/controllers/accounts_controller.py:2885 msgid "Conversion rate is 1.00, but document currency is different from company currency" msgstr "" -#: erpnext/controllers/accounts_controller.py:2869 +#: erpnext/controllers/accounts_controller.py:2881 msgid "Conversion rate must be 1.00 if document currency is same as company currency" msgstr "" @@ -12956,7 +12992,7 @@ msgstr "" #: erpnext/accounts/report/general_ledger/general_ledger.js:153 #: erpnext/accounts/report/general_ledger/general_ledger.py:767 #: erpnext/accounts/report/gross_profit/gross_profit.js:68 -#: erpnext/accounts/report/gross_profit/gross_profit.py:370 +#: erpnext/accounts/report/gross_profit/gross_profit.py:375 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:308 #: erpnext/accounts/report/purchase_register/purchase_register.js:46 #: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:29 @@ -12973,7 +13009,7 @@ msgstr "" #: erpnext/assets/doctype/asset_repair/asset_repair.json #: erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:29 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:526 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:528 #: erpnext/buying/doctype/purchase_order/purchase_order.json #: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json #: erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -13041,8 +13077,8 @@ msgstr "" msgid "Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1419 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:875 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1420 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:876 msgid "Cost Center is required in row {0} in Taxes table for type {1}" msgstr "" @@ -13070,7 +13106,7 @@ msgstr "" msgid "Cost Center {} is a group cost center and group cost centers cannot be used in transactions" msgstr "" -#: erpnext/accounts/report/financial_statements.py:641 +#: erpnext/accounts/report/financial_statements.py:661 msgid "Cost Center: {0} does not exist" msgstr "" @@ -13098,14 +13134,17 @@ msgid "Cost of Delivered Items" msgstr "" #. Option for the 'Account Type' (Select) field in DocType 'Account' +#. Label of the cost_of_good_sold_section (Section Break) field in DocType +#. 'Item Default' #: erpnext/accounts/doctype/account/account.json #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:45 #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:64 #: erpnext/accounts/report/account_balance/account_balance.js:43 +#: erpnext/stock/doctype/item_default/item_default.json msgid "Cost of Goods Sold" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:583 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:584 msgid "Cost of Goods Sold Account in Items Table" msgstr "" @@ -13377,7 +13416,7 @@ msgstr "" #: erpnext/public/js/communication.js:19 erpnext/public/js/communication.js:31 #: erpnext/public/js/communication.js:41 #: erpnext/public/js/controllers/transaction.js:379 -#: erpnext/public/js/controllers/transaction.js:2841 +#: erpnext/public/js/controllers/transaction.js:2866 #: erpnext/selling/doctype/customer/customer.js:182 #: erpnext/selling/doctype/quotation/quotation.js:125 #: erpnext/selling/doctype/quotation/quotation.js:134 @@ -13410,8 +13449,8 @@ msgstr "" #: erpnext/stock/doctype/item/item.js:167 #: erpnext/stock/doctype/item/item.js:174 #: erpnext/stock/doctype/item/item.js:182 -#: erpnext/stock/doctype/item/item.js:559 -#: erpnext/stock/doctype/item/item.js:816 +#: erpnext/stock/doctype/item/item.js:574 +#: erpnext/stock/doctype/item/item.js:831 #: erpnext/stock/doctype/material_request/material_request.js:142 #: erpnext/stock/doctype/material_request/material_request.js:151 #: erpnext/stock/doctype/material_request/material_request.js:157 @@ -13430,11 +13469,11 @@ msgstr "" #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:70 #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:82 #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:108 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:286 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:291 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:298 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:304 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:307 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:287 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:292 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:299 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:305 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:308 #: erpnext/stock/doctype/stock_entry/stock_entry.js:170 #: erpnext/stock/doctype/stock_entry/stock_entry.js:172 #: erpnext/stock/doctype/stock_entry/stock_entry.js:247 @@ -13661,12 +13700,12 @@ msgstr "" msgid "Create Users" msgstr "" -#: erpnext/stock/doctype/item/item.js:812 +#: erpnext/stock/doctype/item/item.js:827 msgid "Create Variant" msgstr "" -#: erpnext/stock/doctype/item/item.js:624 -#: erpnext/stock/doctype/item/item.js:668 +#: erpnext/stock/doctype/item/item.js:639 +#: erpnext/stock/doctype/item/item.js:683 msgid "Create Variants" msgstr "" @@ -13674,12 +13713,12 @@ msgstr "" msgid "Create Workstation" msgstr "" -#: erpnext/stock/doctype/item/item.js:651 -#: erpnext/stock/doctype/item/item.js:805 +#: erpnext/stock/doctype/item/item.js:666 +#: erpnext/stock/doctype/item/item.js:820 msgid "Create a variant with the template image." msgstr "" -#: erpnext/stock/stock_ledger.py:1915 +#: erpnext/stock/stock_ledger.py:1994 msgid "Create an incoming stock transaction for the Item." msgstr "" @@ -13805,10 +13844,11 @@ msgstr "" #: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html:11 #: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py:84 #: erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py:146 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:450 #: erpnext/accounts/report/general_ledger/general_ledger.html:93 #: erpnext/accounts/report/purchase_register/purchase_register.py:241 #: erpnext/accounts/report/sales_register/sales_register.py:277 -#: erpnext/accounts/report/trial_balance/trial_balance.py:482 +#: erpnext/accounts/report/trial_balance/trial_balance.py:531 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py:212 #: erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.py:34 msgid "Credit" @@ -13873,9 +13913,11 @@ msgstr "" msgid "Credit Card Entry" msgstr "" +#. Label of the credit_days (Int) field in DocType 'Payment Schedule' #. Label of the credit_days (Int) field in DocType 'Payment Term' #. Label of the credit_days (Int) field in DocType 'Payment Terms Template #. Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Credit Days" @@ -13927,9 +13969,11 @@ msgstr "" msgid "Credit Limits" msgstr "" +#. Label of the credit_months (Int) field in DocType 'Payment Schedule' #. Label of the credit_months (Int) field in DocType 'Payment Term' #. Label of the credit_months (Int) field in DocType 'Payment Terms Template #. Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Credit Months" @@ -14180,19 +14224,21 @@ msgstr "" #: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py:101 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js:118 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py:294 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:57 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:422 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:208 #: erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py:209 #: erpnext/accounts/report/financial_statements.html:29 -#: erpnext/accounts/report/financial_statements.py:679 +#: erpnext/accounts/report/financial_statements.py:699 #: erpnext/accounts/report/general_ledger/general_ledger.js:147 -#: erpnext/accounts/report/gross_profit/gross_profit.py:443 +#: erpnext/accounts/report/gross_profit/gross_profit.py:448 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:709 #: erpnext/accounts/report/payment_ledger/payment_ledger.py:220 #: erpnext/accounts/report/profitability_analysis/profitability_analysis.py:176 #: erpnext/accounts/report/purchase_register/purchase_register.py:229 #: erpnext/accounts/report/sales_register/sales_register.py:265 #: erpnext/accounts/report/trial_balance/trial_balance.js:76 -#: erpnext/accounts/report/trial_balance/trial_balance.py:454 +#: erpnext/accounts/report/trial_balance/trial_balance.py:503 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py:233 #: erpnext/accounts/workspace/accounting/accounting.json #: erpnext/buying/doctype/purchase_order/purchase_order.json @@ -14563,7 +14609,7 @@ msgstr "" #: erpnext/accounts/doctype/tax_rule/tax_rule.json #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:38 #: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py:29 -#: erpnext/accounts/report/gross_profit/gross_profit.py:391 +#: erpnext/accounts/report/gross_profit/gross_profit.py:396 #: erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py:37 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js:22 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:224 @@ -14821,7 +14867,7 @@ msgstr "" #: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:185 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:56 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:228 -#: erpnext/accounts/report/gross_profit/gross_profit.py:398 +#: erpnext/accounts/report/gross_profit/gross_profit.py:403 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:211 #: erpnext/accounts/report/sales_register/sales_register.js:27 #: erpnext/accounts/report/sales_register/sales_register.py:202 @@ -14929,7 +14975,7 @@ msgstr "" #: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:156 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:92 #: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py:35 -#: erpnext/accounts/report/gross_profit/gross_profit.py:405 +#: erpnext/accounts/report/gross_profit/gross_profit.py:410 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:231 #: erpnext/accounts/report/sales_register/sales_register.py:193 #: erpnext/buying/doctype/purchase_order/purchase_order.json @@ -15028,7 +15074,7 @@ msgstr "" msgid "Customer Provided" msgstr "" -#: erpnext/setup/doctype/company/company.py:392 +#: erpnext/setup/doctype/company/company.py:395 msgid "Customer Service" msgstr "" @@ -15187,6 +15233,8 @@ msgstr "" msgid "DFS" msgstr "" +#. Option for the 'Frequency' (Select) field in DocType 'Process Statement Of +#. Accounts' #. Option for the 'Periodicity' (Select) field in DocType 'Asset Maintenance #. Task' #. Option for the 'Frequency To Collect Progress' (Select) field in DocType @@ -15198,6 +15246,7 @@ msgstr "" #. Option for the 'Frequency' (Select) field in DocType 'Company' #. Option for the 'How frequently?' (Select) field in DocType 'Email Digest' #. Option for the 'Frequency' (Select) field in DocType 'Video Settings' +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json #: erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json #: erpnext/projects/doctype/project/project.json #: erpnext/public/js/stock_analytics.js:81 @@ -15477,6 +15526,10 @@ msgstr "" msgid "Day to Send" msgstr "" +#. Option for the 'Due Date Based On' (Select) field in DocType 'Payment +#. Schedule' +#. Option for the 'Discount Validity Based On' (Select) field in DocType +#. 'Payment Schedule' #. Option for the 'Due Date Based On' (Select) field in DocType 'Payment Term' #. Option for the 'Discount Validity Based On' (Select) field in DocType #. 'Payment Term' @@ -15484,11 +15537,16 @@ msgstr "" #. Template Detail' #. Option for the 'Discount Validity Based On' (Select) field in DocType #. 'Payment Terms Template Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Day(s) after invoice date" msgstr "" +#. Option for the 'Due Date Based On' (Select) field in DocType 'Payment +#. Schedule' +#. Option for the 'Discount Validity Based On' (Select) field in DocType +#. 'Payment Schedule' #. Option for the 'Due Date Based On' (Select) field in DocType 'Payment Term' #. Option for the 'Discount Validity Based On' (Select) field in DocType #. 'Payment Term' @@ -15496,6 +15554,7 @@ msgstr "" #. Template Detail' #. Option for the 'Discount Validity Based On' (Select) field in DocType #. 'Payment Terms Template Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Day(s) after the end of the invoice month" @@ -15562,10 +15621,11 @@ msgstr "" #: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html:10 #: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py:77 #: erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py:139 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:443 #: erpnext/accounts/report/general_ledger/general_ledger.html:92 #: erpnext/accounts/report/purchase_register/purchase_register.py:240 #: erpnext/accounts/report/sales_register/sales_register.py:276 -#: erpnext/accounts/report/trial_balance/trial_balance.py:475 +#: erpnext/accounts/report/trial_balance/trial_balance.py:524 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py:205 #: erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.py:27 msgid "Debit" @@ -15664,7 +15724,7 @@ msgstr "" msgid "Debit To is required" msgstr "" -#: erpnext/accounts/general_ledger.py:522 +#: erpnext/accounts/general_ledger.py:523 msgid "Debit and Credit not equal for {0} #{1}. Difference is {2}." msgstr "" @@ -15805,14 +15865,14 @@ msgstr "" #. Label of the default_advance_paid_account (Link) field in DocType 'Company' #: erpnext/setup/doctype/company/company.json -#: erpnext/setup/doctype/company/company.py:222 +#: erpnext/setup/doctype/company/company.py:225 msgid "Default Advance Paid Account" msgstr "" #. Label of the default_advance_received_account (Link) field in DocType #. 'Company' #: erpnext/setup/doctype/company/company.json -#: erpnext/setup/doctype/company/company.py:211 +#: erpnext/setup/doctype/company/company.py:214 msgid "Default Advance Received Account" msgstr "" @@ -15831,7 +15891,7 @@ msgstr "" msgid "Default BOM for {0} not found" msgstr "" -#: erpnext/controllers/accounts_controller.py:3711 +#: erpnext/controllers/accounts_controller.py:3736 msgid "Default BOM not found for FG Item {0}" msgstr "" @@ -15867,6 +15927,11 @@ msgstr "" msgid "Default Buying Terms" msgstr "" +#. Label of the default_cogs_account (Link) field in DocType 'Item Default' +#: erpnext/stock/doctype/item_default/item_default.json +msgid "Default COGS Account" +msgstr "" + #. Label of the default_cash_account (Link) field in DocType 'Company' #: erpnext/setup/doctype/company/company.json msgid "Default Cash Account" @@ -16072,11 +16137,14 @@ msgid "Default Priority" msgstr "" #. Label of the default_provisional_account (Link) field in DocType 'Company' +#: erpnext/setup/doctype/company/company.json +msgid "Default Provisional Account" +msgstr "" + #. Label of the default_provisional_account (Link) field in DocType 'Item #. Default' -#: erpnext/setup/doctype/company/company.json #: erpnext/stock/doctype/item_default/item_default.json -msgid "Default Provisional Account" +msgid "Default Provisional Account (Service)" msgstr "" #. Label of the purchase_uom (Link) field in DocType 'Item' @@ -16248,11 +16316,13 @@ msgstr "" #. Label of the defaults_tab (Section Break) field in DocType 'Customer' #. Label of the defaults (Section Break) field in DocType 'Brand' #. Label of the defaults (Section Break) field in DocType 'Item Group' +#. Label of the defaults_tab (Tab Break) field in DocType 'Item' #. Label of the defaults_tab (Tab Break) field in DocType 'Stock Settings' #: erpnext/buying/doctype/supplier/supplier.json #: erpnext/selling/doctype/customer/customer.json #: erpnext/setup/doctype/brand/brand.json #: erpnext/setup/doctype/item_group/item_group.json +#: erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/stock_settings/stock_settings.json msgid "Defaults" msgstr "" @@ -16338,7 +16408,7 @@ msgstr "" msgid "Dekagram/Litre" msgstr "" -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:108 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:116 msgid "Delay (In Days)" msgstr "" @@ -16355,8 +16425,8 @@ msgstr "" msgid "Delay in payment (Days)" msgstr "" -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:79 #: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:80 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:81 msgid "Delayed" msgstr "" @@ -16788,7 +16858,7 @@ msgstr "" #. History' #. Label of the department (Link) field in DocType 'Sales Person' #: erpnext/assets/doctype/asset/asset.json -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:533 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:535 #: erpnext/projects/doctype/activity_cost/activity_cost.json #: erpnext/projects/doctype/project/project.json #: erpnext/projects/doctype/task/task.json @@ -16864,8 +16934,8 @@ msgid "Depreciate based on shifts" msgstr "" #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:213 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:451 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:519 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:453 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:521 msgid "Depreciated Amount" msgstr "" @@ -16889,7 +16959,7 @@ msgstr "" msgid "Depreciation Amount" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:631 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:639 msgid "Depreciation Amount during the period" msgstr "" @@ -16903,7 +16973,7 @@ msgstr "" msgid "Depreciation Details" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:637 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:645 msgid "Depreciation Eliminated due to disposal of assets" msgstr "" @@ -16921,7 +16991,7 @@ msgstr "" msgid "Depreciation Entry Posting Status" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1128 +#: erpnext/assets/doctype/asset/asset.py:1126 msgid "Depreciation Entry against asset {0}" msgstr "" @@ -16972,7 +17042,7 @@ msgstr "" msgid "Depreciation Row {0}: Depreciation Posting Date cannot be before Available-for-use Date" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:588 +#: erpnext/assets/doctype/asset/asset.py:586 msgid "Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}" msgstr "" @@ -16999,11 +17069,11 @@ msgstr "" msgid "Depreciation Schedule View" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:416 +#: erpnext/assets/doctype/asset/asset.py:414 msgid "Depreciation cannot be calculated for fully depreciated assets" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:649 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:657 msgid "Depreciation eliminated via reversal" msgstr "" @@ -17178,7 +17248,7 @@ msgstr "" #: erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json #: erpnext/accounts/doctype/share_type/share_type.json #: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py:72 -#: erpnext/accounts/report/gross_profit/gross_profit.py:308 +#: erpnext/accounts/report/gross_profit/gross_profit.py:313 #: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:175 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:195 #: erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py:72 @@ -17225,7 +17295,7 @@ msgstr "" #: erpnext/projects/doctype/task_type/task_type.json #: erpnext/projects/doctype/timesheet_detail/timesheet_detail.json #: erpnext/public/js/bank_reconciliation_tool/data_table_manager.js:55 -#: erpnext/public/js/controllers/transaction.js:2778 +#: erpnext/public/js/controllers/transaction.js:2803 #: erpnext/selling/doctype/installation_note_item/installation_note_item.json #: erpnext/selling/doctype/product_bundle/product_bundle.json #: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json @@ -17406,11 +17476,11 @@ msgstr "" msgid "Difference Account" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:575 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:576 msgid "Difference Account in Items Table" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:564 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:565 msgid "Difference Account must be a Asset/Liability type account (Temporary Opening), since this Stock Entry is an Opening Entry" msgstr "" @@ -17608,6 +17678,7 @@ msgid "Disable Serial No And Batch Selector" msgstr "" #. Label of the disabled (Check) field in DocType 'Accounting Dimension Filter' +#. Label of the disabled (Check) field in DocType 'Accounting Period' #. Label of the disabled (Check) field in DocType 'Bank Account' #. Label of the disabled (Check) field in DocType 'Cost Center' #. Label of the disabled (Check) field in DocType 'Currency Exchange Settings' @@ -17638,6 +17709,7 @@ msgstr "" #. Label of the disabled (Check) field in DocType 'Item Variant Attribute' #. Label of the disabled (Check) field in DocType 'Warehouse' #: erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json +#: erpnext/accounts/doctype/accounting_period/accounting_period.json #: erpnext/accounts/doctype/bank_account/bank_account.json #: erpnext/accounts/doctype/cost_center/cost_center.json #: erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -17856,18 +17928,23 @@ msgstr "" msgid "Discount Type" msgstr "" +#. Label of the discount_validity (Int) field in DocType 'Payment Schedule' #. Label of the discount_validity (Int) field in DocType 'Payment Term' #. Label of the discount_validity (Int) field in DocType 'Payment Terms #. Template Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Discount Validity" msgstr "" +#. Label of the discount_validity_based_on (Select) field in DocType 'Payment +#. Schedule' #. Label of the discount_validity_based_on (Select) field in DocType 'Payment #. Term' #. Label of the discount_validity_based_on (Select) field in DocType 'Payment #. Terms Template Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Discount Validity Based On" @@ -17987,7 +18064,7 @@ msgstr "" msgid "Dislikes" msgstr "" -#: erpnext/setup/doctype/company/company.py:386 +#: erpnext/setup/doctype/company/company.py:389 msgid "Dispatch" msgstr "" @@ -18328,7 +18405,7 @@ msgstr "" msgid "Document Type already used as a dimension" msgstr "" -#: erpnext/setup/install.py:152 +#: erpnext/setup/install.py:186 msgid "Documentation" msgstr "" @@ -18666,9 +18743,11 @@ msgstr "" msgid "Due Date" msgstr "" +#. Label of the due_date_based_on (Select) field in DocType 'Payment Schedule' #. Label of the due_date_based_on (Select) field in DocType 'Payment Term' #. Label of the due_date_based_on (Select) field in DocType 'Payment Terms #. Template Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Due Date Based On" @@ -19741,7 +19820,7 @@ msgstr "" msgid "Enter amount to be redeemed." msgstr "" -#: erpnext/stock/doctype/item/item.js:974 +#: erpnext/stock/doctype/item/item.js:989 msgid "Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field." msgstr "" @@ -19757,7 +19836,7 @@ msgstr "" msgid "Enter date to scrap asset" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:414 +#: erpnext/assets/doctype/asset/asset.py:412 msgid "Enter depreciation details" msgstr "" @@ -19788,7 +19867,7 @@ msgstr "" msgid "Enter the name of the bank or lending institution before submitting." msgstr "" -#: erpnext/stock/doctype/item/item.js:1000 +#: erpnext/stock/doctype/item/item.js:1015 msgid "Enter the opening stock units." msgstr "" @@ -20001,7 +20080,7 @@ msgstr "" msgid "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions, then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item, leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings." msgstr "" -#: erpnext/stock/stock_ledger.py:2181 +#: erpnext/stock/stock_ledger.py:2260 msgid "Example: Serial No {0} reserved in {1}." msgstr "" @@ -20051,7 +20130,7 @@ msgstr "" #: erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json #: erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json #: erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json -#: erpnext/setup/doctype/company/company.py:558 +#: erpnext/setup/doctype/company/company.py:561 msgid "Exchange Gain/Loss" msgstr "" @@ -20264,7 +20343,7 @@ msgstr "" #: erpnext/manufacturing/doctype/workstation/workstation_job_card.html:49 #: erpnext/projects/doctype/project/project.json #: erpnext/projects/doctype/task/task.json -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:104 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:112 #: erpnext/projects/web_form/tasks/tasks.json #: erpnext/templates/pages/task_info.html:64 msgid "Expected End Date" @@ -20288,7 +20367,7 @@ msgstr "" #: erpnext/manufacturing/doctype/workstation/workstation_job_card.html:45 #: erpnext/projects/doctype/project/project.json #: erpnext/projects/doctype/task/task.json -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:98 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:106 #: erpnext/projects/web_form/tasks/tasks.json #: erpnext/templates/pages/task_info.html:59 msgid "Expected Start Date" @@ -20334,7 +20413,7 @@ msgstr "" msgid "Expense" msgstr "" -#: erpnext/controllers/stock_controller.py:787 +#: erpnext/controllers/stock_controller.py:811 msgid "Expense / Difference account ({0}) must be a 'Profit or Loss' account" msgstr "" @@ -20382,7 +20461,7 @@ msgstr "" msgid "Expense Account" msgstr "" -#: erpnext/controllers/stock_controller.py:767 +#: erpnext/controllers/stock_controller.py:791 msgid "Expense Account Missing" msgstr "" @@ -20482,7 +20561,7 @@ msgstr "" msgid "Expiry Date" msgstr "" -#: erpnext/stock/doctype/batch/batch.py:199 +#: erpnext/stock/doctype/batch/batch.py:212 msgid "Expiry Date Mandatory" msgstr "" @@ -20664,7 +20743,7 @@ msgstr "" msgid "Failed to login" msgstr "" -#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:129 +#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:163 msgid "Failed to parse MT940 format. Error: {0}" msgstr "" @@ -20681,7 +20760,7 @@ msgstr "" msgid "Failed to setup defaults" msgstr "" -#: erpnext/setup/doctype/company/company.py:740 +#: erpnext/setup/doctype/company/company.py:758 msgid "Failed to setup defaults for country {0}. Please contact support." msgstr "" @@ -20829,7 +20908,7 @@ msgid "Fetching Sales Orders..." msgstr "" #: erpnext/accounts/doctype/dunning/dunning.js:135 -#: erpnext/public/js/controllers/transaction.js:1456 +#: erpnext/public/js/controllers/transaction.js:1470 msgid "Fetching exchange rates ..." msgstr "" @@ -21013,6 +21092,7 @@ msgstr "" #: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js:48 #: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js:51 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js:104 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:51 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:32 #: erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js:51 #: erpnext/accounts/report/general_ledger/general_ledger.js:16 @@ -21134,15 +21214,15 @@ msgstr "" msgid "Finished Good Item Quantity" msgstr "" -#: erpnext/controllers/accounts_controller.py:3697 +#: erpnext/controllers/accounts_controller.py:3722 msgid "Finished Good Item is not specified for service item {0}" msgstr "" -#: erpnext/controllers/accounts_controller.py:3714 +#: erpnext/controllers/accounts_controller.py:3739 msgid "Finished Good Item {0} Qty can not be zero" msgstr "" -#: erpnext/controllers/accounts_controller.py:3708 +#: erpnext/controllers/accounts_controller.py:3733 msgid "Finished Good Item {0} must be a sub-contracted item" msgstr "" @@ -21185,7 +21265,7 @@ msgstr "" msgid "Finished Good {0} must be a sub-contracted item." msgstr "" -#: erpnext/setup/doctype/company/company.py:291 +#: erpnext/setup/doctype/company/company.py:294 msgid "Finished Goods" msgstr "" @@ -21223,7 +21303,7 @@ msgstr "" msgid "Finished Goods based Operating Cost" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1387 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1388 msgid "Finished Item {0} does not match with Work Order {1}" msgstr "" @@ -21307,6 +21387,7 @@ msgstr "" #: erpnext/accounts/doctype/monthly_distribution/monthly_distribution.json #: erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json #: erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.html:1 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:18 #: erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js:16 #: erpnext/accounts/report/profitability_analysis/profitability_analysis.js:38 #: erpnext/accounts/report/trial_balance/trial_balance.js:16 @@ -21366,7 +21447,7 @@ msgstr "" #. Capitalization Asset Item' #. Label of the fixed_asset_account (Link) field in DocType 'Asset Category #. Account' -#: erpnext/assets/doctype/asset/asset.py:770 +#: erpnext/assets/doctype/asset/asset.py:768 #: erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json #: erpnext/assets/doctype/asset_category_account/asset_category_account.json msgid "Fixed Asset Account" @@ -21531,7 +21612,7 @@ msgstr "" msgid "For Item" msgstr "" -#: erpnext/controllers/stock_controller.py:1333 +#: erpnext/controllers/stock_controller.py:1357 msgid "For Item {0} cannot be received more than {1} qty against the {2} {3}" msgstr "" @@ -21562,7 +21643,7 @@ msgstr "" msgid "For Production" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:669 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:670 msgid "For Quantity (Manufactured Qty) is mandatory" msgstr "" @@ -21600,11 +21681,11 @@ msgstr "" msgid "For Work Order" msgstr "" -#: erpnext/controllers/status_updater.py:274 +#: erpnext/controllers/status_updater.py:276 msgid "For an item {0}, quantity must be negative number" msgstr "" -#: erpnext/controllers/status_updater.py:271 +#: erpnext/controllers/status_updater.py:273 msgid "For an item {0}, quantity must be positive number" msgstr "" @@ -21634,7 +21715,7 @@ 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:279 +#: erpnext/controllers/status_updater.py:281 msgid "For item {0}, rate must be a positive number. To Allow negative rates, enable {1} in {2}" msgstr "" @@ -21655,7 +21736,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:1419 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1420 msgid "For quantity {0} should not be greater than allowed quantity {1}" msgstr "" @@ -21673,6 +21754,12 @@ msgstr "" msgid "For row {0}: Enter Planned Qty" msgstr "" +#. Description of the 'Service Expense Account' (Link) field in DocType +#. 'Company' +#: erpnext/setup/doctype/company/company.json +msgid "For service item" +msgstr "" + #: erpnext/accounts/doctype/pricing_rule/pricing_rule.py:178 msgid "For the 'Apply Rule On Other' condition the field {0} is mandatory" msgstr "" @@ -21682,11 +21769,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:809 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:810 msgid "For the item {0}, the quantity should be {1} according to the BOM {2}." msgstr "" -#: erpnext/public/js/controllers/transaction.js:1266 +#: erpnext/public/js/controllers/transaction.js:1280 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 "" @@ -21695,7 +21782,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:1075 +#: erpnext/controllers/sales_and_purchase_return.py:1115 msgid "For the {0}, the quantity is required to make the return entry" msgstr "" @@ -21728,6 +21815,12 @@ msgstr "" msgid "Forecasting Method" msgstr "" +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:279 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:280 +#: erpnext/accounts/report/consolidated_trial_balance/test_consolidated_trial_balance.py:88 +msgid "Foreign Currency Translation Reserve" +msgstr "" + #. Label of the foreign_trade_details (Section Break) field in DocType 'Item' #: erpnext/stock/doctype/item/item.json msgid "Foreign Trade Details" @@ -21938,6 +22031,7 @@ msgstr "" #: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js:16 #: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js:16 #: erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js:8 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:39 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:16 #: erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js:37 #: erpnext/accounts/report/financial_ratios/financial_ratios.js:41 @@ -21992,7 +22086,7 @@ msgstr "" #: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js:7 #: erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js:15 #: erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.js:8 -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:8 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:14 #: erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js:28 #: erpnext/public/js/stock_analytics.js:74 #: erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js:8 @@ -22467,6 +22561,10 @@ msgstr "" msgid "G - D" msgstr "" +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html:16 +msgid "GENERAL LEDGER" +msgstr "" + #: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:170 #: erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py:238 msgid "GL Balance" @@ -22531,7 +22629,7 @@ msgstr "" #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:74 #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:98 -#: erpnext/setup/doctype/company/company.py:566 +#: erpnext/setup/doctype/company/company.py:569 msgid "Gain/Loss on Asset Disposal" msgstr "" @@ -22780,7 +22878,7 @@ msgstr "" #: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js:119 #: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js:142 #: erpnext/manufacturing/doctype/production_plan/production_plan.json -#: erpnext/public/js/controllers/buying.js:324 +#: erpnext/public/js/controllers/buying.js:329 #: erpnext/selling/doctype/quotation/quotation.js:167 #: erpnext/selling/doctype/sales_order/sales_order.js:174 #: erpnext/selling/doctype/sales_order/sales_order.js:1021 @@ -22789,7 +22887,7 @@ msgstr "" #: erpnext/stock/doctype/material_request/material_request.js:126 #: erpnext/stock/doctype/material_request/material_request.js:221 #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:160 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:277 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:278 #: erpnext/stock/doctype/stock_entry/stock_entry.js:319 #: erpnext/stock/doctype/stock_entry/stock_entry.js:366 #: erpnext/stock/doctype/stock_entry/stock_entry.js:395 @@ -22825,7 +22923,7 @@ msgstr "" msgid "Get Items from Open Material Requests" msgstr "" -#: erpnext/public/js/controllers/buying.js:601 +#: erpnext/public/js/controllers/buying.js:606 msgid "Get Items from Product Bundle" msgstr "" @@ -22999,7 +23097,7 @@ msgstr "" msgid "Goods" msgstr "" -#: erpnext/setup/doctype/company/company.py:292 +#: erpnext/setup/doctype/company/company.py:295 #: erpnext/stock/doctype/stock_entry/stock_entry_list.js:21 msgid "Goods In Transit" msgstr "" @@ -23008,7 +23106,7 @@ msgstr "" msgid "Goods Transferred" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1908 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1909 msgid "Goods are already received against the outward entry {0}" msgstr "" @@ -23242,7 +23340,7 @@ msgstr "" #. Label of the gross_profit (Currency) field in DocType 'Quotation Item' #. Label of the gross_profit (Currency) field in DocType 'Sales Order Item' #: erpnext/accounts/report/gross_profit/gross_profit.json -#: erpnext/accounts/report/gross_profit/gross_profit.py:350 +#: erpnext/accounts/report/gross_profit/gross_profit.py:355 #: erpnext/accounts/workspace/financial_reports/financial_reports.json #: erpnext/selling/doctype/quotation_item/quotation_item.json #: erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -23253,7 +23351,7 @@ msgstr "" msgid "Gross Profit / Loss" msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:357 +#: erpnext/accounts/report/gross_profit/gross_profit.py:362 msgid "Gross Profit Percent" msgstr "" @@ -23261,26 +23359,6 @@ msgstr "" msgid "Gross Profit Ratio" msgstr "" -#. Label of the gross_purchase_amount (Currency) field in DocType 'Asset -#. Depreciation Schedule' -#: erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:437 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:498 -msgid "Gross Purchase Amount" -msgstr "" - -#: erpnext/assets/doctype/asset/asset.py:384 -msgid "Gross Purchase Amount is mandatory" -msgstr "" - -#: erpnext/assets/doctype/asset/asset.py:444 -msgid "Gross Purchase Amount should be equal to purchase amount of one single Asset." -msgstr "" - -#: erpnext/assets/doctype/asset_depreciation_schedule/deppreciation_schedule_controller.py:388 -msgid "Gross Purchase Amount {0} cannot be depreciated over {1} cycles." -msgstr "" - #. Label of the gross_weight_pkg (Float) field in DocType 'Packing Slip' #: erpnext/stock/doctype/packing_slip/packing_slip.json msgid "Gross Weight" @@ -23659,7 +23737,7 @@ msgstr "" msgid "Here are the error logs for the aforementioned failed depreciation entries: {0}" msgstr "" -#: erpnext/stock/stock_ledger.py:1900 +#: erpnext/stock/stock_ledger.py:1979 msgid "Here are the options to proceed:" msgstr "" @@ -23879,7 +23957,7 @@ msgstr "" msgid "Hrs" msgstr "" -#: erpnext/setup/doctype/company/company.py:398 +#: erpnext/setup/doctype/company/company.py:401 msgid "Human Resources" msgstr "" @@ -24202,7 +24280,7 @@ msgstr "" msgid "If no taxes are set, and Taxes and Charges Template is selected, the system will automatically apply the taxes from the chosen template." msgstr "" -#: erpnext/stock/stock_ledger.py:1910 +#: erpnext/stock/stock_ledger.py:1989 msgid "If not, you can Cancel / Submit this entry" msgstr "" @@ -24237,7 +24315,7 @@ msgstr "" msgid "If the account is frozen, entries are allowed to restricted users." msgstr "" -#: erpnext/stock/stock_ledger.py:1903 +#: erpnext/stock/stock_ledger.py:1982 msgid "If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table." msgstr "" @@ -24318,7 +24396,7 @@ msgstr "" msgid "If yes, then this warehouse will be used to store rejected materials" msgstr "" -#: erpnext/stock/doctype/item/item.js:986 +#: erpnext/stock/doctype/item/item.js:1001 msgid "If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item." msgstr "" @@ -24942,7 +25020,7 @@ msgstr "" msgid "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent" msgstr "" -#: erpnext/stock/doctype/item/item.js:1019 +#: erpnext/stock/doctype/item/item.js:1034 msgid "In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc." msgstr "" @@ -25028,6 +25106,7 @@ msgstr "" #: erpnext/accounts/report/balance_sheet/balance_sheet.js:28 #: erpnext/accounts/report/cash_flow/cash_flow.js:20 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js:131 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:85 #: erpnext/accounts/report/general_ledger/general_ledger.js:186 #: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js:29 #: erpnext/accounts/report/trial_balance/trial_balance.js:104 @@ -25168,7 +25247,7 @@ msgstr "" #: erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:407 #: erpnext/accounts/report/account_balance/account_balance.js:27 -#: erpnext/accounts/report/financial_statements.py:756 +#: erpnext/accounts/report/financial_statements.py:776 #: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:176 #: erpnext/accounts/report/profitability_analysis/profitability_analysis.py:183 msgid "Income" @@ -25248,7 +25327,7 @@ msgstr "" msgid "Incorrect Balance Qty After Transaction" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:954 +#: erpnext/controllers/subcontracting_controller.py:960 msgid "Incorrect Batch Consumed" msgstr "" @@ -25256,7 +25335,7 @@ msgstr "" msgid "Incorrect Check in (group) Warehouse for Reorder" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:814 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:815 msgid "Incorrect Component Quantity" msgstr "" @@ -25282,7 +25361,7 @@ msgstr "" msgid "Incorrect Serial No Valuation" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:967 +#: erpnext/controllers/subcontracting_controller.py:973 msgid "Incorrect Serial Number Consumed" msgstr "" @@ -25463,13 +25542,13 @@ msgstr "" msgid "Inspected By" msgstr "" -#: erpnext/controllers/stock_controller.py:1227 +#: erpnext/controllers/stock_controller.py:1251 msgid "Inspection Rejected" msgstr "" #. Label of the inspection_required (Check) field in DocType 'Stock Entry' -#: erpnext/controllers/stock_controller.py:1197 -#: erpnext/controllers/stock_controller.py:1199 +#: erpnext/controllers/stock_controller.py:1221 +#: erpnext/controllers/stock_controller.py:1223 #: erpnext/stock/doctype/stock_entry/stock_entry.json msgid "Inspection Required" msgstr "" @@ -25486,7 +25565,7 @@ msgstr "" msgid "Inspection Required before Purchase" msgstr "" -#: erpnext/controllers/stock_controller.py:1212 +#: erpnext/controllers/stock_controller.py:1236 msgid "Inspection Submission" msgstr "" @@ -25565,21 +25644,21 @@ msgstr "" msgid "Insufficient Capacity" msgstr "" -#: erpnext/controllers/accounts_controller.py:3629 -#: erpnext/controllers/accounts_controller.py:3653 +#: erpnext/controllers/accounts_controller.py:3654 +#: erpnext/controllers/accounts_controller.py:3678 msgid "Insufficient Permissions" msgstr "" #: erpnext/stock/doctype/pick_list/pick_list.py:120 #: erpnext/stock/doctype/pick_list/pick_list.py:138 #: erpnext/stock/doctype/pick_list/pick_list.py:1009 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:789 -#: erpnext/stock/serial_batch_bundle.py:1103 erpnext/stock/stock_ledger.py:1585 -#: erpnext/stock/stock_ledger.py:2072 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:790 +#: erpnext/stock/serial_batch_bundle.py:1105 erpnext/stock/stock_ledger.py:1664 +#: erpnext/stock/stock_ledger.py:2151 msgid "Insufficient Stock" msgstr "" -#: erpnext/stock/stock_ledger.py:2087 +#: erpnext/stock/stock_ledger.py:2166 msgid "Insufficient Stock for Batch" msgstr "" @@ -25781,7 +25860,7 @@ msgstr "" msgid "Internal Work History" msgstr "" -#: erpnext/controllers/stock_controller.py:1294 +#: erpnext/controllers/stock_controller.py:1318 msgid "Internal transfers can only be done in company's default currency" msgstr "" @@ -25811,8 +25890,8 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:972 #: erpnext/assets/doctype/asset_category/asset_category.py:69 #: erpnext/assets/doctype/asset_category/asset_category.py:97 -#: erpnext/controllers/accounts_controller.py:3014 -#: erpnext/controllers/accounts_controller.py:3022 +#: erpnext/controllers/accounts_controller.py:3026 +#: erpnext/controllers/accounts_controller.py:3034 msgid "Invalid Account" msgstr "" @@ -25837,7 +25916,7 @@ msgstr "" msgid "Invalid Barcode. There is no Item attached to this barcode." msgstr "" -#: erpnext/public/js/controllers/transaction.js:3041 +#: erpnext/public/js/controllers/transaction.js:3081 msgid "Invalid Blanket Order for the selected Customer and Item" msgstr "" @@ -25851,7 +25930,7 @@ msgstr "" #: erpnext/assets/doctype/asset/asset.py:293 #: erpnext/assets/doctype/asset/asset.py:300 -#: erpnext/controllers/accounts_controller.py:3037 +#: erpnext/controllers/accounts_controller.py:3049 msgid "Invalid Cost Center" msgstr "" @@ -25880,10 +25959,6 @@ msgstr "" msgid "Invalid Formula" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:449 -msgid "Invalid Gross Purchase Amount" -msgstr "" - #: erpnext/selling/report/lost_quotations/lost_quotations.py:65 msgid "Invalid Group By" msgstr "" @@ -25902,8 +25977,12 @@ msgstr "" msgid "Invalid Ledger Entries" msgstr "" +#: erpnext/assets/doctype/asset/asset.py:447 +msgid "Invalid Net Purchase Amount" +msgstr "" + #: erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py:77 -#: erpnext/accounts/general_ledger.py:792 +#: erpnext/accounts/general_ledger.py:793 msgid "Invalid Opening Entry" msgstr "" @@ -25915,7 +25994,7 @@ msgstr "" msgid "Invalid Parent Account" msgstr "" -#: erpnext/public/js/controllers/buying.js:423 +#: erpnext/public/js/controllers/buying.js:428 msgid "Invalid Part Number" msgstr "" @@ -25945,7 +26024,7 @@ msgstr "" msgid "Invalid Purchase Invoice" msgstr "" -#: erpnext/controllers/accounts_controller.py:3666 +#: erpnext/controllers/accounts_controller.py:3691 msgid "Invalid Qty" msgstr "" @@ -25965,8 +26044,8 @@ msgstr "" msgid "Invalid Sales Invoices" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:534 -#: erpnext/assets/doctype/asset/asset.py:553 +#: erpnext/assets/doctype/asset/asset.py:532 +#: erpnext/assets/doctype/asset/asset.py:551 msgid "Invalid Schedule" msgstr "" @@ -25974,7 +26053,7 @@ msgstr "" msgid "Invalid Selling Price" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1462 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1463 msgid "Invalid Serial and Batch Bundle" msgstr "" @@ -26021,8 +26100,8 @@ msgstr "" #: erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py:108 #: erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py:118 -#: erpnext/accounts/general_ledger.py:835 -#: erpnext/accounts/general_ledger.py:845 +#: erpnext/accounts/general_ledger.py:836 +#: erpnext/accounts/general_ledger.py:846 msgid "Invalid value {0} for {1} against account {2}" msgstr "" @@ -26933,7 +27012,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:2457 +#: erpnext/public/js/controllers/transaction.js:2482 msgid "It is needed to fetch Item Details." msgstr "" @@ -26983,7 +27062,7 @@ msgstr "" #: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js:33 #: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py:204 #: erpnext/buying/workspace/buying/buying.json -#: erpnext/controllers/taxes_and_totals.py:1123 +#: erpnext/controllers/taxes_and_totals.py:1129 #: erpnext/manufacturing/doctype/blanket_order/blanket_order.json #: erpnext/manufacturing/doctype/bom/bom.js:986 #: erpnext/manufacturing/doctype/bom/bom.json @@ -27217,7 +27296,7 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:1043 #: 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:287 +#: erpnext/accounts/report/gross_profit/gross_profit.py:292 #: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:150 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:170 #: erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py:37 @@ -27269,7 +27348,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:213 -#: erpnext/public/js/controllers/transaction.js:2752 +#: erpnext/public/js/controllers/transaction.js:2777 #: erpnext/public/js/stock_reservation.js:112 #: erpnext/public/js/stock_reservation.js:317 erpnext/public/js/utils.js:488 #: erpnext/public/js/utils.js:644 @@ -27459,7 +27538,7 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json #: erpnext/accounts/doctype/tax_rule/tax_rule.json #: erpnext/accounts/report/gross_profit/gross_profit.js:44 -#: erpnext/accounts/report/gross_profit/gross_profit.py:300 +#: erpnext/accounts/report/gross_profit/gross_profit.py:305 #: erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js:21 #: erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py:28 #: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js:28 @@ -27680,7 +27759,7 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json #: erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py:74 #: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py:71 -#: erpnext/accounts/report/gross_profit/gross_profit.py:294 +#: erpnext/accounts/report/gross_profit/gross_profit.py:299 #: erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py:33 #: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:156 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:176 @@ -27730,7 +27809,7 @@ msgstr "" #: erpnext/manufacturing/report/production_planning_report/production_planning_report.py:359 #: 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:2758 +#: erpnext/public/js/controllers/transaction.js:2783 #: erpnext/public/js/utils.js:734 #: erpnext/selling/doctype/quotation_item/quotation_item.json #: erpnext/selling/doctype/sales_order/sales_order.js:1068 @@ -27817,7 +27896,7 @@ msgstr "" msgid "Item Price Stock" msgstr "" -#: erpnext/stock/get_item_details.py:1071 +#: erpnext/stock/get_item_details.py:1089 msgid "Item Price added for {0} in Price List {1}" msgstr "" @@ -27825,7 +27904,7 @@ msgstr "" msgid "Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates." msgstr "" -#: erpnext/stock/get_item_details.py:1050 +#: erpnext/stock/get_item_details.py:1068 msgid "Item Price updated for {0} in Price List {1}" msgstr "" @@ -28003,7 +28082,7 @@ msgstr "" msgid "Item Variant Settings" msgstr "" -#: erpnext/stock/doctype/item/item.js:835 +#: erpnext/stock/doctype/item/item.js:850 msgid "Item Variant {0} already exists with same attributes" msgstr "" @@ -28072,7 +28151,7 @@ msgstr "" msgid "Item and Warranty Details" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2842 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2897 msgid "Item for row {0} does not match Material Request" msgstr "" @@ -28102,11 +28181,11 @@ msgstr "" msgid "Item operation" msgstr "" -#: erpnext/controllers/accounts_controller.py:3689 +#: erpnext/controllers/accounts_controller.py:3714 msgid "Item qty can not be updated as raw materials are already processed." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:905 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:906 msgid "Item rate has been updated to zero as Allow Zero Valuation Rate is checked for item {0}" msgstr "" @@ -28149,11 +28228,11 @@ msgstr "" msgid "Item {0} does not exist in the system or has expired" msgstr "" -#: erpnext/controllers/stock_controller.py:421 +#: erpnext/controllers/stock_controller.py:445 msgid "Item {0} does not exist." msgstr "" -#: erpnext/controllers/selling_controller.py:803 +#: erpnext/controllers/selling_controller.py:810 msgid "Item {0} entered multiple times." msgstr "" @@ -28201,7 +28280,7 @@ msgstr "" msgid "Item {0} is not a subcontracted item" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1820 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1821 msgid "Item {0} is not active or end of life has been reached" msgstr "" @@ -28221,7 +28300,7 @@ msgstr "" msgid "Item {0} must be a non-stock item" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1204 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1205 msgid "Item {0} not found in 'Raw Materials Supplied' table in {1} {2}" msgstr "" @@ -28274,7 +28353,7 @@ msgstr "" msgid "Item-wise Sales Register" msgstr "" -#: erpnext/stock/get_item_details.py:711 +#: erpnext/stock/get_item_details.py:714 msgid "Item/Item Code required to get Item Tax Template." msgstr "" @@ -28390,7 +28469,7 @@ msgstr "" msgid "Items and Pricing" msgstr "" -#: erpnext/controllers/accounts_controller.py:3911 +#: erpnext/controllers/accounts_controller.py:3936 msgid "Items cannot be updated as Subcontracting Order is created against the Purchase Order {0}." msgstr "" @@ -28398,7 +28477,7 @@ msgstr "" msgid "Items for Raw Material Request" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:901 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:902 msgid "Items rate has been updated to zero as Allow Zero Valuation Rate is checked for the following items: {0}" msgstr "" @@ -29327,7 +29406,7 @@ msgstr "" msgid "Legacy Fields" msgstr "" -#: erpnext/setup/doctype/company/company.py:422 +#: erpnext/setup/doctype/company/company.py:425 #: erpnext/setup/setup_wizard/data/industry_type.txt:30 msgid "Legal" msgstr "" @@ -29477,7 +29556,7 @@ msgstr "" msgid "Likes" msgstr "" -#: erpnext/controllers/status_updater.py:403 +#: erpnext/controllers/status_updater.py:459 msgid "Limit Crossed" msgstr "" @@ -29667,7 +29746,7 @@ msgstr "" #: erpnext/assets/doctype/linked_location/linked_location.json #: erpnext/assets/doctype/location/location.json #: erpnext/assets/doctype/location/location_tree.js:10 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:541 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:543 #: erpnext/assets/workspace/assets/assets.json #: erpnext/setup/doctype/vehicle/vehicle.json #: erpnext/stock/doctype/serial_no/serial_no.json @@ -29923,7 +30002,7 @@ msgstr "" msgid "MRP Log documents are being created in the background." msgstr "" -#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:124 +#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:156 msgid "MT940 file detected. Please enable 'Import MT940 Format' to proceed." msgstr "" @@ -29947,10 +30026,10 @@ msgstr "" msgid "Machine operator errors" msgstr "" -#: erpnext/setup/doctype/company/company.py:604 -#: erpnext/setup/doctype/company/company.py:619 -#: erpnext/setup/doctype/company/company.py:620 -#: erpnext/setup/doctype/company/company.py:621 +#: erpnext/setup/doctype/company/company.py:622 +#: erpnext/setup/doctype/company/company.py:637 +#: erpnext/setup/doctype/company/company.py:638 +#: erpnext/setup/doctype/company/company.py:639 msgid "Main" msgstr "" @@ -30231,7 +30310,7 @@ msgstr "" msgid "Make Difference Entry" msgstr "" -#: erpnext/stock/doctype/item/item.js:522 +#: erpnext/stock/doctype/item/item.js:537 msgid "Make Lead Time" msgstr "" @@ -30253,7 +30332,7 @@ msgstr "" msgid "Make Quotation" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:363 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:364 msgid "Make Return Entry" msgstr "" @@ -30269,7 +30348,7 @@ msgid "Make Serial No / Batch from Work Order" msgstr "" #: erpnext/manufacturing/doctype/job_card/job_card.js:92 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:289 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:290 msgid "Make Stock Entry" msgstr "" @@ -30290,11 +30369,11 @@ msgstr "" msgid "Make {0}" msgstr "" -#: erpnext/stock/doctype/item/item.js:630 +#: erpnext/stock/doctype/item/item.js:645 msgid "Make {0} Variant" msgstr "" -#: erpnext/stock/doctype/item/item.js:632 +#: erpnext/stock/doctype/item/item.js:647 msgid "Make {0} Variants" msgstr "" @@ -30325,7 +30404,7 @@ msgstr "" msgid "Manage your orders" msgstr "" -#: erpnext/setup/doctype/company/company.py:404 +#: erpnext/setup/doctype/company/company.py:407 msgid "Management" msgstr "" @@ -30348,13 +30427,13 @@ msgstr "" #: erpnext/manufacturing/doctype/bom/bom.py:261 #: erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py:71 #: erpnext/public/js/controllers/accounts.js:277 -#: erpnext/public/js/controllers/transaction.js:3177 +#: erpnext/public/js/controllers/transaction.js:3217 #: erpnext/public/js/utils/party.js:321 #: erpnext/stock/doctype/delivery_note/delivery_note.js:164 #: erpnext/stock/doctype/delivery_note/delivery_note.js:206 #: erpnext/stock/doctype/inventory_dimension/inventory_dimension.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:138 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:254 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:255 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:101 msgid "Mandatory" msgstr "" @@ -30471,8 +30550,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:982 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:998 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:983 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:999 #: 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 @@ -30546,7 +30625,7 @@ msgstr "" msgid "Manufacturer Part Number" msgstr "" -#: erpnext/public/js/controllers/buying.js:420 +#: erpnext/public/js/controllers/buying.js:425 msgid "Manufacturer Part Number {0} is invalid" msgstr "" @@ -30610,7 +30689,7 @@ msgstr "" msgid "Manufacturing Manager" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:2044 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2045 msgid "Manufacturing Quantity is mandatory" msgstr "" @@ -30784,12 +30863,13 @@ msgstr "" msgid "Market Segment" msgstr "" -#: erpnext/setup/doctype/company/company.py:356 +#: erpnext/setup/doctype/company/company.py:359 msgid "Marketing" msgstr "" #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:60 #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:85 +#: erpnext/setup/doctype/company/company.py:578 msgid "Marketing Expenses" msgstr "" @@ -30846,7 +30926,7 @@ msgstr "" #. Option for the 'Purpose' (Select) field in DocType 'Stock Entry Type' #: erpnext/setup/setup_wizard/operations/install_fixtures.py:121 #: erpnext/stock/doctype/stock_entry/stock_entry.json -#: erpnext/stock/doctype/stock_entry/stock_entry.py:983 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:984 #: erpnext/stock/doctype/stock_entry_type/stock_entry_type.json msgid "Material Consumption for Manufacture" msgstr "" @@ -31032,7 +31112,7 @@ msgstr "" msgid "Material Request used to make this Stock Entry" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1220 +#: erpnext/controllers/subcontracting_controller.py:1226 msgid "Material Request {0} is cancelled or stopped" msgstr "" @@ -31137,7 +31217,7 @@ msgstr "" msgid "Material to Supplier" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1439 +#: erpnext/controllers/subcontracting_controller.py:1445 msgid "Materials are already received against the {0} {1}" msgstr "" @@ -31227,11 +31307,11 @@ msgstr "" msgid "Maximum Payment Amount" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3380 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3435 msgid "Maximum Samples - {0} can be retained for Batch {1} and Item {2}." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3371 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3426 msgid "Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}." msgstr "" @@ -31320,7 +31400,7 @@ msgstr "" msgid "Megawatt" msgstr "" -#: erpnext/stock/stock_ledger.py:1916 +#: erpnext/stock/stock_ledger.py:1995 msgid "Mention Valuation Rate in the Item master." msgstr "" @@ -31438,7 +31518,7 @@ msgstr "" msgid "Messages greater than 160 characters will be split into multiple messages" msgstr "" -#: erpnext/setup/install.py:124 +#: erpnext/setup/install.py:125 msgid "Messaging CRM Campagin" msgstr "" @@ -31716,7 +31796,7 @@ msgstr "" msgid "Miscellaneous Expenses" msgstr "" -#: erpnext/controllers/buying_controller.py:635 +#: erpnext/controllers/buying_controller.py:689 msgid "Mismatch" msgstr "" @@ -31754,7 +31834,7 @@ msgstr "" msgid "Missing Finance Book" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1397 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1398 msgid "Missing Finished Good" msgstr "" @@ -31762,7 +31842,7 @@ msgstr "" msgid "Missing Formula" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:821 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:822 msgid "Missing Item" msgstr "" @@ -31993,6 +32073,10 @@ msgstr "" msgid "Month" msgstr "" +#. Option for the 'Due Date Based On' (Select) field in DocType 'Payment +#. Schedule' +#. Option for the 'Discount Validity Based On' (Select) field in DocType +#. 'Payment Schedule' #. Option for the 'Due Date Based On' (Select) field in DocType 'Payment Term' #. Option for the 'Discount Validity Based On' (Select) field in DocType #. 'Payment Term' @@ -32000,6 +32084,7 @@ msgstr "" #. Template Detail' #. Option for the 'Discount Validity Based On' (Select) field in DocType #. 'Payment Terms Template Detail' +#: erpnext/accounts/doctype/payment_schedule/payment_schedule.json #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json msgid "Month(s) after the end of the invoice month" @@ -32021,7 +32106,7 @@ msgstr "" #: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json #: erpnext/accounts/report/budget_variance_report/budget_variance_report.js:62 #: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js:75 -#: erpnext/accounts/report/gross_profit/gross_profit.py:418 +#: erpnext/accounts/report/gross_profit/gross_profit.py:423 #: erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json #: erpnext/buying/report/purchase_analytics/purchase_analytics.js:61 #: erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.json @@ -32186,8 +32271,8 @@ msgstr "" #: erpnext/manufacturing/doctype/plant_floor/stock_summary_template.html:58 #: erpnext/stock/dashboard/item_dashboard_list.html:53 -#: erpnext/stock/doctype/batch/batch.js:80 -#: erpnext/stock/doctype/batch/batch.js:138 +#: erpnext/stock/doctype/batch/batch.js:91 +#: erpnext/stock/doctype/batch/batch.js:149 #: erpnext/stock/doctype/batch/batch_dashboard.py:10 msgid "Move" msgstr "" @@ -32264,7 +32349,7 @@ msgstr "" msgid "Multiple fiscal years exist for the date {0}. Please set company in Fiscal Year" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1404 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1405 msgid "Multiple items cannot be marked as finished item" msgstr "" @@ -32455,10 +32540,6 @@ msgstr "" msgid "Needs Analysis" msgstr "" -#: erpnext/stock/serial_batch_bundle.py:1396 -msgid "Negative Batch Quantity" -msgstr "" - #: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:616 msgid "Negative Quantity is not allowed" msgstr "" @@ -32541,8 +32622,8 @@ msgstr "" msgid "Net Amount (Company Currency)" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:655 -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:661 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:663 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:669 msgid "Net Asset value as on" msgstr "" @@ -32604,11 +32685,28 @@ msgstr "" msgid "Net Profit/Loss" msgstr "" -#. Label of the gross_purchase_amount (Currency) field in DocType 'Asset' +#. Label of the net_purchase_amount (Currency) field in DocType 'Asset' +#. Label of the net_purchase_amount (Currency) field in DocType 'Asset +#. Depreciation Schedule' #: erpnext/assets/doctype/asset/asset.json +#: erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:439 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:500 msgid "Net Purchase Amount" msgstr "" +#: erpnext/assets/doctype/asset/asset.py:382 +msgid "Net Purchase Amount is mandatory" +msgstr "" + +#: erpnext/assets/doctype/asset/asset.py:442 +msgid "Net Purchase Amount should be equal to purchase amount of one single Asset." +msgstr "" + +#: erpnext/assets/doctype/asset_depreciation_schedule/deppreciation_schedule_controller.py:388 +msgid "Net Purchase Amount {0} cannot be depreciated over {1} cycles." +msgstr "" + #. Label of the net_rate (Currency) field in DocType 'POS Invoice Item' #. Label of the net_rate (Currency) field in DocType 'Purchase Invoice Item' #. Label of the net_rate (Currency) field in DocType 'Sales Invoice Item' @@ -32791,11 +32889,11 @@ msgstr "" msgid "New Balance In Base Currency" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:156 +#: erpnext/stock/doctype/batch/batch.js:167 msgid "New Batch ID (Optional)" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:150 +#: erpnext/stock/doctype/batch/batch.js:161 msgid "New Batch Qty" msgstr "" @@ -33063,7 +33161,7 @@ msgstr "" msgid "No Item with Serial No {0}" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1353 +#: erpnext/controllers/subcontracting_controller.py:1359 msgid "No Items selected for transfer." msgstr "" @@ -33136,7 +33234,7 @@ msgstr "" msgid "No Tax Withholding data found for the current posting date." msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:876 +#: erpnext/accounts/report/gross_profit/gross_profit.py:881 msgid "No Terms" msgstr "" @@ -33152,7 +33250,7 @@ msgstr "" msgid "No Work Orders were created" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:805 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:806 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py:792 msgid "No accounting entries for the following warehouses" msgstr "" @@ -33205,7 +33303,7 @@ msgstr "" msgid "No failed logs" msgstr "" -#: erpnext/controllers/subcontracting_controller.py:1262 +#: erpnext/controllers/subcontracting_controller.py:1268 msgid "No item available for transfer." msgstr "" @@ -33339,7 +33437,7 @@ msgstr "" msgid "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." msgstr "" -#: erpnext/public/js/controllers/buying.js:530 +#: erpnext/public/js/controllers/buying.js:535 msgid "No pending Material Requests found to link for the given items." msgstr "" @@ -33592,13 +33690,13 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan/production_plan.py:1056 #: erpnext/manufacturing/doctype/production_plan/production_plan.py:1768 #: erpnext/projects/doctype/timesheet/timesheet.json -#: erpnext/public/js/controllers/buying.js:531 +#: erpnext/public/js/controllers/buying.js:536 #: erpnext/selling/doctype/customer/customer.py:129 #: erpnext/selling/doctype/sales_order/sales_order.js:1403 -#: erpnext/stock/doctype/item/item.js:536 +#: erpnext/stock/doctype/item/item.js:551 #: erpnext/stock/doctype/item/item.py:582 #: erpnext/stock/doctype/item_price/item_price.json -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1405 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1406 #: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:978 #: erpnext/templates/pages/timelog_info.html:43 msgid "Note" @@ -33998,7 +34096,7 @@ msgstr "" msgid "On This Date" msgstr "" -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:79 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:80 msgid "On Track" msgstr "" @@ -34102,7 +34200,7 @@ msgstr "" msgid "Only leaf nodes are allowed in transaction" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:997 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:998 msgid "Only one {0} entry can be created against the Work Order {1}" msgstr "" @@ -34307,12 +34405,14 @@ msgstr "" msgid "Opening & Closing" msgstr "" -#: erpnext/accounts/report/trial_balance/trial_balance.py:468 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:436 +#: erpnext/accounts/report/trial_balance/trial_balance.py:517 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py:198 msgid "Opening (Cr)" msgstr "" -#: erpnext/accounts/report/trial_balance/trial_balance.py:461 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:429 +#: erpnext/accounts/report/trial_balance/trial_balance.py:510 #: erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py:191 msgid "Opening (Dr)" msgstr "" @@ -34324,8 +34424,8 @@ msgstr "" #: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:159 #: erpnext/assets/doctype/asset/asset.json #: erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:444 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:512 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:446 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:514 msgid "Opening Accumulated Depreciation" msgstr "" @@ -34367,7 +34467,7 @@ msgstr "" msgid "Opening Entry" msgstr "" -#: erpnext/accounts/general_ledger.py:791 +#: erpnext/accounts/general_ledger.py:792 msgid "Opening Entry can not be created after Period Closing Voucher is created." msgstr "" @@ -34394,7 +34494,7 @@ msgstr "" msgid "Opening Invoice Item" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1633 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1634 #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1837 msgid "Opening Invoice has rounding adjustment of {0}.

'{1}' account is required to post these values. Please set it in Company: {2}.

Or, '{3}' can be enabled to not post any rounding adjustment." msgstr "" @@ -34632,7 +34732,7 @@ msgstr "" #: erpnext/manufacturing/doctype/bom/bom.json #: erpnext/manufacturing/doctype/work_order/work_order.js:302 #: erpnext/manufacturing/doctype/work_order/work_order.json -#: erpnext/setup/doctype/company/company.py:374 +#: erpnext/setup/doctype/company/company.py:377 #: erpnext/setup/doctype/email_digest/email_digest.json #: erpnext/templates/generators/bom.html:61 msgid "Operations" @@ -35220,7 +35320,7 @@ msgstr "" msgid "Over Billing Allowance (%)" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:1262 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:1264 msgid "Over Billing Allowance exceeded for Purchase Receipt Item {0} ({1}) by {2}%" msgstr "" @@ -35238,11 +35338,11 @@ msgstr "" msgid "Over Picking Allowance" msgstr "" -#: erpnext/controllers/stock_controller.py:1460 +#: erpnext/controllers/stock_controller.py:1484 msgid "Over Receipt" msgstr "" -#: erpnext/controllers/status_updater.py:408 +#: erpnext/controllers/status_updater.py:464 msgid "Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role." msgstr "" @@ -35257,7 +35357,7 @@ msgstr "" msgid "Over Transfer Allowance (%)" msgstr "" -#: erpnext/controllers/status_updater.py:410 +#: erpnext/controllers/status_updater.py:466 msgid "Overbilling of {0} {1} ignored for item {2} because you have {3} role." msgstr "" @@ -35692,7 +35792,7 @@ msgstr "" msgid "Packed Items" msgstr "" -#: erpnext/controllers/stock_controller.py:1298 +#: erpnext/controllers/stock_controller.py:1322 msgid "Packed Items cannot be transferred internally" msgstr "" @@ -35971,7 +36071,7 @@ msgstr "" msgid "Parent Company" msgstr "" -#: erpnext/setup/doctype/company/company.py:493 +#: erpnext/setup/doctype/company/company.py:496 msgid "Parent Company must be a group company" msgstr "" @@ -36037,7 +36137,7 @@ msgstr "" msgid "Parent Row No" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:499 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:506 msgid "Parent Row No not found for {0}" msgstr "" @@ -36076,7 +36176,7 @@ msgstr "" msgid "Parent Warehouse" msgstr "" -#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:132 +#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:166 msgid "Parsed file is not in valid MT940 format or contains no transactions." msgstr "" @@ -37029,7 +37129,7 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json #: erpnext/buying/doctype/purchase_order/purchase_order.json -#: erpnext/controllers/accounts_controller.py:2637 +#: erpnext/controllers/accounts_controller.py:2649 #: erpnext/selling/doctype/quotation/quotation.json #: erpnext/selling/doctype/sales_order/sales_order.json msgid "Payment Schedule" @@ -37052,7 +37152,7 @@ msgstr "" #: erpnext/accounts/doctype/payment_term/payment_term.json #: erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json #: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1204 -#: erpnext/accounts/report/gross_profit/gross_profit.py:424 +#: erpnext/accounts/report/gross_profit/gross_profit.py:429 #: erpnext/accounts/workspace/accounting/accounting.json #: erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py:30 msgid "Payment Term" @@ -37498,10 +37598,11 @@ msgstr "" msgid "Period Based On" msgstr "" -#: erpnext/accounts/general_ledger.py:803 +#: erpnext/accounts/general_ledger.py:804 msgid "Period Closed" msgstr "" +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:69 #: erpnext/accounts/report/trial_balance/trial_balance.js:88 msgid "Period Closing Entry For Current Period" msgstr "" @@ -38079,7 +38180,7 @@ msgid "Please Select a Customer" msgstr "" #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:139 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:255 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:256 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:102 msgid "Please Select a Supplier" msgstr "" @@ -38136,7 +38237,7 @@ msgstr "" msgid "Please add {1} role to user {0}." msgstr "" -#: erpnext/controllers/stock_controller.py:1471 +#: erpnext/controllers/stock_controller.py:1495 msgid "Please adjust the qty or edit {0} to proceed." msgstr "" @@ -38226,7 +38327,7 @@ msgstr "" msgid "Please create purchase from internal sale or delivery document itself" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:394 +#: erpnext/assets/doctype/asset/asset.py:392 msgid "Please create purchase receipt or purchase invoice for the item {0}" msgstr "" @@ -38238,7 +38339,7 @@ msgstr "" msgid "Please disable workflow temporarily for Journal Entry {0}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:448 +#: erpnext/assets/doctype/asset/asset.py:446 msgid "Please do not book expense of multiple assets against one single Asset." msgstr "" @@ -38272,7 +38373,7 @@ msgstr "" msgid "Please enable {0} in the {1}." msgstr "" -#: erpnext/controllers/selling_controller.py:805 +#: erpnext/controllers/selling_controller.py:812 msgid "Please enable {} in {} to allow same item in multiple rows" msgstr "" @@ -38292,7 +38393,7 @@ msgstr "" msgid "Please ensure {} account {} is a Receivable account." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:550 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:551 msgid "Please enter Difference Account or set default Stock Adjustment Account for company {0}" msgstr "" @@ -38326,7 +38427,7 @@ msgstr "" msgid "Please enter Item Code to get Batch Number" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2913 +#: erpnext/public/js/controllers/transaction.js:2938 msgid "Please enter Item Code to get batch no" msgstr "" @@ -38399,7 +38500,7 @@ msgstr "" msgid "Please enter company name first" msgstr "" -#: erpnext/controllers/accounts_controller.py:2863 +#: erpnext/controllers/accounts_controller.py:2875 msgid "Please enter default currency in Company Master" msgstr "" @@ -38439,7 +38540,7 @@ msgstr "" msgid "Please enter the phone number first" msgstr "" -#: erpnext/controllers/buying_controller.py:1102 +#: erpnext/controllers/buying_controller.py:1156 msgid "Please enter the {schedule_date}." msgstr "" @@ -38491,12 +38592,12 @@ msgstr "" msgid "Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone." msgstr "" -#: erpnext/stock/doctype/item/item.js:535 +#: erpnext/stock/doctype/item/item.js:550 msgid "Please mention 'Weight UOM' along with Weight." msgstr "" -#: erpnext/accounts/general_ledger.py:640 -#: erpnext/accounts/general_ledger.py:647 +#: erpnext/accounts/general_ledger.py:641 +#: erpnext/accounts/general_ledger.py:648 msgid "Please mention '{0}' in Company: {1}" msgstr "" @@ -38533,8 +38634,8 @@ msgstr "" msgid "Please select Template Type to download template" msgstr "" -#: erpnext/controllers/taxes_and_totals.py:719 -#: erpnext/public/js/controllers/taxes_and_totals.js:814 +#: erpnext/controllers/taxes_and_totals.py:725 +#: erpnext/public/js/controllers/taxes_and_totals.js:815 msgid "Please select Apply Discount On" msgstr "" @@ -38546,7 +38647,7 @@ msgstr "" msgid "Please select BOM for Item in Row {0}" msgstr "" -#: erpnext/controllers/buying_controller.py:562 +#: erpnext/controllers/buying_controller.py:616 msgid "Please select BOM in BOM field for Item {item_code}." msgstr "" @@ -38587,7 +38688,7 @@ msgstr "" msgid "Please select Customer first" msgstr "" -#: erpnext/setup/doctype/company/company.py:440 +#: erpnext/setup/doctype/company/company.py:443 msgid "Please select Existing Company for creating Chart of Accounts" msgstr "" @@ -38648,11 +38749,11 @@ msgstr "" msgid "Please select Stock Asset Account" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1325 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1326 msgid "Please select Subcontracting Order instead of Purchase Order {0}" msgstr "" -#: erpnext/controllers/accounts_controller.py:2712 +#: erpnext/controllers/accounts_controller.py:2724 msgid "Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0}" msgstr "" @@ -38669,7 +38770,7 @@ msgstr "" #: erpnext/manufacturing/doctype/bom/bom.js:631 #: erpnext/manufacturing/doctype/bom/bom.py:261 #: erpnext/public/js/controllers/accounts.js:277 -#: erpnext/public/js/controllers/transaction.js:3177 +#: erpnext/public/js/controllers/transaction.js:3217 msgid "Please select a Company first." msgstr "" @@ -38852,7 +38953,7 @@ msgstr "" msgid "Please set 'Gain/Loss Account on Asset Disposal' in Company {0}" msgstr "" -#: erpnext/accounts/general_ledger.py:546 +#: erpnext/accounts/general_ledger.py:547 msgid "Please set '{0}' in Company: {1}" msgstr "" @@ -38920,6 +39021,10 @@ msgstr "" msgid "Please set Parent Row No for item {0}" msgstr "" +#: erpnext/controllers/buying_controller.py:332 +msgid "Please set Purchase Expense Contra Account in Company {0}" +msgstr "" + #: erpnext/accounts/doctype/ledger_merge/ledger_merge.js:24 #: erpnext/accounts/doctype/ledger_merge/ledger_merge.js:35 msgid "Please set Root Type" @@ -38962,7 +39067,7 @@ msgstr "" msgid "Please set a default Holiday List for Employee {0} or Company {1}" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1102 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1103 msgid "Please set account in Warehouse {0}" msgstr "" @@ -38971,7 +39076,7 @@ msgstr "" msgid "Please set an Address on the Company '%s'" msgstr "" -#: erpnext/controllers/stock_controller.py:762 +#: erpnext/controllers/stock_controller.py:786 msgid "Please set an Expense Account in the Items table" msgstr "" @@ -39015,7 +39120,7 @@ msgstr "" msgid "Please set default UOM in Stock Settings" msgstr "" -#: erpnext/controllers/stock_controller.py:621 +#: erpnext/controllers/stock_controller.py:645 msgid "Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer" msgstr "" @@ -39036,11 +39141,11 @@ msgstr "" msgid "Please set one of the following:" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:525 +#: erpnext/assets/doctype/asset/asset.py:523 msgid "Please set opening number of booked depreciations" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2600 +#: erpnext/public/js/controllers/transaction.js:2625 msgid "Please set recurring after saving" msgstr "" @@ -39083,7 +39188,7 @@ msgstr "" msgid "Please set {0} first." msgstr "" -#: erpnext/stock/doctype/batch/batch.py:194 +#: erpnext/stock/doctype/batch/batch.py:207 msgid "Please set {0} for Batched Item {1}, which is used to set {2} on Submit." msgstr "" @@ -39111,7 +39216,7 @@ msgstr "" msgid "Please share this email with your support team so that they can find and fix the issue." msgstr "" -#: erpnext/public/js/controllers/transaction.js:2453 +#: erpnext/public/js/controllers/transaction.js:2478 msgid "Please specify" msgstr "" @@ -39126,7 +39231,7 @@ msgid "Please specify Company to proceed" msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.js:1475 -#: erpnext/controllers/accounts_controller.py:2996 +#: erpnext/controllers/accounts_controller.py:3008 #: erpnext/public/js/controllers/accounts.js:117 msgid "Please specify a valid Row ID for row {0} in table {1}" msgstr "" @@ -39342,7 +39447,7 @@ msgstr "" #: erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py:66 #: erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py:151 #: erpnext/accounts/report/general_ledger/general_ledger.py:670 -#: erpnext/accounts/report/gross_profit/gross_profit.py:275 +#: erpnext/accounts/report/gross_profit/gross_profit.py:280 #: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:183 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:203 #: erpnext/accounts/report/payment_ledger/payment_ledger.py:143 @@ -39432,7 +39537,7 @@ msgstr "" #: erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json -#: erpnext/accounts/report/gross_profit/gross_profit.py:281 +#: erpnext/accounts/report/gross_profit/gross_profit.py:286 #: erpnext/assets/doctype/asset_capitalization/asset_capitalization.json #: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -39453,7 +39558,7 @@ msgstr "" msgid "Posting Time" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1992 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1993 msgid "Posting date and posting time is mandatory" msgstr "" @@ -39740,7 +39845,7 @@ msgstr "" msgid "Price List Currency" msgstr "" -#: erpnext/stock/get_item_details.py:1244 +#: erpnext/stock/get_item_details.py:1262 msgid "Price List Currency not selected" msgstr "" @@ -40234,7 +40339,7 @@ msgstr "" msgid "Print Style" msgstr "" -#: erpnext/setup/install.py:101 +#: erpnext/setup/install.py:102 msgid "Print UOM after Quantity" msgstr "" @@ -40252,7 +40357,7 @@ msgstr "" msgid "Print settings updated in respective print format" msgstr "" -#: erpnext/setup/install.py:108 +#: erpnext/setup/install.py:109 msgid "Print taxes with zero amount" msgstr "" @@ -40336,8 +40441,8 @@ msgstr "" #: erpnext/accounts/doctype/tax_rule/tax_rule.json #: erpnext/projects/doctype/project/project.json #: erpnext/projects/doctype/task/task.json -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:18 -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:93 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:24 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:101 #: erpnext/projects/report/project_summary/project_summary.js:36 #: erpnext/projects/web_form/tasks/tasks.json #: erpnext/stock/doctype/putaway_rule/putaway_rule.json @@ -40582,8 +40687,8 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json #: erpnext/buying/workspace/buying/buying.json -#: erpnext/public/js/controllers/buying.js:320 -#: erpnext/public/js/controllers/buying.js:605 +#: erpnext/public/js/controllers/buying.js:325 +#: erpnext/public/js/controllers/buying.js:610 #: erpnext/selling/doctype/product_bundle/product_bundle.json #: erpnext/selling/workspace/selling/selling.json #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -40649,7 +40754,7 @@ msgstr "" #. Reservation Entry' #: erpnext/manufacturing/doctype/workstation/workstation.json #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json -#: erpnext/setup/doctype/company/company.py:380 +#: erpnext/setup/doctype/company/company.py:383 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json msgid "Production" msgstr "" @@ -40843,7 +40948,7 @@ msgstr "" msgid "Progress % for a task cannot be more than 100." msgstr "" -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:94 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:102 msgid "Progress (%)" msgstr "" @@ -40943,7 +41048,7 @@ msgstr "" #: erpnext/accounts/report/general_ledger/general_ledger.js:164 #: erpnext/accounts/report/general_ledger/general_ledger.py:760 #: erpnext/accounts/report/gross_profit/gross_profit.js:79 -#: erpnext/accounts/report/gross_profit/gross_profit.py:363 +#: erpnext/accounts/report/gross_profit/gross_profit.py:368 #: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:225 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:273 #: erpnext/accounts/report/purchase_register/purchase_register.py:207 @@ -40982,6 +41087,8 @@ msgstr "" #: erpnext/projects/doctype/timesheet/timesheet_calendar.js:22 #: erpnext/projects/doctype/timesheet_detail/timesheet_detail.json #: erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py:34 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:8 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:96 #: erpnext/projects/report/project_summary/project_summary.py:47 #: erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js:19 #: erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js:46 @@ -41328,7 +41435,7 @@ msgstr "" msgid "Providing" msgstr "" -#: erpnext/setup/doctype/company/company.py:463 +#: erpnext/setup/doctype/company/company.py:466 msgid "Provisional Account" msgstr "" @@ -41394,7 +41501,7 @@ msgstr "" #: erpnext/accounts/doctype/tax_rule/tax_rule.json #: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json #: erpnext/projects/doctype/project/project_dashboard.py:16 -#: erpnext/setup/doctype/company/company.py:368 +#: erpnext/setup/doctype/company/company.py:371 #: erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/item_lead_time/item_lead_time.json #: erpnext/stock/doctype/item_reorder/item_reorder.json @@ -41422,7 +41529,7 @@ msgstr "" #. Label of the purchase_date (Date) field in DocType 'Asset' #: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py:204 #: erpnext/assets/doctype/asset/asset.json -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:490 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:492 msgid "Purchase Date" msgstr "" @@ -41441,6 +41548,33 @@ msgstr "" msgid "Purchase Details" msgstr "" +#. Label of the purchase_expense_section (Section Break) field in DocType +#. 'Company' +#: erpnext/setup/doctype/company/company.json +msgid "Purchase Expense" +msgstr "" + +#. Label of the purchase_expense_account (Link) field in DocType 'Company' +#. Label of the purchase_expense_account (Link) field in DocType 'Item Default' +#: erpnext/setup/doctype/company/company.json +#: erpnext/stock/doctype/item_default/item_default.json +msgid "Purchase Expense Account" +msgstr "" + +#. Label of the purchase_expense_contra_account (Link) field in DocType +#. 'Company' +#. Label of the purchase_expense_contra_account (Link) field in DocType 'Item +#. Default' +#: erpnext/setup/doctype/company/company.json +#: erpnext/stock/doctype/item_default/item_default.json +msgid "Purchase Expense Contra Account" +msgstr "" + +#: erpnext/controllers/buying_controller.py:342 +#: erpnext/controllers/buying_controller.py:356 +msgid "Purchase Expense for Item {0}" +msgstr "" + #. Option for the 'Reference Type' (Select) field in DocType 'Journal Entry #. Account' #. Option for the 'Invoice Type' (Select) field in DocType 'Payment @@ -41482,7 +41616,7 @@ msgstr "" #: erpnext/stock/doctype/landed_cost_item/landed_cost_item.json #: erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:134 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:296 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:297 #: erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js:30 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json #: erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -41525,7 +41659,7 @@ msgstr "" msgid "Purchase Invoice {0} is already submitted" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:2018 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:2019 msgid "Purchase Invoices" msgstr "" @@ -41592,7 +41726,7 @@ msgstr "" #: erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js:48 #: erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py:203 #: erpnext/buying/workspace/buying/buying.json -#: erpnext/controllers/buying_controller.py:834 +#: erpnext/controllers/buying_controller.py:888 #: erpnext/crm/doctype/contract/contract.json #: erpnext/manufacturing/doctype/blanket_order/blanket_order.js:54 #: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json @@ -41602,7 +41736,7 @@ msgstr "" #: 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:179 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:250 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:251 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json #: erpnext/stock/doctype/stock_entry/stock_entry.json #: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -41834,7 +41968,7 @@ msgstr "" msgid "Purchase Receipt Trends" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:391 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:392 msgid "Purchase Receipt doesn't have any Item for which Retain Sample is enabled." msgstr "" @@ -41853,7 +41987,7 @@ msgstr "" msgid "Purchase Register" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:286 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:287 msgid "Purchase Return" msgstr "" @@ -42000,7 +42134,7 @@ msgstr "" msgid "Purpose" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:374 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:375 msgid "Purpose must be one of {0}" msgstr "" @@ -42058,7 +42192,7 @@ msgstr "" #: erpnext/accounts/doctype/pricing_rule/pricing_rule.json #: erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json #: erpnext/accounts/print_format/sales_invoice_print/sales_invoice_print.html:91 -#: erpnext/accounts/report/gross_profit/gross_profit.py:320 +#: erpnext/accounts/report/gross_profit/gross_profit.py:325 #: erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json #: erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py:240 #: erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.py:224 @@ -42518,7 +42652,7 @@ msgstr "" msgid "Quality Inspection(s)" msgstr "" -#: erpnext/setup/doctype/company/company.py:410 +#: erpnext/setup/doctype/company/company.py:413 msgid "Quality Management" msgstr "" @@ -42638,7 +42772,7 @@ msgstr "" #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:194 #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:218 #: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json -#: erpnext/public/js/controllers/buying.js:612 +#: erpnext/public/js/controllers/buying.js:617 #: erpnext/public/js/stock_analytics.js:50 #: erpnext/public/js/utils/serial_no_batch_selector.js:485 #: erpnext/selling/doctype/quotation_item/quotation_item.json @@ -43656,6 +43790,10 @@ msgstr "" msgid "Rebuilding BTree for period ..." msgstr "" +#: erpnext/stock/doctype/batch/batch.js:26 +msgid "Recalculate Batch Qty" +msgstr "" + #: erpnext/stock/doctype/bin/bin.js:10 msgid "Recalculate Bin Qty" msgstr "" @@ -44197,7 +44335,7 @@ msgstr "" msgid "Reference Date" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2713 +#: erpnext/public/js/controllers/transaction.js:2738 msgid "Reference Date for Early Payment Discount" msgstr "" @@ -44783,7 +44921,7 @@ msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order_list.js:58 #: erpnext/crm/doctype/opportunity/opportunity.js:130 #: erpnext/stock/doctype/delivery_note/delivery_note.js:360 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:312 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:313 #: erpnext/support/doctype/issue/issue.js:39 msgid "Reopen" msgstr "" @@ -44924,7 +45062,7 @@ msgstr "" msgid "Report View" msgstr "" -#: erpnext/setup/install.py:154 +#: erpnext/setup/install.py:188 msgid "Report an Issue" msgstr "" @@ -45316,7 +45454,7 @@ msgstr "" msgid "Research" msgstr "" -#: erpnext/setup/doctype/company/company.py:416 +#: erpnext/setup/doctype/company/company.py:419 msgid "Research & Development" msgstr "" @@ -45457,7 +45595,7 @@ msgstr "" msgid "Reserved Quantity for Production" msgstr "" -#: erpnext/stock/stock_ledger.py:2187 +#: erpnext/stock/stock_ledger.py:2266 msgid "Reserved Serial No." msgstr "" @@ -45473,11 +45611,11 @@ msgstr "" #: 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/stock_ledger.py:2171 +#: erpnext/stock/stock_ledger.py:2250 msgid "Reserved Stock" msgstr "" -#: erpnext/stock/stock_ledger.py:2216 +#: erpnext/stock/stock_ledger.py:2295 msgid "Reserved Stock for Batch" msgstr "" @@ -45489,7 +45627,7 @@ msgstr "" msgid "Reserved Stock for Sub-assembly" msgstr "" -#: erpnext/controllers/buying_controller.py:571 +#: erpnext/controllers/buying_controller.py:625 msgid "Reserved Warehouse is mandatory for the Item {item_code} in Raw Materials supplied." msgstr "" @@ -45772,7 +45910,7 @@ msgstr "" msgid "Retained Earnings" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:302 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:303 msgid "Retention Stock Entry" msgstr "" @@ -45866,13 +46004,13 @@ msgstr "" msgid "Return Issued" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:362 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:363 msgid "Return Qty" msgstr "" #. Label of the return_qty_from_rejected_warehouse (Check) field in DocType #. 'Purchase Receipt Item' -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:338 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:339 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json msgid "Return Qty from Rejected Warehouse" msgstr "" @@ -46285,8 +46423,8 @@ msgstr "" msgid "Rounding Loss Allowance should be between 0 and 1" msgstr "" -#: erpnext/controllers/stock_controller.py:633 -#: erpnext/controllers/stock_controller.py:648 +#: erpnext/controllers/stock_controller.py:657 +#: erpnext/controllers/stock_controller.py:672 msgid "Rounding gain/loss Entry for Stock Transfer" msgstr "" @@ -46418,27 +46556,27 @@ msgstr "" msgid "Row #{0}: Cannot allocate more than {1} against payment term {2}" msgstr "" -#: erpnext/controllers/accounts_controller.py:3563 +#: erpnext/controllers/accounts_controller.py:3588 msgid "Row #{0}: Cannot delete item {1} which has already been billed." msgstr "" -#: erpnext/controllers/accounts_controller.py:3537 +#: erpnext/controllers/accounts_controller.py:3562 msgid "Row #{0}: Cannot delete item {1} which has already been delivered" msgstr "" -#: erpnext/controllers/accounts_controller.py:3556 +#: erpnext/controllers/accounts_controller.py:3581 msgid "Row #{0}: Cannot delete item {1} which has already been received" msgstr "" -#: erpnext/controllers/accounts_controller.py:3543 +#: erpnext/controllers/accounts_controller.py:3568 msgid "Row #{0}: Cannot delete item {1} which has work order assigned to it." msgstr "" -#: erpnext/controllers/accounts_controller.py:3549 +#: erpnext/controllers/accounts_controller.py:3574 msgid "Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order." msgstr "" -#: erpnext/controllers/accounts_controller.py:3804 +#: erpnext/controllers/accounts_controller.py:3829 msgid "Row #{0}: Cannot set Rate if the billed amount is greater than the amount for Item {1}." msgstr "" @@ -46486,7 +46624,7 @@ msgstr "" msgid "Row #{0}: Default BOM not found for FG Item {1}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:552 +#: erpnext/assets/doctype/asset/asset.py:550 msgid "Row #{0}: Depreciation Start Date is required" msgstr "" @@ -46498,7 +46636,7 @@ msgstr "" msgid "Row #{0}: Expected Delivery Date cannot be before Purchase Order Date" msgstr "" -#: erpnext/controllers/stock_controller.py:764 +#: erpnext/controllers/stock_controller.py:788 msgid "Row #{0}: Expense Account not set for the Item {1}. {2}" msgstr "" @@ -46514,7 +46652,7 @@ msgstr "" msgid "Row #{0}: Finished Good Item {1} must be a sub-contracted item" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:334 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:335 msgid "Row #{0}: Finished Good must be {1}" msgstr "" @@ -46570,11 +46708,11 @@ msgstr "" msgid "Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:546 +#: erpnext/assets/doctype/asset/asset.py:544 msgid "Row #{0}: Next Depreciation Date cannot be before Available-for-use Date" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:541 +#: erpnext/assets/doctype/asset/asset.py:539 msgid "Row #{0}: Next Depreciation Date cannot be before Purchase Date" msgstr "" @@ -46586,11 +46724,11 @@ msgstr "" msgid "Row #{0}: Only {1} available to reserve for the Item {2}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:518 +#: erpnext/assets/doctype/asset/asset.py:516 msgid "Row #{0}: Opening Accumulated Depreciation must be less than or equal to {1}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:701 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:702 msgid "Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}." msgstr "" @@ -46627,15 +46765,15 @@ msgstr "" msgid "Row #{0}: Qty should be less than or equal to Available Qty to Reserve (Actual Qty - Reserved Qty) {1} for Iem {2} against Batch {3} in Warehouse {4}." msgstr "" -#: erpnext/controllers/stock_controller.py:1193 +#: erpnext/controllers/stock_controller.py:1217 msgid "Row #{0}: Quality Inspection is required for Item {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:1208 +#: erpnext/controllers/stock_controller.py:1232 msgid "Row #{0}: Quality Inspection {1} is not submitted for the item: {2}" msgstr "" -#: erpnext/controllers/stock_controller.py:1223 +#: erpnext/controllers/stock_controller.py:1247 msgid "Row #{0}: Quality Inspection {1} was rejected for item {2}" msgstr "" @@ -46644,7 +46782,7 @@ msgid "Row #{0}: Quantity cannot be a non-positive number. Please increase the q msgstr "" #: erpnext/controllers/accounts_controller.py:1375 -#: erpnext/controllers/accounts_controller.py:3663 +#: erpnext/controllers/accounts_controller.py:3688 msgid "Row #{0}: Quantity for Item {1} cannot be zero." msgstr "" @@ -46784,7 +46922,7 @@ msgstr "" msgid "Row #{0}: Timings conflicts with row {1}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:531 +#: erpnext/assets/doctype/asset/asset.py:529 msgid "Row #{0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations" msgstr "" @@ -46796,7 +46934,7 @@ msgstr "" msgid "Row #{0}: You must select an Asset for Item {1}." msgstr "" -#: erpnext/public/js/controllers/buying.js:260 +#: erpnext/public/js/controllers/buying.js:265 msgid "Row #{0}: {1} can not be negative for item {2}" msgstr "" @@ -46820,27 +46958,27 @@ msgstr "" msgid "Row #{idx}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor." msgstr "" -#: erpnext/controllers/buying_controller.py:502 +#: erpnext/controllers/buying_controller.py:556 msgid "Row #{idx}: Item rate has been updated as per valuation rate since its an internal stock transfer." msgstr "" -#: erpnext/controllers/buying_controller.py:976 +#: erpnext/controllers/buying_controller.py:1030 msgid "Row #{idx}: Please enter a location for the asset item {item_code}." msgstr "" -#: erpnext/controllers/buying_controller.py:632 +#: erpnext/controllers/buying_controller.py:686 msgid "Row #{idx}: Received Qty must be equal to Accepted + Rejected Qty for Item {item_code}." msgstr "" -#: erpnext/controllers/buying_controller.py:645 +#: erpnext/controllers/buying_controller.py:699 msgid "Row #{idx}: {field_label} can not be negative for item {item_code}." msgstr "" -#: erpnext/controllers/buying_controller.py:591 +#: erpnext/controllers/buying_controller.py:645 msgid "Row #{idx}: {field_label} is mandatory." msgstr "" -#: erpnext/controllers/buying_controller.py:613 +#: erpnext/controllers/buying_controller.py:667 msgid "Row #{idx}: {field_label} is not allowed in Purchase Return." msgstr "" @@ -46848,7 +46986,7 @@ msgstr "" msgid "Row #{idx}: {from_warehouse_field} and {to_warehouse_field} cannot be same." msgstr "" -#: erpnext/controllers/buying_controller.py:1094 +#: erpnext/controllers/buying_controller.py:1148 msgid "Row #{idx}: {schedule_date} cannot be before {transaction_date}." msgstr "" @@ -46937,11 +47075,11 @@ 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:1256 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1257 msgid "Row {0}# Item {1} cannot be transferred more than {2} against {3} {4}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1280 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1281 msgid "Row {0}# Item {1} not found in 'Raw Materials Supplied' table in {2} {3}" msgstr "" @@ -46973,7 +47111,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:977 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:978 msgid "Row {0}: As {1} is enabled, raw materials cannot be added to {2} entry. Use {3} entry to consume raw materials." msgstr "" @@ -46989,7 +47127,7 @@ msgstr "" msgid "Row {0}: Conversion Factor is mandatory" msgstr "" -#: erpnext/controllers/accounts_controller.py:3034 +#: erpnext/controllers/accounts_controller.py:3046 msgid "Row {0}: Cost Center {1} does not belong to Company {2}" msgstr "" @@ -47009,11 +47147,11 @@ msgstr "" msgid "Row {0}: Debit entry can not be linked with a {1}" msgstr "" -#: erpnext/controllers/selling_controller.py:827 +#: erpnext/controllers/selling_controller.py:834 msgid "Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same" msgstr "" -#: erpnext/controllers/accounts_controller.py:2625 +#: erpnext/controllers/accounts_controller.py:2637 msgid "Row {0}: Due Date in the Payment Terms table cannot be before Posting Date" msgstr "" @@ -47022,12 +47160,12 @@ msgid "Row {0}: Either Delivery Note Item or Packed Item reference is mandatory. msgstr "" #: erpnext/accounts/doctype/journal_entry/journal_entry.py:1075 -#: erpnext/controllers/taxes_and_totals.py:1203 +#: erpnext/controllers/taxes_and_totals.py:1209 msgid "Row {0}: Exchange Rate is mandatory" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:493 -msgid "Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount" +#: erpnext/assets/doctype/asset/asset.py:491 +msgid "Row {0}: Expected Value After Useful Life must be less than Net Purchase Amount" msgstr "" #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:532 @@ -47055,7 +47193,7 @@ msgstr "" msgid "Row {0}: From Time and To Time of {1} is overlapping with {2}" msgstr "" -#: erpnext/controllers/stock_controller.py:1289 +#: erpnext/controllers/stock_controller.py:1313 msgid "Row {0}: From Warehouse is mandatory for internal transfers" msgstr "" @@ -47075,7 +47213,7 @@ msgstr "" msgid "Row {0}: Item Tax template updated as per validity and rate applied" msgstr "" -#: erpnext/controllers/selling_controller.py:592 +#: erpnext/controllers/selling_controller.py:599 msgid "Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer" msgstr "" @@ -47159,7 +47297,7 @@ msgstr "" msgid "Row {0}: Qty cannot be greater than {1} for the Item {2}." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:419 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:420 msgid "Row {0}: Qty in Stock UOM can not be zero." msgstr "" @@ -47171,7 +47309,7 @@ msgstr "" msgid "Row {0}: Quantity cannot be negative." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:775 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:776 msgid "Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3})" msgstr "" @@ -47179,11 +47317,11 @@ msgstr "" msgid "Row {0}: Shift cannot be changed since the depreciation has already been processed" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1293 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1294 msgid "Row {0}: Subcontracted Item is mandatory for the raw material {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:1280 +#: erpnext/controllers/stock_controller.py:1304 msgid "Row {0}: Target Warehouse is mandatory for internal transfers" msgstr "" @@ -47191,11 +47329,11 @@ msgstr "" msgid "Row {0}: Task {1} does not belong to Project {2}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:464 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:465 msgid "Row {0}: The item {1}, quantity must be positive number" msgstr "" -#: erpnext/controllers/accounts_controller.py:3011 +#: erpnext/controllers/accounts_controller.py:3023 msgid "Row {0}: The {3} Account {1} does not belong to the company {2}" msgstr "" @@ -47203,7 +47341,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:413 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:414 msgid "Row {0}: UOM Conversion Factor is mandatory" msgstr "" @@ -47240,7 +47378,7 @@ msgstr "" msgid "Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}." msgstr "" -#: erpnext/controllers/buying_controller.py:958 +#: erpnext/controllers/buying_controller.py:1012 msgid "Row {idx}: Asset Naming Series is mandatory for the auto creation of assets for item {item_code}." msgstr "" @@ -47266,7 +47404,7 @@ msgstr "" msgid "Rows with Same Account heads will be merged on Ledger" msgstr "" -#: erpnext/controllers/accounts_controller.py:2636 +#: erpnext/controllers/accounts_controller.py:2648 msgid "Rows with duplicate due dates in other rows were found: {0}" msgstr "" @@ -47274,7 +47412,7 @@ msgstr "" msgid "Rows: {0} have 'Payment Entry' as reference_type. This should not be set manually." msgstr "" -#: erpnext/controllers/accounts_controller.py:268 +#: erpnext/controllers/accounts_controller.py:273 msgid "Rows: {0} in {1} section are Invalid. Reference Name should point to a valid Payment Entry or Journal Entry." msgstr "" @@ -47373,7 +47511,7 @@ msgstr "" msgid "SO Total Qty" msgstr "" -#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html:16 +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:26 #: erpnext/accounts/report/general_ledger/general_ledger.html:60 msgid "STATEMENT OF ACCOUNTS" msgstr "" @@ -47445,8 +47583,8 @@ msgstr "" #: erpnext/regional/report/vat_audit_report/vat_audit_report.py:185 #: erpnext/selling/doctype/quotation/quotation.json #: erpnext/selling/doctype/sales_order/sales_order.json -#: erpnext/setup/doctype/company/company.py:362 -#: erpnext/setup/doctype/company/company.py:533 +#: erpnext/setup/doctype/company/company.py:365 +#: erpnext/setup/doctype/company/company.py:536 #: erpnext/setup/doctype/company/company_dashboard.py:9 #: erpnext/setup/doctype/sales_person/sales_person_dashboard.py:12 #: erpnext/setup/setup_wizard/operations/install_fixtures.py:280 @@ -47454,7 +47592,7 @@ msgstr "" msgid "Sales" msgstr "" -#: erpnext/setup/doctype/company/company.py:533 +#: erpnext/setup/doctype/company/company.py:536 msgid "Sales Account" msgstr "" @@ -47546,8 +47684,8 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice_reference/sales_invoice_reference.json #: erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html:5 #: erpnext/accounts/report/gross_profit/gross_profit.js:30 -#: erpnext/accounts/report/gross_profit/gross_profit.py:262 -#: erpnext/accounts/report/gross_profit/gross_profit.py:269 +#: erpnext/accounts/report/gross_profit/gross_profit.py:267 +#: erpnext/accounts/report/gross_profit/gross_profit.py:274 #: erpnext/accounts/workspace/accounting/accounting.json #: erpnext/accounts/workspace/receivables/receivables.json #: erpnext/crm/doctype/contract/contract.json @@ -48056,7 +48194,7 @@ msgstr "" #: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:191 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:80 #: erpnext/accounts/report/gross_profit/gross_profit.js:50 -#: erpnext/accounts/report/gross_profit/gross_profit.py:377 +#: erpnext/accounts/report/gross_profit/gross_profit.py:382 #: erpnext/crm/workspace/crm/crm.json #: erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.json #: erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.json @@ -48137,7 +48275,7 @@ msgstr "" msgid "Sales Representative" msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:876 +#: erpnext/accounts/report/gross_profit/gross_profit.py:881 #: erpnext/stock/doctype/delivery_note/delivery_note.js:273 msgid "Sales Return" msgstr "" @@ -48354,12 +48492,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:2771 +#: erpnext/public/js/controllers/transaction.js:2796 #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Sample Size" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3362 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3417 msgid "Sample quantity {0} cannot be more than received quantity {1}" msgstr "" @@ -48741,7 +48879,7 @@ msgstr "" msgid "Secretary" msgstr "" -#: erpnext/accounts/report/financial_statements.py:650 +#: erpnext/accounts/report/financial_statements.py:670 msgid "Section" msgstr "" @@ -48795,7 +48933,7 @@ msgstr "" msgid "Select Alternative Items for Sales Order" msgstr "" -#: erpnext/stock/doctype/item/item.js:646 +#: erpnext/stock/doctype/item/item.js:661 msgid "Select Attribute Values" msgstr "" @@ -48901,7 +49039,7 @@ msgstr "" msgid "Select Items based on Delivery Date" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2810 +#: erpnext/public/js/controllers/transaction.js:2835 msgid "Select Items for Quality Inspection" msgstr "" @@ -48961,7 +49099,7 @@ msgstr "" msgid "Select Supplier Address" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:137 +#: erpnext/stock/doctype/batch/batch.js:148 msgid "Select Target Warehouse" msgstr "" @@ -49018,7 +49156,7 @@ msgstr "" msgid "Select a company" msgstr "" -#: erpnext/stock/doctype/item/item.js:981 +#: erpnext/stock/doctype/item/item.js:996 msgid "Select an Item Group." msgstr "" @@ -49034,7 +49172,7 @@ msgstr "" msgid "Select an item from each set to be used in the Sales Order." msgstr "" -#: erpnext/stock/doctype/item/item.js:659 +#: erpnext/stock/doctype/item/item.js:674 msgid "Select at least one value from each of the attributes." msgstr "" @@ -49048,7 +49186,7 @@ msgstr "" msgid "Select company name first." msgstr "" -#: erpnext/controllers/accounts_controller.py:2884 +#: erpnext/controllers/accounts_controller.py:2896 msgid "Select finance book for the item {0} at row {1}" msgstr "" @@ -49187,7 +49325,7 @@ msgstr "" msgid "Selling" msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:336 +#: erpnext/accounts/report/gross_profit/gross_profit.py:341 msgid "Selling Amount" msgstr "" @@ -49421,7 +49559,7 @@ 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:2784 +#: erpnext/public/js/controllers/transaction.js:2809 #: 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 @@ -49478,7 +49616,7 @@ msgstr "" msgid "Serial No Range" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2202 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2216 msgid "Serial No Reserved" msgstr "" @@ -49525,7 +49663,7 @@ msgstr "" msgid "Serial No and Batch Traceability" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1025 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1039 msgid "Serial No is mandatory" msgstr "" @@ -49554,7 +49692,7 @@ msgstr "" msgid "Serial No {0} does not exist" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2871 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2885 msgid "Serial No {0} does not exists" msgstr "" @@ -49603,11 +49741,11 @@ msgstr "" msgid "Serial Nos and Batches" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1546 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1560 msgid "Serial Nos are created successfully" msgstr "" -#: erpnext/stock/stock_ledger.py:2177 +#: erpnext/stock/stock_ledger.py:2256 msgid "Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding." msgstr "" @@ -49681,11 +49819,11 @@ msgstr "" msgid "Serial and Batch Bundle" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1768 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1782 msgid "Serial and Batch Bundle created" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1840 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1854 msgid "Serial and Batch Bundle updated" msgstr "" @@ -49901,8 +50039,10 @@ msgstr "" msgid "Service End Date" msgstr "" +#. Label of the service_expense_account (Link) field in DocType 'Company' #. Label of the service_expense_account (Link) field in DocType 'Subcontracting #. Receipt Item' +#: erpnext/setup/doctype/company/company.json #: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json msgid "Service Expense Account" msgstr "" @@ -50047,12 +50187,12 @@ msgid "Service Stop Date" msgstr "" #: erpnext/accounts/deferred_revenue.py:44 -#: erpnext/public/js/controllers/transaction.js:1638 +#: erpnext/public/js/controllers/transaction.js:1652 msgid "Service Stop Date cannot be after Service End Date" msgstr "" #: erpnext/accounts/deferred_revenue.py:41 -#: erpnext/public/js/controllers/transaction.js:1635 +#: erpnext/public/js/controllers/transaction.js:1649 msgid "Service Stop Date cannot be before Service Start Date" msgstr "" @@ -50265,11 +50405,11 @@ msgstr "" msgid "Set by Item Tax Template" msgstr "" -#: erpnext/setup/doctype/company/company.py:452 +#: erpnext/setup/doctype/company/company.py:455 msgid "Set default inventory account for perpetual inventory" msgstr "" -#: erpnext/setup/doctype/company/company.py:462 +#: erpnext/setup/doctype/company/company.py:465 msgid "Set default {0} account for non stock items" msgstr "" @@ -50309,15 +50449,15 @@ msgstr "" msgid "Set this if the customer is a Public Administration company." msgstr "" -#: erpnext/assets/doctype/asset/asset.py:769 +#: erpnext/assets/doctype/asset/asset.py:767 msgid "Set {0} in asset category {1} for company {2}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1102 +#: erpnext/assets/doctype/asset/asset.py:1100 msgid "Set {0} in asset category {1} or company {2}" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1099 +#: erpnext/assets/doctype/asset/asset.py:1097 msgid "Set {0} in company {1}" msgstr "" @@ -50496,7 +50636,7 @@ msgstr "" msgid "Shelf Life In Days" msgstr "" -#: erpnext/stock/doctype/batch/batch.py:195 +#: erpnext/stock/doctype/batch/batch.py:208 msgid "Shelf Life in Days" msgstr "" @@ -50842,6 +50982,10 @@ msgstr "" msgid "Show Dimension Wise Stock" msgstr "" +#: erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js:29 +msgid "Show Disabled Items" +msgstr "" + #: erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js:16 msgid "Show Disabled Warehouses" msgstr "" @@ -50865,6 +51009,7 @@ msgstr "" msgid "Show GL Balance" msgstr "" +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:91 #: erpnext/accounts/report/trial_balance/trial_balance.js:116 msgid "Show Group Accounts" msgstr "" @@ -51021,6 +51166,7 @@ msgstr "" msgid "Show pending entries" msgstr "" +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:80 #: erpnext/accounts/report/trial_balance/trial_balance.js:99 msgid "Show unclosed fiscal year's P&L balances" msgstr "" @@ -51031,6 +51177,7 @@ msgstr "" #: erpnext/accounts/report/balance_sheet/balance_sheet.js:34 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js:137 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:75 #: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js:35 #: erpnext/accounts/report/profitability_analysis/profitability_analysis.js:71 #: erpnext/accounts/report/trial_balance/trial_balance.js:94 @@ -51113,7 +51260,7 @@ msgstr "" msgid "Simultaneous" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:538 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:539 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 "" @@ -51378,7 +51525,7 @@ msgstr "" msgid "Source and Target Location cannot be same" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:649 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:650 msgid "Source and target warehouse cannot be same for row {0}" msgstr "" @@ -51391,8 +51538,8 @@ msgstr "" msgid "Source of Funds (Liabilities)" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:626 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:643 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:627 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:644 msgid "Source warehouse is mandatory for row {0}" msgstr "" @@ -51432,8 +51579,8 @@ msgid "Specify conditions to calculate shipping amount" msgstr "" #: erpnext/assets/doctype/asset/asset.js:557 -#: erpnext/stock/doctype/batch/batch.js:80 -#: erpnext/stock/doctype/batch/batch.js:172 +#: erpnext/stock/doctype/batch/batch.js:91 +#: erpnext/stock/doctype/batch/batch.js:183 #: erpnext/support/doctype/issue/issue.js:114 msgid "Split" msgstr "" @@ -51443,7 +51590,7 @@ msgstr "" msgid "Split Asset" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:171 +#: erpnext/stock/doctype/batch/batch.js:182 msgid "Split Batch" msgstr "" @@ -51466,7 +51613,7 @@ msgstr "" msgid "Split Qty" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1241 +#: erpnext/assets/doctype/asset/asset.py:1239 msgid "Split Quantity must be less than Asset Quantity" msgstr "" @@ -51893,7 +52040,7 @@ msgstr "" #: erpnext/assets/doctype/asset/asset.json #: erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:16 -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:489 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:491 #: erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json #: erpnext/buying/doctype/purchase_order/purchase_order.js:381 #: erpnext/buying/doctype/purchase_order/purchase_order.js:387 @@ -51955,8 +52102,8 @@ msgstr "" #: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py:35 -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:24 -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:92 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:30 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:100 #: erpnext/projects/report/project_summary/project_summary.js:23 #: erpnext/projects/report/project_summary/project_summary.py:64 #: erpnext/projects/web_form/tasks/tasks.json @@ -51989,8 +52136,8 @@ msgstr "" #: erpnext/stock/doctype/delivery_trip/delivery_trip.json #: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/pick_list/pick_list.json -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:283 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:312 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:284 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:313 #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/quality_inspection/quality_inspection.json #: erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -52070,8 +52217,8 @@ msgstr "" #: erpnext/accounts/doctype/account/account.json #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:50 #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:73 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1344 -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1370 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1345 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1371 #: erpnext/accounts/report/account_balance/account_balance.js:58 msgid "Stock Adjustment" msgstr "" @@ -52173,7 +52320,7 @@ msgstr "" msgid "Stock Details" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:743 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:744 msgid "Stock Entries already created for Work Order {0}: {1}" msgstr "" @@ -52232,7 +52379,7 @@ msgstr "" msgid "Stock Entry has been already created against this Pick List" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:125 +#: erpnext/stock/doctype/batch/batch.js:136 msgid "Stock Entry {0} created" msgstr "" @@ -52306,8 +52453,8 @@ msgstr "" msgid "Stock Ledger Variance" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:68 -#: erpnext/stock/doctype/item/item.js:499 +#: erpnext/stock/doctype/batch/batch.js:79 +#: erpnext/stock/doctype/item/item.js:514 msgid "Stock Levels" msgstr "" @@ -52868,7 +53015,7 @@ msgstr "" msgid "Stopped Work Order cannot be cancelled, Unstop it first to cancel" msgstr "" -#: erpnext/setup/doctype/company/company.py:289 +#: erpnext/setup/doctype/company/company.py:292 #: erpnext/setup/setup_wizard/operations/defaults_setup.py:33 #: erpnext/setup/setup_wizard/operations/install_fixtures.py:511 #: erpnext/stock/doctype/item/item.py:296 @@ -53058,7 +53205,7 @@ msgstr "" #. Label of the subcontracting_order (Link) field in DocType 'Subcontracting #. Receipt Supplied Item' #: erpnext/buying/doctype/purchase_order/purchase_order.js:440 -#: erpnext/controllers/subcontracting_controller.py:1050 +#: erpnext/controllers/subcontracting_controller.py:1056 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json #: erpnext/stock/doctype/stock_entry/stock_entry.json #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json @@ -53166,7 +53313,7 @@ msgstr "" #: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task_tree.js:65 #: erpnext/projects/doctype/task_depends_on/task_depends_on.json -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:91 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:92 #: erpnext/projects/web_form/tasks/tasks.json #: erpnext/quality_management/doctype/non_conformance/non_conformance.json #: erpnext/support/doctype/issue/issue.js:108 @@ -53370,7 +53517,7 @@ msgstr "" msgid "Subscription Start Date" msgstr "" -#: erpnext/accounts/doctype/subscription/subscription.py:728 +#: erpnext/accounts/doctype/subscription/subscription.py:719 msgid "Subscription for Future dates cannot be processed." msgstr "" @@ -53778,7 +53925,7 @@ msgstr "" msgid "Supplier Invoice Date" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1737 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1738 msgid "Supplier Invoice Date cannot be greater than Posting Date" msgstr "" @@ -53793,7 +53940,7 @@ msgstr "" msgid "Supplier Invoice No" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1764 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1765 msgid "Supplier Invoice No exists in Purchase Invoice {0}" msgstr "" @@ -54365,7 +54512,7 @@ msgstr "" msgid "TDS Computation Summary" msgstr "" -#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1521 +#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1522 msgid "TDS Deducted" msgstr "" @@ -54581,12 +54728,12 @@ msgstr "" msgid "Target Warehouse is required before Submit" msgstr "" -#: erpnext/controllers/selling_controller.py:833 +#: erpnext/controllers/selling_controller.py:840 msgid "Target Warehouse is set for some items but the customer is not an internal customer." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:632 -#: erpnext/stock/doctype/stock_entry/stock_entry.py:639 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:633 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:640 msgid "Target warehouse is mandatory for row {0}" msgstr "" @@ -54621,7 +54768,7 @@ msgstr "" #: erpnext/projects/doctype/task_depends_on/task_depends_on.json #: erpnext/projects/doctype/timesheet_detail/timesheet_detail.json #: erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py:33 -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:90 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py:91 #: erpnext/projects/web_form/tasks/tasks.json #: erpnext/projects/workspace/projects/projects.json #: erpnext/public/js/projects/timer.js:15 @@ -55026,7 +55173,7 @@ msgstr "" #. Label of the taxable_amount (Currency) field in DocType 'Tax Withheld #. Vouchers' #: erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json -#: erpnext/controllers/taxes_and_totals.py:1123 +#: erpnext/controllers/taxes_and_totals.py:1129 msgid "Taxable Amount" msgstr "" @@ -55458,7 +55605,7 @@ msgstr "" #: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py:182 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:68 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:221 -#: erpnext/accounts/report/gross_profit/gross_profit.py:411 +#: erpnext/accounts/report/gross_profit/gross_profit.py:416 #: erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js:8 #: erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py:21 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:262 @@ -55557,8 +55704,8 @@ msgstr "" msgid "The BOM which will be replaced" msgstr "" -#: erpnext/stock/serial_batch_bundle.py:1393 -msgid "The Batch {0} has negative quantity {1}. Please correct the quantity." +#: erpnext/stock/serial_batch_bundle.py:1403 +msgid "The Batch {0} has negative batch quantity {1}. To fix this, go to the batch and click on Recalculate Batch Qty. If the issue still persists, create an inward entry." msgstr "" #: erpnext/crm/doctype/email_campaign/email_campaign.py:71 @@ -55597,7 +55744,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:2214 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:2264 msgid "The Process Loss Qty has reset as per job cards Process Loss Qty" msgstr "" @@ -55609,11 +55756,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:2199 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:2213 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:1459 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1460 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 "" @@ -55621,7 +55768,7 @@ msgstr "" msgid "The Stock Entry of type 'Manufacture' is known as backflush. Raw materials being consumed to manufacture finished goods is known as backflushing.

When creating Manufacture Entry, raw-material items are backflushed based on BOM of production item. If you want raw-material items to be backflushed based on Material Transfer entry made against that Work Order instead, then you can set it under this field." msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1929 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1930 msgid "The Work Order is mandatory for Disassembly Order" msgstr "" @@ -55721,7 +55868,7 @@ msgstr "" msgid "The holiday on {0} is not between From Date and To Date" msgstr "" -#: erpnext/controllers/buying_controller.py:1161 +#: erpnext/controllers/buying_controller.py:1215 msgid "The item {item} is not marked as {type_of} item. You can enable it as {type_of} item from its Item master." msgstr "" @@ -55729,7 +55876,7 @@ msgstr "" msgid "The items {0} and {1} are present in the following {2} :" msgstr "" -#: erpnext/controllers/buying_controller.py:1154 +#: erpnext/controllers/buying_controller.py:1208 msgid "The items {items} are not marked as {type_of} item. You can enable them as {type_of} item from their Item masters." msgstr "" @@ -55840,7 +55987,7 @@ msgstr "" msgid "The serial and batch bundle {0} not linked to {1} {2}" msgstr "" -#: erpnext/stock/doctype/batch/batch.py:412 +#: erpnext/stock/doctype/batch/batch.py:425 msgid "The serial no {0} does not belong to item {1}" msgstr "" @@ -55895,7 +56042,7 @@ msgstr "" msgid "The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3}" msgstr "" -#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:121 +#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py:153 msgid "The uploaded file does not appear to be in valid MT940 format." msgstr "" @@ -55943,7 +56090,7 @@ msgstr "" msgid "The {0} ({1}) must be equal to {2} ({3})" msgstr "" -#: erpnext/public/js/controllers/transaction.js:3216 +#: erpnext/public/js/controllers/transaction.js:3256 msgid "The {0} contains Unit Price Items." msgstr "" @@ -55963,7 +56110,7 @@ msgstr "" msgid "Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc." msgstr "" -#: erpnext/assets/doctype/asset/asset.py:598 +#: erpnext/assets/doctype/asset/asset.py:596 msgid "There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset." msgstr "" @@ -55987,7 +56134,7 @@ msgstr "" msgid "There are no slots available on this date" msgstr "" -#: erpnext/stock/doctype/item/item.js:1005 +#: erpnext/stock/doctype/item/item.js:1020 msgid "There are two options to maintain valuation of stock. FIFO (first in - first out) and Moving Average. To understand this topic in detail please visit Item Valuation, FIFO and Moving Average." msgstr "" @@ -56015,11 +56162,11 @@ msgstr "" msgid "There is already an active Subcontracting BOM {0} for the Finished Good {1}." msgstr "" -#: erpnext/stock/doctype/batch/batch.py:420 +#: erpnext/stock/doctype/batch/batch.py:433 msgid "There is no batch found against the {0}: {1}" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:1396 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:1397 msgid "There must be atleast 1 Finished Good in this Stock Entry" msgstr "" @@ -56098,7 +56245,7 @@ msgstr "" msgid "This covers all scorecards tied to this Setup" msgstr "" -#: erpnext/controllers/status_updater.py:391 +#: erpnext/controllers/status_updater.py:447 msgid "This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?" msgstr "" @@ -56197,7 +56344,7 @@ msgstr "" msgid "This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox." msgstr "" -#: erpnext/stock/doctype/item/item.js:993 +#: erpnext/stock/doctype/item/item.js:1008 msgid "This is for raw material Items that'll be used to create finished goods. If the Item is an additional service like 'washing' that'll be used in the BOM, keep this unchecked." msgstr "" @@ -56241,7 +56388,7 @@ msgstr "" msgid "This schedule was created when Asset {0} was scrapped." msgstr "" -#: erpnext/assets/doctype/asset/asset.py:1375 +#: erpnext/assets/doctype/asset/asset.py:1373 msgid "This schedule was created when Asset {0} was {1} into new Asset {2}." msgstr "" @@ -56289,7 +56436,7 @@ msgstr "" msgid "This will restrict user access to other employee records" msgstr "" -#: erpnext/controllers/selling_controller.py:834 +#: erpnext/controllers/selling_controller.py:841 msgid "This {} will be treated as material transfer." msgstr "" @@ -56645,6 +56792,7 @@ msgstr "" #: erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js:23 #: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js:23 #: erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js:15 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:45 #: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js:24 #: erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js:44 #: erpnext/accounts/report/financial_ratios/financial_ratios.js:48 @@ -56698,7 +56846,7 @@ msgstr "" #: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js:14 #: erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js:23 #: erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.js:14 -#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:13 +#: erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js:19 #: erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js:34 #: erpnext/public/js/stock_analytics.js:75 #: erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js:15 @@ -56945,7 +57093,7 @@ msgid "To Value" msgstr "" #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:224 -#: erpnext/stock/doctype/batch/batch.js:103 +#: erpnext/stock/doctype/batch/batch.js:114 msgid "To Warehouse" msgstr "" @@ -56962,11 +57110,11 @@ msgstr "" msgid "To add subcontracted Item's raw materials if include exploded items is disabled." msgstr "" -#: erpnext/controllers/status_updater.py:386 +#: erpnext/controllers/status_updater.py:442 msgid "To allow over billing, update \"Over Billing Allowance\" in Accounts Settings or the Item." msgstr "" -#: erpnext/controllers/status_updater.py:382 +#: erpnext/controllers/status_updater.py:438 msgid "To allow over receipt / delivery, update \"Over Receipt/Delivery Allowance\" in Stock Settings or the Item." msgstr "" @@ -57009,7 +57157,7 @@ msgid "To include sub-assembly costs and scrap items in Finished Goods on a work msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.py:2308 -#: erpnext/controllers/accounts_controller.py:3044 +#: erpnext/controllers/accounts_controller.py:3056 msgid "To include tax in row {0} in Item rate, taxes in rows {1} must also be included" msgstr "" @@ -57042,9 +57190,9 @@ msgstr "" msgid "To use a different finance book, please uncheck 'Include Default FB Assets'" msgstr "" -#: erpnext/accounts/report/financial_statements.py:604 +#: erpnext/accounts/report/financial_statements.py:624 #: erpnext/accounts/report/general_ledger/general_ledger.py:310 -#: erpnext/accounts/report/trial_balance/trial_balance.py:288 +#: erpnext/accounts/report/trial_balance/trial_balance.py:319 msgid "To use a different finance book, please uncheck 'Include Default FB Entries'" msgstr "" @@ -57134,15 +57282,17 @@ msgstr "" #: erpnext/accounts/report/accounts_receivable/accounts_receivable.html:74 #: erpnext/accounts/report/accounts_receivable/accounts_receivable.html:241 #: erpnext/accounts/report/accounts_receivable/accounts_receivable.html:279 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:247 +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.py:248 #: erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py:230 -#: erpnext/accounts/report/financial_statements.py:700 +#: erpnext/accounts/report/financial_statements.py:720 #: erpnext/accounts/report/general_ledger/general_ledger.html:138 #: erpnext/accounts/report/general_ledger/general_ledger.py:398 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:701 #: erpnext/accounts/report/profitability_analysis/profitability_analysis.py:94 #: erpnext/accounts/report/profitability_analysis/profitability_analysis.py:99 -#: erpnext/accounts/report/trial_balance/trial_balance.py:355 -#: erpnext/accounts/report/trial_balance/trial_balance.py:356 +#: erpnext/accounts/report/trial_balance/trial_balance.py:404 +#: erpnext/accounts/report/trial_balance/trial_balance.py:405 #: erpnext/buying/doctype/purchase_order/purchase_order.json #: erpnext/buying/doctype/supplier_quotation/supplier_quotation.json #: erpnext/crm/doctype/opportunity/opportunity.json @@ -57651,7 +57801,7 @@ msgstr "" msgid "Total Paid Amount" msgstr "" -#: erpnext/controllers/accounts_controller.py:2690 +#: erpnext/controllers/accounts_controller.py:2702 msgid "Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total" msgstr "" @@ -57970,8 +58120,8 @@ msgstr "" #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py:746 #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py:747 -#: erpnext/accounts/report/financial_statements.py:346 -#: erpnext/accounts/report/financial_statements.py:347 +#: erpnext/accounts/report/financial_statements.py:352 +#: erpnext/accounts/report/financial_statements.py:353 msgid "Total {0} ({1})" msgstr "" @@ -58728,7 +58878,7 @@ msgstr "" msgid "UOM Name" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:3284 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:3339 msgid "UOM conversion factor required for UOM: {0} in Item: {1}" msgstr "" @@ -59303,7 +59453,7 @@ msgstr "" msgid "Update latest price in all BOMs" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:405 +#: erpnext/assets/doctype/asset/asset.py:403 msgid "Update stock must be enabled for the purchase invoice {0}" msgstr "" @@ -59319,7 +59469,7 @@ msgstr "" msgid "Update timestamp on new communication" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:539 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:546 msgid "Updated successfully" msgstr "" @@ -59566,7 +59716,7 @@ msgstr "" msgid "User Details" msgstr "" -#: erpnext/setup/install.py:153 +#: erpnext/setup/install.py:187 msgid "User Forum" msgstr "" @@ -59885,7 +60035,7 @@ msgstr "" #. Label of the valuation_rate (Currency) field in DocType 'Stock #. Reconciliation Item' #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json -#: erpnext/accounts/report/gross_profit/gross_profit.py:329 +#: erpnext/accounts/report/gross_profit/gross_profit.py:334 #: erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json #: erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json #: erpnext/manufacturing/doctype/bom/bom.json @@ -59912,11 +60062,11 @@ msgstr "" msgid "Valuation Rate (In / Out)" msgstr "" -#: erpnext/stock/stock_ledger.py:1919 +#: erpnext/stock/stock_ledger.py:1998 msgid "Valuation Rate Missing" msgstr "" -#: erpnext/stock/stock_ledger.py:1897 +#: erpnext/stock/stock_ledger.py:1976 msgid "Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}." msgstr "" @@ -59948,7 +60098,7 @@ msgid "Valuation rate for the item as per Sales Invoice (Only for Internal Trans msgstr "" #: erpnext/accounts/doctype/payment_entry/payment_entry.py:2332 -#: erpnext/controllers/accounts_controller.py:3068 +#: erpnext/controllers/accounts_controller.py:3080 msgid "Valuation type charges can not be marked as Inclusive" msgstr "" @@ -60028,8 +60178,8 @@ msgstr "" msgid "Value Proposition" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:589 -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:619 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:597 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:627 msgid "Value as on" msgstr "" @@ -60042,19 +60192,19 @@ msgstr "" msgid "Value of Goods" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:613 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:621 msgid "Value of New Capitalized Asset" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:595 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:603 msgid "Value of New Purchase" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:607 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:615 msgid "Value of Scrapped Asset" msgstr "" -#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:601 +#: erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py:609 msgid "Value of Sold Asset" msgstr "" @@ -60152,7 +60302,7 @@ msgstr "" msgid "Variant Of" msgstr "" -#: erpnext/stock/doctype/item/item.js:683 +#: erpnext/stock/doctype/item/item.js:698 msgid "Variant creation has been queued." msgstr "" @@ -60204,7 +60354,7 @@ msgstr "" msgid "Vendor Invoices" msgstr "" -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:539 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:541 msgid "Vendor Name" msgstr "" @@ -60297,8 +60447,8 @@ msgstr "" #: erpnext/stock/doctype/item/item.js:142 #: erpnext/stock/doctype/item/item.js:150 #: erpnext/stock/doctype/item/item.js:158 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:232 -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:243 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:233 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.js:244 #: erpnext/stock/doctype/stock_entry/stock_entry.js:297 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:46 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:62 @@ -60499,7 +60649,7 @@ msgstr "" msgid "Voucher No" msgstr "" -#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1251 +#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1265 msgid "Voucher No is mandatory" msgstr "" @@ -60698,7 +60848,7 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json #: erpnext/accounts/report/gross_profit/gross_profit.js:56 -#: erpnext/accounts/report/gross_profit/gross_profit.py:314 +#: erpnext/accounts/report/gross_profit/gross_profit.py:319 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js:41 #: erpnext/accounts/report/purchase_register/purchase_register.js:52 #: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:28 @@ -60933,7 +61083,7 @@ msgstr "" msgid "Warehouse {0} is not allowed for Sales Order {1}, it should be {2}" msgstr "" -#: erpnext/controllers/stock_controller.py:661 +#: erpnext/controllers/stock_controller.py:685 msgid "Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}." msgstr "" @@ -61051,6 +61201,7 @@ msgid "Warning on Negative Stock" msgstr "" #: erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py:114 +#: erpnext/stock/serial_batch_bundle.py:1406 msgid "Warning!" msgstr "" @@ -61412,7 +61563,7 @@ msgstr "" msgid "When a parent warehouse is chosen, the system conducts Project Qty checks against the associated child warehouses" msgstr "" -#: erpnext/stock/doctype/item/item.js:1012 +#: erpnext/stock/doctype/item/item.js:1027 msgid "When creating an Item, entering a value for this field will automatically create an Item Price at the backend." msgstr "" @@ -61473,6 +61624,7 @@ msgstr "" msgid "With Operations" msgstr "" +#: erpnext/accounts/report/consolidated_trial_balance/consolidated_trial_balance.js:63 #: erpnext/accounts/report/trial_balance/trial_balance.js:82 msgid "With Period Closing Entry For Opening Balances" msgstr "" @@ -61497,7 +61649,7 @@ msgstr "" #: erpnext/assets/doctype/asset/asset_list.js:12 #: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card_operation/job_card_operation.json -#: erpnext/setup/doctype/company/company.py:290 +#: erpnext/setup/doctype/company/company.py:293 #: erpnext/support/doctype/warranty_claim/warranty_claim.json msgid "Work In Progress" msgstr "" @@ -61615,7 +61767,7 @@ msgstr "" msgid "Work Order {0} created" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:693 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:694 msgid "Work Order {0}: Job Card not found for the operation {1}" msgstr "" @@ -61803,7 +61955,7 @@ msgstr "" #: erpnext/accounts/doctype/pos_invoice/pos_invoice.json #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json -#: erpnext/setup/doctype/company/company.py:551 +#: erpnext/setup/doctype/company/company.py:554 msgid "Write Off" msgstr "" @@ -62039,11 +62191,11 @@ msgstr "" msgid "You are importing data for the code list:" msgstr "" -#: erpnext/controllers/accounts_controller.py:3650 +#: erpnext/controllers/accounts_controller.py:3675 msgid "You are not allowed to update as per the conditions set in {} Workflow." msgstr "" -#: erpnext/accounts/general_ledger.py:782 +#: erpnext/accounts/general_ledger.py:783 msgid "You are not authorized to add or update entries before {0}" msgstr "" @@ -62116,15 +62268,15 @@ msgstr "" msgid "You cannot change the rate if BOM is mentioned against any Item." msgstr "" -#: erpnext/accounts/doctype/accounting_period/accounting_period.py:128 +#: erpnext/accounts/doctype/accounting_period/accounting_period.py:130 msgid "You cannot create a {0} within the closed Accounting Period {1}" msgstr "" -#: erpnext/accounts/general_ledger.py:176 +#: erpnext/accounts/general_ledger.py:177 msgid "You cannot create or cancel any accounting entries with in the closed Accounting Period {0}" msgstr "" -#: erpnext/accounts/general_ledger.py:802 +#: erpnext/accounts/general_ledger.py:803 msgid "You cannot create/amend any accounting entries till this date." msgstr "" @@ -62152,7 +62304,7 @@ msgstr "" msgid "You cannot repost item valuation before {}" msgstr "" -#: erpnext/accounts/doctype/subscription/subscription.py:712 +#: erpnext/accounts/doctype/subscription/subscription.py:703 msgid "You cannot restart a Subscription that is not cancelled." msgstr "" @@ -62168,7 +62320,7 @@ msgstr "" msgid "You cannot {0} this document because another Period Closing Entry {1} exists after {2}" msgstr "" -#: erpnext/controllers/accounts_controller.py:3626 +#: erpnext/controllers/accounts_controller.py:3651 msgid "You do not have permissions to {} items in a {}." msgstr "" @@ -62224,7 +62376,7 @@ msgstr "" msgid "You need to cancel POS Closing Entry {} to be able to cancel this document." msgstr "" -#: erpnext/controllers/accounts_controller.py:3019 +#: erpnext/controllers/accounts_controller.py:3031 msgid "You selected the account group {1} as {2} Account in row {0}. Please select a single account." msgstr "" @@ -62280,7 +62432,7 @@ msgstr "" msgid "Zero Rated" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:419 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:420 msgid "Zero quantity" msgstr "" @@ -62293,11 +62445,11 @@ msgstr "" msgid "[Important] [ERPNext] Auto Reorder Errors" msgstr "" -#: erpnext/controllers/status_updater.py:283 +#: erpnext/controllers/status_updater.py:285 msgid "`Allow Negative rates for Items`" msgstr "" -#: erpnext/stock/stock_ledger.py:1911 +#: erpnext/stock/stock_ledger.py:1990 msgid "after" msgstr "" @@ -62512,7 +62664,7 @@ msgstr "" msgid "per hour" msgstr "" -#: erpnext/stock/stock_ledger.py:1912 +#: erpnext/stock/stock_ledger.py:1991 msgid "performing either one below:" msgstr "" @@ -62580,12 +62732,12 @@ msgstr "" msgid "sold" msgstr "" -#: erpnext/accounts/doctype/subscription/subscription.py:688 +#: erpnext/accounts/doctype/subscription/subscription.py:679 msgid "subscription is already cancelled." msgstr "" -#: erpnext/controllers/status_updater.py:394 -#: erpnext/controllers/status_updater.py:414 +#: erpnext/controllers/status_updater.py:450 +#: erpnext/controllers/status_updater.py:470 msgid "target_ref_field" msgstr "" @@ -62707,7 +62859,7 @@ msgstr "" msgid "{0} account is not of type {1}" msgstr "" -#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:498 +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:499 msgid "{0} account not found while submitting purchase receipt" msgstr "" @@ -62765,7 +62917,7 @@ msgstr "" msgid "{0} created" msgstr "" -#: erpnext/setup/doctype/company/company.py:198 +#: erpnext/setup/doctype/company/company.py:201 msgid "{0} currency must be same as company's default currency. Please select another account." msgstr "" @@ -62807,7 +62959,7 @@ msgstr "" msgid "{0} hours" msgstr "" -#: erpnext/controllers/accounts_controller.py:2630 +#: erpnext/controllers/accounts_controller.py:2642 msgid "{0} in row {1}" msgstr "" @@ -62843,15 +62995,15 @@ msgid "{0} is mandatory for Item {1}" msgstr "" #: erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py:99 -#: erpnext/accounts/general_ledger.py:826 +#: erpnext/accounts/general_ledger.py:827 msgid "{0} is mandatory for account {1}" msgstr "" -#: erpnext/public/js/controllers/taxes_and_totals.js:128 +#: erpnext/public/js/controllers/taxes_and_totals.js:129 msgid "{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}" msgstr "" -#: erpnext/controllers/accounts_controller.py:2976 +#: erpnext/controllers/accounts_controller.py:2988 msgid "{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}." msgstr "" @@ -62863,7 +63015,7 @@ msgstr "" msgid "{0} is not a group node. Please select a group node as parent cost center" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.py:470 +#: erpnext/stock/doctype/stock_entry/stock_entry.py:471 msgid "{0} is not a stock Item" msgstr "" @@ -62902,7 +63054,7 @@ msgstr "" msgid "{0} is required" msgstr "" -#: erpnext/assets/doctype/asset/asset.py:434 +#: erpnext/assets/doctype/asset/asset.py:432 msgid "{0} is still in Draft. Please submit it before saving the Asset." msgstr "" @@ -62938,7 +63090,7 @@ msgstr "" msgid "{0} payment entries can not be filtered by {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:1463 +#: erpnext/controllers/stock_controller.py:1487 msgid "{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}." msgstr "" @@ -62962,16 +63114,16 @@ msgstr "" msgid "{0} units of {1} are required in {2} with the inventory dimension: {3} ({4}) on {5} {6} for {7} to complete the transaction." msgstr "" -#: erpnext/stock/stock_ledger.py:1558 erpnext/stock/stock_ledger.py:2063 -#: erpnext/stock/stock_ledger.py:2077 +#: erpnext/stock/stock_ledger.py:1637 erpnext/stock/stock_ledger.py:2142 +#: erpnext/stock/stock_ledger.py:2156 msgid "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction." msgstr "" -#: erpnext/stock/stock_ledger.py:2164 erpnext/stock/stock_ledger.py:2209 +#: erpnext/stock/stock_ledger.py:2243 erpnext/stock/stock_ledger.py:2288 msgid "{0} units of {1} needed in {2} on {3} {4} to complete this transaction." msgstr "" -#: erpnext/stock/stock_ledger.py:1552 +#: erpnext/stock/stock_ledger.py:1631 msgid "{0} units of {1} needed in {2} to complete this transaction." msgstr "" @@ -62983,7 +63135,7 @@ msgstr "" msgid "{0} valid serial nos for Item {1}" msgstr "" -#: erpnext/stock/doctype/item/item.js:688 +#: erpnext/stock/doctype/item/item.js:703 msgid "{0} variants created." msgstr "" @@ -63056,7 +63208,7 @@ msgid "{0} {1} is associated with {2}, but Party Account is {3}" msgstr "" #: erpnext/controllers/selling_controller.py:504 -#: erpnext/controllers/subcontracting_controller.py:1050 +#: erpnext/controllers/subcontracting_controller.py:1056 msgid "{0} {1} is cancelled or closed" msgstr "" @@ -63144,7 +63296,7 @@ msgstr "" msgid "{0} {1}: Accounting Entry for {2} can only be made in currency: {3}" msgstr "" -#: erpnext/controllers/stock_controller.py:793 +#: erpnext/controllers/stock_controller.py:817 msgid "{0} {1}: Cost Center is mandatory for Item {2}" msgstr "" @@ -63210,27 +63362,27 @@ msgstr "" msgid "{0}: {1} must be less than {2}" msgstr "" -#: erpnext/controllers/buying_controller.py:935 +#: erpnext/controllers/buying_controller.py:989 msgid "{count} Assets created for {item_code}" msgstr "" -#: erpnext/controllers/buying_controller.py:833 +#: erpnext/controllers/buying_controller.py:887 msgid "{doctype} {name} is cancelled or closed." msgstr "" -#: erpnext/controllers/buying_controller.py:554 +#: erpnext/controllers/buying_controller.py:608 msgid "{field_label} is mandatory for sub-contracted {doctype}." msgstr "" -#: erpnext/controllers/stock_controller.py:1744 +#: erpnext/controllers/stock_controller.py:1768 msgid "{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})" msgstr "" -#: erpnext/controllers/buying_controller.py:658 +#: erpnext/controllers/buying_controller.py:712 msgid "{ref_doctype} {ref_name} is {status}." msgstr "" -#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:430 +#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.py:432 msgid "{}" msgstr "" diff --git a/erpnext/locale/my.po b/erpnext/locale/my.po new file mode 100644 index 00000000000..76e46cd8b02 --- /dev/null +++ b/erpnext/locale/my.po @@ -0,0 +1,63249 @@ +msgid "" +msgstr "" +"Project-Id-Version: frappe\n" +"Report-Msgid-Bugs-To: hello@frappe.io\n" +"POT-Creation-Date: 2025-10-05 09:35+0000\n" +"PO-Revision-Date: 2025-10-11 11:22\n" +"Last-Translator: hello@frappe.io\n" +"Language-Team: Burmese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Crowdin-Project: frappe\n" +"X-Crowdin-Project-ID: 639578\n" +"X-Crowdin-Language: my\n" +"X-Crowdin-File: /[frappe.erpnext] develop/erpnext/locale/main.pot\n" +"X-Crowdin-File-ID: 46\n" +"Language: my_MM\n" + +#. Label of the column_break_32 (Column Break) field in DocType 'Email Digest' +#: erpnext/setup/doctype/email_digest/email_digest.json +msgid " " +msgstr "" + +#: erpnext/selling/doctype/quotation/quotation.js:73 +msgid " Address" +msgstr " လိပ်စာ" + +#: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:677 +msgid " Amount" +msgstr " ပမာဏ" + +#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:114 +msgid " BOM" +msgstr "" + +#. Label of the istable (Check) field in DocType 'Inventory Dimension' +#: erpnext/stock/doctype/inventory_dimension/inventory_dimension.json +msgid " Is Child Table" +msgstr "" + +#. Label of the is_subcontracted (Check) field in DocType 'Job Card' +#: erpnext/manufacturing/doctype/job_card/job_card.json +msgid " Is Subcontracted" +msgstr "" + +#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:174 +msgid " Item" +msgstr " ပစ္စည်း" + +#: erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py:147 +#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:192 +#: erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py:107 +#: erpnext/selling/report/sales_analytics/sales_analytics.py:128 +msgid " Name" +msgstr " အမည်" + +#: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:668 +msgid " Rate" +msgstr " နှုန်း" + +#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:122 +msgid " Raw Material" +msgstr " ကုန်ကြမ်း" + +#. Label of the reserve_stock (Check) field in DocType 'Work Order' +#: erpnext/manufacturing/doctype/work_order/work_order.json +msgid " Reserve Stock" +msgstr "" + +#. Label of the skip_material_transfer (Check) field in DocType 'BOM Operation' +#: erpnext/manufacturing/doctype/bom_operation/bom_operation.json +msgid " Skip Material Transfer" +msgstr "" + +#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:133 +#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:163 +msgid " Sub Assembly" +msgstr "" + +#: erpnext/projects/doctype/project_update/project_update.py:104 +msgid " Summary" +msgstr "" + +#: erpnext/stock/doctype/item/item.py:249 +msgid "\"Customer Provided Item\" cannot be Purchase Item also" +msgstr "" + +#: erpnext/stock/doctype/item/item.py:251 +msgid "\"Customer Provided Item\" cannot have Valuation Rate" +msgstr "" + +#: erpnext/stock/doctype/item/item.py:327 +msgid "\"Is Fixed Asset\" cannot be unchecked, as Asset record exists against the item" +msgstr "" + +#: erpnext/public/js/utils/serial_no_batch_selector.js:262 +msgid "\"SN-01::10\" for \"SN-01\" to \"SN-10\"" +msgstr "" + +#: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:148 +msgid "# In Stock" +msgstr "" + +#: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:141 +msgid "# Req'd Items" +msgstr "" + +#. Label of the per_delivered (Percent) field in DocType 'Sales Order' +#: erpnext/selling/doctype/sales_order/sales_order.json +msgid "% Delivered" +msgstr "" + +#. Label of the per_billed (Percent) field in DocType 'Timesheet' +#. Label of the per_billed (Percent) field in DocType 'Sales Order' +#. Label of the per_billed (Percent) field in DocType 'Delivery Note' +#. Label of the per_billed (Percent) field in DocType 'Purchase Receipt' +#: erpnext/projects/doctype/timesheet/timesheet.json +#: erpnext/selling/doctype/sales_order/sales_order.json +#: erpnext/stock/doctype/delivery_note/delivery_note.json +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +msgid "% Amount Billed" +msgstr "" + +#. Label of the per_billed (Percent) field in DocType 'Purchase Order' +#: erpnext/buying/doctype/purchase_order/purchase_order.json +msgid "% Billed" +msgstr "" + +#. Label of the percent_complete_method (Select) field in DocType 'Project' +#: erpnext/projects/doctype/project/project.json +msgid "% Complete Method" +msgstr "" + +#. Label of the percent_complete (Percent) field in DocType 'Project' +#: erpnext/projects/doctype/project/project.json +msgid "% Completed" +msgstr "" + +#. Label of the per_delivered (Percent) field in DocType 'Pick List' +#: erpnext/stock/doctype/pick_list/pick_list.json +msgid "% Delivered" +msgstr "" + +#: erpnext/manufacturing/doctype/bom/bom.js:920 +#, python-format +msgid "% Finished Item Quantity" +msgstr "" + +#. Label of the per_installed (Percent) field in DocType 'Delivery Note' +#: erpnext/stock/doctype/delivery_note/delivery_note.json +msgid "% Installed" +msgstr "" + +#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:70 +#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html:16 +msgid "% Occupied" +msgstr "" + +#: erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py:285 +#: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:340 +msgid "% Of Grand Total" +msgstr "" + +#. Label of the per_ordered (Percent) field in DocType 'Material Request' +#: erpnext/stock/doctype/material_request/material_request.json +msgid "% Ordered" +msgstr "" + +#. Label of the per_picked (Percent) field in DocType 'Sales Order' +#: erpnext/selling/doctype/sales_order/sales_order.json +msgid "% Picked" +msgstr "" + +#. Label of the process_loss_percentage (Percent) field in DocType 'BOM' +#. Label of the process_loss_percentage (Percent) field in DocType 'Stock +#. Entry' +#: erpnext/manufacturing/doctype/bom/bom.json +#: erpnext/stock/doctype/stock_entry/stock_entry.json +msgid "% Process Loss" +msgstr "" + +#. Label of the progress (Percent) field in DocType 'Task' +#: erpnext/projects/doctype/task/task.json +msgid "% Progress" +msgstr "" + +#. Label of the per_received (Percent) field in DocType 'Purchase Order' +#. Label of the per_received (Percent) field in DocType 'Material Request' +#. Label of the per_received (Percent) field in DocType 'Subcontracting Order' +#: erpnext/buying/doctype/purchase_order/purchase_order.json +#: erpnext/stock/doctype/material_request/material_request.json +#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json +msgid "% Received" +msgstr "" + +#. Label of the per_returned (Percent) field in DocType 'Delivery Note' +#. Label of the per_returned (Percent) field in DocType 'Purchase Receipt' +#. Label of the per_returned (Percent) field in DocType 'Subcontracting +#. Receipt' +#: erpnext/stock/doctype/delivery_note/delivery_note.json +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +msgid "% Returned" +msgstr "" + +#. Description of the '% Amount Billed' (Percent) field in DocType 'Sales +#. Order' +#: erpnext/selling/doctype/sales_order/sales_order.json +#, python-format +msgid "% of materials billed against this Sales Order" +msgstr "" + +#. Description of the '% Delivered' (Percent) field in DocType 'Pick List' +#: erpnext/stock/doctype/pick_list/pick_list.json +#, python-format +msgid "% of materials delivered against this Pick List" +msgstr "" + +#. Description of the '% Delivered' (Percent) field in DocType 'Sales Order' +#: erpnext/selling/doctype/sales_order/sales_order.json +#, python-format +msgid "% of materials delivered against this Sales Order" +msgstr "" + +#: erpnext/controllers/accounts_controller.py:2293 +msgid "'Account' in the Accounting section of Customer {0}" +msgstr "" + +#: erpnext/selling/doctype/sales_order/sales_order.py:299 +msgid "'Allow Multiple Sales Orders Against a Customer's Purchase Order'" +msgstr "" + +#: erpnext/controllers/trends.py:56 +msgid "'Based On' and 'Group By' can not be same" +msgstr "" + +#: erpnext/selling/report/inactive_customers/inactive_customers.py:18 +msgid "'Days Since Last Order' must be greater than or equal to zero" +msgstr "" + +#: erpnext/controllers/accounts_controller.py:2298 +msgid "'Default {0} Account' in Company {1}" +msgstr "" + +#: erpnext/accounts/doctype/journal_entry/journal_entry.py:1283 +msgid "'Entries' cannot be empty" +msgstr "" + +#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:24 +#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:127 +#: erpnext/stock/report/stock_analytics/stock_analytics.py:313 +msgid "'From Date' is required" +msgstr "'နေ့စွဲမှ' ကို ထည့်သွင်းရန် လိုအပ်သည်" + +#: erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py:18 +msgid "'From Date' must be after 'To Date'" +msgstr "" + +#: erpnext/stock/doctype/item/item.py:410 +msgid "'Has Serial No' can not be 'Yes' for non-stock item" +msgstr "" + +#: erpnext/stock/doctype/quality_inspection/quality_inspection.py:139 +msgid "'Inspection Required before Delivery' has disabled for the item {0}, no need to create the QI" +msgstr "" + +#: erpnext/stock/doctype/quality_inspection/quality_inspection.py:130 +msgid "'Inspection Required before Purchase' has disabled for the item {0}, no need to create the QI" +msgstr "" + +#: erpnext/stock/report/stock_ledger/stock_ledger.py:598 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:631 +msgid "'Opening'" +msgstr "စာရင်းဖွင့်" + +#: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:27 +#: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:129 +#: erpnext/stock/report/stock_analytics/stock_analytics.py:319 +msgid "'To Date' is required" +msgstr "'နေ့စွဲအထိ' ကို ထည့်သွင်းရန် လိုအပ်သည်" + +#: erpnext/stock/doctype/packing_slip/packing_slip.py:95 +msgid "'To Package No.' cannot be less than 'From Package No.'" +msgstr "" + +#: erpnext/controllers/sales_and_purchase_return.py:81 +msgid "'Update Stock' can not be checked because items are not delivered via {0}" +msgstr "" + +#: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:381 +msgid "'Update Stock' cannot be checked for fixed asset sale" +msgstr "" + +#: erpnext/accounts/doctype/bank_account/bank_account.py:64 +msgid "'{0}' account is already used by {1}. Use another account." +msgstr "" + +#: erpnext/accounts/doctype/pos_settings/pos_settings.py:43 +msgid "'{0}' has been already added." +msgstr "" + +#: erpnext/setup/doctype/company/company.py:210 +#: erpnext/setup/doctype/company/company.py:221 +msgid "'{0}' should be in company currency {1}." +msgstr "" + +#: erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py:174 +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:203 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:106 +msgid "(A) Qty After Transaction" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:208 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:111 +msgid "(B) Expected Qty After Transaction" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:223 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:126 +msgid "(C) Total Qty in Queue" +msgstr "" + +#: erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py:184 +msgid "(C) Total qty in queue" +msgstr "" + +#: erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py:194 +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:233 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:136 +msgid "(D) Balance Stock Value" +msgstr "" + +#: erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py:199 +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:238 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:141 +msgid "(E) Balance Stock Value in Queue" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:248 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:151 +msgid "(F) Change in Stock Value" +msgstr "" + +#: erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py:192 +msgid "(Forecast)" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:253 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:156 +msgid "(G) Sum of Change in Stock Value" +msgstr "" + +#. Description of the 'Daily Yield' (Percent) field in DocType 'Item Lead Time' +#: erpnext/stock/doctype/item_lead_time/item_lead_time.json +msgid "(Good Units Produced / Total Units Produced) × 100" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:263 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:166 +msgid "(H) Change in Stock Value (FIFO Queue)" +msgstr "" + +#: erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py:209 +msgid "(H) Valuation Rate" +msgstr "" + +#. Description of the 'Actual Operating Cost' (Currency) field in DocType 'Work +#. Order Operation' +#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json +msgid "(Hour Rate / 60) * Actual Operation Time" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:273 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:176 +msgid "(I) Valuation Rate" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:278 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:181 +msgid "(J) Valuation Rate as per FIFO" +msgstr "" + +#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:288 +#: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:191 +msgid "(K) Valuation = Value (D) ÷ Qty (A)" +msgstr "" + +#. Description of the 'Applicable on Cumulative Expense' (Check) field in +#. DocType 'Budget' +#: erpnext/accounts/doctype/budget/budget.json +msgid "(Purchase Order + Material Request + Actual Expense)" +msgstr "" + +#. Description of the 'From No' (Int) field in DocType 'Share Transfer' +#. Description of the 'To No' (Int) field in DocType 'Share Transfer' +#: erpnext/accounts/doctype/share_transfer/share_transfer.json +msgid "(including)" +msgstr "" + +#. Description of the 'Sales Taxes and Charges' (Table) field in DocType 'Sales +#. Taxes and Charges Template' +#: erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json +msgid "* Will be calculated in the transaction." +msgstr "" + +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html:112 +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:360 +msgid "0 - 30 Days" +msgstr "" + +#: erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py:114 +msgid "0-30" +msgstr "" + +#: erpnext/manufacturing/report/work_order_summary/work_order_summary.py:110 +msgid "0-30 Days" +msgstr "" + +#. Description of the 'Conversion Factor' (Float) field in DocType 'Loyalty +#. Program' +#: erpnext/accounts/doctype/loyalty_program/loyalty_program.json +msgid "1 Loyalty Points = How much base currency?" +msgstr "" + +#. Option for the 'Frequency' (Select) field in DocType 'Video Settings' +#: erpnext/utilities/doctype/video_settings/video_settings.json +msgid "1 hr" +msgstr "" + +#. Option for the 'No of Employees' (Select) field in DocType 'Lead' +#. Option for the 'No of Employees' (Select) field in DocType 'Opportunity' +#. Option for the 'No. of Employees' (Select) field in DocType 'Prospect' +#: erpnext/crm/doctype/lead/lead.json +#: erpnext/crm/doctype/opportunity/opportunity.json +#: erpnext/crm/doctype/prospect/prospect.json +msgid "1-10" +msgstr "" + +#. Option for the 'No of Employees' (Select) field in DocType 'Lead' +#. Option for the 'No of Employees' (Select) field in DocType 'Opportunity' +#. Option for the 'No. of Employees' (Select) field in DocType 'Prospect' +#: erpnext/crm/doctype/lead/lead.json +#: erpnext/crm/doctype/opportunity/opportunity.json +#: erpnext/crm/doctype/prospect/prospect.json +msgid "1000+" +msgstr "" + +#. Option for the 'No of Employees' (Select) field in DocType 'Lead' +#. Option for the 'No of Employees' (Select) field in DocType 'Opportunity' +#. Option for the 'No. of Employees' (Select) field in DocType 'Prospect' +#: erpnext/crm/doctype/lead/lead.json +#: erpnext/crm/doctype/opportunity/opportunity.json +#: erpnext/crm/doctype/prospect/prospect.json +msgid "11-50" +msgstr "" + +#: erpnext/regional/report/uae_vat_201/uae_vat_201.py:95 +#: erpnext/regional/report/uae_vat_201/uae_vat_201.py:101 +msgid "1{0}" +msgstr "၁ {0}" + +#. Option for the 'Periodicity' (Select) field in DocType 'Asset Maintenance +#. Task' +#: erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json +msgid "2 Yearly" +msgstr "၂ နှစ်တိုင်း" + +#. Option for the 'No of Employees' (Select) field in DocType 'Lead' +#. Option for the 'No of Employees' (Select) field in DocType 'Opportunity' +#. Option for the 'No. of Employees' (Select) field in DocType 'Prospect' +#: erpnext/crm/doctype/lead/lead.json +#: erpnext/crm/doctype/opportunity/opportunity.json +#: erpnext/crm/doctype/prospect/prospect.json +msgid "201-500" +msgstr "" + +#. Option for the 'Periodicity' (Select) field in DocType 'Asset Maintenance +#. Task' +#: erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json +msgid "3 Yearly" +msgstr "၃ နှစ်တိုင်း" + +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html:113 +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:361 +msgid "30 - 60 Days" +msgstr "၃၀ - ၆၀ ရက်" + +#. Option for the 'Frequency' (Select) field in DocType 'Video Settings' +#: erpnext/utilities/doctype/video_settings/video_settings.json +msgid "30 mins" +msgstr "၃၀ မိနစ်" + +#: erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py:115 +msgid "30-60" +msgstr "၃၀ - ၆၀" + +#: erpnext/manufacturing/report/work_order_summary/work_order_summary.py:110 +msgid "30-60 Days" +msgstr "၃၀ - ၆၀ ရက်" + +#. Option for the 'No of Employees' (Select) field in DocType 'Lead' +#. Option for the 'No of Employees' (Select) field in DocType 'Opportunity' +#. Option for the 'No. of Employees' (Select) field in DocType 'Prospect' +#: erpnext/crm/doctype/lead/lead.json +#: erpnext/crm/doctype/opportunity/opportunity.json +#: erpnext/crm/doctype/prospect/prospect.json +msgid "501-1000" +msgstr "၅၀၁ - ၁၀၀၀" + +#. Option for the 'No of Employees' (Select) field in DocType 'Lead' +#. Option for the 'No of Employees' (Select) field in DocType 'Opportunity' +#. Option for the 'No. of Employees' (Select) field in DocType 'Prospect' +#: erpnext/crm/doctype/lead/lead.json +#: erpnext/crm/doctype/opportunity/opportunity.json +#: erpnext/crm/doctype/prospect/prospect.json +msgid "51-200" +msgstr "၅၁ - ၂၀၀" + +#. Option for the 'Frequency' (Select) field in DocType 'Video Settings' +#: erpnext/utilities/doctype/video_settings/video_settings.json +msgid "6 hrs" +msgstr "၆ နာရီ" + +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html:114 +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:362 +msgid "60 - 90 Days" +msgstr "၆၀ - ၉၀ ရက်" + +#: erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py:116 +msgid "60-90" +msgstr "၆၀ - ၉၀" + +#: erpnext/manufacturing/report/work_order_summary/work_order_summary.py:110 +msgid "60-90 Days" +msgstr "" + +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html:115 +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html:363 +msgid "90 - 120 Days" +msgstr "၉၀ - ၁၂၀ ရက်" + +#: erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py:117 +#: erpnext/manufacturing/report/work_order_summary/work_order_summary.py:110 +msgid "90 Above" +msgstr "၉၀ အထက်" + +#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:61 +msgid "From Time cannot be later than To Time for {0}" +msgstr "" + +#. Content of the 'Help Text' (HTML) field in DocType 'Process Statement Of +#. Accounts' +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +#, python-format +msgid "
\n" +"

Note

\n" +"\n" +"

Examples

\n" +"\n" +"\n" +"" +msgstr "" + +#. Content of the 'Other Details' (HTML) field in DocType 'Purchase Receipt' +#. Content of the 'Other Details' (HTML) field in DocType 'Subcontracting +#. Receipt' +#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +msgid "
Other Details
" +msgstr "" + +#. Content of the 'no_bank_transactions' (HTML) field in DocType 'Bank +#. Reconciliation Tool' +#: erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json +msgid "
No Matching Bank Transactions Found
" +msgstr "" + +#: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:262 +msgid "
{0}
" +msgstr "" + +#. Content of the 'settings' (HTML) field in DocType 'Cheque Print Template' +#: erpnext/accounts/doctype/cheque_print_template/cheque_print_template.json +msgid "
\n" +"

All dimensions in centimeter only

\n" +"
" +msgstr "" + +#. Content of the 'about' (HTML) field in DocType 'Product Bundle' +#: erpnext/selling/doctype/product_bundle/product_bundle.json +msgid "

About Product Bundle

\n\n" +"

Aggregate group of Items into another Item. This is useful if you are bundling a certain Items into a package and you maintain stock of the packed Items and not the aggregate Item.

\n" +"

The package Item will have Is Stock Item as No and Is Sales Item as Yes.

\n" +"

Example:

\n" +"

If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.

" +msgstr "" + +#. Content of the 'Help' (HTML) field in DocType 'Currency Exchange Settings' +#: erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json +msgid "

Currency Exchange Settings Help

\n" +"

There are 3 variables that could be used within the endpoint, result key and in values of the parameter.

\n" +"

Exchange rate between {from_currency} and {to_currency} on {transaction_date} is fetched by the API.

\n" +"

Example: If your endpoint is exchange.com/2021-08-01, then, you will have to input exchange.com/{transaction_date}

" +msgstr "" + +#. Content of the 'Body and Closing Text Help' (HTML) field in DocType 'Dunning +#. Letter Text' +#: erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json +msgid "

Body Text and Closing Text Example

\n\n" +"
We have noticed that you have not yet paid invoice {{sales_invoice}} for {{frappe.db.get_value(\"Currency\", currency, \"symbol\")}} {{outstanding_amount}}. This is a friendly reminder that the invoice was due on {{due_date}}. Please pay the amount due immediately to avoid any further dunning cost.
\n\n" +"

How to get fieldnames

\n\n" +"

The fieldnames you can use in your template are the fields in the document. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n" +"

Templating

\n\n" +"

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" +msgstr "" + +#. Content of the 'Contract Template Help' (HTML) field in DocType 'Contract +#. Template' +#: erpnext/crm/doctype/contract_template/contract_template.json +msgid "

Contract Template Example

\n\n" +"
Contract for Customer {{ party_name }}\n\n"
+"-Valid From : {{ start_date }} \n"
+"-Valid To : {{ end_date }}\n"
+"
\n\n" +"

How to get fieldnames

\n\n" +"

The field names you can use in your Contract Template are the fields in the Contract for which you are creating the template. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)

\n\n" +"

Templating

\n\n" +"

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" +msgstr "" + +#. Content of the 'Terms and Conditions Help' (HTML) field in DocType 'Terms +#. and Conditions' +#: erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +msgid "

Standard Terms and Conditions Example

\n\n" +"
Delivery Terms for Order number {{ name }}\n\n"
+"-Order Date : {{ transaction_date }} \n"
+"-Expected Delivery Date : {{ delivery_date }}\n"
+"
\n\n" +"

How to get fieldnames

\n\n" +"

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n" +"

Templating

\n\n" +"

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" +msgstr "" + +#. Content of the 'html_5' (HTML) field in DocType 'Bank Statement Import' +#: erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json +msgid "
Or
" +msgstr "" + +#. Content of the 'account_no_settings' (HTML) field in DocType 'Cheque Print +#. Template' +#: erpnext/accounts/doctype/cheque_print_template/cheque_print_template.json +msgid "" +msgstr "" + +#. Content of the 'html_19' (HTML) field in DocType 'Cheque Print Template' +#: erpnext/accounts/doctype/cheque_print_template/cheque_print_template.json +msgid "" +msgstr "" + +#. Content of the 'Date Settings' (HTML) field in DocType 'Cheque Print +#. Template' +#: erpnext/accounts/doctype/cheque_print_template/cheque_print_template.json +msgid "" +msgstr "" + +#: erpnext/accounts/doctype/bank_clearance/bank_clearance.py:123 +msgid "
  • Clearance date must be after cheque date for row(s): {0}
  • " +msgstr "" + +#: erpnext/controllers/accounts_controller.py:2186 +msgid "
  • Item {0} in row(s) {1} billed more than {2}
  • " +msgstr "" + +#: erpnext/accounts/doctype/bank_clearance/bank_clearance.py:118 +msgid "
  • Payment document required for row(s): {0}
  • " +msgstr "" + +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py:163 +msgid "
  • {}
  • " +msgstr "" + +#: erpnext/controllers/accounts_controller.py:2183 +msgid "

    Cannot overbill for the following Items:

    " +msgstr "" + +#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py:157 +msgid "

    Following {0}s doesn't belong to Company {1} :

    " +msgstr "" + +#. Content of the 'html_llwp' (HTML) field in DocType 'Request for Quotation' +#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +msgid "

    In your Email Template, you can use the following special variables:\n" +"

    \n" +"\n" +"

    \n" +"

    Apart from these, you can access all values in this RFQ, like {{ message_for_supplier }} or {{ terms }}.

    " +msgstr "" + +#: erpnext/accounts/doctype/bank_clearance/bank_clearance.py:116 +msgid "

    Please correct the following row(s):

    -
    +
    {% if(row.status_image) { %} diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index e27eac9ee6b..f299377cb23 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -645,7 +645,7 @@ erpnext.utils.update_child_items = function (opts) { get_query: function () { let filters; if (frm.doc.doctype == "Sales Order") { - filters = { is_sales_item: 1 }; + filters = { is_sales_item: 1, is_stock_item: !frm.doc.is_subcontracted }; } else if (frm.doc.doctype == "Purchase Order") { if (frm.doc.is_subcontracted) { if (frm.doc.is_old_subcontracting_flow) { @@ -801,7 +801,7 @@ erpnext.utils.update_child_items = function (opts) { } if ( - frm.doc.doctype == "Purchase Order" && + ["Purchase Order", "Sales Order"].includes(frm.doc.doctype) && frm.doc.is_subcontracted && !frm.doc.is_old_subcontracting_flow ) { @@ -857,7 +857,7 @@ erpnext.utils.update_child_items = function (opts) { }, ], primary_action: function () { - if (frm.doctype == "Sales Order" && has_reserved_stock) { + if (frm.doctype == "Sales Order" && has_reserved_stock && frm.doc.is_subcontracted == 0) { this.hide(); frappe.confirm( __( diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js index ef0848f4949..8fd0518f5a5 100644 --- a/erpnext/public/js/utils/sales_common.js +++ b/erpnext/public/js/utils/sales_common.js @@ -113,6 +113,7 @@ erpnext.sales_common = { ); this.toggle_editable_price_list_rate(); + this.change_warehouse_labels_for_return(); } company() { @@ -504,6 +505,33 @@ erpnext.sales_common = { this.frm.set_value("discount_amount", 0); this.frm.set_value("additional_discount_percentage", 0); } + + is_return() { + let reset = !this.frm.doc.is_return; + this.change_warehouse_labels_for_return(reset); + } + + change_warehouse_labels_for_return(reset) { + // swap source and target warehouse labels for return + let source_warehouse_label = __("Source Warehouse"); + let target_warehouse_label = __("Set Target Warehouse"); + + if (this.frm.doc.doctype == "Delivery Note") { + source_warehouse_label = __("Set Source Warehouse"); + } + + if (reset) { + // reset to original labels + this.frm.set_df_property("set_warehouse", "label", source_warehouse_label); + this.frm.set_df_property("set_target_warehouse", "label", target_warehouse_label); + return; + } + + if (this.frm.doc.is_return) { + this.frm.set_df_property("set_warehouse", "label", target_warehouse_label); + this.frm.set_df_property("set_target_warehouse", "label", source_warehouse_label); + } + } }; }, }; diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index e02d7a3d785..5cc7238d6dd 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -457,7 +457,8 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { (["Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype) && !this.frm.doc.is_return) || (this.frm.doc.doctype === "Stock Entry" && - this.frm.doc.purpose === "Material Receipt") + (this.frm.doc.purpose === "Material Receipt" || + (this.frm.doc.purpose === "Manufacture" && this.item.is_finished_item))) ) { is_inward = true; } @@ -542,6 +543,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { based_on: based_on, posting_date: this.frm.doc.posting_date, posting_time: this.frm.doc.posting_time, + scio_detail: this.item.scio_detail, }, callback: (r) => { if (r.message) { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 133205f251e..360f68768fe 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -44,6 +44,15 @@ frappe.ui.form.on("Sales Order", { }; }); + frm.set_query("sales_person", "sales_team", function () { + return { + filters: { + is_group: 0, + enabled: 1, + }, + }; + }); + frm.set_df_property("packed_items", "cannot_add_rows", true); frm.set_df_property("packed_items", "cannot_delete_rows", true); }, @@ -54,7 +63,8 @@ frappe.ui.form.on("Sales Order", { frm.doc.status !== "Closed" && flt(frm.doc.per_delivered) < 100 && flt(frm.doc.per_billed) < 100 && - frm.has_perm("write") + frm.has_perm("write") && + !frm.doc.is_subcontracted ) { frm.add_custom_button(__("Update Items"), () => { erpnext.utils.update_child_items({ @@ -84,7 +94,8 @@ frappe.ui.form.on("Sales Order", { if ( frm.doc.__onload && frm.doc.__onload.has_reserved_stock && - frappe.model.can_cancel("Stock Reservation Entry") + frappe.model.can_cancel("Stock Reservation Entry") && + !frm.doc.is_subcontracted ) { frm.add_custom_button( __("Unreserve"), @@ -93,16 +104,21 @@ frappe.ui.form.on("Sales Order", { ); } - frm.doc.items.forEach((item) => { - if (flt(item.stock_reserved_qty) > 0 && frappe.model.can_read("Stock Reservation Entry")) { - frm.add_custom_button( - __("Reserved Stock"), - () => frm.events.show_reserved_stock(frm), - __("Stock Reservation") - ); - return; - } - }); + if (!frm.doc.is_subcontracted) { + frm.doc.items.forEach((item) => { + if ( + flt(item.stock_reserved_qty) > 0 && + frappe.model.can_read("Stock Reservation Entry") + ) { + frm.add_custom_button( + __("Reserved Stock"), + () => frm.events.show_reserved_stock(frm), + __("Stock Reservation") + ); + return; + } + }); + } } if (frm.doc.docstatus === 0) { @@ -112,7 +128,7 @@ frappe.ui.form.on("Sales Order", { frm.events.get_items_from_internal_purchase_order(frm); } - if (frm.doc.docstatus === 0) { + if (frm.doc.docstatus === 0 && !frm.doc.is_subcontracted) { frappe.call({ method: "erpnext.selling.doctype.sales_order.sales_order.get_stock_reservation_status", callback: function (r) { @@ -749,10 +765,28 @@ frappe.ui.form.on("Sales Order", { frm.schedule_dialog.fields_dict.delivery_schedule.refresh(); }, + + get_subcontracting_boms_for_finished_goods: function (fg_item) { + return frappe.call({ + method: "erpnext.subcontracting.doctype.subcontracting_bom.subcontracting_bom.get_subcontracting_boms_for_finished_goods", + args: { + fg_items: fg_item, + }, + }); + }, + + get_subcontracting_boms_for_service_item: function (service_item) { + return frappe.call({ + method: "erpnext.subcontracting.doctype.subcontracting_bom.subcontracting_bom.get_subcontracting_boms_for_service_item", + args: { + service_item: service_item, + }, + }); + }, }); frappe.ui.form.on("Sales Order Item", { - item_code: function (frm, cdt, cdn) { + item_code: async function (frm, cdt, cdn) { var row = locals[cdt][cdn]; if (frm.doc.delivery_date) { row.delivery_date = frm.doc.delivery_date; @@ -760,6 +794,50 @@ frappe.ui.form.on("Sales Order Item", { } else { frm.script_manager.copy_from_first_row("items", row, ["delivery_date"]); } + + if (frm.doc.is_subcontracted) { + if (row.item_code && !row.fg_item) { + var result = await frm.events.get_subcontracting_boms_for_service_item(row.item_code); + + if (result.message && Object.keys(result.message).length) { + var finished_goods = Object.keys(result.message); + + // Set FG if only one active Subcontracting BOM is found + if (finished_goods.length === 1) { + row.fg_item = result.message[finished_goods[0]].finished_good; + row.uom = result.message[finished_goods[0]].finished_good_uom; + refresh_field("items"); + } else { + const dialog = new frappe.ui.Dialog({ + title: __("Select Finished Good"), + size: "small", + fields: [ + { + fieldname: "finished_good", + fieldtype: "Autocomplete", + label: __("Finished Good"), + options: finished_goods, + }, + ], + primary_action_label: __("Select"), + primary_action: () => { + var subcontracting_bom = result.message[dialog.get_value("finished_good")]; + + if (subcontracting_bom) { + row.fg_item = subcontracting_bom.finished_good; + row.uom = subcontracting_bom.finished_good_uom; + refresh_field("items"); + } + + dialog.hide(); + }, + }); + + dialog.show(); + } + } + } + } }, delivery_date: function (frm, cdt, cdn) { @@ -782,6 +860,50 @@ frappe.ui.form.on("Sales Order Item", { }, }); }, + + fg_item: async function (frm, cdt, cdn) { + if (frm.doc.is_subcontracted) { + var row = locals[cdt][cdn]; + + if (row.fg_item) { + var result = await frm.events.get_subcontracting_boms_for_finished_goods(row.fg_item); + + if (result.message && Object.keys(result.message).length) { + frappe.model.set_value(cdt, cdn, "item_code", result.message.service_item); + frappe.model.set_value( + cdt, + cdn, + "qty", + flt(row.fg_item_qty) * flt(result.message.conversion_factor) + ); + frappe.model.set_value(cdt, cdn, "uom", result.message.service_item_uom); + } + } + } + }, + + qty: async function (frm, cdt, cdn) { + if (frm.doc.is_subcontracted) { + var row = locals[cdt][cdn]; + + if (row.fg_item) { + var result = await frm.events.get_subcontracting_boms_for_finished_goods(row.fg_item); + + if ( + result.message && + row.item_code == result.message.service_item && + row.uom == result.message.service_item_uom + ) { + frappe.model.set_value( + cdt, + cdn, + "fg_item_qty", + flt(row.qty) / flt(result.message.conversion_factor) + ); + } + } + } + }, }); erpnext.selling.SalesOrderController = class SalesOrderController extends erpnext.selling.SellingController { @@ -795,6 +917,22 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex let allow_delivery = false; if (doc.docstatus == 1) { + if ( + !["Closed", "Completed"].includes(doc.status) && + flt(doc.per_delivered) < 100 && + flt(doc.per_billed) < 100 + ) { + if (!doc.__onload || doc.__onload.can_update_items) { + this.frm.add_custom_button(__("Update Items"), () => { + erpnext.utils.update_child_items({ + frm: this.frm, + child_docname: "items", + child_doctype: "Sales Order Detail", + cannot_add_row: false, + }); + }); + } + } if (this.frm.has_perm("submit")) { if (doc.status === "On Hold") { // un-hold @@ -847,11 +985,24 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } } + if (doc.is_subcontracted) { + if (!doc.items.every((item) => item.qty == item.subcontracted_qty)) { + this.frm.add_custom_button( + __("Subcontracting Inward Order"), + () => { + me.make_subcontracting_inward_order(); + }, + __("Create") + ); + } + } + if ( (!doc.__onload || !doc.__onload.has_reserved_stock) && flt(doc.per_picked) < 100 && flt(doc.per_delivered) < 100 && - frappe.model.can_create("Pick List") + frappe.model.can_create("Pick List") && + !doc.is_subcontracted ) { this.frm.add_custom_button( __("Pick List"), @@ -880,7 +1031,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex ); } - if (frappe.model.can_create("Work Order")) { + if (frappe.model.can_create("Work Order") && !doc.is_subcontracted) { this.frm.add_custom_button( __("Work Order"), () => this.make_work_order(), @@ -890,7 +1041,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // sales invoice - if (flt(doc.per_billed) < 100 && frappe.model.can_create("Sales Invoice")) { + if ( + (flt(doc.per_billed) < 100 && frappe.model.can_create("Sales Invoice")) || + doc.is_subcontracted + ) { this.frm.add_custom_button( __("Sales Invoice"), () => me.make_sales_invoice(), @@ -902,13 +1056,16 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex if ( (!doc.order_type || ((order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered) < 100)) && - frappe.model.can_create("Material Request") + frappe.model.can_create("Material Request") && + !doc.is_subcontracted ) { - this.frm.add_custom_button( - __("Material Request"), - () => this.make_material_request(), - __("Create") - ); + if (!doc.is_subcontracted) { + this.frm.add_custom_button( + __("Material Request"), + () => this.make_material_request(), + __("Create") + ); + } this.frm.add_custom_button( __("Request for Raw Materials"), () => this.make_raw_material_request(), @@ -917,7 +1074,11 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // Make Purchase Order - if (!this.frm.doc.is_internal_customer && frappe.model.can_create("Purchase Order")) { + if ( + !this.frm.doc.is_internal_customer && + frappe.model.can_create("Purchase Order") && + !doc.is_subcontracted + ) { this.frm.add_custom_button( __("Purchase Order"), () => this.make_purchase_order(), @@ -991,7 +1152,11 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } } - if (this.frm.doc.docstatus === 0 && frappe.model.can_read("Quotation")) { + if ( + this.frm.doc.docstatus === 0 && + frappe.model.can_read("Quotation") && + !this.frm.doc.is_subcontracted + ) { this.frm.add_custom_button( __("Quotation"), function () { @@ -1011,7 +1176,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex get_query_filters: { company: me.frm.doc.company, docstatus: 1, - status: ["!=", "Lost"], + status: ["not in", ["Lost", "Ordered"]], }, allow_child_item_selection: true, child_fieldname: "items", @@ -1606,6 +1771,14 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex }, }); } + + make_subcontracting_inward_order() { + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.sales_order.sales_order.make_subcontracting_inward_order", + frm: this.frm, + freeze_message: __("Creating Subcontracting Inward Order ..."), + }); + } }; extend_cscript(cur_frm.cscript, new erpnext.selling.SalesOrderController({ frm: cur_frm })); diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index b253d2297f1..564c3daf882 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -25,6 +25,7 @@ "company", "skip_delivery_note", "has_unit_price_items", + "is_subcontracted", "amended_from", "accounting_dimensions_section", "cost_center", @@ -1035,8 +1036,8 @@ }, { "collapsible": 1, - "collapsible_depends_on": "packed_items", - "depends_on": "packed_items", + "collapsible_depends_on": "eval:!doc.is_subcontracted && doc.packed_items", + "depends_on": "eval:!doc.is_subcontracted && doc.packed_items", "fieldname": "packing_list", "fieldtype": "Section Break", "hide_days": 1, @@ -1607,7 +1608,7 @@ }, { "default": "0", - "depends_on": "eval: (doc.docstatus == 0 || doc.reserve_stock)", + "depends_on": "eval: ((doc.docstatus == 0 || doc.reserve_stock) && !doc.is_subcontracted)", "description": "If checked, Stock will be reserved on Submit", "fieldname": "reserve_stock", "fieldtype": "Check", @@ -1688,13 +1689,21 @@ "fieldtype": "Data", "is_virtual": 1, "label": "Last Scanned Warehouse" + }, + { + "default": "0", + "fieldname": "is_subcontracted", + "fieldtype": "Check", + "label": "Is Subcontracted", + "print_hide": 1 } ], + "grid_page_length": 50, "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2025-07-28 12:14:29.760988", + "modified": "2025-10-12 12:14:29.760988", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 4ca8c279907..95eaa788cab 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -120,6 +120,7 @@ class SalesOrder(SellingController): incoterm: DF.Link | None inter_company_order_reference: DF.Link | None is_internal_customer: DF.Check + is_subcontracted: DF.Check items: DF.Table[SalesOrderItem] language: DF.Link | None letter_head: DF.Link | None @@ -195,6 +196,10 @@ class SalesOrder(SellingController): def onload(self) -> None: super().onload() + if self.get("is_subcontracted"): + self.set_onload("can_update_items", self.can_update_items()) + return + if frappe.get_single_value("Stock Settings", "enable_stock_reservation"): if self.has_unreserved_stock(): self.set_onload("has_unreserved_stock", True) @@ -202,6 +207,15 @@ class SalesOrder(SellingController): if has_reserved_stock(self.doctype, self.name): self.set_onload("has_reserved_stock", True) + def can_update_items(self) -> bool: + result = True + + if self.is_subcontracted: + if frappe.db.exists("Subcontracting Inward Order", {"sales_order": self.name, "docstatus": 1}): + result = False + + return result + def before_validate(self): self.set_has_unit_price_items() self.flags.allow_zero_qty = self.has_unit_price_items @@ -233,6 +247,7 @@ class SalesOrder(SellingController): make_packing_list(self) self.validate_with_previous_doc() + self.validate_fg_item_for_subcontracting() self.set_status() if not self.billing_status: @@ -243,7 +258,39 @@ class SalesOrder(SellingController): self.advance_payment_status = "Not Requested" self.reset_default_field_value("set_warehouse", "items", "warehouse") - self.enable_auto_reserve_stock() + if not self.get("is_subcontracted"): + self.enable_auto_reserve_stock() + + def validate_fg_item_for_subcontracting(self): + if self.is_subcontracted: + for item in self.items: + if not item.fg_item: + frappe.throw( + _("Row #{0}: Finished Good Item is not specified for service item {1}").format( + item.idx, item.item_code + ) + ) + else: + if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"): + frappe.throw( + _("Row #{0}: Finished Good Item {1} must be a sub-contracted item").format( + item.idx, item.fg_item + ) + ) + if not frappe.db.get_value( + "Subcontracting BOM", + {"finished_good": item.fg_item, "is_active": 1}, + "finished_good_bom", + ) and not frappe.get_value("Item", item.fg_item, "default_bom"): + frappe.throw( + _("Row #{0}: BOM not found for FG Item {1}").format(item.idx, item.fg_item) + ) + if not item.fg_item_qty: + frappe.throw(_("Row #{0}: Finished Good Item Qty can not be zero").format(item.idx)) + else: + for item in self.items: + item.set("fg_item", None) + item.set("fg_item_qty", 0) def enable_auto_reserve_stock(self): if self.is_new() and frappe.get_single_value("Stock Settings", "auto_reserve_stock"): @@ -449,7 +496,7 @@ class SalesOrder(SellingController): update_coupon_code_count(self.coupon_code, "used") - if self.get("reserve_stock"): + if self.get("reserve_stock") and not self.get("is_subcontracted"): self.create_stock_reservation_entries() def on_cancel(self): @@ -535,9 +582,23 @@ class SalesOrder(SellingController): if status == "Draft" and self.docstatus == 1: self.check_credit_limit() self.update_reserved_qty() + self.update_subcontracting_order_status() self.notify_update() clear_doctype_notifications(self) + def update_subcontracting_order_status(self): + from erpnext.subcontracting.doctype.subcontracting_inward_order.subcontracting_inward_order import ( + update_subcontracting_inward_order_status as update_scio_status, + ) + + if self.is_subcontracted: + scio = frappe.get_cached_value( + "Subcontracting Inward Order", {"sales_order": self.name, "docstatus": 1}, "name" + ) + + if scio: + update_scio_status(scio, "Closed" if self.status == "Closed" else None) + def update_reserved_qty(self, so_item_rows=None): """update requested qty (before ordered_qty is updated)""" item_wh_list = [] @@ -1290,6 +1351,46 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, a child_filter = d.name in filtered_items if filtered_items else True return child_filter + def add_self_rm(doclist): + parent = frappe.qb.DocType("Subcontracting Inward Order") + child = frappe.qb.DocType("Subcontracting Inward Order Received Item") + query = ( + frappe.qb.from_(parent) + .join(child) + .on(parent.name == child.parent) + .select( + child.required_qty, + child.consumed_qty, + (child.billed_qty - child.returned_qty).as_("qty"), + child.rm_item_code, + child.stock_uom, + child.name, + ) + .where( + (parent.docstatus == 1) + & (parent.sales_order == source_name) + & (child.is_customer_provided_item == 0) + ) + ) + result = query.run(as_dict=True) + + if result: + idx = len(doclist.items) + 1 + for item in result: + if (qty := max(item.required_qty, item.consumed_qty) - item.qty) > 0: + doclist.append( + "items", + { + "item_code": item.rm_item_code, + "qty": qty, + "uom": item.stock_uom, + "scio_detail": item.name, + }, + ) + doclist.process_item_selection(idx) + idx += 1 + doclist.has_subcontracted = 1 + doclist = get_mapped_doc( "Sales Order", source_name, @@ -1328,6 +1429,9 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, a ignore_permissions=ignore_permissions, ) + if frappe.get_cached_value("Sales Order", source_name, "is_subcontracted"): + add_self_rm(doclist) + automatically_fetch_payment_terms = cint( frappe.get_single_value("Accounts Settings", "automatically_fetch_payment_terms") ) @@ -2005,3 +2109,71 @@ def get_work_order_items(sales_order, for_raw_material_request=0): @frappe.whitelist() def get_stock_reservation_status(): return frappe.get_single_value("Stock Settings", "enable_stock_reservation") + + +@frappe.whitelist() +def make_subcontracting_inward_order(source_name, target_doc=None): + if not is_so_fully_subcontracted(source_name): + return get_mapped_subcontracting_inward_order(source_name, target_doc) + else: + frappe.throw(_("This Sales Order has been fully subcontracted.")) + + +def is_so_fully_subcontracted(so_name): + table = frappe.qb.DocType("Sales Order Item") + query = ( + frappe.qb.from_(table) + .select(table.name) + .where((table.parent == so_name) & (table.qty != table.subcontracted_qty)) + ) + return not query.run(as_dict=True) + + +def get_mapped_subcontracting_inward_order(source_name, target_doc=None): + def post_process(source_doc, target_doc): + if ( + frappe.db.count( + "Warehouse", {"customer": source_doc.customer, "disabled": 0, "is_rejected_warehouse": 0} + ) + == 1 + ): + target_doc.customer_warehouse = frappe.get_cached_value( + "Warehouse", + {"customer": source_doc.customer, "disabled": 0, "is_rejected_warehouse": 0}, + "name", + ) + target_doc.populate_items_table() + + if target_doc and isinstance(target_doc, str): + target_doc = json.loads(target_doc) + for key in ["service_items", "items", "received_items"]: + if key in target_doc: + del target_doc[key] + target_doc = json.dumps(target_doc) + + target_doc = get_mapped_doc( + "Sales Order", + source_name, + { + "Sales Order": { + "doctype": "Subcontracting Inward Order", + "field_map": {}, + "field_no_map": ["total_qty", "total", "net_total"], + "validation": { + "docstatus": ["=", 1], + }, + }, + "Sales Order Item": { + "doctype": "Subcontracting Inward Order Service Item", + "field_map": { + "name": "sales_order_item", + }, + "field_no_map": ["qty", "fg_item_qty", "amount"], + "condition": lambda item: item.qty != item.subcontracted_qty, + }, + }, + target_doc, + post_process, + ) + + return target_doc diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index f3fae44330d..ea9c8d2f96e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -30,5 +30,6 @@ def get_data(): {"label": _("Reference"), "items": ["Quotation", "Auto Repeat", "Stock Reservation Entry"]}, {"label": _("Payment"), "items": ["Payment Entry", "Payment Request", "Journal Entry"]}, {"label": _("Schedule"), "items": ["Delivery Schedule Item"]}, + {"label": _("Subcontracting Inward"), "items": ["Subcontracting Inward Order"]}, ], } diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 7aba6c3475b..2d01539ca04 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2613,6 +2613,7 @@ def make_sales_order(**args): so.customer = args.customer or "_Test Customer" so.currency = args.currency or "INR" so.po_no = args.po_no or "" + so.is_subcontracted = args.is_subcontracted or 0 if args.selling_price_list: so.selling_price_list = args.selling_price_list diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index bfb839b2343..d57bb04d13f 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -7,6 +7,8 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "fg_item", + "fg_item_qty", "item_code", "customer_item_code", "ensure_delivery_based_on_produced_serial_no", @@ -25,6 +27,7 @@ "quantity_and_rate", "qty", "stock_uom", + "subcontracted_qty", "col_break2", "uom", "conversion_factor", @@ -468,6 +471,7 @@ { "collapsible": 1, "collapsible_depends_on": "eval:doc.delivered_by_supplier==1||doc.supplier", + "depends_on": "eval:!parent.is_subcontracted", "fieldname": "drop_ship_section", "fieldtype": "Section Break", "label": "Drop Ship", @@ -490,6 +494,7 @@ }, { "collapsible": 1, + "depends_on": "eval:!parent.is_subcontracted", "fieldname": "item_weight_details", "fieldtype": "Section Break", "label": "Item Weight Details" @@ -517,6 +522,7 @@ "options": "UOM" }, { + "depends_on": "eval:!parent.is_subcontracted", "fieldname": "warehouse_and_reference", "fieldtype": "Section Break", "label": "Warehouse and Reference" @@ -879,7 +885,7 @@ { "allow_on_submit": 1, "default": "1", - "depends_on": "eval:doc.is_stock_item", + "depends_on": "eval:(doc.is_stock_item && !parent.is_subcontracted)", "fieldname": "reserve_stock", "fieldtype": "Check", "label": "Reserve Stock", @@ -935,6 +941,7 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:!parent.is_subcontracted", "fieldname": "available_quantity_section", "fieldtype": "Section Break", "label": "Available Quantity" @@ -977,12 +984,39 @@ "fieldname": "add_schedule", "fieldtype": "Button", "label": "Add Schedule" + }, + { + "allow_on_submit": 1, + "default": "0", + "depends_on": "eval:parent.is_subcontracted", + "fieldname": "subcontracted_qty", + "fieldtype": "Float", + "label": "Subcontracted Quantity", + "no_copy": 1, + "non_negative": 1, + "read_only": 1 + }, + { + "depends_on": "eval:parent.is_subcontracted", + "fieldname": "fg_item", + "fieldtype": "Link", + "label": "Finished Good", + "mandatory_depends_on": "eval:parent.is_subcontracted", + "options": "Item" + }, + { + "depends_on": "eval:parent.is_subcontracted", + "fieldname": "fg_item_qty", + "fieldtype": "Float", + "label": "Finished Good Qty", + "mandatory_depends_on": "eval:parent.is_subcontracted" } ], + "grid_page_length": 50, "idx": 1, "istable": 1, "links": [], - "modified": "2025-08-21 17:01:54.269105", + "modified": "2025-10-13 10:57:43.378448", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.py b/erpnext/selling/doctype/sales_order_item/sales_order_item.py index 731cff665da..9128f8a3e41 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.py +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.py @@ -42,6 +42,8 @@ class SalesOrderItem(Document): discount_percentage: DF.Percent distributed_discount_amount: DF.Currency ensure_delivery_based_on_produced_serial_no: DF.Check + fg_item: DF.Link | None + fg_item_qty: DF.Float grant_commission: DF.Check gross_profit: DF.Currency image: DF.Attach | None @@ -84,6 +86,7 @@ class SalesOrderItem(Document): stock_reserved_qty: DF.Float stock_uom: DF.Link | None stock_uom_rate: DF.Currency + subcontracted_qty: DF.Float supplier: DF.Link | None target_warehouse: DF.Link | None total_weight: DF.Float diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index 7d94cb6848e..4ed1419ac82 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -11,6 +11,7 @@ "customer_group", "column_break_4", "territory", + "item_price_tab", "item_price_settings_section", "selling_price_list", "maintain_same_rate_action", @@ -22,6 +23,7 @@ "validate_selling_price", "editable_bundle_item_rates", "allow_negative_rates_for_items", + "transaction_tab", "sales_transactions_settings_section", "so_required", "dn_required", @@ -38,7 +40,12 @@ "allow_zero_qty_in_quotation", "allow_zero_qty_in_sales_order", "experimental_section", - "use_legacy_js_reactivity" + "use_legacy_js_reactivity", + "subcontracting_inward_tab", + "section_break_zwh6", + "allow_delivery_of_overproduced_qty", + "column_break_mla9", + "deliver_scrap_items" ], "fields": [ { @@ -232,6 +239,44 @@ "fieldtype": "Check", "label": "Allow Quotation with Zero Quantity" }, + { + "fieldname": "section_break_zwh6", + "fieldtype": "Section Break", + "label": "Subcontracting Inward Settings" + }, + { + "default": "0", + "description": "If enabled, system will allow user to deliver the entire quantity of the finished goods produced against the Subcontracting Inward Order. If disabled, system will allow delivery of only the ordered quantity.", + "fieldname": "allow_delivery_of_overproduced_qty", + "fieldtype": "Check", + "label": "Allow Delivery of Overproduced Qty" + }, + { + "fieldname": "column_break_mla9", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If enabled, the Scrap Item generated against a Finished Good will also be added in the Stock Entry when delivering that Finished Good.", + "fieldname": "deliver_scrap_items", + "fieldtype": "Check", + "label": "Deliver Scrap Items" + }, + { + "fieldname": "item_price_tab", + "fieldtype": "Tab Break", + "label": "Item Price" + }, + { + "fieldname": "transaction_tab", + "fieldtype": "Tab Break", + "label": "Transaction" + }, + { + "fieldname": "subcontracting_inward_tab", + "fieldtype": "Tab Break", + "label": "Subcontracting Inward" + }, { "default": "0", "fieldname": "fallback_to_default_price_list", @@ -251,7 +296,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-09-24 16:08:48.865885", + "modified": "2025-10-12 16:08:48.865885", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.py b/erpnext/selling/doctype/selling_settings/selling_settings.py index 528e11f0d2d..9d343b2c21c 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.py +++ b/erpnext/selling/doctype/selling_settings/selling_settings.py @@ -21,6 +21,7 @@ class SellingSettings(Document): from frappe.types import DF allow_against_multiple_purchase_orders: DF.Check + allow_delivery_of_overproduced_qty: DF.Check allow_multiple_items: DF.Check allow_negative_rates_for_items: DF.Check allow_sales_order_creation_for_expired_quotation: DF.Check @@ -29,6 +30,7 @@ class SellingSettings(Document): blanket_order_allowance: DF.Float cust_master_name: DF.Literal["Customer Name", "Naming Series", "Auto Name"] customer_group: DF.Link | None + deliver_scrap_items: DF.Check dn_required: DF.Literal["No", "Yes"] dont_reserve_sales_order_qty_on_sales_return: DF.Check editable_bundle_item_rates: DF.Check diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 1035fa3fb58..1018036c11b 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -289,6 +289,7 @@ erpnext.company.setup_queries = function (frm) { ["default_provisional_account", { root_type: ["in", ["Liability", "Asset"]] }], ["default_advance_received_account", { root_type: "Liability", account_type: "Receivable" }], ["default_advance_paid_account", { root_type: "Asset", account_type: "Payable" }], + ["service_expense_account", { root_type: "Expense" }], ], function (i, v) { erpnext.company.set_custom_query(frm, v); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 8d713600ba7..349fe5c0771 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -108,6 +108,7 @@ "transactions_annual_history", "purchase_expense_section", "purchase_expense_account", + "service_expense_account", "column_break_ereg", "purchase_expense_contra_account", "stock_tab", @@ -869,6 +870,13 @@ "fieldtype": "Link", "label": "Purchase Expense Contra Account", "options": "Account" + }, + { + "description": "For service item", + "fieldname": "service_expense_account", + "fieldtype": "Link", + "label": "Service Expense Account", + "options": "Account" } ], "icon": "fa fa-building", @@ -876,7 +884,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2025-10-01 17:34:10.971627", + "modified": "2025-10-10 15:12:37.941251", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 56f88c215ae..60788b98d2a 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -100,6 +100,7 @@ class Company(NestedSet): round_off_for_opening: DF.Link | None sales_monthly_history: DF.SmallText | None series_for_depreciation_entry: DF.Data | None + service_expense_account: DF.Link | None stock_adjustment_account: DF.Link | None stock_received_but_not_billed: DF.Link | None submit_err_jv: DF.Check @@ -570,6 +571,21 @@ class Company(NestedSet): self.db_set("disposal_account", disposal_acct) + if not self.service_expense_account: + service_expense_acct = frappe.db.get_value( + "Account", + { + "account_name": _("Marketing Expenses"), + "company": self.name, + "is_group": 0, + "root_type": "Expense", + }, + "name", + ) + + if service_expense_acct: + self.db_set("service_expense_account", service_expense_acct) + def _set_default_account(self, fieldname, account_type): if self.get(fieldname): return diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py index 754e263c30a..36c24c0f37e 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.py +++ b/erpnext/setup/doctype/sales_partner/sales_partner.py @@ -44,7 +44,7 @@ class SalesPartner(WebsiteGenerator): load_address_and_contact(self) def autoname(self): - self.name = self.partner_name + pass def validate(self): if not self.route: diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 227df38e116..7f0e234a2e0 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -9,6 +9,7 @@ from frappe.desk.notifications import clear_notifications from frappe.model.document import Document from frappe.utils import cint, comma_and, create_batch, get_link_to_form from frappe.utils.background_jobs import get_job, is_job_enqueued +from frappe.utils.caching import request_cache LEDGER_ENTRY_DOCTYPES = frozenset( ( @@ -482,6 +483,7 @@ def get_doctypes_to_be_ignored(): @frappe.whitelist() +@request_cache def is_deletion_doc_running(company: str | None = None, err_msg: str | None = None): if not company: return diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index fd98e124d70..ed3e6da0e9a 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -2,6 +2,8 @@ # License: GNU General Public License v3. See license.txt +import os + import frappe from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields @@ -34,6 +36,7 @@ def after_install(): update_roles() make_default_operations() update_pegged_currencies() + create_letter_head() frappe.db.commit() @@ -250,6 +253,20 @@ def update_roles(): def create_default_role_profiles(): for role_profile_name, roles in DEFAULT_ROLE_PROFILES.items(): + if frappe.db.exists("Role Profile", role_profile_name): + role_profile = frappe.get_doc("Role Profile", role_profile_name) + existing_roles = [row.role for row in role_profile.roles] + + role_profile.roles = [row for row in role_profile.roles if row.role in roles] + + for role in roles: + if role not in existing_roles: + role_profile.append("roles", {"role": role}) + + role_profile.save(ignore_permissions=True) + + continue + role_profile = frappe.new_doc("Role Profile") role_profile.role_profile = role_profile_name for role in roles: @@ -279,6 +296,28 @@ def update_pegged_currencies(): doc.save() +def create_letter_head(): + base_path = frappe.get_app_path("erpnext", "accounts", "letterhead") + + letterheads = { + "Company Letterhead": "company_letterhead.html", + "Company Letterhead - Grey": "company_letterhead_grey.html", + } + + for name, filename in letterheads.items(): + if not frappe.db.exists("Letter Head", name): + content = frappe.read_file(os.path.join(base_path, filename)) + doc = frappe.get_doc( + { + "doctype": "Letter Head", + "letter_head_name": name, + "source": "HTML", + "content": content, + } + ) + doc.insert(ignore_permissions=True) + + DEFAULT_ROLE_PROFILES = { "Inventory": [ "Stock User", diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index f9dde8c24c8..5677c07b87c 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -122,6 +122,30 @@ def install(country=None): "purpose": "Material Consumption for Manufacture", "is_standard": 1, }, + { + "doctype": "Stock Entry Type", + "name": _("Receive from Customer"), + "purpose": "Receive from Customer", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": _("Return Raw Material to Customer"), + "purpose": "Return Raw Material to Customer", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": _("Subcontracting Delivery"), + "purpose": "Subcontracting Delivery", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": _("Subcontracting Return"), + "purpose": "Subcontracting Return", + "is_standard": 1, + }, # territory: with two default territories, one for home country and one named Rest of the World { "doctype": "Territory", diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index d9d8a8f567c..05d8cf1f2f7 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -81,6 +81,8 @@ def update_page_info(bootinfo): def bootinfo(bootinfo): if bootinfo.get("user") and bootinfo["user"].get("name"): bootinfo["user"]["employee"] = "" + frappe.session.data.employee = "" employee = frappe.db.get_value("Employee", {"user_id": bootinfo["user"]["name"]}, "name") if employee: bootinfo["user"]["employee"] = employee + frappe.session.data.employee = employee diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index ad8218cfcfb..2c08635e03f 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -158,7 +158,9 @@ class Batch(Document): @frappe.whitelist() def recalculate_batch_qty(self): - batches = get_batch_qty(batch_no=self.name, item_code=self.item) + batches = get_batch_qty( + batch_no=self.name, item_code=self.item, for_stock_levels=True, consider_negative_batches=True + ) batch_qty = 0.0 if batches: for row in batches: @@ -260,6 +262,7 @@ def get_batch_qty( "warehouse": warehouse, "creation": creation, "batch_no": batch_no, + "based_on": frappe.get_single_value("Stock Settings", "pick_serial_and_batch_based_on"), "ignore_voucher_nos": ignore_voucher_nos, "for_stock_levels": for_stock_levels, "consider_negative_batches": consider_negative_batches, diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 0cd4e24ff93..527d672cc6a 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -334,6 +334,7 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends ( if ( doc.docstatus == 1 && !doc.is_return && + doc.per_returned != 100 && doc.status != "Closed" && flt(doc.per_billed) < 100 && frappe.model.can_create("Sales Invoice") diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index ae2c9dae53f..28bc77874bd 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -10,6 +10,8 @@ from frappe.contacts.doctype.address.address import get_company_address from frappe.desk.notifications import clear_doctype_notifications from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values +from frappe.query_builder import DocType +from frappe.query_builder.functions import Abs, Sum from frappe.utils import cint, flt from erpnext.accounts.party import get_due_date @@ -792,35 +794,39 @@ def get_list_context(context=None): def get_invoiced_qty_map(delivery_note): """returns a map: {dn_detail: invoiced_qty}""" - invoiced_qty_map = {} + sii = DocType("Sales Invoice Item") - for dn_detail, qty in frappe.db.sql( - """select dn_detail, qty from `tabSales Invoice Item` - where delivery_note=%s and docstatus=1""", - delivery_note, - ): - if not invoiced_qty_map.get(dn_detail): - invoiced_qty_map[dn_detail] = 0 - invoiced_qty_map[dn_detail] += qty + invoiced_qty_map = frappe._dict( + ( + frappe.qb.from_(sii) + .select(sii.dn_detail, Sum(sii.qty).as_("qty")) + .where((sii.delivery_note == delivery_note) & (sii.docstatus == 1)) + .groupby(sii.dn_detail) + ).run() + ) return invoiced_qty_map def get_returned_qty_map(delivery_note): """returns a map: {so_detail: returned_qty}""" + dn = DocType("Delivery Note") + dni = DocType("Delivery Note Item") + returned_qty_map = frappe._dict( - frappe.db.sql( - """select dn_item.dn_detail, sum(abs(dn_item.qty)) as qty - from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn - where dn.name = dn_item.parent - and dn.docstatus = 1 - and dn.is_return = 1 - and dn.return_against = %s - and dn_item.qty <= 0 - group by dn_item.item_code - """, - delivery_note, - ) + ( + frappe.qb.from_(dni) + .join(dn) + .on(dn.name == dni.parent) + .select(dni.dn_detail, Sum(Abs(dni.qty)).as_("qty")) + .where( + (dn.docstatus == 1) + & (dn.is_return == 1) + & (dn.return_against == delivery_note) + & (dni.qty <= 0) + ) + .groupby(dni.dn_detail) + ).run() ) return returned_qty_map diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 61ec1dd48d4..d5b2d42b8f5 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -2659,6 +2659,127 @@ class TestDeliveryNote(IntegrationTestCase): status = frappe.db.get_value("Serial No", row, "status") self.assertEqual(status, "Active") + def test_sales_return_for_product_bundle(self): + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return + from erpnext.stock.doctype.item.test_item import make_item + + rm_items = [] + for item_code, properties in { + "_Packed Service Item": {"is_stock_item": 0}, + "_Packed FG Item New 1": { + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "SN-PACKED-1-.#####", + }, + "_Packed FG Item New 2": { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-PACKED-2-.#####", + }, + "_Packed FG Item New 3": { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-PACKED-3-.#####", + "has_serial_no": 1, + "serial_no_series": "SN-PACKED-3-.#####", + }, + }.items(): + if not frappe.db.exists("Item", item_code): + make_item(item_code, properties) + + if item_code != "_Packed Service Item": + rm_items.append(item_code) + + for rate in [100, 200]: + make_stock_entry(item=item_code, target="_Test Warehouse - _TC", qty=5, rate=rate) + + make_product_bundle("_Packed Service Item", rm_items) + dn = create_delivery_note( + item_code="_Packed Service Item", + warehouse="_Test Warehouse - _TC", + qty=5, + ) + + serial_batch_map = {} + for row in dn.packed_items: + self.assertTrue(row.serial_and_batch_bundle) + if row.item_code not in serial_batch_map: + serial_batch_map[row.item_code] = frappe._dict( + { + "serial_nos": [], + "batches": defaultdict(int), + "serial_no_valuation": defaultdict(float), + "batch_no_valuation": defaultdict(float), + } + ) + + doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for entry in doc.entries: + if entry.serial_no: + serial_batch_map[row.item_code].serial_nos.append(entry.serial_no) + serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no] = entry.incoming_rate + if entry.batch_no: + serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty + serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no] = entry.incoming_rate + + dn1 = make_sales_return(dn.name) + dn1.items[0].qty = -2 + dn1.submit() + dn1.reload() + + for row in dn1.packed_items: + doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for entry in doc.entries: + if entry.serial_no: + self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no], + ) + serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no) + serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no) + + elif entry.batch_no: + serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty + self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches) + self.assertEqual(entry.qty, 2.0) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no], + ) + + dn2 = make_sales_return(dn.name) + dn2.items[0].qty = -3 + dn2.submit() + dn2.reload() + + for row in dn2.packed_items: + doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for entry in doc.entries: + if entry.serial_no: + self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no], + ) + serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no) + serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no) + + elif entry.batch_no: + serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty + self.assertEqual(serial_batch_map[row.item_code].batches[entry.batch_no], 0.0) + + self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches) + + self.assertEqual(entry.qty, 3.0) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no], + ) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 1e0e2d4bbd9..525b083f5f0 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -221,7 +221,13 @@ frappe.ui.form.on("Item", { const stock_exists = frm.doc.__onload && frm.doc.__onload.stock_exists ? 1 : 0; - ["is_stock_item", "has_serial_no", "has_batch_no", "has_variants"].forEach((fieldname) => { + [ + "is_stock_item", + "is_customer_provided_item", + "has_serial_no", + "has_batch_no", + "has_variants", + ].forEach((fieldname) => { frm.set_df_property(fieldname, "read_only", stock_exists); }); diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index e9f49cfabca..cc89b85b823 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -586,7 +586,7 @@ "label": "Is Customer Provided Item" }, { - "depends_on": "eval:doc.is_customer_provided_item==1", + "depends_on": "eval:doc.is_customer_provided_item", "fieldname": "customer", "fieldtype": "Link", "label": "Customer", @@ -771,10 +771,9 @@ }, { "default": "0", - "description": "If subcontracted to a vendor", "fieldname": "is_sub_contracted_item", "fieldtype": "Check", - "label": "Supply Raw Materials for Purchase", + "label": "Is Subcontracted Item", "oldfieldname": "is_sub_contracted_item", "oldfieldtype": "Select" }, @@ -954,7 +953,7 @@ "image_field": "image", "links": [], "make_attachments_public": 1, - "modified": "2025-10-01 16:58:40.946604", + "modified": "2025-10-13 16:58:40.946604", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 04310bdf188..f91beb8b488 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -965,7 +965,12 @@ class Item(Document): if self.is_new(): return - restricted_fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no") + restricted_fields = ( + "has_serial_no", + "is_stock_item", + "valuation_method", + "has_batch_no", + ) values = frappe.db.get_value("Item", self.name, restricted_fields, as_dict=True) if not values: diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index 4f69bb59c97..2d6d1947534 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -67,6 +67,10 @@ class PackedItem(Document): def make_packing_list(doc): "Make/Update packing list for Product Bundle Item." + + if doc.get("is_subcontracted"): + return + if doc.get("_action") and doc._action == "update_after_submit": return diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 026965d8870..9c7035feafe 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -200,6 +200,7 @@ erpnext.stock.PurchaseReceiptController = class PurchaseReceiptController extend } onload() { + super.onload(); this.frm.set_query("supplier", function () { return { filters: { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 3e9ad4c5836..c512ca97eac 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1215,7 +1215,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate buying_settings = frappe.get_single("Buying Settings") over_billing_allowance = frappe.get_single_value("Accounts Settings", "over_billing_allowance") - total_amount, total_billed_amount = 0, 0 + total_amount, total_billed_amount, pi_landed_cost_amount = 0, 0, 0 item_wise_returned_qty = get_item_wise_returned_qty(pr_doc) if adjust_incoming_rate: @@ -1255,6 +1255,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate ) * item.qty adjusted_amt = flt(adjusted_amt * flt(pr_doc.conversion_rate), item.precision("amount")) + pi_landed_cost_amount += adjusted_amt item.db_set("amount_difference_with_purchase_invoice", adjusted_amt, update_modified=False) elif amount and item.billed_amt > amount: per_over_billed = (flt(item.billed_amt / amount, 2) * 100) - 100 @@ -1265,6 +1266,9 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate ) ) + if pi_landed_cost_amount < 0: + total_billed_amount += abs(pi_landed_cost_amount) + percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6) pr_doc.db_set("per_billed", percent_billed) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 61fb9336b97..f24c826d508 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -778,7 +778,8 @@ "fieldtype": "Data", "hidden": 1, "label": "Material Request Item", - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "expense_account", @@ -1039,7 +1040,8 @@ "fieldtype": "Link", "label": "Rejected Serial and Batch Bundle", "no_copy": 1, - "options": "Serial and Batch Bundle" + "options": "Serial and Batch Bundle", + "search_index": 1 }, { "depends_on": "eval:doc.use_serial_batch_fields === 0", @@ -1148,7 +1150,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2025-03-07 10:25:15.554985", + "modified": "2025-10-14 12:58:20.384056", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", @@ -1160,4 +1162,4 @@ "sort_field": "creation", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} 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 386e89af49b..49962a6827a 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 @@ -7,6 +7,8 @@ import json from collections import Counter, defaultdict import frappe +import frappe.query_builder +import frappe.query_builder.functions from frappe import _, _dict, bold from frappe.model.document import Document from frappe.model.naming import make_autoname @@ -295,7 +297,7 @@ class SerialandBatchBundle(Document): } ) - if self.returned_against and self.docstatus == 1: + if (self.returned_against or self.voucher_type == "Stock Reconciliation") and self.docstatus == 1: kwargs["ignore_voucher_detail_no"] = self.voucher_detail_no if self.docstatus == 1: @@ -638,6 +640,17 @@ class SerialandBatchBundle(Document): if not rate and self.voucher_detail_no and self.voucher_no: rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field) + is_packed_item = False + if rate is None and child_table in ["Delivery Note Item", "Sales Invoice Item"]: + rate = frappe.db.get_value( + "Packed Item", + self.voucher_detail_no, + "incoming_rate", + ) + + if rate is not None: + is_packed_item = True + stock_queue = [] batches = [] if prev_sle and prev_sle.stock_queue: @@ -659,6 +672,9 @@ class SerialandBatchBundle(Document): elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference: continue + if is_packed_item and d.incoming_rate: + rate = d.incoming_rate + d.incoming_rate = flt(rate) if d.qty: d.stock_value_difference = flt(d.qty) * d.incoming_rate @@ -1902,10 +1918,9 @@ def get_serial_and_batch_ledger(**kwargs): def get_auto_data(**kwargs): kwargs = frappe._dict(kwargs) if cint(kwargs.has_serial_no): - return get_available_serial_nos(kwargs) - + return get_serial_nos_from_sre(kwargs) if kwargs.scio_detail else get_available_serial_nos(kwargs) elif cint(kwargs.has_batch_no): - return get_auto_batch_nos(kwargs) + return get_batch_nos_from_sre(kwargs) if kwargs.scio_detail else get_auto_batch_nos(kwargs) def get_available_batches_qty(available_batches): @@ -2007,6 +2022,28 @@ def get_available_serial_nos(kwargs): ) +def get_serial_nos_from_sre(kwargs): + table = frappe.qb.DocType("Stock Reservation Entry") + child_table = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(table) + .join(child_table) + .on(table.name == child_table.parent) + .select(child_table.serial_no, child_table.batch_no, child_table.warehouse) + .where( + (table.docstatus == 1) + & (table.voucher_detail_no == kwargs.scio_detail) + & (child_table.qty != child_table.delivered_qty) + ) + .limit(cint(kwargs.qty) or 10000000) + ) + if kwargs.based_on == "LIFO": + query = query.orderby(child_table.creation, order=frappe.query_builder.Order.desc) + else: + query = query.orderby(child_table.creation) + return query.run(as_dict=True) + + def get_non_expired_batches(batches): filters = {} if isinstance(batches, list): @@ -2080,13 +2117,13 @@ def get_bundle_wise_serial_nos(data, kwargs): def get_reserved_voucher_details(kwargs): reserved_voucher_details = [] - value = { - "Delivery Note": ["Delivery Note Item", "against_sales_order"], - "Stock Entry": ["Stock Entry", "work_order"], - "Work Order": ["Work Order", "production_plan"], + field_mapper = { + "Delivery Note": [["Delivery Note Item", "against_sales_order"]], + "Stock Entry": [["Stock Entry", "work_order"], ["Stock Entry", "subcontracting_inward_order"]], + "Work Order": [["Work Order", "production_plan"], ["Work Order", "subcontracting_inward_order"]], }.get(kwargs.get("sabb_voucher_type")) - if not value or not kwargs.get("sabb_voucher_no"): + if not field_mapper or not kwargs.get("sabb_voucher_no"): return reserved_voucher_details voucher_based_filters = { @@ -2105,11 +2142,15 @@ def get_reserved_voucher_details(kwargs): }, }.get(kwargs.get("sabb_voucher_type")) - reserved_voucher_details = frappe.get_all( - value[0], - pluck=value[1], - filters=voucher_based_filters, - ) + reserved_voucher_details = [] + for row in field_mapper: + reserved_voucher_details.extend( + frappe.get_all( + row[0], + pluck=row[1], + filters=voucher_based_filters, + ) + ) return reserved_voucher_details @@ -2337,15 +2378,15 @@ def get_reserved_batches_for_sre(kwargs) -> dict: if kwargs.batch_no: if isinstance(kwargs.batch_no, list): - query = query.where(sb_entry.batch_no.notin(kwargs.batch_no)) + query = query.where(sb_entry.batch_no.isin(kwargs.batch_no)) else: - query = query.where(sb_entry.batch_no != kwargs.batch_no) + query = query.where(sb_entry.batch_no == kwargs.batch_no) if kwargs.warehouse: if isinstance(kwargs.warehouse, list): - query = query.where(sre.warehouse.notin(kwargs.warehouse)) + query = query.where(sre.warehouse.isin(kwargs.warehouse)) else: - query = query.where(sre.warehouse != kwargs.warehouse) + query = query.where(sre.warehouse == kwargs.warehouse) if kwargs.ignore_voucher_nos: query = query.where(sre.name.notin(kwargs.ignore_voucher_nos)) @@ -2415,6 +2456,43 @@ def get_auto_batch_nos(kwargs): return get_qty_based_available_batches(available_batches, qty) +def get_batch_nos_from_sre(kwargs): + from frappe.query_builder.functions import Max, Min, Sum + + table = frappe.qb.DocType("Stock Reservation Entry") + child_table = frappe.qb.DocType("Serial and Batch Entry") + + if kwargs.based_on == "LIFO": + creation_field = Max(child_table.creation).as_("sort_creation") + order = frappe.query_builder.Order.desc + else: + creation_field = Min(child_table.creation).as_("sort_creation") + order = frappe.query_builder.Order.asc + + query = ( + frappe.qb.from_(table) + .join(child_table) + .on(table.name == child_table.parent) + .select( + child_table.batch_no, + child_table.warehouse, + Sum(child_table.qty - child_table.delivered_qty).as_("qty"), + creation_field, + ) + .where( + (table.docstatus == 1) + & (table.voucher_detail_no == kwargs.scio_detail) + & (child_table.qty != child_table.delivered_qty) + ) + .groupby(child_table.batch_no, child_table.warehouse) + .orderby("sort_creation", order=order) + .orderby(child_table.batch_no, order=frappe.query_builder.Order.asc) + ) + + result = query.run(as_dict=True) + return get_qty_based_available_batches(result, flt(kwargs.qty)) if flt(kwargs.qty) else result + + def get_batches_to_be_considered(sales_order_name): parent = frappe.qb.DocType("Stock Reservation Entry") child = frappe.qb.DocType("Serial and Batch Entry") @@ -2788,7 +2866,10 @@ def get_stock_ledgers_for_serial_nos(kwargs): else: query = query.where(stock_ledger_entry[field] == kwargs.get(field)) - if kwargs.voucher_no: + if kwargs.ignore_voucher_detail_no: + query = query.where(stock_ledger_entry.voucher_detail_no != kwargs.ignore_voucher_detail_no) + + elif kwargs.voucher_no: query = query.where(stock_ledger_entry.voucher_no != kwargs.voucher_no) return query.run(as_dict=True) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 485fe11cfbc..ae999fd97e7 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -87,15 +87,13 @@ frappe.ui.form.on("Stock Entry", { frappe.throw(__("Please enter Item Code to get Batch Number")); } else { if ( - in_list( - [ - "Material Transfer for Manufacture", - "Manufacture", - "Repack", - "Send to Subcontractor", - ], - doc.purpose - ) + [ + "Material Transfer for Manufacture", + "Manufacture", + "Repack", + "Send to Subcontractor", + "Receive from Customer", + ].includes(doc.purpose) ) { filters = { item_code: item.item_code, @@ -214,7 +212,7 @@ frappe.ui.form.on("Stock Entry", { refresh: function (frm) { frm.trigger("get_items_from_transit_entry"); - if (!frm.doc.docstatus) { + if (!frm.doc.docstatus && !frm.doc.subcontracting_inward_order) { frm.trigger("validate_purpose_consumption"); frm.add_custom_button( __("Material Request"), @@ -299,7 +297,7 @@ frappe.ui.form.on("Stock Entry", { } } - if (frm.doc.docstatus === 0) { + if (frm.doc.docstatus === 0 && !frm.doc.subcontracting_inward_order) { frm.add_custom_button( __("Purchase Invoice"), function () { @@ -367,7 +365,11 @@ frappe.ui.form.on("Stock Entry", { ); } - if (frm.doc.docstatus === 0 && frm.doc.purpose == "Material Issue") { + if ( + frm.doc.docstatus === 0 && + frm.doc.purpose == "Material Issue" && + !frm.doc.subcontracting_inward_order + ) { frm.add_custom_button( __("Expired Batches"), function () { @@ -420,6 +422,17 @@ frappe.ui.form.on("Stock Entry", { } frm.events.set_route_options_for_new_doc(frm); + + frm.set_df_property( + "items", + "cannot_add_rows", + frm.doc.subcontracting_inward_order && + [ + "Return Raw Material to Customer", + "Subcontracting Return", + "Subcontracting Delivery", + ].includes(frm.doc.purpose) + ); }, set_route_options_for_new_doc(frm) { @@ -445,7 +458,7 @@ frappe.ui.form.on("Stock Entry", { }, get_items_from_transit_entry: function (frm) { - if (frm.doc.docstatus === 0) { + if (frm.doc.docstatus === 0 && !frm.doc.subcontracting_inward_order) { frm.add_custom_button( __("Transit Entry"), function () { @@ -609,7 +622,8 @@ frappe.ui.form.on("Stock Entry", { frm.doc.docstatus === 0 && ["Material Issue", "Material Receipt", "Material Transfer", "Send to Subcontractor"].includes( frm.doc.purpose - ) + ) && + !frm.doc.subcontracting_inward_order ) { frm.add_custom_button( __("Bill of Materials"), @@ -622,10 +636,6 @@ frappe.ui.form.on("Stock Entry", { }, get_items_from_bom: function (frm) { - let filters = function () { - return { filters: { docstatus: 1 } }; - }; - let fields = [ { fieldname: "bom", @@ -1084,10 +1094,28 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle this.frm.add_fetch("purchase_order", "supplier", "supplier"); } else { this.frm.add_fetch("subcontracting_order", "supplier", "supplier"); + this.frm.add_fetch("subcontracting_inward_order", "customer", "customer"); } frappe.dynamic_link = { doc: this.frm.doc, fieldname: "supplier", doctype: "Supplier" }; this.frm.set_query("supplier_address", erpnext.queries.address_query); + + const operator = this.frm.doc.subcontracting_inward_order ? "in" : "not in"; + this.frm.set_query("stock_entry_type", function () { + return { + filters: { + purpose: [ + operator, + [ + "Receive from Customer", + "Return Raw Material to Customer", + "Subcontracting Delivery", + "Subcontracting Return", + ], + ], + }, + }; + }); } onload_post_render() { @@ -1100,7 +1128,6 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle } refresh() { - var me = this; erpnext.toggle_naming_series(); this.toggle_related_fields(this.frm.doc); this.toggle_enable_bom(); @@ -1369,6 +1396,8 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle doc.delivery_note_no = doc.sales_invoice_no = null; + } else if (doc.purpose === "Receive from Customer") { + doc.supplier = doc.supplier_name = doc.supplier_address = doc.purchase_receipt_no = null; } else { doc.customer = doc.customer_name = diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index c2c17416c3e..23c393bbca9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -17,6 +17,7 @@ "job_card", "purchase_order", "subcontracting_order", + "subcontracting_inward_order", "delivery_note_no", "sales_invoice_no", "pick_list", @@ -129,7 +130,7 @@ "label": "Purpose", "oldfieldname": "purpose", "oldfieldtype": "Select", - "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble", + "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble\nReceive from Customer\nReturn Raw Material to Customer\nSubcontracting Delivery\nSubcontracting Return", "read_only": 1, "search_index": 1 }, @@ -706,17 +707,25 @@ "depends_on": "eval:doc.purpose == \"Material Transfer for Manufacture\"", "fieldname": "is_additional_transfer_entry", "fieldtype": "Check", - "hidden": 1, "label": "Is Additional Transfer Entry", "read_only": 1 + }, + { + "depends_on": "subcontracting_inward_order", + "fieldname": "subcontracting_inward_order", + "fieldtype": "Link", + "label": "Subcontracting Inward Order", + "options": "Subcontracting Inward Order", + "read_only": 1 } ], + "grid_page_length": 50, "icon": "fa fa-file-text", "idx": 1, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-09-29 15:56:21.344296", + "modified": "2025-10-13 15:09:23.905118", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5098f63a68f..6c42f878f1b 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2,7 +2,6 @@ # License: GNU General Public License v3. See license.txt -import copy import json from collections import defaultdict @@ -79,11 +78,12 @@ class MaxSampleAlreadyRetainedError(frappe.ValidationError): from erpnext.controllers.stock_controller import StockController +from erpnext.controllers.subcontracting_inward_controller import SubcontractingInwardController form_grid_templates = {"items": "templates/form_grid/stock_entry_grid.html"} -class StockEntry(StockController): +class StockEntry(StockController, SubcontractingInwardController): # begin: auto-generated types # This code is auto-generated. Do not modify anything in this block. @@ -138,6 +138,10 @@ class StockEntry(StockController): "Repack", "Send to Subcontractor", "Disassemble", + "Receive from Customer", + "Return Raw Material to Customer", + "Subcontracting Delivery", + "Subcontracting Return", ] remarks: DF.Text | None sales_invoice_no: DF.Link | None @@ -147,6 +151,7 @@ class StockEntry(StockController): source_address_display: DF.TextEditor | None source_warehouse_address: DF.Link | None stock_entry_type: DF.Link + subcontracting_inward_order: DF.Link | None subcontracting_order: DF.Link | None supplier: DF.Link | None supplier_address: DF.Link | None @@ -174,6 +179,15 @@ class StockEntry(StockController): "order_supplied_items_field": "Purchase Order Item Supplied", } ) + elif self.subcontracting_inward_order: + self.subcontract_data = frappe._dict( + { + "order_doctype": "Subcontracting Inward Order", + "order_field": "subcontracting_inward_order", + "rm_detail_field": "scio_detail", + "order_received_items_field": "Subcontracting Inward Order Received Item", + } + ) else: self.subcontract_data = frappe._dict( { @@ -248,15 +262,20 @@ class StockEntry(StockController): self.reset_default_field_value("from_warehouse", "items", "s_warehouse") self.reset_default_field_value("to_warehouse", "items", "t_warehouse") - def on_submit(self): self.validate_closed_subcontracting_order() + self.validate_subcontract_order() + + super().validate_subcontracting_inward() + + def on_submit(self): self.make_bundle_using_old_serial_batch_fields() self.update_work_order() self.update_disassembled_order() + self.adjust_stock_reservation_entries_for_return() + self.update_sre_for_subcontracting_delivery() self.update_stock_ledger() self.make_stock_reserve_for_wip_and_fg() - self.validate_subcontract_order() self.update_subcontract_order_supplied_items() self.update_subcontracting_order_status() self.update_pick_list_status() @@ -273,6 +292,8 @@ class StockEntry(StockController): if self.purpose == "Material Transfer" and self.outgoing_stock_entry: self.set_material_request_transfer_status("Completed") + super().on_submit_subcontracting_inward() + def on_cancel(self): self.delink_asset_repair_sabb() self.validate_closed_subcontracting_order() @@ -284,7 +305,8 @@ class StockEntry(StockController): self.validate_work_order_status() self.update_work_order() - self.update_disassembled_order(is_cancel=True) + self.update_disassembled_order() + self.cancel_stock_reservation_entries_for_inward() self.update_stock_ledger() self.ignore_linked_doctypes = ( @@ -299,6 +321,8 @@ class StockEntry(StockController): self.update_cost_in_project() self.update_transferred_qty() self.update_quality_inspection() + self.adjust_stock_reservation_entries_for_return() + self.update_sre_for_subcontracting_delivery() self.delete_auto_created_batches() self.delete_linked_stock_entry() @@ -307,6 +331,8 @@ class StockEntry(StockController): if self.purpose == "Material Transfer" and self.outgoing_stock_entry: self.set_material_request_transfer_status("In Transit") + super().on_cancel_subcontracting_inward() + def on_update(self): super().on_update() self.set_serial_and_batch_bundle() @@ -369,11 +395,17 @@ class StockEntry(StockController): "Send to Subcontractor", "Material Consumption for Manufacture", "Disassemble", + "Receive from Customer", + "Return Raw Material to Customer", + "Subcontracting Delivery", + "Subcontracting Return", ] if self.purpose not in valid_purposes: frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes))) + super().validate_purpose() + def delete_linked_stock_entry(self): if self.purpose == "Send to Warehouse": for d in frappe.get_all( @@ -507,6 +539,9 @@ class StockEntry(StockController): flt(item.qty) * flt(item.conversion_factor), self.precision("transfer_qty", item) ) + if self.purpose == "Subcontracting Delivery": + item.expense_account = frappe.get_value("Company", self.company, "default_expense_account") + def validate_fg_completed_qty(self): if self.purpose != "Manufacture": return @@ -576,7 +611,10 @@ class StockEntry(StockController): title=_("Difference Account in Items Table"), ) - if self.purpose != "Material Issue" and acc_details.account_type == "Cost of Goods Sold": + if ( + self.purpose not in ["Material Issue", "Subcontracting Delivery"] + and acc_details.account_type == "Cost of Goods Sold" + ): frappe.msgprint( _( "At row #{0}: you have selected the Difference Account {1}, which is a Cost of Goods Sold type account. Please select a different account" @@ -595,6 +633,8 @@ class StockEntry(StockController): "Send to Subcontractor", "Material Transfer for Manufacture", "Material Consumption for Manufacture", + "Return Raw Material to Customer", + "Subcontracting Delivery", ] target_mandatory = [ @@ -602,6 +642,8 @@ class StockEntry(StockController): "Material Transfer", "Send to Subcontractor", "Material Transfer for Manufacture", + "Receive from Customer", + "Subcontracting Return", ] validate_for_manufacture = any([d.bom_no for d in self.get("items")]) @@ -862,7 +904,7 @@ class StockEntry(StockController): if d.s_warehouse or d.set_basic_rate_manually: continue - if d.allow_zero_valuation_rate: + if d.allow_zero_valuation_rate and self.purpose != "Receive from Customer": d.basic_rate = 0.0 items.append(d.item_code) @@ -1086,13 +1128,15 @@ class StockEntry(StockController): self.purpose = frappe.get_cached_value("Stock Entry Type", self.stock_entry_type, "purpose") def make_serial_and_batch_bundle_for_outward(self): - if self.docstatus == 0: - return - serial_or_batch_items = get_serial_or_batch_items(self.items) if not serial_or_batch_items: return + serial_nos, batch_nos = self.set_serial_batch_fields_for_subcontracting_inward() + + if self.docstatus == 0: + return + already_picked_serial_nos = [] for row in self.items: @@ -1118,7 +1162,9 @@ class StockEntry(StockController): "ignore_serial_nos": already_picked_serial_nos, "qty": row.transfer_qty * -1, } - ).update_serial_and_batch_entries() + ).update_serial_and_batch_entries( + serial_nos=serial_nos.get(row.name), batch_nos=batch_nos.get(row.name) + ) elif not row.serial_and_batch_bundle: bundle_doc = SerialBatchCreation( { @@ -1133,7 +1179,9 @@ class StockEntry(StockController): "company": self.company, "do_not_submit": True, } - ).make_serial_and_batch_bundle() + ).make_serial_and_batch_bundle( + serial_nos=serial_nos.get(row.name), batch_nos=batch_nos.get(row.name) + ) if not bundle_doc: continue @@ -1146,6 +1194,32 @@ class StockEntry(StockController): row.serial_and_batch_bundle = bundle_doc.name + def set_serial_batch_fields_for_subcontracting_inward(self): + serial_nos, batch_nos = frappe._dict(), frappe._dict() + for row in self.items: + if self.purpose in [ + "Return Raw Material to Customer", + "Subcontracting Delivery", + "Subcontracting Return", + ]: + if not row.serial_and_batch_bundle: + serial_nos_list, batch_nos_list = self.get_serial_nos_and_batches_from_sres( + row.scio_detail, only_pending=self.purpose != "Subcontracting Return" + ) + + if len(batch_nos_list) > 1: + row.use_serial_batch_fields = 0 + + if row.use_serial_batch_fields: + if serial_nos_list and not row.serial_no: + row.serial_no = "\n".join(serial_nos_list) + if batch_nos_list and not row.batch_no: + row.batch_no = next(iter(batch_nos_list.keys())) + + serial_nos[row.name], batch_nos[row.name] = serial_nos_list, batch_nos_list + + return serial_nos, batch_nos + def validate_subcontract_order(self): """Throw exception if more raw material is transferred against Subcontract Order than in the raw materials supplied table""" @@ -1329,8 +1403,12 @@ class StockEntry(StockController): ) def validate_closed_subcontracting_order(self): - if self.get("subcontracting_order"): - check_on_hold_or_closed_status("Subcontracting Order", self.subcontracting_order) + order = self.get("subcontracting_order") or self.get("subcontracting_inward_order") + if order: + check_on_hold_or_closed_status( + "Subcontracting Order" if self.get("subcontracting_order") else "Subcontracting Inward Order", + order, + ) def mark_finished_and_scrap_items(self): if self.purpose != "Repack" and any( @@ -1740,12 +1818,12 @@ class StockEntry(StockController): if not pro_doc.operations: pro_doc.set_actual_dates() - def update_disassembled_order(self, is_cancel=False): + def update_disassembled_order(self): if not self.work_order: return if self.purpose == "Disassemble" and self.fg_completed_qty: pro_doc = frappe.get_doc("Work Order", self.work_order) - pro_doc.run_method("update_disassembled_qty", self.fg_completed_qty, is_cancel) + pro_doc.run_method("update_disassembled_qty", self.fg_completed_qty, self._action == "cancel") def make_stock_reserve_for_wip_and_fg(self): if self.is_stock_reserve_for_work_order(): @@ -1754,6 +1832,7 @@ class StockEntry(StockController): self.purpose == "Manufacture" and not pro_doc.sales_order and not pro_doc.production_plan_sub_assembly_item + and not pro_doc.subcontracting_inward_order ): return @@ -1774,7 +1853,7 @@ class StockEntry(StockController): def is_stock_reserve_for_work_order(self): if ( self.work_order - and self.stock_entry_type in ["Material Transfer for Manufacture", "Manufacture"] + and self.purpose in ["Material Transfer for Manufacture", "Manufacture"] and frappe.get_cached_value("Work Order", self.work_order, "reserve_stock") ): return True @@ -2106,10 +2185,13 @@ class StockEntry(StockController): if not frappe.get_cached_value("Work Order", self.work_order, "reserve_stock"): return + skip_transfer = frappe.get_cached_value("Work Order", self.work_order, "skip_transfer") + if ( self.purpose not in ["Material Transfer for Manufacture"] and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on") != "BOM" + and not skip_transfer ): return @@ -2172,6 +2254,10 @@ class StockEntry(StockController): self.append("items", new_row) sorted_items = sorted(self.items, key=lambda x: x.item_code) + if self.purpose == "Manufacture": + # ensure finished item at last + sorted_items = sorted(sorted_items, key=lambda x: (x.t_warehouse)) + idx = 0 for row in sorted_items: idx += 1 @@ -2821,7 +2907,8 @@ class StockEntry(StockController): child_qty = flt(item_row["qty"], precision) if not self.is_return and child_qty <= 0 and not item_row.get("is_scrap_item"): - continue + if self.purpose != "Receive from Customer": + continue se_child = self.append("items") stock_uom = item_row.get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom") @@ -2830,7 +2917,7 @@ class StockEntry(StockController): se_child.item_code = item_row.get("item_code") or cstr(d) se_child.uom = item_row["uom"] if item_row.get("uom") else stock_uom se_child.stock_uom = stock_uom - se_child.qty = child_qty + se_child.qty = child_qty if child_qty > 0 else 0 se_child.allow_alternative_item = item_row.get("allow_alternative_item", 0) se_child.subcontracted_item = item_row.get("main_item_code") se_child.cost_center = item_row.get("cost_center") or get_default_cost_center( @@ -2840,6 +2927,7 @@ class StockEntry(StockController): se_child.is_scrap_item = item_row.get("is_scrap_item", 0) se_child.po_detail = item_row.get("po_detail") se_child.sco_rm_detail = item_row.get("sco_rm_detail") + se_child.scio_detail = item_row.get("scio_detail") for field in [ self.subcontract_data.rm_detail_field, diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 365db3e2332..d0143941660 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1317,18 +1317,9 @@ class TestStockEntry(IntegrationTestCase): posting_date="2021-07-02", # Illegal SE purpose="Material Transfer", ), - dict( - item_code=item_code, - qty=2, - from_warehouse=warehouse_names[0], - to_warehouse=warehouse_names[1], - batch_no=batch_no, - posting_date="2021-07-02", # Illegal SE - purpose="Material Transfer", - ), ] - self.assertRaises(frappe.ValidationError, create_stock_entries, sequence_of_entries) + self.assertRaises(NegativeStockError, create_stock_entries, sequence_of_entries) @IntegrationTestCase.change_settings("Stock Settings", {"allow_negative_stock": 0}) def test_future_negative_sle_batch(self): diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 198585dbb53..8092c188a58 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -38,6 +38,7 @@ "sample_quantity", "rates_section", "basic_rate", + "customer_provided_item_cost", "additional_cost", "landed_cost_voucher_amount", "valuation_rate", @@ -75,6 +76,7 @@ "ste_detail", "po_detail", "sco_rm_detail", + "scio_detail", "putaway_rule", "column_break_51", "reference_purchase_receipt", @@ -97,7 +99,8 @@ "label": "Source Warehouse", "oldfieldname": "s_warehouse", "oldfieldtype": "Link", - "options": "Warehouse" + "options": "Warehouse", + "read_only_depends_on": "eval:in_list([\"Subcontracting Delivery\", \"Return Raw Material to Customer\"], parent.purpose)" }, { "fieldname": "col_break1", @@ -189,6 +192,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Basic Rate (as per Stock UOM)", + "non_negative": 1, "oldfieldname": "incoming_rate", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -447,7 +451,8 @@ "no_copy": 1, "options": "Stock Entry", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "ste_detail", @@ -455,7 +460,8 @@ "label": "Stock Entry Child", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "column_break_51", @@ -557,7 +563,8 @@ "default": "0", "fieldname": "is_finished_item", "fieldtype": "Check", - "label": "Is Finished Item" + "label": "Is Finished Item", + "read_only_depends_on": "eval:in_list([\"Subcontracting Delivery\", \"Subcontracting Return\"], parent.purpose)" }, { "fieldname": "job_card_item", @@ -614,6 +621,26 @@ "label": "Landed Cost Voucher Amount", "no_copy": 1, "read_only": 1 + }, + { + "depends_on": "eval:parent.purpose == \"Receive from Customer\"", + "fieldname": "customer_provided_item_cost", + "fieldtype": "Currency", + "label": "Customer Provided Item Cost", + "no_copy": 1, + "non_negative": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "scio_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "SCIO Detail", + "print_hide": 1, + "read_only": 1, + "search_index": 1 } ], "grid_page_length": 50, @@ -621,7 +648,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-06-09 10:24:34.717676", + "modified": "2025-10-14 14:10:38.373099", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py index b6c1dcca180..f5783d96a88 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py @@ -27,6 +27,7 @@ class StockEntryDetail(Document): bom_no: DF.Link | None conversion_factor: DF.Float cost_center: DF.Link | None + customer_provided_item_cost: DF.Currency description: DF.TextEditor | None expense_account: DF.Link | None has_item_scanned: DF.Check @@ -53,6 +54,7 @@ class StockEntryDetail(Document): retain_sample: DF.Check s_warehouse: DF.Link | None sample_quantity: DF.Int + scio_detail: DF.Data | None sco_rm_detail: DF.Data | None serial_and_batch_bundle: DF.Link | None serial_no: DF.Text | None diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json index d3026d6be8b..8b52dcd30ca 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json @@ -17,7 +17,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Purpose", - "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble", + "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble\nReceive from Customer\nReturn Raw Material to Customer\nSubcontracting Delivery\nSubcontracting Return", "reqd": 1, "set_only_once": 1 }, @@ -36,8 +36,9 @@ "read_only": 1 } ], + "grid_page_length": 50, "links": [], - "modified": "2025-01-15 16:00:22.696958", + "modified": "2025-09-04 13:03:31.283348", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Type", @@ -86,9 +87,10 @@ } ], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "ASC", "states": [], "track_changes": 1, "translated_doctype": 1 -} \ No newline at end of file +} 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 27ad10aa46b..1e05b680fb1 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py @@ -21,7 +21,6 @@ class StockEntryType(Document): add_to_transit: DF.Check is_standard: DF.Check purpose: DF.Literal[ - "", "Material Issue", "Material Receipt", "Material Transfer", @@ -31,6 +30,10 @@ class StockEntryType(Document): "Repack", "Send to Subcontractor", "Disassemble", + "Receive from Customer", + "Return Raw Material to Customer", + "Subcontracting Delivery", + "Subcontracting Return", ] # end: auto-generated types @@ -50,6 +53,10 @@ class StockEntryType(Document): "Repack", "Send to Subcontractor", "Disassemble", + "Receive from Customer", + "Return Raw Material to Customer", + "Subcontracting Delivery", + "Subcontracting Return", ]: frappe.throw(f"Stock Entry Type {self.name} cannot be set as standard") diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 205fda5c41e..2969c502200 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -588,6 +588,10 @@ class StockReconciliation(StockController): if row.get(field): key.append(row.get(field)) + for dimension in get_inventory_dimensions(): + if row.get(dimension.get("fieldname")): + key.append(row.get(dimension.get("fieldname"))) + if key in item_warehouse_combinations: self.validation_messages.append( _get_msg(row_num, _("Same item and warehouse combination already entered.")) diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json index c3e99c5addd..5f81d391b59 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json @@ -84,7 +84,7 @@ "no_copy": 1, "oldfieldname": "voucher_type", "oldfieldtype": "Data", - "options": "\nSales Order\nWork Order\nProduction Plan", + "options": "\nSales Order\nWork Order\nSubcontracting Inward Order\nProduction Plan", "print_width": "150px", "read_only": 1, "width": "150px" @@ -288,7 +288,7 @@ "fieldtype": "Select", "label": "From Voucher Type", "no_copy": 1, - "options": "\nPick List\nPurchase Receipt\nStock Entry\nWork Order\nProduction Plan", + "options": "\nPick List\nPurchase Receipt\nStock Entry\nWork Order\nProduction Plan\nSubcontracting Inward Order", "print_hide": 1, "read_only": 1, "report_hide": 1 @@ -344,7 +344,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-08-25 19:48:33.170835", + "modified": "2025-10-12 19:48:33.170835", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reservation Entry", diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py index 1ecfa67dccf..018d0b20dc0 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py @@ -33,7 +33,13 @@ class StockReservationEntry(Document): from_voucher_detail_no: DF.Data | None from_voucher_no: DF.DynamicLink | None from_voucher_type: DF.Literal[ - "", "Pick List", "Purchase Receipt", "Stock Entry", "Work Order", "Production Plan" + "", + "Pick List", + "Purchase Receipt", + "Stock Entry", + "Work Order", + "Production Plan", + "Subcontracting Inward Order", ] has_batch_no: DF.Check has_serial_no: DF.Check @@ -57,7 +63,9 @@ class StockReservationEntry(Document): voucher_detail_no: DF.Data | None voucher_no: DF.DynamicLink | None voucher_qty: DF.Float - voucher_type: DF.Literal["", "Sales Order", "Work Order", "Production Plan"] + voucher_type: DF.Literal[ + "", "Sales Order", "Work Order", "Subcontracting Inward Order", "Production Plan" + ] warehouse: DF.Link | None # end: auto-generated types @@ -236,12 +244,11 @@ class StockReservationEntry(Document): def validate_reservation_based_on_qty(self) -> None: """Validates `Reserved Qty` when `Reservation Based On` is `Qty`.""" - if self.reservation_based_on == "Qty": + if self.reservation_based_on == "Qty" and self.voucher_type != "Subcontracting Inward Order": self.validate_with_allowed_qty(self.reserved_qty) def auto_reserve_serial_and_batch(self, based_on: str | None = None) -> None: """Auto pick Serial and Batch Nos to reserve when `Reservation Based On` is `Serial and Batch`.""" - if ( not self.from_voucher_type and (self.get("_action") == "submit") @@ -409,7 +416,8 @@ class StockReservationEntry(Document): frappe.throw(msg) # Should be called after validating Serial and Batch Nos. - self.validate_with_allowed_qty(qty_to_be_reserved) + if self.voucher_type != "Subcontracting Inward Order": + self.validate_with_allowed_qty(qty_to_be_reserved) self.db_set("reserved_qty", qty_to_be_reserved) def update_reserved_qty_in_voucher( @@ -613,10 +621,10 @@ class StockReservationEntry(Document): data = row_wise_serial_batch[row] if entry.serial_no in data.serial_nos: - entry.delivered_qty = 1 + entry.delivered_qty = flt(1) elif entry.batch_no in data.batch_nos: - entry.delivered_qty = data.batch_nos[entry.batch_no] + entry.delivered_qty = flt(data.batch_nos[entry.batch_no]) entry.db_update() @@ -1215,15 +1223,25 @@ class StockReservation: return available_qty - def transfer_reservation_entries_to(self, docnames, from_doctype, to_doctype): + def transfer_reservation_entries_to( + self, docnames, from_doctype, to_doctype, against_fg_item=None, qty_change=None + ): if isinstance(docnames, str): docnames = [docnames] items_to_reserve = self.get_items_to_reserve(docnames, from_doctype, to_doctype) + + if qty_change: + for key, value in qty_change.items(): + row = next((item for item in items_to_reserve if item.voucher_detail_no == key), None) + if row: + row.qty += value + row.required_qty += value + if not items_to_reserve: return - reservation_entries = self.get_reserved_entries(from_doctype, docnames) + reservation_entries = self.get_reserved_entries(from_doctype, docnames, against_fg_item) if not reservation_entries: return @@ -1386,7 +1404,7 @@ class StockReservation: sre.save() sre.submit() - def get_reserved_entries(self, doctype, docnames): + def get_reserved_entries(self, doctype, docnames, against_fg_item=None): if isinstance(docnames, str): docnames = [docnames] @@ -1426,6 +1444,17 @@ class StockReservation: .orderby(sabb_entry.idx) ) + if against_fg_item: + query = query.where( + sre.voucher_detail_no.isin( + frappe.get_all( + "Subcontracting Inward Order Received Item", + {"reference_name": against_fg_item, "docstatus": 1}, + pluck="name", + ) + ) + ) + return query.run(as_dict=True) def get_items_to_reserve(self, docnames, from_doctype, to_doctype): diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js index 6606a4f11a0..562f45b08cc 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.js +++ b/erpnext/stock/doctype/warehouse/warehouse.js @@ -84,6 +84,10 @@ frappe.ui.form.on("Warehouse", { } frm.toggle_enable(["is_group", "company"], false); + + if (frm.doc.customer) { + frm.set_df_property("customer", "read_only", frm.doc.__onload.stock_exists); + } }, }); diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 9ff2fa2fa2f..9b673be581e 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -18,6 +18,7 @@ "column_break_4", "account", "company", + "customer", "address_and_contact", "address_html", "column_break_10", @@ -258,6 +259,15 @@ "fieldname": "is_rejected_warehouse", "fieldtype": "Check", "label": "Is Rejected Warehouse" + }, + { + "allow_in_quick_entry": 1, + "depends_on": "eval:!doc.disabled", + "description": "Only to be used for Subcontracting Inward.", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer" } ], "grid_page_length": 50, @@ -265,7 +275,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2025-06-26 11:19:04.673115", + "modified": "2025-09-05 14:47:17.140099", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 978f2d4d13c..63a2f209d02 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -29,6 +29,7 @@ class Warehouse(NestedSet): address_line_2: DF.Data | None city: DF.Data | None company: DF.Link + customer: DF.Link | None default_in_transit_warehouse: DF.Link | None disabled: DF.Check email_id: DF.Data | None @@ -58,13 +59,13 @@ class Warehouse(NestedSet): self.name = self.warehouse_name def onload(self): - """load account name for General Ledger Report""" if self.company and cint(frappe.db.get_value("Company", self.company, "enable_perpetual_inventory")): account = self.account or get_warehouse_account(self) if account: self.set_onload("account", account) load_address_and_contact(self) + self.set_onload("stock_exists", self.check_if_sle_exists(non_cancelled_only=True)) def validate(self): self.warn_about_multiple_warehouse_account() @@ -150,8 +151,11 @@ class Warehouse(NestedSet): indicator="orange", ) - def check_if_sle_exists(self): - return frappe.db.exists("Stock Ledger Entry", {"warehouse": self.name}) + def check_if_sle_exists(self, non_cancelled_only=False): + filters = {"warehouse": self.name} + if non_cancelled_only: + filters["is_cancelled"] = 0 + return frappe.db.exists("Stock Ledger Entry", filters) def check_if_child_exists(self): return frappe.db.exists("Warehouse", {"parent_warehouse": self.name}) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 22e0c69e8f4..7f6915ccd6d 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -337,7 +337,7 @@ def validate_item_details(ctx: ItemDetailsCtx, item): throw(_(msg), title=_("Template Item Selected")) - elif ctx.transaction_type == "buying" and ctx.doctype != "Material Request": + elif ctx.doctype != "Material Request": if ctx.is_subcontracted: if ctx.is_old_subcontracting_flow: if item.is_sub_contracted_item != 1: @@ -502,6 +502,9 @@ def get_basic_details(ctx: ItemDetailsCtx, item, overwrite_warehouse=True) -> It } ) + if not item.is_stock_item and not out.expense_account: + out.expense_account = frappe.get_cached_value("Company", ctx.company, "service_expense_account") + default_supplier = get_default_supplier(ctx, item_defaults, item_group_defaults, brand_defaults) if default_supplier: out.supplier = default_supplier diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index b895db99e40..e8cae6c8226 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -1026,13 +1026,23 @@ class SerialBatchCreation: for d in remove_list: package.remove(d) - def make_serial_and_batch_bundle(self): + def make_serial_and_batch_bundle( + self, serial_nos=None, batch_nos=None + ): # passing None instead of [] due to ruff linter error B006 + serial_nos = serial_nos or [] + batch_nos = batch_nos or [] + doc = frappe.new_doc("Serial and Batch Bundle") valid_columns = doc.meta.get_valid_columns() for key, value in self.__dict__.items(): if key in valid_columns: doc.set(key, value) + if serial_nos: + self.serial_nos = serial_nos + if batch_nos: + self.batches = batch_nos + if self.type_of_transaction == "Outward": self.set_auto_serial_batch_entries_for_outward() elif self.type_of_transaction == "Inward": @@ -1081,10 +1091,21 @@ class SerialBatchCreation: self.batch_no = batches[0] self.serial_nos = self.get_auto_created_serial_nos() - def update_serial_and_batch_entries(self): + def update_serial_and_batch_entries( + self, serial_nos=None, batch_nos=None + ): # passing None instead of [] due to ruff linter error B006 + serial_nos = serial_nos or [] + batch_nos = batch_nos or [] + doc = frappe.get_doc("Serial and Batch Bundle", self.serial_and_batch_bundle) doc.type_of_transaction = self.type_of_transaction doc.set("entries", []) + + if serial_nos: + self.serial_nos = serial_nos + if batch_nos: + self.batch_nos = batch_nos + self.set_auto_serial_batch_entries_for_outward() self.set_serial_batch_entries(doc) if not doc.get("entries"): @@ -1293,6 +1314,12 @@ class SerialBatchCreation: if self.get("voucher_type"): voucher_type = self.get("voucher_type") + posting_date = frappe.db.get_value( + voucher_type, + voucher_no, + "posting_date", + ) + for _i in range(abs(cint(self.actual_qty))): serial_no = make_autoname(self.serial_no_series, "Serial No") sr_nos.append(serial_no) @@ -1312,6 +1339,7 @@ class SerialBatchCreation: "Active", voucher_type, voucher_no, + posting_date, self.batch_no, ) ) @@ -1332,6 +1360,7 @@ class SerialBatchCreation: "status", "reference_doctype", "reference_name", + "posting_date", "batch_no", ] @@ -1391,12 +1420,12 @@ def get_batch_current_qty(batch): def throw_negative_batch_validation(batch_no, qty): - frappe.msgprint( + # This validation is important for backdated stock transactions with batch items + frappe.throw( _( "The Batch {0} has negative batch quantity {1}. To fix this, go to the batch and click on Recalculate Batch Qty. If the issue still persists, create an inward entry." ).format(bold(get_link_to_form("Batch", batch_no)), bold(qty)), - title=_("Warning!"), - indicator="orange", + title=_("Negative Stock Error"), ) @@ -1421,3 +1450,28 @@ def get_batchwise_qty(voucher_type, voucher_no): return frappe._dict({}) return frappe._dict(batches) + + +def get_serial_batch_list_from_item(item): + serial_list, batch_list = [], [] + if item.serial_and_batch_bundle: + table = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(table) + .select(table.serial_no, table.batch_no) + .where(table.parent == item.serial_and_batch_bundle) + ) + result = query.run(as_dict=True) + + for row in result: + if row.serial_no and row.serial_no not in serial_list: + serial_list.append(row.serial_no) + if row.batch_no and row.batch_no not in batch_list: + batch_list.append(row.batch_no) + else: + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + serial_list = get_serial_nos(item.serial_no) if item.serial_no else [] + batch_list = [item.batch_no] if item.batch_no else [] + + return serial_list, batch_list diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index bab68615d30..0c355d67d9d 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -736,6 +736,10 @@ class update_entries_after: elif dependant_sle.voucher_type == "Stock Entry" and is_transfer_stock_entry( dependant_sle.voucher_no ): + if self.distinct_item_warehouses[key].get("transfer_entry_to_repost"): + return + + val["transfer_entry_to_repost"] = True self.distinct_item_warehouses[key] = val self.new_items_found = True @@ -892,9 +896,8 @@ class update_entries_after: sle.stock_value = self.wh_data.stock_value sle.stock_queue = json.dumps(self.wh_data.stock_queue) - if not sle.is_adjustment_entry: - sle.stock_value_difference = stock_value_difference - elif sle.is_adjustment_entry and not self.args.get("sle_id"): + sle.stock_value_difference = stock_value_difference + if sle.is_adjustment_entry and flt(sle.qty_after_transaction, self.flt_precision) == 0: sle.stock_value_difference = ( get_stock_value_difference( sle.item_code, sle.warehouse, sle.posting_date, sle.posting_time, sle.voucher_no @@ -1074,16 +1077,15 @@ class update_entries_after: for d in sabb_data: incoming_rate = get_incoming_rate_for_serial_and_batch(self.item_code, d, sn_obj) - - if flt(incoming_rate, self.currency_precision) == flt( - d.valuation_rate, self.currency_precision - ) and not getattr(d, "stock_queue", None): - continue - amount = incoming_rate * flt(d.qty) tot_amt += flt(amount) total_qty += flt(d.qty) + if flt(incoming_rate, self.currency_precision) == flt( + d.incoming_rate, self.currency_precision + ) and not getattr(d, "stock_queue", None): + continue + values_to_update = { "incoming_rate": incoming_rate, "stock_value_difference": amount, diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/__init__.py b/erpnext/subcontracting/doctype/subcontracting_inward_order/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.js b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.js new file mode 100644 index 00000000000..358e0307841 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.js @@ -0,0 +1,246 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// client script for Subcontracting Inward Order Item is not necessarily required as the server side code will do everything that is necessary. +// this is just so that the user does not get potentially confused +frappe.ui.form.on("Subcontracting Inward Order Item", { + qty(frm, cdt, cdn) { + const row = locals[cdt][cdn]; + const service_item = frm.doc.service_items[row.idx - 1]; + frappe.model.set_value( + service_item.doctype, + service_item.name, + "qty", + row.qty * row.subcontracting_conversion_factor + ); + frappe.model.set_value(service_item.doctype, service_item.name, "fg_item_qty", row.qty); + }, + before_items_remove(frm, cdt, cdn) { + const row = locals[cdt][cdn]; + frm.toggle_enable(["service_items"], true); + frm.get_field("service_items").grid.grid_rows[row.idx - 1].remove(); + frm.toggle_enable(["service_items"], false); + }, +}); + +frappe.ui.form.on("Subcontracting Inward Order", { + setup: (frm) => { + frm.get_field("items").grid.cannot_add_rows = true; + + frm.set_query("customer_warehouse", () => { + return { + filters: { + is_group: 0, + is_rejected_warehouse: 0, + company: frm.doc.company, + customer: frm.doc.customer, + disabled: 0, + }, + }; + }); + + frm.set_query("sales_order", () => { + return { + filters: { + docstatus: 1, + is_subcontracted: 1, + }, + }; + }); + + frm.set_query("delivery_warehouse", "items", () => { + return { + filters: { + is_group: 0, + is_rejected_warehouse: 0, + company: frm.doc.company, + disabled: 0, + customer: ["is", "not set"], + }, + }; + }); + + frm.set_query("set_delivery_warehouse", () => { + return { + filters: { + is_group: 0, + is_rejected_warehouse: 0, + company: frm.doc.company, + disabled: 0, + customer: ["is", "not set"], + }, + }; + }); + }, + + set_delivery_warehouse: (frm) => { + frm.doc.items.forEach((item) => + frappe.model.set_value( + item.doctype, + item.name, + "delivery_warehouse", + frm.doc.set_delivery_warehouse + ) + ); + }, + + sales_order: (frm) => { + frm.set_value("service_items", null); + frm.set_value("items", null); + frm.set_value("received_items", null); + + if (frm.doc.sales_order) { + erpnext.utils.map_current_doc({ + method: "erpnext.selling.doctype.sales_order.sales_order.make_subcontracting_inward_order", + source_name: frm.doc.sales_order, + target_doc: frm, + freeze: true, + freeze_message: __("Mapping Subcontracting Inward Order ..."), + }); + } + }, + + refresh: function (frm) { + if (frm.doc.docstatus == 1) { + if (frm.has_perm("submit")) { + if (frm.doc.status == "Closed") { + frm.add_custom_button( + __("Re-open"), + () => frm.events.update_subcontracting_inward_order_status(frm), + __("Status") + ); + } else { + frm.add_custom_button( + __("Close"), + () => frm.events.update_subcontracting_inward_order_status(frm, "Closed"), + __("Status") + ); + } + } + if (frm.doc.status != "Closed") { + const is_raw_materials_received = frm.doc.received_items.some((item) => + item.is_customer_provided_item + ? item.received_qty - item.work_order_qty - item.returned_qty > 0 + : false + ); + if (is_raw_materials_received) { + frm.add_custom_button( + __("Raw Materials to Customer"), + () => frm.trigger("make_rm_return"), + __("Return") + ); + if (frm.doc.per_produced < 100) { + frm.add_custom_button( + __("Work Order"), + () => frm.events.make_work_order(frm), + __("Create") + ); + } + } + + if (frm.doc.per_produced < 100) { + frm.add_custom_button( + __("Material from Customer"), + () => frm.events.make_stock_entry(frm), + __("Receive") + ); + } + if (frm.doc.per_produced > 0 && frm.doc.per_delivered < 100) { + frm.add_custom_button( + __("Subcontracting Delivery"), + () => frm.events.make_subcontracting_delivery(frm), + __("Create") + ); + } + if (frm.doc.per_delivered > 0 && frm.doc.per_returned < 100) { + frm.add_custom_button( + __("Finished Goods Return"), + () => frm.events.make_subcontracting_return(frm), + __("Return") + ); + } + if (frm.doc.per_produced < 100) { + frm.page.set_inner_btn_group_as_primary(__("Receive")); + } else if (frm.doc.per_delivered < 100) { + frm.page.set_inner_btn_group_as_primary(__("Create")); + } else if (frm.doc.per_delivered >= 100 && frm.doc.per_returned < 100) { + frm.page.set_inner_btn_group_as_primary(__("Return")); + } + } + } + }, + + update_subcontracting_inward_order_status(frm, status) { + frappe.call({ + method: "erpnext.subcontracting.doctype.subcontracting_inward_order.subcontracting_inward_order.update_subcontracting_inward_order_status", + args: { + scio: frm.doc.name, + status: status, + }, + callback: function (r) { + if (!r.exc) { + frm.reload_doc(); + } + }, + }); + }, + + make_work_order(frm) { + frappe.call({ + method: "make_work_order", + freeze: true, + doc: frm.doc, + callback: function () { + frm.reload_doc(); + }, + }); + }, + + make_stock_entry(frm) { + frappe.call({ + method: "make_rm_stock_entry_inward", + freeze: true, + doc: frm.doc, + callback: (r) => { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + }, + }); + }, + + make_rm_return(frm) { + frappe.call({ + method: "make_rm_return", + freeze: true, + doc: frm.doc, + callback: (r) => { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + }, + }); + }, + + make_subcontracting_delivery(frm) { + frappe.call({ + method: "make_subcontracting_delivery", + freeze: true, + doc: frm.doc, + callback: (r) => { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + }, + }); + }, + + make_subcontracting_return(frm) { + frappe.call({ + method: "make_subcontracting_return", + freeze: true, + doc: frm.doc, + callback: (r) => { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + }, + }); + }, +}); diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json new file mode 100644 index 00000000000..a3b6b10a012 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json @@ -0,0 +1,374 @@ +{ + "actions": [], + "allow_auto_repeat": 1, + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2025-03-24 12:50:26.464612", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "title", + "naming_series", + "sales_order", + "customer", + "customer_name", + "currency", + "column_break_7", + "company", + "transaction_date", + "customer_warehouse", + "amended_from", + "items_section", + "set_delivery_warehouse", + "items", + "raw_materials_received_section", + "received_items", + "scrap_items_generated_section", + "scrap_items", + "service_items_section", + "service_items", + "tab_other_info", + "order_status_section", + "status", + "per_raw_material_received", + "per_produced", + "per_delivered", + "column_break_39", + "per_raw_material_returned", + "per_process_loss", + "per_returned", + "tab_connections" + ], + "fields": [ + { + "allow_on_submit": 1, + "default": "{customer_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "SCI-ORD-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Subcontracting Sales Order", + "options": "Sales Order", + "reqd": 1 + }, + { + "bold": 1, + "fieldname": "customer", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Customer", + "options": "Customer", + "print_hide": 1, + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "bold": 1, + "fetch_from": "customer.customer_name", + "fieldname": "customer_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Customer Name", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "print_hide": 1, + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "default": "Today", + "fetch_from": "sales_order.transaction_date", + "fetch_if_empty": 1, + "fieldname": "transaction_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "options": "Subcontracting Inward Order", + "print_hide": 1, + "read_only": 1 + }, + { + "allow_bulk_edit": 1, + "depends_on": "sales_order", + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "options": "Subcontracting Inward Order Item", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "service_items_section", + "fieldtype": "Section Break", + "label": "Service Items" + }, + { + "fieldname": "service_items", + "fieldtype": "Table", + "label": "Service Items", + "options": "Subcontracting Inward Order Service Item", + "read_only": 1, + "reqd": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "received_items", + "depends_on": "received_items", + "fieldname": "raw_materials_received_section", + "fieldtype": "Section Break", + "label": "Raw Materials Required" + }, + { + "allow_on_submit": 1, + "fieldname": "received_items", + "fieldtype": "Table", + "label": "Required Items", + "no_copy": 1, + "options": "Subcontracting Inward Order Received Item", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "order_status_section", + "fieldtype": "Section Break", + "label": "Order Status" + }, + { + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Draft\nOpen\nOngoing\nProduced\nDelivered\nCancelled\nClosed", + "print_hide": 1, + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "column_break_39", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "per_delivered", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Delivered", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "tab_other_info", + "fieldtype": "Tab Break", + "label": "Other Info" + }, + { + "fieldname": "tab_connections", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "per_produced", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Produced", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "items_section", + "fieldtype": "Section Break", + "label": "Items" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "per_process_loss", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Process Loss", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "set_delivery_warehouse", + "fieldtype": "Link", + "label": "Set Delivery Warehouse", + "no_copy": 1, + "options": "Warehouse" + }, + { + "fieldname": "customer_warehouse", + "fieldtype": "Link", + "label": "Customer Warehouse", + "options": "Warehouse", + "reqd": 1 + }, + { + "depends_on": "scrap_items", + "fieldname": "scrap_items_generated_section", + "fieldtype": "Section Break", + "label": "Scrap Items Generated" + }, + { + "fieldname": "scrap_items", + "fieldtype": "Table", + "label": "Scrap Items", + "no_copy": 1, + "options": "Subcontracting Inward Order Scrap Item" + }, + { + "fieldname": "per_returned", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Returned", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "per_raw_material_returned", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Raw Material Returned", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "per_raw_material_received", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Raw Material Received", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "customer.default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Customer Currency", + "options": "Currency", + "read_only": 1 + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2025-09-05 14:41:46.859510", + "modified_by": "Administrator", + "module": "Subcontracting", + "name": "Subcontracting Inward Order", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "write": 1 + } + ], + "row_format": "Dynamic", + "search_fields": "status, transaction_date, customer", + "show_name_in_global_search": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "timeline_field": "customer", + "title_field": "customer_name", + "track_changes": 1 +} diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.py b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.py new file mode 100644 index 00000000000..0b0ffd67589 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.py @@ -0,0 +1,548 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.mapper import get_mapped_doc +from frappe.utils import comma_and, flt, get_link_to_form + +from erpnext.buying.utils import check_on_hold_or_closed_status +from erpnext.controllers.subcontracting_controller import SubcontractingController + + +class SubcontractingInwardOrder(SubcontractingController): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.subcontracting.doctype.subcontracting_inward_order_item.subcontracting_inward_order_item import ( + SubcontractingInwardOrderItem, + ) + from erpnext.subcontracting.doctype.subcontracting_inward_order_received_item.subcontracting_inward_order_received_item import ( + SubcontractingInwardOrderReceivedItem, + ) + from erpnext.subcontracting.doctype.subcontracting_inward_order_scrap_item.subcontracting_inward_order_scrap_item import ( + SubcontractingInwardOrderScrapItem, + ) + from erpnext.subcontracting.doctype.subcontracting_inward_order_service_item.subcontracting_inward_order_service_item import ( + SubcontractingInwardOrderServiceItem, + ) + + amended_from: DF.Link | None + company: DF.Link + currency: DF.Link | None + customer: DF.Link + customer_name: DF.Data + customer_warehouse: DF.Link + items: DF.Table[SubcontractingInwardOrderItem] + naming_series: DF.Literal["SCI-ORD-.YYYY.-"] + per_delivered: DF.Percent + per_process_loss: DF.Percent + per_produced: DF.Percent + per_raw_material_received: DF.Percent + per_raw_material_returned: DF.Percent + per_returned: DF.Percent + received_items: DF.Table[SubcontractingInwardOrderReceivedItem] + sales_order: DF.Link + scrap_items: DF.Table[SubcontractingInwardOrderScrapItem] + service_items: DF.Table[SubcontractingInwardOrderServiceItem] + set_delivery_warehouse: DF.Link | None + status: DF.Literal["Draft", "Open", "Ongoing", "Produced", "Delivered", "Cancelled", "Closed"] + title: DF.Data | None + transaction_date: DF.Date + # end: auto-generated types + + pass + + def validate(self): + super().validate() + self.set_is_customer_provided_item() + self.validate_customer_provided_items() + self.validate_customer_warehouse() + self.validate_service_items() + self.set_missing_values() + + def on_submit(self): + self.update_status() + self.update_subcontracted_quantity_in_so() + + def on_cancel(self): + self.update_status() + self.update_subcontracted_quantity_in_so() + + def update_status(self, status=None, update_modified=True): + if self.status == "Closed" and self.status != status: + check_on_hold_or_closed_status("Sales Order", self.sales_order) + + total_to_be_received = total_received = total_rm_returned = 0 + for rm in self.get("received_items"): + if rm.get("is_customer_provided_item"): + total_to_be_received += flt(rm.required_qty) + total_received += flt(rm.received_qty) + total_rm_returned += flt(rm.returned_qty) + + total_to_be_produced = total_produced = total_process_loss = total_delivered = total_fg_returned = 0 + for item in self.get("items"): + total_to_be_produced += flt(item.qty) + total_produced += flt(item.produced_qty) + total_process_loss += flt(item.process_loss_qty) + total_delivered += flt(item.delivered_qty) + total_fg_returned += flt(item.returned_qty) + + per_raw_material_received = flt(total_received / total_to_be_received * 100, 2) + per_raw_material_returned = flt(total_rm_returned / total_received * 100, 2) if total_received else 0 + per_produced = flt(total_produced / total_to_be_produced * 100, 2) + per_process_loss = flt(total_process_loss / total_produced * 100, 2) if total_produced else 0 + per_delivered = flt(total_delivered / total_to_be_produced * 100, 2) + per_returned = flt(total_fg_returned / total_delivered * 100, 2) if total_delivered else 0 + + self.db_set("per_raw_material_received", per_raw_material_received, update_modified=update_modified) + self.db_set("per_raw_material_returned", per_raw_material_returned, update_modified=update_modified) + self.db_set("per_produced", per_produced, update_modified=update_modified) + self.db_set("per_process_loss", per_process_loss, update_modified=update_modified) + self.db_set("per_delivered", per_delivered, update_modified=update_modified) + self.db_set("per_returned", per_returned, update_modified=update_modified) + + if self.docstatus >= 1 and not status: + if self.docstatus == 1: + if self.status == "Draft": + status = "Open" + elif self.per_delivered == 100: + status = "Delivered" + elif self.per_produced == 100: + status = "Produced" + elif self.per_raw_material_received > 0: + status = "Ongoing" + else: + status = "Open" + elif self.docstatus == 2: + status = "Cancelled" + + if status and self.status != status: + self.db_set("status", status, update_modified=update_modified) + + def update_subcontracted_quantity_in_so(self): + for service_item in self.service_items: + doc = frappe.get_doc("Sales Order Item", service_item.sales_order_item) + doc.subcontracted_qty = ( + (doc.subcontracted_qty + service_item.qty) + if self._action == "submit" + else (doc.subcontracted_qty - service_item.qty) + ) + doc.save() + + def validate_customer_warehouse(self): + if frappe.get_cached_value("Warehouse", self.customer_warehouse, "customer") != self.customer: + frappe.throw( + _("Customer Warehouse {0} does not belong to Customer {1}.").format( + frappe.bold(self.customer_warehouse), frappe.bold(self.customer) + ) + ) + + def validate_service_items(self): + sales_order_items = [item.sales_order_item for item in self.items] + self.service_items = [ + service_item + for service_item in self.service_items + if service_item.sales_order_item in sales_order_items + ] + + for service_item in self.service_items: + item = next(item for item in self.items if item.sales_order_item == service_item.sales_order_item) + service_item.qty = item.qty * item.subcontracting_conversion_factor + service_item.fg_item_qty = item.qty + service_item.amount = service_item.qty * service_item.rate + + def populate_items_table(self): + items = [] + + for si in self.service_items: + if si.fg_item: + item = frappe.get_doc("Item", si.fg_item) + + so_item = frappe.get_doc("Sales Order Item", si.sales_order_item) + available_qty = so_item.stock_qty - so_item.subcontracted_qty + + if available_qty == 0: + continue + + si.required_qty = available_qty + conversion_factor = so_item.stock_qty / so_item.fg_item_qty + si.fg_item_qty = flt( + available_qty / conversion_factor, frappe.get_precision("Sales Order Item", "qty") + ) + si.amount = available_qty * si.rate + + bom = ( + frappe.db.get_value( + "Subcontracting BOM", + {"finished_good": item.name, "is_active": 1}, + "finished_good_bom", + ) + or item.default_bom + ) + + items.append( + { + "item_code": item.name, + "item_name": item.item_name, + "expected_delivery_date": frappe.get_cached_value( + "Sales Order Item", si.sales_order_item, "delivery_date" + ), + "description": item.description, + "qty": si.fg_item_qty, + "subcontracting_conversion_factor": conversion_factor, + "stock_uom": item.stock_uom, + "bom": bom, + "sales_order_item": si.sales_order_item, + } + ) + else: + frappe.throw( + _("Please select Finished Good Item for Service Item {0}").format( + si.item_name or si.item_code + ) + ) + + if items: + for item in items: + self.append("items", item) + + def validate_customer_provided_items(self): + """Check if atleast one raw material is customer provided""" + for item in self.get("items"): + raw_materials = [rm for rm in self.get("received_items") if rm.main_item_code == item.item_code] + if not any([rm.is_customer_provided_item for rm in raw_materials]): + frappe.throw( + _( + "Atleast one raw material for Finished Good Item {0} should be customer provided." + ).format(frappe.bold(item.item_code)) + ) + + def set_is_customer_provided_item(self): + for item in self.get("received_items"): + item.is_customer_provided_item = frappe.get_cached_value( + "Item", item.rm_item_code, "is_customer_provided_item" + ) + + @frappe.whitelist() + def make_work_order(self): + """Create Work Order from Subcontracting Inward Order.""" + wo_list = [] + + for item in self.get_production_items(): + work_order = self.create_work_order(item) + if work_order: + wo_list.append(work_order) + + self.show_list_created_message("Work Order", wo_list) + + if not wo_list: + frappe.msgprint(_("No Work Orders were created")) + + return wo_list + + def get_production_items(self): + item_list = [] + + for d in self.items: + if d.produced_qty >= d.qty: + continue + + item_details = { + "production_item": d.item_code, + "use_multi_level_bom": d.include_exploded_items, + "subcontracting_inward_order": self.name, + "bom_no": d.bom, + "stock_uom": d.stock_uom, + "company": self.company, + "project": frappe.get_cached_value("Sales Order", self.sales_order, "project"), + "source_warehouse": self.customer_warehouse, + "subcontracting_inward_order_item": d.name, + "reserve_stock": 1, + "fg_warehouse": d.delivery_warehouse, + } + + qty = min( + [ + flt( + (item.received_qty - item.returned_qty - item.work_order_qty) + / flt(item.required_qty / d.qty, d.precision("qty")), + d.precision("qty"), + ) + for item in self.get("received_items") + if item.reference_name == d.name and item.is_customer_provided_item + ] + ) + qty = int(qty) if frappe.get_cached_value("UOM", d.stock_uom, "must_be_whole_number") else qty + + item_details.update({"qty": qty, "max_producible_qty": qty}) + item_list.append(item_details) + + return item_list + + def create_work_order(self, item): + from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError + + if flt(item.get("qty")) <= 0: + return + + wo = frappe.new_doc("Work Order") + wo.update(item) + + wo.set_work_order_operations() + wo.set_required_items() + + try: + wo.flags.ignore_mandatory = True + wo.flags.ignore_validate = True + wo.insert() + return wo.name + except OverProductionError: + pass + + def show_list_created_message(self, doctype, doc_list=None): + if not doc_list: + return + + frappe.flags.mute_messages = False + if doc_list: + doc_list = [get_link_to_form(doctype, p) for p in doc_list] + frappe.msgprint(_("{0} created").format(comma_and(doc_list))) + + @frappe.whitelist() + def make_rm_stock_entry_inward(self, target_doc=None): + def calculate_qty_as_per_bom(rm_item): + data = frappe.get_value( + "Subcontracting Inward Order Item", + {"name": rm_item.reference_name}, + ["process_loss_qty", "include_exploded_items"], + as_dict=True, + ) + stock_qty = frappe.get_value( + "BOM Explosion Item" if data.include_exploded_items else "BOM Item", + {"name": rm_item.bom_detail_no}, + "stock_qty", + ) + qty = flt( + stock_qty * data.process_loss_qty, + frappe.get_precision("Subcontracting Inward Order Received Item", "required_qty"), + ) + return rm_item.required_qty - rm_item.received_qty + rm_item.returned_qty + qty + + if target_doc and target_doc.get("items"): + target_doc.items = [] + + stock_entry = get_mapped_doc( + "Subcontracting Inward Order", + self.name, + { + "Subcontracting Inward Order": { + "doctype": "Stock Entry", + "validation": { + "docstatus": ["=", 1], + }, + }, + }, + target_doc, + ignore_child_tables=True, + ) + + stock_entry.purpose = "Receive from Customer" + stock_entry.subcontracting_inward_order = self.name + + stock_entry.set_stock_entry_type() + + for rm_item in self.received_items: + if not rm_item.required_qty or not rm_item.is_customer_provided_item: + continue + + items_dict = { + rm_item.get("rm_item_code"): { + "scio_detail": rm_item.get("name"), + "qty": calculate_qty_as_per_bom(rm_item), + "to_warehouse": rm_item.get("warehouse"), + "stock_uom": rm_item.get("stock_uom"), + } + } + + stock_entry.add_to_stock_entry_detail(items_dict) + + if target_doc: + return stock_entry + else: + return stock_entry.as_dict() + + @frappe.whitelist() + def make_rm_return(self, target_doc=None): + if target_doc and target_doc.get("items"): + target_doc.items = [] + + stock_entry = get_mapped_doc( + "Subcontracting Inward Order", + self.name, + { + "Subcontracting Inward Order": { + "doctype": "Stock Entry", + "validation": { + "docstatus": ["=", 1], + }, + }, + }, + target_doc, + ignore_child_tables=True, + ) + + stock_entry.purpose = "Return Raw Material to Customer" + stock_entry.set_stock_entry_type() + stock_entry.subcontracting_inward_order = self.name + + for rm_item in self.received_items: + items_dict = { + rm_item.get("rm_item_code"): { + "scio_detail": rm_item.get("name"), + "qty": rm_item.received_qty - rm_item.work_order_qty - rm_item.returned_qty, + "from_warehouse": rm_item.get("warehouse"), + "stock_uom": rm_item.get("stock_uom"), + } + } + + stock_entry.add_to_stock_entry_detail(items_dict) + + if target_doc: + return stock_entry + else: + return stock_entry.as_dict() + + @frappe.whitelist() + def make_subcontracting_delivery(self, target_doc=None): + if target_doc and target_doc.get("items"): + target_doc.items = [] + + stock_entry = get_mapped_doc( + "Subcontracting Inward Order", + self.name, + { + "Subcontracting Inward Order": { + "doctype": "Stock Entry", + "validation": { + "docstatus": ["=", 1], + }, + }, + }, + target_doc, + ignore_child_tables=True, + ) + + stock_entry.purpose = "Subcontracting Delivery" + stock_entry.set_stock_entry_type() + stock_entry.subcontracting_inward_order = self.name + scio_details = [] + + allow_over = frappe.get_single_value("Selling Settings", "allow_delivery_of_overproduced_qty") + for fg_item in self.items: + qty = ( + fg_item.produced_qty + if allow_over + else min(fg_item.qty, fg_item.produced_qty) - fg_item.delivered_qty - fg_item.returned_qty + ) + if qty < 0: + continue + + scio_details.append(fg_item.name) + items_dict = { + fg_item.item_code: { + "qty": qty, + "from_warehouse": fg_item.delivery_warehouse, + "stock_uom": fg_item.stock_uom, + "scio_detail": fg_item.name, + "is_finished_item": 1, + } + } + + stock_entry.add_to_stock_entry_detail(items_dict) + + if ( + frappe.get_single_value("Selling Settings", "deliver_scrap_items") + and self.scrap_items + and scio_details + ): + scrap_items = [ + scrap_item for scrap_item in self.scrap_items if scrap_item.reference_name in scio_details + ] + for scrap_item in scrap_items: + qty = scrap_item.produced_qty - scrap_item.delivered_qty + if qty > 0: + items_dict = { + scrap_item.item_code: { + "qty": scrap_item.produced_qty - scrap_item.delivered_qty, + "from_warehouse": scrap_item.warehouse, + "stock_uom": scrap_item.stock_uom, + "scio_detail": scrap_item.name, + "is_scrap_item": 1, + } + } + + stock_entry.add_to_stock_entry_detail(items_dict) + + if target_doc: + return stock_entry + else: + return stock_entry.as_dict() + + @frappe.whitelist() + def make_subcontracting_return(self, target_doc=None): + if target_doc and target_doc.get("items"): + target_doc.items = [] + + stock_entry = get_mapped_doc( + "Subcontracting Inward Order", + self.name, + { + "Subcontracting Inward Order": { + "doctype": "Stock Entry", + "validation": { + "docstatus": ["=", 1], + }, + "field_map": {"name": "subcontracting_inward_order"}, + }, + }, + target_doc, + ignore_child_tables=True, + ) + + stock_entry.purpose = "Subcontracting Return" + stock_entry.set_stock_entry_type() + + for fg_item in self.items: + qty = fg_item.delivered_qty - fg_item.returned_qty + if qty < 0: + continue + + items_dict = { + fg_item.item_code: { + "qty": qty, + "stock_uom": fg_item.stock_uom, + "scio_detail": fg_item.name, + "is_finished_item": 1, + } + } + + stock_entry.add_to_stock_entry_detail(items_dict) + + if target_doc: + return stock_entry + else: + return stock_entry.as_dict() + + +@frappe.whitelist() +def update_subcontracting_inward_order_status(scio, status=None): + if isinstance(scio, str): + scio = frappe.get_doc("Subcontracting Inward Order", scio) + + scio.update_status(status) diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order_dashboard.py b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order_dashboard.py new file mode 100644 index 00000000000..45fe3f95e93 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order_dashboard.py @@ -0,0 +1,17 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "subcontracting_inward_order", + "transactions": [ + { + "label": _("Transactions"), + "items": ["Stock Entry"], + }, + { + "label": _("Manufacturing"), + "items": ["Work Order"], + }, + ], + } diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order_list.js b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order_list.js new file mode 100644 index 00000000000..37f61fc5da0 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order_list.js @@ -0,0 +1,17 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.listview_settings["Subcontracting Inward Order"] = { + get_indicator: function (doc) { + const status_colors = { + Draft: "red", + Open: "orange", + Ongoing: "yellow", + Produced: "blue", + Delivered: "green", + Closed: "grey", + Cancelled: "red", + }; + return [__(doc.status), status_colors[doc.status], "status,=," + doc.status]; + }, +}; diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/test_subcontracting_inward_order.py b/erpnext/subcontracting/doctype/subcontracting_inward_order/test_subcontracting_inward_order.py new file mode 100644 index 00000000000..0b35362063a --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/test_subcontracting_inward_order.py @@ -0,0 +1,559 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe +from frappe.tests import IntegrationTestCase + +# On IntegrationTestCase, the doctype test records and all +# link-field test record dependencies are recursively loaded +# Use these module variables to add/remove to/from that list +EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] +IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] + +from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry as make_stock_entry_from_wo +from erpnext.selling.doctype.sales_order.sales_order import make_subcontracting_inward_order +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + +class IntegrationTestSubcontractingInwardOrder(IntegrationTestCase): + """ + Integration tests for SubcontractingInwardOrder. + Use this class for testing interactions between multiple components. + """ + + def setUp(self): + create_test_data() + make_stock_entry( + item_code="Self RM", qty=100, to_warehouse="Stores - _TC", purpose="Material Receipt" + ) + return super().setUp() + + def test_customer_provided_item_cost_field(self): + so, scio = create_so_scio() + + rm_in = frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()) + rm_in.save() + for item in rm_in.get("items"): + item.basic_rate = 10 + rm_in.append( + "additional_costs", + { + "expense_account": "Freight and Forwarding Charges - _TC", + "description": "Test", + "amount": 100, + }, + ) + rm_in.submit() + + for item in rm_in.get("items"): + self.assertEqual(item.customer_provided_item_cost, 15) + + def test_add_extra_customer_provided_item(self): + so, scio = create_so_scio() + + rm_in = frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()) + rm_in.save() + rm_in.append( + "items", + { + "item_code": "Basic RM 2", + "qty": 5, + "t_warehouse": rm_in.items[0].t_warehouse, + "basic_rate": 10, + "transfer_qty": 5, + "uom": "Nos", + "conversion_factor": 1, + }, + ) + rm_in.submit() + + scio.reload() + self.assertTrue( + next((item for item in scio.received_items if item.rm_item_code == "Basic RM 2"), None) + ) + + def test_add_extra_item_during_manufacture(self): + make_stock_entry( + item_code="Self RM 2", qty=5, to_warehouse="Stores - _TC", purpose="Material Receipt" + ) + so, scio = create_so_scio() + frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()).submit() + + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + next( + item for item in wo.required_items if item.item_code == "Self RM" + ).source_warehouse = "Stores - _TC" + wo.submit() + + manufacture = frappe.new_doc("Stock Entry").update(make_stock_entry_from_wo(wo.name, "Manufacture")) + manufacture.save() + frappe.new_doc( + "Stock Entry Detail", + parent=manufacture.name, + parenttype="Stock Entry", + parentfield="items", + idx=6, + item_code="Self RM 2", + qty=5, + s_warehouse="Stores - _TC", + basic_rate=10, + transfer_qty=5, + uom="Nos", + conversion_factor=1, + cost_center="Main - _TC", + ).insert() + manufacture.reload() + manufacture.submit() + scio.reload() + self.assertTrue( + next((item for item in scio.received_items if item.rm_item_code == "Self RM 2"), None) + ) + + def test_work_order_creation_qty(self): + new_bom = frappe.copy_doc(frappe.get_doc("BOM", "BOM-Basic FG Item-001")) + new_bom.items = new_bom.items[:3] + new_bom.items[1].qty = 2 + new_bom.items[2].qty = 3 + new_bom.submit() + sc_bom = frappe.get_doc("Subcontracting BOM", "SB-0001") + sc_bom.finished_good_bom = new_bom.name + sc_bom.save() + + so, scio = create_so_scio() + + rm_in = frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()) + rm_in.items[0].qty = 3 + rm_in.items[1].qty = 5 + rm_in.items[2].qty = 12 + rm_in.submit() + + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + self.assertEqual(wo.qty, 2) + + def test_rm_return(self): + from erpnext.stock.serial_batch_bundle import get_batch_nos, get_serial_nos + + so, scio = create_so_scio() + + rm_in = frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()) + rm_in.items[3].qty = 2 + rm_in.submit() + + serial_nos = get_serial_nos(rm_in.items[3].serial_and_batch_bundle) + batch_nos = list(get_batch_nos(rm_in.items[3].serial_and_batch_bundle).keys()) + + scio.reload() + rm_in = frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()) + backup = rm_in.items[-1] + rm_in.items.clear() + rm_in.items.append(backup) + + rm_in.items[0].qty = 1 + rm_in.submit() + + serial_nos += get_serial_nos(rm_in.items[0].serial_and_batch_bundle) + batch_nos += list(get_batch_nos(rm_in.items[0].serial_and_batch_bundle).keys()) + + scio.reload() + rm_return = frappe.new_doc("Stock Entry").update(scio.make_rm_return()) + rm_return.submit() + + self.assertEqual( + sorted(get_serial_nos(rm_return.items[-1].serial_and_batch_bundle)), sorted(serial_nos) + ) + self.assertEqual( + sorted(list(get_batch_nos(rm_return.items[-1].serial_and_batch_bundle).keys())), sorted(batch_nos) + ) + + def test_subcontracting_delivery(self): + from erpnext.stock.serial_batch_bundle import get_serial_batch_list_from_item + + extra_serial, _ = get_serial_batch_list_from_item( + make_stock_entry( + item_code="FG Item with Serial", + qty=1, + to_warehouse="Stores - _TC", + purpose="Material Receipt", + ).items[0] + ) + so, scio = create_so_scio(service_item="Service Item 2", fg_item="FG Item with Serial") + frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()).submit() + + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + wo.required_items[-1].source_warehouse = "Stores - _TC" + wo.submit() + + manufacture = frappe.new_doc("Stock Entry").update(make_stock_entry_from_wo(wo.name, "Manufacture")) + manufacture.submit() + + serial_list, _ = get_serial_batch_list_from_item( + next(item for item in manufacture.items if item.is_finished_item) + ) + + scio.reload() + delivery = frappe.new_doc("Stock Entry").update(scio.make_subcontracting_delivery()) + delivery.items[0].use_serial_batch_fields = 1 + delivery.save() + delivery_serial_list, _ = get_serial_batch_list_from_item(delivery.items[0]) + self.assertEqual(sorted(serial_list), sorted(delivery_serial_list)) + + delivery_serial_list[-1] = extra_serial[0] + delivery.items[0].serial_no = "\n".join(delivery_serial_list) + self.assertRaises(frappe.ValidationError, delivery.submit) + + def test_fg_item_fields(self): + so, scio = create_so_scio() + frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()).submit() + + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + wo.required_items[-1].source_warehouse = "Stores - _TC" + wo.submit() + + manufacture = frappe.new_doc("Stock Entry").update(make_stock_entry_from_wo(wo.name, "Manufacture")) + manufacture.save() + manufacture.fg_completed_qty = 5 + manufacture.process_loss_qty = 1 + manufacture.items[-1].qty = 4 + manufacture.submit() + + scio.reload() + self.assertEqual(scio.items[0].qty, 5) + self.assertEqual(scio.items[0].process_loss_qty, 1) + self.assertEqual(scio.items[0].produced_qty, 4) + rm_in = scio.make_rm_stock_entry_inward() + for item in rm_in.get("items"): + self.assertEqual(item.qty, 1) + + delivery = frappe.new_doc("Stock Entry").update(scio.make_subcontracting_delivery()) + delivery.items[0].qty = 5 + self.assertRaises(frappe.ValidationError, delivery.submit) + delivery.items[0].qty = 2 + delivery.submit() + + scio.reload() + fg_return = frappe.new_doc("Stock Entry").update(scio.make_subcontracting_return()) + self.assertEqual(fg_return.items[0].qty, 2) + fg_return.items[0].qty = 1 + fg_return.items[0].t_warehouse = "Stores - _TC" + fg_return.submit() + + scio.reload() + self.assertEqual(scio.items[0].delivered_qty, 2) + self.assertEqual(scio.items[0].returned_qty, 1) + + @IntegrationTestCase.change_settings("Selling Settings", {"allow_delivery_of_overproduced_qty": 1}) + @IntegrationTestCase.change_settings( + "Manufacturing Settings", {"overproduction_percentage_for_work_order": 20} + ) + def test_over_production_delivery(self): + so, scio = create_so_scio() + frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()).submit() + + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + wo.required_items[-1].source_warehouse = "Stores - _TC" + wo.submit() + + manufacture = frappe.new_doc("Stock Entry").update(make_stock_entry_from_wo(wo.name, "Manufacture")) + manufacture.items[-1].qty = 6 + manufacture.fg_completed_qty = 6 + manufacture.submit() + + scio.reload() + self.assertEqual(scio.items[0].produced_qty, 6) + + delivery = frappe.new_doc("Stock Entry").update(scio.make_subcontracting_delivery()) + self.assertEqual(delivery.items[0].qty, 6) + delivery.submit() + + frappe.db.set_single_value("Selling Settings", "allow_delivery_of_overproduced_qty", 0) + delivery.cancel() + scio.reload() + delivery = frappe.new_doc("Stock Entry").update(scio.make_subcontracting_delivery()) + self.assertEqual(delivery.items[0].qty, 5) + delivery.items[0].qty = 6 + self.assertRaises(frappe.ValidationError, delivery.submit) + + @IntegrationTestCase.change_settings("Selling Settings", {"deliver_scrap_items": 1}) + def test_scrap_delivery(self): + new_bom = frappe.copy_doc(frappe.get_doc("BOM", "BOM-Basic FG Item-001")) + new_bom.scrap_items.append(frappe.new_doc("BOM Scrap Item", item_code="Basic RM 2", qty=1)) + new_bom.submit() + sc_bom = frappe.get_doc("Subcontracting BOM", "SB-0001") + sc_bom.finished_good_bom = new_bom.name + sc_bom.save() + + so, scio = create_so_scio() + frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()).submit() + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + wo.required_items[-1].source_warehouse = "Stores - _TC" + wo.submit() + + frappe.new_doc("Stock Entry").update(make_stock_entry_from_wo(wo.name, "Manufacture")).submit() + + scio.reload() + self.assertEqual(scio.scrap_items[0].item_code, "Basic RM 2") + + delivery = frappe.new_doc("Stock Entry").update(scio.make_subcontracting_delivery()) + self.assertEqual(delivery.items[-1].item_code, "Basic RM 2") + + frappe.db.set_single_value("Selling Settings", "deliver_scrap_items", 0) + delivery = frappe.new_doc("Stock Entry").update(scio.make_subcontracting_delivery()) + self.assertNotEqual(delivery.items[-1].item_code, "Basic RM 2") + + def test_self_rm_billed_qty(self): + so, scio = create_so_scio() + frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()).submit() + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + wo.required_items[-1].source_warehouse = "Stores - _TC" + wo.submit() + frappe.new_doc("Stock Entry").update(make_stock_entry_from_wo(wo.name, "Manufacture")).submit() + scio.reload() + frappe.new_doc("Stock Entry").update(scio.make_subcontracting_delivery()).submit() + scio.reload() + + from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice + + si = make_sales_invoice(so.name) + self.assertEqual(si.items[-1].item_code, "Self RM") + self.assertEqual(si.items[-1].qty, 5) + si.items[-1].qty = 3 + si.submit() + scio.reload() + self.assertEqual(scio.received_items[-1].billed_qty, 3) + + si = make_sales_invoice(so.name) + self.assertEqual(si.items[-1].qty, 2) + si.submit() + scio.reload() + self.assertEqual(scio.received_items[-1].billed_qty, 5) + + scio.reload() + si = make_sales_invoice(so.name) + self.assertEqual(len(si.items), 1) + + def test_extra_items_reservation_transfer(self): + so, scio = create_so_scio() + rm_in = frappe.new_doc("Stock Entry").update(scio.make_rm_stock_entry_inward()) + rm_in.items[-2].qty = 7 + rm_in.submit() + + wo_list = [] + scio.reload() + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + wo.required_items[-1].source_warehouse = "Stores - _TC" + wo.qty = 3 + wo.submit() + wo_list.append(wo.name) + self.assertEqual(wo.required_items[-2].stock_reserved_qty, 3) + + scio.reload() + self.assertEqual(scio.received_items[-2].work_order_qty, 3) + + wo = frappe.get_doc("Work Order", scio.make_work_order()[0]) + wo.skip_transfer = 1 + wo.required_items[-1].source_warehouse = "Stores - _TC" + wo.qty = 2 + wo.submit() + wo_list.append(wo.name) + + from frappe.query_builder.functions import Sum + + table = frappe.qb.DocType("Stock Reservation Entry") + query = ( + frappe.qb.from_(table) + .select(Sum(table.reserved_qty)) + .where( + (table.voucher_type == "Work Order") + & (table.item_code == rm_in.items[-2].item_code) + & (table.voucher_no.isin(wo_list)) + ) + ) + reserved_qty = query.run()[0][0] + self.assertEqual(reserved_qty, 7) + + +def create_so_scio(service_item="Service Item 1", fg_item="Basic FG Item"): + item_list = [{"item_code": service_item, "qty": 5, "fg_item": fg_item, "fg_item_qty": 5}] + so = make_sales_order(is_subcontracted=1, item_list=item_list) + scio = make_subcontracting_inward_order(so.name) + scio.items[0].delivery_warehouse = "_Test Warehouse - _TC" + scio.submit() + scio.reload() + return so, scio + + +def create_test_data(): + make_subcontracted_items() + make_raw_materials() + make_service_items() + make_bom_for_subcontracted_items() + make_subcontracting_boms() + create_warehouse("_Test Customer Warehouse - _TC", {"customer": "_Test Customer"}) + + +def make_subcontracted_items(): + sub_contracted_items = { + "Basic FG Item": {}, + "FG Item with Serial": { + "has_serial_no": 1, + "serial_no_series": "FGS.####", + }, + "FG Item with Batch": { + "has_batch_no": 1, + "create_new_batch": 1, + "batch_series": "FGB.####", + }, + "FG Item with Serial and Batch": { + "has_serial_no": 1, + "serial_no_series": "FGS.####", + "has_batch_no": 1, + "create_new_batch": 1, + "batch_series": "FGB.####", + }, + } + + for item, properties in sub_contracted_items.items(): + if not frappe.db.exists("Item", item): + properties.update({"is_stock_item": 1, "is_sub_contracted_item": 1}) + make_item(item, properties) + + +def make_raw_materials(): + customer_provided_raw_materials = { + "Basic RM": {}, + "Basic RM 2": {}, + "RM with Serial": {"has_serial_no": 1, "serial_no_series": "RMS.####"}, + "RM with Batch": { + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "RMB.####", + }, + "RM with Serial and Batch": { + "has_serial_no": 1, + "serial_no_series": "RMS.####", + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "RMB.####", + }, + } + + for item, properties in customer_provided_raw_materials.items(): + if not frappe.db.exists("Item", item): + properties.update({"is_stock_item": 1, "is_purchase_item": 0, "is_customer_provided_item": 1}) + make_item(item, properties) + + self_raw_materials = { + "Self RM": {}, + "Self RM 2": {}, + } + + for item, properties in self_raw_materials.items(): + if not frappe.db.exists("Item", item): + properties.update({"is_stock_item": 1, "valuation_rate": 10}) + make_item(item, properties) + + +def make_service_items(): + from erpnext.controllers.tests.test_subcontracting_controller import make_service_item + + service_items = { + "Service Item 1": {}, + "Service Item 2": {}, + "Service Item 3": {}, + "Service Item 4": {}, + } + + for item, properties in service_items.items(): + make_service_item(item, properties) + + +def make_bom_for_subcontracted_items(): + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + boms = { + "Basic FG Item": [ + "Basic RM", + "RM with Serial", + "RM with Batch", + "RM with Serial and Batch", + "Self RM", + ], + "FG Item with Serial": [ + "Basic RM", + "RM with Serial", + "RM with Batch", + "RM with Serial and Batch", + "Self RM", + ], + "FG Item with Batch": [ + "Basic RM", + "RM with Serial", + "RM with Batch", + "RM with Serial and Batch", + "Self RM", + ], + "FG Item with Serial and Batch": [ + "Basic RM", + "RM with Serial", + "RM with Batch", + "RM with Serial and Batch", + "Self RM", + ], + } + + for item_code, raw_materials in boms.items(): + if not frappe.db.exists("BOM", {"item": item_code}): + make_bom( + item=item_code, raw_materials=raw_materials, rate=100, currency="INR", set_as_default_bom=1 + ) + + +def make_subcontracting_boms(): + subcontracting_boms = [ + { + "finished_good": "Basic FG Item", + "service_item": "Service Item 1", + }, + { + "finished_good": "FG Item with Serial", + "service_item": "Service Item 2", + }, + { + "finished_good": "FG Item with Batch", + "service_item": "Service Item 3", + }, + { + "finished_good": "FG Item with Serial and Batch", + "service_item": "Service Item 4", + }, + ] + + for subcontracting_bom in subcontracting_boms: + if not frappe.db.exists("Subcontracting BOM", {"finished_good": subcontracting_bom["finished_good"]}): + doc = frappe.get_doc( + { + "doctype": "Subcontracting BOM", + "finished_good": subcontracting_bom["finished_good"], + "service_item": subcontracting_bom["service_item"], + "is_active": 1, + } + ) + doc.insert() + doc.save() diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json b/erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json new file mode 100644 index 00000000000..878918d3341 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json @@ -0,0 +1,202 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2025-03-24 12:53:33.849013", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "item_name", + "column_break_3", + "bom", + "delivery_warehouse", + "include_exploded_items", + "quantity_section", + "qty", + "produced_qty", + "returned_qty", + "column_break_13", + "stock_uom", + "process_loss_qty", + "delivered_qty", + "conversion_factor", + "sales_order_item", + "subcontracting_conversion_factor" + ], + "fields": [ + { + "bold": 1, + "columns": 2, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "item_code.item_name", + "fetch_if_empty": 1, + "fieldname": "item_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Item Name", + "print_hide": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "columns": 1, + "default": "1", + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity", + "non_negative": 1, + "print_width": "60px", + "reqd": 1, + "width": "60px" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break", + "print_hide": 1 + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "print_width": "100px", + "read_only": 1, + "reqd": 1, + "width": "100px" + }, + { + "default": "1", + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 1, + "label": "Conversion Factor", + "read_only": 1 + }, + { + "depends_on": "item_code", + "fetch_from": "item_code.default_bom", + "fetch_if_empty": 1, + "fieldname": "bom", + "fieldtype": "Link", + "in_list_view": 1, + "label": "BOM", + "options": "BOM", + "print_hide": 1, + "reqd": 1 + }, + { + "default": "0", + "fieldname": "include_exploded_items", + "fieldtype": "Check", + "label": "Include Exploded Items", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "delivered_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Delivered Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "returned_qty", + "fieldtype": "Float", + "label": "Returned Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Sales Order Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "subcontracting_conversion_factor", + "fieldtype": "Float", + "hidden": 1, + "label": "Subcontracting Conversion Factor", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "produced_qty", + "fieldtype": "Float", + "label": "Produced Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "process_loss_qty", + "fieldtype": "Float", + "label": "Process Loss Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "quantity_section", + "fieldtype": "Section Break", + "label": "Quantity" + }, + { + "fieldname": "delivery_warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Delivery Warehouse", + "no_copy": 1, + "options": "Warehouse", + "reqd": 1 + } + ], + "grid_page_length": 50, + "idx": 1, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-10-14 10:29:29.256455", + "modified_by": "Administrator", + "module": "Subcontracting", + "name": "Subcontracting Inward Order Item", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "row_format": "Dynamic", + "search_fields": "item_name", + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.py new file mode 100644 index 00000000000..862a3698385 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.py @@ -0,0 +1,52 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document +from frappe.query_builder.functions import Sum + + +class SubcontractingInwardOrderItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + bom: DF.Link + conversion_factor: DF.Float + delivered_qty: DF.Float + delivery_warehouse: DF.Link + include_exploded_items: DF.Check + item_code: DF.Link + item_name: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + process_loss_qty: DF.Float + produced_qty: DF.Float + qty: DF.Float + returned_qty: DF.Float + sales_order_item: DF.Data | None + stock_uom: DF.Link + subcontracting_conversion_factor: DF.Float + # end: auto-generated types + + pass + + def update_manufacturing_qty_fields(self): + table = frappe.qb.DocType("Work Order") + query = ( + frappe.qb.from_(table) + .select( + Sum(table.produced_qty).as_("produced_qty"), + Sum(table.process_loss_qty).as_("process_loss_qty"), + ) + .where((table.subcontracting_inward_order_item == self.name) & (table.docstatus == 1)) + ) + result = query.run(as_dict=True)[0] + + self.db_set("produced_qty", result.produced_qty) + self.db_set("process_loss_qty", result.process_loss_qty) diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.json b/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.json new file mode 100644 index 00000000000..d4ed145129a --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.json @@ -0,0 +1,191 @@ +{ + "actions": [], + "creation": "2025-03-24 13:56:42.877800", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "main_item_code", + "rm_item_code", + "is_customer_provided_item", + "is_additional_item", + "column_break_3", + "stock_uom", + "warehouse", + "column_break_6", + "bom_detail_no", + "reference_name", + "section_break_13", + "required_qty", + "billed_qty", + "received_qty", + "column_break_16", + "consumed_qty", + "work_order_qty", + "returned_qty" + ], + "fields": [ + { + "columns": 2, + "fieldname": "main_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "read_only": 1 + }, + { + "columns": 2, + "fieldname": "rm_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Raw Material Item Code", + "options": "Item", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "bom_detail_no", + "fieldtype": "Data", + "label": "BOM Detail No", + "read_only": 1 + }, + { + "fieldname": "reference_name", + "fieldtype": "Data", + "label": "Reference Name", + "read_only": 1 + }, + { + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, + { + "columns": 2, + "default": "0", + "depends_on": "eval:doc.required_qty", + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty", + "no_copy": 1, + "non_negative": 1, + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.is_customer_provided_item", + "fieldname": "received_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Received Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "consumed_qty", + "fieldtype": "Float", + "label": "Consumed Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.returned_qty", + "fieldname": "returned_qty", + "fieldtype": "Float", + "label": "Returned Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "depends_on": "eval:doc.work_order_qty", + "fieldname": "work_order_qty", + "fieldtype": "Float", + "label": "Work Order Qty", + "no_copy": 1, + "non_negative": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_customer_provided_item", + "fieldtype": "Check", + "label": "Is Customer Provided Item", + "read_only": 1, + "reqd": 1 + }, + { + "depends_on": "eval:!doc.is_customer_provided_item", + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "no_copy": 1, + "options": "Warehouse", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:!doc.is_customer_provided_item", + "fieldname": "billed_qty", + "fieldtype": "Float", + "label": "Billed Qty", + "no_copy": 1, + "non_negative": 1, + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:!doc.bom_detail_no", + "fieldname": "is_additional_item", + "fieldtype": "Check", + "label": "Is Additional Item", + "read_only": 1 + } + ], + "grid_page_length": 50, + "hide_toolbar": 1, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-10-14 10:18:58.905093", + "modified_by": "Administrator", + "module": "Subcontracting", + "name": "Subcontracting Inward Order Received Item", + "owner": "Administrator", + "permissions": [], + "row_format": "Dynamic", + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.py new file mode 100644 index 00000000000..22994d9e24f --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.py @@ -0,0 +1,36 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SubcontractingInwardOrderReceivedItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + billed_qty: DF.Float + bom_detail_no: DF.Data | None + consumed_qty: DF.Float + is_additional_item: DF.Check + is_customer_provided_item: DF.Check + main_item_code: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + received_qty: DF.Float + reference_name: DF.Data | None + required_qty: DF.Float + returned_qty: DF.Float + rm_item_code: DF.Link + stock_uom: DF.Link + warehouse: DF.Link | None + work_order_qty: DF.Float + # end: auto-generated types + + pass diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/subcontracting_inward_order_scrap_item.json b/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/subcontracting_inward_order_scrap_item.json new file mode 100644 index 00000000000..78902701532 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/subcontracting_inward_order_scrap_item.json @@ -0,0 +1,112 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-08-12 11:34:16.393300", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "fg_item_code", + "column_break_hoxe", + "stock_uom", + "warehouse", + "column_break_rptg", + "reference_name", + "section_break_gqk9", + "produced_qty", + "column_break_n4xc", + "delivered_qty" + ], + "fields": [ + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_hoxe", + "fieldtype": "Column Break" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_rptg", + "fieldtype": "Column Break" + }, + { + "fieldname": "reference_name", + "fieldtype": "Data", + "label": "Reference Name", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "section_break_gqk9", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "produced_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Produced Qty", + "non_negative": 1, + "reqd": 1 + }, + { + "default": "0", + "fieldname": "delivered_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Delivered Qty", + "non_negative": 1, + "reqd": 1 + }, + { + "fieldname": "fg_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Finished Good Item Code", + "options": "Item", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_n4xc", + "fieldtype": "Column Break" + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-10-14 10:28:30.192350", + "modified_by": "Administrator", + "module": "Subcontracting", + "name": "Subcontracting Inward Order Scrap Item", + "owner": "Administrator", + "permissions": [], + "row_format": "Dynamic", + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/subcontracting_inward_order_scrap_item.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/subcontracting_inward_order_scrap_item.py new file mode 100644 index 00000000000..d7aaae229dd --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_scrap_item/subcontracting_inward_order_scrap_item.py @@ -0,0 +1,29 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SubcontractingInwardOrderScrapItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + delivered_qty: DF.Float + fg_item_code: DF.Link + item_code: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + produced_qty: DF.Float + reference_name: DF.Data + stock_uom: DF.Link + warehouse: DF.Link + # end: auto-generated types + + pass diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.json b/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.json new file mode 100644 index 00000000000..715f90105a7 --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.json @@ -0,0 +1,147 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2025-03-24 14:01:02.572511", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "column_break_2", + "item_name", + "section_break_4", + "qty", + "uom", + "column_break_6", + "rate", + "amount", + "section_break_10", + "fg_item", + "column_break_12", + "fg_item_qty", + "sales_order_item" + ], + "fields": [ + { + "bold": 1, + "columns": 2, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Item Name", + "print_hide": 1, + "reqd": 1 + }, + { + "bold": 1, + "columns": 1, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity", + "print_width": "60px", + "reqd": 1, + "width": "60px" + }, + { + "bold": 1, + "columns": 2, + "fetch_from": "item_code.standard_rate", + "fetch_if_empty": 1, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "options": "currency", + "reqd": 1 + }, + { + "columns": 2, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "fg_item", + "fieldtype": "Link", + "label": "Finished Good Item", + "options": "Item", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "fg_item_qty", + "fieldtype": "Float", + "label": "Finished Good Item Quantity", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Sales Order Item", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM", + "read_only": 1, + "reqd": 1 + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-09-05 13:33:49.154869", + "modified_by": "Administrator", + "module": "Subcontracting", + "name": "Subcontracting Inward Order Service Item", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "row_format": "Dynamic", + "search_fields": "item_name", + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.py b/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.py new file mode 100644 index 00000000000..f157ba39b9a --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order_service_item/subcontracting_inward_order_service_item.py @@ -0,0 +1,31 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SubcontractingInwardOrderServiceItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + amount: DF.Currency + fg_item: DF.Link + fg_item_qty: DF.Float + item_code: DF.Link + item_name: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + qty: DF.Float + rate: DF.Currency + sales_order_item: DF.Data | None + uom: DF.Link + # end: auto-generated types + + pass diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index 96a0ed11931..5c596c676e9 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -252,12 +252,12 @@ class SubcontractingOrder(SubcontractingController): if si.fg_item: item = frappe.get_doc("Item", si.fg_item) - qty, subcontracted_quantity, fg_item_qty = frappe.db.get_value( + qty, subcontracted_qty, fg_item_qty = frappe.db.get_value( "Purchase Order Item", si.purchase_order_item, - ["qty", "subcontracted_quantity", "fg_item_qty"], + ["qty", "subcontracted_qty", "fg_item_qty"], ) - available_qty = flt(qty) - flt(subcontracted_quantity) + available_qty = flt(qty) - flt(subcontracted_qty) if available_qty == 0: continue @@ -342,23 +342,23 @@ class SubcontractingOrder(SubcontractingController): def update_subcontracted_quantity_in_po(self, cancel=False): for service_item in self.service_items: - subcontracted_quantity = flt( + subcontracted_qty = flt( frappe.db.get_value( - "Purchase Order Item", service_item.purchase_order_item, "subcontracted_quantity" + "Purchase Order Item", service_item.purchase_order_item, "subcontracted_qty" ) ) - subcontracted_quantity = ( - (subcontracted_quantity + service_item.qty) + subcontracted_qty = ( + (subcontracted_qty + service_item.qty) if not cancel - else (subcontracted_quantity - service_item.qty) + else (subcontracted_qty - service_item.qty) ) frappe.db.set_value( "Purchase Order Item", service_item.purchase_order_item, - "subcontracted_quantity", - subcontracted_quantity, + "subcontracted_qty", + subcontracted_qty, ) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index c9fe457ef79..fee1cac2542 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -336,6 +336,10 @@ frappe.ui.form.on("Subcontracting Receipt", { reset_raw_materials_table: (frm) => { frm.clear_table("supplied_items"); + frm.doc.__unsaved = true; + if (!frm.doc.set_posting_time) { + frm.set_value("posting_time", frappe.datetime.now_time()); + } frm.call({ method: "reset_raw_materials", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index f3a37cc8bb1..79b46ec146a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -649,6 +649,7 @@ "label": "Raw Materials Actions" }, { + "description": "Click this button if you encounter a negative stock error for a serial or batch item. The system will fetch the available serials or batches automatically.", "fieldname": "reset_raw_materials_table", "fieldtype": "Button", "label": "Reset Raw Materials Table" @@ -678,7 +679,7 @@ "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2024-12-06 15:24:38.384232", + "modified": "2025-10-08 21:43:27.065640", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", @@ -739,6 +740,7 @@ "write": 1 } ], + "row_format": "Dynamic", "search_fields": "status, posting_date, supplier", "show_name_in_global_search": 1, "sort_field": "creation", @@ -747,4 +749,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index e7555194672..72d434eea55 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -198,6 +198,7 @@ class SubcontractingReceipt(SubcontractingController): @frappe.whitelist() def reset_raw_materials(self): self.supplied_items = [] + self.flags.reset_raw_materials = True self.create_raw_materials_supplied() def validate_closed_subcontracting_order(self):