diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 69c1e16bb1e..1a5ee36b95a 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -202,7 +202,7 @@ class Account(NestedSet): msg = _( "There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report" ).format( - frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type + frappe.bold(_("Account Type")), doc_before_save.account_type, doc_before_save.account_type ) frappe.msgprint(msg) self.add_comment("Comment", msg) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index 9faf8693a8a..fda868cfe51 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -87,19 +87,15 @@ class POSClosingEntry(StatusUpdater): as_dict=1, )[0] if pos_invoice.consolidated_invoice: - invalid_row.setdefault("msg", []).append( - _("POS Invoice is {}").format(frappe.bold("already consolidated")) - ) + invalid_row.setdefault("msg", []).append(_("POS Invoice is already consolidated")) invalid_rows.append(invalid_row) continue if pos_invoice.pos_profile != self.pos_profile: invalid_row.setdefault("msg", []).append( - _("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile)) + _("POS Profile doesn't match {}").format(frappe.bold(self.pos_profile)) ) if pos_invoice.docstatus != 1: - invalid_row.setdefault("msg", []).append( - _("POS Invoice is not {}").format(frappe.bold("submitted")) - ) + invalid_row.setdefault("msg", []).append(_("POS Invoice is not submitted")) if pos_invoice.owner != self.user: invalid_row.setdefault("msg", []).append( _("POS Invoice isn't created by user {}").format(frappe.bold(self.owner)) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index ef4db1dac98..d517c9d6da2 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -188,7 +188,7 @@ class POSInvoice(SalesInvoice): def validate(self): if not cint(self.is_pos): frappe.throw( - _("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment")) + _("POS Invoice should have the field {0} checked.").format(frappe.bold(_("Include Payment"))) ) # run on validate method of selling controller diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 2cf204dd347..aecdac0b3a1 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -97,16 +97,15 @@ class POSInvoiceMergeLog(Document): return_against_status = frappe.db.get_value("POS Invoice", return_against, "status") if return_against_status != "Consolidated": # if return entry is not getting merged in the current pos closing and if it is not consolidated - bold_unconsolidated = frappe.bold("not Consolidated") - msg = _("Row #{}: Original Invoice {} of return invoice {} is {}.").format( - d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated - ) + msg = _( + "Row #{}: The original Invoice {} of return invoice {} is not consolidated." + ).format(d.idx, bold_return_against, bold_pos_invoice) msg += " " msg += _( - "Original invoice should be consolidated before or along with the return invoice." + "The original invoice should be consolidated before or along with the return invoice." ) msg += "

" - msg += _("You can add original invoice {} manually to proceed.").format( + msg += _("You can add the original invoice {} manually to proceed.").format( bold_return_against ) frappe.throw(msg) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index de29ec0f004..1a1ff78a217 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -186,7 +186,8 @@ class PricingRule(Document): if not self.priority: throw( _("As the field {0} is enabled, the field {1} is mandatory.").format( - frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority") + frappe.bold(_("Apply Discount on Discounted Rate")), + frappe.bold(_("Priority")), ) ) @@ -194,7 +195,7 @@ class PricingRule(Document): throw( _( "As the field {0} is enabled, the value of the field {1} should be more than 1." - ).format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")) + ).format(frappe.bold(_("Apply Discount on Discounted Rate")), frappe.bold(_("Priority"))) ) def validate_applicable_for_selling_or_buying(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c33b2c0ff17..78fc02999ef 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -377,16 +377,16 @@ class PurchaseInvoice(BuyingController): if account.report_type != "Balance Sheet": frappe.throw( _( - "Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account." - ).format(frappe.bold("Credit To")), + "Please ensure that the {0} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account." + ).format(frappe.bold(_("Credit To"))), title=_("Invalid Account"), ) if self.supplier and account.account_type != "Payable": frappe.throw( _( - "Please ensure {} account {} is a Payable account. Change the account type to Payable or select a different account." - ).format(frappe.bold("Credit To"), frappe.bold(self.credit_to)), + "Please ensure that the {0} account {1} is a Payable account. You can change the account type to Payable or select a different account." + ).format(frappe.bold(_("Credit To")), frappe.bold(self.credit_to)), title=_("Invalid Account"), ) @@ -634,7 +634,7 @@ class PurchaseInvoice(BuyingController): "To submit the invoice without purchase order please set {0} as {1} in {2}" ).format( frappe.bold(_("Purchase Order Required")), - frappe.bold("No"), + frappe.bold(_("No")), get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"), ) throw(msg, title=_("Mandatory Purchase Order")) @@ -655,7 +655,7 @@ class PurchaseInvoice(BuyingController): "To submit the invoice without purchase receipt please set {0} as {1} in {2}" ).format( frappe.bold(_("Purchase Receipt Required")), - frappe.bold("No"), + frappe.bold(_("No")), get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"), ) throw(msg, title=_("Mandatory Purchase Receipt")) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5d6c8a353fb..1966a78ef49 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -514,7 +514,7 @@ class SalesInvoice(SellingController): ) if pos_closing_entry and pos_closing_entry[0]: msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format( - frappe.bold("Consolidated Sales Invoice"), + frappe.bold(_("Consolidated Sales Invoice")), get_link_to_form("POS Closing Entry", pos_closing_entry[0]), ) frappe.throw(msg, title=_("Not Allowed")) @@ -858,7 +858,7 @@ class SalesInvoice(SellingController): if account.report_type != "Balance Sheet": msg = ( - _("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To")) + _("Please ensure {} account is a Balance Sheet account.").format(frappe.bold(_("Debit To"))) + " " ) msg += _( @@ -869,7 +869,7 @@ class SalesInvoice(SellingController): if self.customer and account.account_type != "Receivable": msg = ( _("Please ensure {} account {} is a Receivable account.").format( - frappe.bold("Debit To"), frappe.bold(self.debit_to) + frappe.bold(_("Debit To")), frappe.bold(self.debit_to) ) + " " ) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index 276e95bd5d1..0c18dac7768 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -336,7 +336,7 @@ def get_tds_docs(filters): def get_tds_docs_query(filters, bank_accounts, tds_accounts): if not tds_accounts: frappe.throw( - _("No {0} Accounts found for this company.").format(frappe.bold("Tax Withholding")), + _("No {0} Accounts found for this company.").format(frappe.bold(_("Tax Withholding"))), title=_("Accounts Missing Error"), ) gle = frappe.qb.DocType("GL Entry") diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 1b9e911be5b..db37e54ada8 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -669,7 +669,7 @@ class Asset(AccountsController): if not fixed_asset_account: frappe.throw( _("Set {0} in asset category {1} for company {2}").format( - frappe.bold("Fixed Asset Account"), + frappe.bold(_("Fixed Asset Account")), frappe.bold(self.asset_category), frappe.bold(self.company), ), @@ -904,7 +904,7 @@ def transfer_asset(args): @frappe.whitelist() def get_item_details(item_code, asset_category, gross_purchase_amount): - asset_category_doc = frappe.get_doc("Asset Category", asset_category) + asset_category_doc = frappe.get_cached_doc("Asset Category", asset_category) books = [] for d in asset_category_doc.finance_books: books.append( diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 651b75d0a70..d9f2231b313 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -11,6 +11,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s onload() { this.setup_queries(); + erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype); } refresh() { diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 63d0404642a..58e120e14b0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -233,7 +233,7 @@ class AccountsController(TransactionBase): ).format( frappe.bold(document_type), get_link_to_form(self.doctype, self.get("return_against")), - frappe.bold("Update Outstanding for Self"), + frappe.bold(_("Update Outstanding for Self")), get_link_to_form("Payment Reconciliation", "Payment Reconciliation"), ) ) @@ -1962,7 +1962,9 @@ class AccountsController(TransactionBase): def raise_missing_debit_credit_account_error(self, party_type, party): """Raise an error if debit to/credit to account does not exist.""" - db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To") + db_or_cr = ( + frappe.bold(_("Debit To")) if self.doctype == "Sales Invoice" else frappe.bold(_("Credit To")) + ) rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable" link_to_party = frappe.utils.get_link_to_form(party_type, party) @@ -3085,9 +3087,9 @@ def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw( - _("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.").format( - frappe.bold("default warehouse"), frappe.bold(item.item_code) - ) + _( + "Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings." + ).format(frappe.bold(item.item_code)) ) set_child_tax_template_and_map(item, child_item, p_doc) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index cc6870f892a..5dace4af884 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -150,7 +150,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i ) msg += "
" + _( "To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings." - ).format(frappe.bold("Allow Rename Attribute Value")) + ).format(frappe.bold(_("Allow Rename Attribute Value"))) frappe.throw(msg, InvalidItemAttributeValueError, title=_("Edit Not Allowed")) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index c1f565e7323..766e67be0f6 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -697,7 +697,7 @@ class SellingController(StockController): duplicate_items_msg = _("Item {0} entered multiple times.").format(frappe.bold(d.item_code)) duplicate_items_msg += "

" duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format( - frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"), + frappe.bold(_("Allow Item to Be Added Multiple Times in a Transaction")), get_link_to_form("Selling Settings", "Selling Settings"), ) if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: diff --git a/erpnext/crm/doctype/prospect/test_prospect.py b/erpnext/crm/doctype/prospect/test_prospect.py index c3930ee6c93..47e38515cac 100644 --- a/erpnext/crm/doctype/prospect/test_prospect.py +++ b/erpnext/crm/doctype/prospect/test_prospect.py @@ -27,6 +27,29 @@ class TestProspect(unittest.TestCase): address_doc.reload() self.assertEqual(address_doc.has_link("Prospect", prospect_doc.name), True) + def test_make_customer_from_prospect(self): + from erpnext.crm.doctype.prospect.prospect import make_customer as make_customer_from_prospect + + frappe.delete_doc_if_exists("Customer", "_Test Prospect") + + prospect = frappe.get_doc( + { + "doctype": "Prospect", + "company_name": "_Test Prospect", + "customer_group": "_Test Customer Group", + } + ) + prospect.insert() + + customer = make_customer_from_prospect("_Test Prospect") + + self.assertEqual(customer.doctype, "Customer") + self.assertEqual(customer.company_name, "_Test Prospect") + self.assertEqual(customer.customer_group, "_Test Customer Group") + + customer.company = "_Test Company" + customer.insert() + def make_prospect(**args): args = frappe._dict(args) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index edfab4777a7..e187c075077 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -96,7 +96,7 @@ def add_bank_accounts(response, bank, company): frappe.throw( _( "Please setup and enable a group account with the Account Type - {0} for the company {1}" - ).format(frappe.bold("Bank"), company) + ).format(frappe.bold(_("Bank")), company) ) for account in response["accounts"]: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 208c9e58d4f..bece96d64e8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -371,3 +371,4 @@ erpnext.patches.v15_0.update_warehouse_field_in_asset_repair_consumed_item_docty erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation +erpnext.patches.v15_0.drop_index_posting_datetime_from_sle diff --git a/erpnext/patches/v15_0/drop_index_posting_datetime_from_sle.py b/erpnext/patches/v15_0/drop_index_posting_datetime_from_sle.py new file mode 100644 index 00000000000..40b0b2d6759 --- /dev/null +++ b/erpnext/patches/v15_0/drop_index_posting_datetime_from_sle.py @@ -0,0 +1,16 @@ +import click +import frappe + + +def execute(): + table = "tabStock Ledger Entry" + index = "posting_datetime_creation_index" + + if not frappe.db.has_index(table, index): + return + + try: + frappe.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`") + click.echo(f"✓ dropped {index} index from {table}") + except Exception: + frappe.log_error("Failed to drop index") diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index 6ff38f91082..bab25b6eca2 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -374,6 +374,14 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { label: "Cost Center", options: "Cost Center", depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", + get_query: () => { + return { + filters: { + is_group: 0, + company: this.company, + }, + }; + }, }, { fieldtype: "Section Break", diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index af32f4ecf84..59f082431b8 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -21,6 +21,7 @@ "gender", "lead_name", "opportunity_name", + "prospect_name", "account_manager", "image", "defaults_tab", @@ -570,6 +571,14 @@ { "fieldname": "column_break_nwor", "fieldtype": "Column Break" + }, + { + "fieldname": "prospect_name", + "fieldtype": "Link", + "label": "From Prospect", + "no_copy": 1, + "options": "Prospect", + "print_hide": 1 } ], "icon": "fa fa-user", diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 71f0022df14..9bd9b5e760a 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -77,6 +77,7 @@ class Customer(TransactionBase): payment_terms: DF.Link | None portal_users: DF.Table[PortalUser] primary_address: DF.Text | None + prospect_name: DF.Link | None represents_company: DF.Link | None sales_team: DF.Table[SalesTeam] salutation: DF.Link | None diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index f9442204df1..2a4b04b9db5 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -347,8 +347,8 @@ def make_sales_order(source_name: str, target_doc=None): return _make_sales_order(source_name, target_doc) -def _make_sales_order(source_name, target_doc=None, customer_group=None, ignore_permissions=False): - customer = _make_customer(source_name, ignore_permissions, customer_group) +def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): + customer = _make_customer(source_name, ignore_permissions) ordered_items = frappe._dict( frappe.db.get_all( "Sales Order Item", @@ -502,51 +502,71 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): return doclist -def _make_customer(source_name, ignore_permissions=False, customer_group=None): +def _make_customer(source_name, ignore_permissions=False): quotation = frappe.db.get_value( - "Quotation", source_name, ["order_type", "party_name", "customer_name"], as_dict=1 + "Quotation", + source_name, + ["order_type", "quotation_to", "party_name", "customer_name"], + as_dict=1, ) - if quotation and quotation.get("party_name"): - if not frappe.db.exists("Customer", quotation.get("party_name")): - lead_name = quotation.get("party_name") - customer_name = frappe.db.get_value( - "Customer", {"lead_name": lead_name}, ["name", "customer_name"], as_dict=True - ) - if not customer_name: - from erpnext.crm.doctype.lead.lead import _make_customer + if quotation.quotation_to == "Customer": + return frappe.get_doc("Customer", quotation.party_name) - customer_doclist = _make_customer(lead_name, ignore_permissions=ignore_permissions) - customer = frappe.get_doc(customer_doclist) - customer.flags.ignore_permissions = ignore_permissions - customer.customer_group = customer_group + # Check if a Customer already exists for the Lead or Prospect. + existing_customer = None + if quotation.quotation_to == "Lead": + existing_customer = frappe.db.get_value("Customer", {"lead_name": quotation.party_name}) + elif quotation.quotation_to == "Prospect": + existing_customer = frappe.db.get_value("Customer", {"prospect_name": quotation.party_name}) - try: - customer.insert() - return customer - except frappe.NameError: - if frappe.defaults.get_global_default("cust_master_name") == "Customer Name": - customer.run_method("autoname") - customer.name += "-" + lead_name - customer.insert() - return customer - else: - raise - except frappe.MandatoryError as e: - mandatory_fields = e.args[0].split(":")[1].split(",") - mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] + if existing_customer: + return frappe.get_doc("Customer", existing_customer) - frappe.local.message_log = [] - lead_link = frappe.utils.get_link_to_form("Lead", lead_name) - message = ( - _("Could not auto create Customer due to the following missing mandatory field(s):") - + "
" - ) - message += "
" - message += _("Please create Customer from Lead {0}.").format(lead_link) + # If no Customer exists, create a new Customer or Prospect. + if quotation.quotation_to == "Lead": + return create_customer_from_lead(quotation.party_name, ignore_permissions=ignore_permissions) + elif quotation.quotation_to == "Prospect": + return create_customer_from_prospect(quotation.party_name, ignore_permissions=ignore_permissions) - frappe.throw(message, title=_("Mandatory Missing")) - else: - return customer_name - else: - return frappe.get_doc("Customer", quotation.get("party_name")) + return None + + +def create_customer_from_lead(lead_name, ignore_permissions=False): + from erpnext.crm.doctype.lead.lead import _make_customer + + customer = _make_customer(lead_name, ignore_permissions=ignore_permissions) + customer.flags.ignore_permissions = ignore_permissions + + try: + customer.insert() + return customer + except frappe.MandatoryError as e: + handle_mandatory_error(e, customer, lead_name) + + +def create_customer_from_prospect(prospect_name, ignore_permissions=False): + from erpnext.crm.doctype.prospect.prospect import make_customer as make_customer_from_prospect + + customer = make_customer_from_prospect(prospect_name) + customer.flags.ignore_permissions = ignore_permissions + + try: + customer.insert() + return customer + except frappe.MandatoryError as e: + handle_mandatory_error(e, customer, prospect_name) + + +def handle_mandatory_error(e, customer, lead_name): + from frappe.utils import get_link_to_form + + mandatory_fields = e.args[0].split(":")[1].split(",") + mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] + + frappe.local.message_log = [] + message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "
" + message += "
" + message += _("Please create Customer from Lead {0}.").format(get_link_to_form("Lead", lead_name)) + + frappe.throw(message, title=_("Mandatory Missing")) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 38a089fdb03..570677aad78 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -936,6 +936,10 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): } def set_missing_values(source, target): + if kwargs.get("ignore_pricing_rule"): + # Skip pricing rule when the dn is creating from the pick list + target.ignore_pricing_rule = 1 + target.run_method("set_missing_values") target.run_method("set_po_nos") target.run_method("calculate_taxes_and_totals") diff --git a/erpnext/setup/doctype/brand/brand.js b/erpnext/setup/doctype/brand/brand.js index 5d16d734e0f..99b4ace6cd2 100644 --- a/erpnext/setup/doctype/brand/brand.js +++ b/erpnext/setup/doctype/brand/brand.js @@ -3,22 +3,14 @@ frappe.ui.form.on("Brand", { setup: (frm) => { - frm.fields_dict["brand_defaults"].grid.get_field("default_warehouse").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("default_warehouse", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { company: row.company }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("default_discount_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("default_discount_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -27,13 +19,9 @@ frappe.ui.form.on("Brand", { is_group: 0, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("buying_cost_center").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("buying_cost_center", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -41,25 +29,17 @@ frappe.ui.form.on("Brand", { company: row.company, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("expense_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("expense_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_expense_account", filters: { company: row.company }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("default_provisional_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("default_provisional_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -68,13 +48,9 @@ frappe.ui.form.on("Brand", { is_group: 0, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("selling_cost_center").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("selling_cost_center", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -82,18 +58,14 @@ frappe.ui.form.on("Brand", { company: row.company, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("income_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("income_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_income_account", filters: { company: row.company }, }; - }; + }); }, }); diff --git a/erpnext/setup/doctype/brand/brand.json b/erpnext/setup/doctype/brand/brand.json index 45b4db81f1f..8511d221551 100644 --- a/erpnext/setup/doctype/brand/brand.json +++ b/erpnext/setup/doctype/brand/brand.json @@ -56,10 +56,11 @@ "idx": 1, "image_field": "image", "links": [], - "modified": "2021-03-01 15:57:30.005783", + "modified": "2024-08-20 14:10:21.377962", "modified_by": "Administrator", "module": "Setup", "name": "Brand", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -108,4 +109,4 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "ASC" -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index f79ea60f5c4..8028b8e6af4 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -203,7 +203,7 @@ class Company(NestedSet): ): frappe.throw( _("'{0}' should be in company currency {1}.").format( - frappe.bold("Default Advance Received Account"), frappe.bold(self.default_currency) + frappe.bold(_("Default Advance Received Account")), frappe.bold(self.default_currency) ) ) @@ -214,7 +214,7 @@ class Company(NestedSet): ): frappe.throw( _("'{0}' should be in company currency {1}.").format( - frappe.bold("Default Advance Paid Account"), frappe.bold(self.default_currency) + frappe.bold(_("Default Advance Paid Account")), frappe.bold(self.default_currency) ) ) @@ -447,7 +447,7 @@ class Company(NestedSet): ): frappe.throw( _("Set default {0} account for non stock items").format( - frappe.bold("Provisional Account") + frappe.bold(_("Provisional Account")) ) ) diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index 6d95d3bcb24..06f2f43374e 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -42,7 +42,6 @@ class CustomerGroup(NestedSet): def validate_currency_for_receivable_and_advance_account(self): for x in self.accounts: - company_default_currency = frappe.get_cached_value("Company", x.company, "default_currency") receivable_account_currency = None advance_account_currency = None @@ -56,21 +55,6 @@ class CustomerGroup(NestedSet): "Account", x.advance_account, "account_currency" ) - if receivable_account_currency and receivable_account_currency != company_default_currency: - frappe.throw( - _("Receivable Account: {0} must be in Company default currency: {1}").format( - frappe.bold(x.account), - frappe.bold(company_default_currency), - ) - ) - - if advance_account_currency and advance_account_currency != company_default_currency: - frappe.throw( - _("Advance Account: {0} must be in Company default currency: {1}").format( - frappe.bold(x.advance_account), frappe.bold(company_default_currency) - ) - ) - if ( receivable_account_currency and advance_account_currency diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.py b/erpnext/setup/doctype/supplier_group/supplier_group.py index fa0c6beac49..5e1cab40450 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.py +++ b/erpnext/setup/doctype/supplier_group/supplier_group.py @@ -37,7 +37,6 @@ class SupplierGroup(NestedSet): def validate_currency_for_payable_and_advance_account(self): for x in self.accounts: - company_default_currency = frappe.get_cached_value("Company", x.company, "default_currency") payable_account_currency = None advance_account_currency = None @@ -49,21 +48,6 @@ class SupplierGroup(NestedSet): "Account", x.advance_account, "account_currency" ) - if payable_account_currency and payable_account_currency != company_default_currency: - frappe.throw( - _("Payable Account: {0} must be in Company default currency: {1}").format( - frappe.bold(x.account), - frappe.bold(company_default_currency), - ) - ) - - if advance_account_currency and advance_account_currency != company_default_currency: - frappe.throw( - _("Advance Account: {0} must be in Company default currency: {1}").format( - frappe.bold(x.advance_account), frappe.bold(company_default_currency) - ) - ) - if ( payable_account_currency and advance_account_currency diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index c539c315747..5d71ca06cb4 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -188,9 +188,9 @@ class Batch(Document): if has_expiry_date and not self.expiry_date: frappe.throw( msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.").format( - frappe.bold("Shelf Life in Days"), + frappe.bold(_("Shelf Life in Days")), get_link_to_form("Item", self.item), - frappe.bold("Batch Expiry Date"), + frappe.bold(_("Batch Expiry Date")), ), title=_("Expiry Date Mandatory"), ) diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 0e466e44d1b..a5a46ff9187 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -19,6 +19,7 @@ "consider_rejected_warehouses", "get_item_locations", "pick_manually", + "ignore_pricing_rule", "section_break_6", "scan_barcode", "column_break_13", @@ -200,11 +201,18 @@ "fieldname": "pick_manually", "fieldtype": "Check", "label": "Pick Manually" + }, + { + "default": "0", + "description": "If enabled then system won't apply the pricing rule on the delivery note which will be create from the pick list", + "fieldname": "ignore_pricing_rule", + "fieldtype": "Check", + "label": "Ignore Pricing Rule" } ], "is_submittable": 1, "links": [], - "modified": "2024-03-27 22:49:16.954637", + "modified": "2024-08-14 13:20:42.168827", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 8d7022c3581..0c93fd6c402 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -50,6 +50,7 @@ class PickList(Document): customer_name: DF.Data | None for_qty: DF.Float group_same_items: DF.Check + ignore_pricing_rule: DF.Check locations: DF.Table[PickListItem] material_request: DF.Link | None naming_series: DF.Literal["STO-PICK-.YYYY.-"] @@ -1144,7 +1145,7 @@ def create_dn_with_so(sales_dict, pick_list): for customer in sales_dict: for so in sales_dict[customer]: delivery_note = None - kwargs = {"skip_item_mapping": True} + kwargs = {"skip_item_mapping": True, "ignore_pricing_rule": pick_list.ignore_pricing_rule} delivery_note = create_delivery_note_from_sales_order(so, delivery_note, kwargs=kwargs) break if delivery_note: diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 9fc304e4e21..ef74ec9d627 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -1205,3 +1205,64 @@ class TestPickList(FrappeTestCase): pl_doc.submit() frappe.db.set_single_value("Stock Settings", "over_picking_allowance", 0) + + def test_ignore_pricing_rule_in_pick_list(self): + frappe.flags.print_stmt = False + warehouse = "_Test Warehouse - _TC" + item = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "batch_number_series": "IPR-PICKLT-.######", + "create_new_batch": 1, + } + ).name + + make_stock_entry( + item=item, + to_warehouse=warehouse, + qty=2, + basic_rate=100, + ) + + pricing_rule = frappe.get_doc( + { + "doctype": "Pricing Rule", + "title": "Same Free Item", + "price_or_product_discount": "Product", + "selling": 1, + "apply_on": "Item Code", + "items": [ + { + "item_code": item, + } + ], + "same_item": 1, + "is_recursive": 1, + "recurse_for": 2, + "free_qty": 1, + "company": "_Test Company", + "customer": "_Test Customer", + } + ) + + pricing_rule.save() + frappe.flags.print_stmt = True + + so = make_sales_order(item_code=item, qty=2, rate=100, do_not_save=True) + so.set_warehouse = warehouse + so.submit() + + self.assertEqual(len(so.items), 2) + self.assertTrue(so.items[1].is_free_item) + + pl = create_pick_list(so.name) + pl.ignore_pricing_rule = 1 + pl.save() + pl.submit() + + self.assertEqual(len(pl.locations), 1) + + delivery_note = create_delivery_note(pl.name) + + self.assertEqual(len(delivery_note.items), 1) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py index 32f3b4fb85b..5fe79384e6d 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py @@ -701,6 +701,57 @@ class TestSerialandBatchBundle(FrappeTestCase): serial_nos = get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle) self.assertEqual(serial_nos, serial_nos1) + def test_auto_create_serial_and_batch_bundle_for_outward_for_batch_item(self): + item_code = make_item( + "Test Auto Create Batch Bundle for Outward 1", + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "batch_number_series": "ACSBBO-TACSB-.#####", + }, + ).name + + if not frappe.db.exists("Batch", "ACSBBO-TACSB-00001"): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": "ACSBBO-TACSB-00001", + "item": item_code, + "company": "_Test Company", + } + ).insert(ignore_permissions=True) + + make_stock_entry( + item_code=item_code, + qty=10, + target="_Test Warehouse - _TC", + rate=500, + use_serial_batch_fields=True, + batch_no="ACSBBO-TACSB-00001", + ) + + dispatch = make_stock_entry( + item_code=item_code, + qty=10, + target="_Test Warehouse - _TC", + rate=500, + do_not_submit=True, + ) + + original_value = frappe.db.get_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward" + ) + + frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0) + self.assertRaises(frappe.ValidationError, dispatch.submit) + + frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1) + dispatch.submit() + + frappe.db.set_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", original_value + ) + def get_batch_from_bundle(bundle): from erpnext.stock.serial_batch_bundle import get_batch_nos diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 14e0fdf6388..83ae8d09f66 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -898,8 +898,8 @@ class StockEntry(StockController): ).format( item.idx, frappe.bold(label), - frappe.bold("Manufacture"), - frappe.bold("Material Consumption for Manufacture"), + frappe.bold(_("Manufacture")), + frappe.bold(_("Material Consumption for Manufacture")), ) ) @@ -909,7 +909,7 @@ class StockEntry(StockController): ): frappe.throw( _("Only one {0} entry can be created against the Work Order {1}").format( - frappe.bold("Manufacture"), frappe.bold(self.work_order) + frappe.bold(_("Manufacture")), frappe.bold(self.work_order) ) ) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index a680b7733d3..a9529cc2ede 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -4,7 +4,7 @@ from frappe.permissions import add_user_permission, remove_user_permission from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, nowtime, today +from frappe.utils import add_days, cstr, flt, get_time, getdate, nowtime, today from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.stock.doctype.item.test_item import ( @@ -1780,6 +1780,74 @@ class TestStockEntry(FrappeTestCase): frappe.db.set_value("Serial and Batch Bundle", sbb, "type_of_transaction", "Inward") self.assertRaises(frappe.ValidationError, se.submit) + def test_stock_entry_for_same_posting_date_and_time(self): + warehouse = "_Test Warehouse - _TC" + item_code = "Test Stock Entry For Same Posting Datetime 1" + make_item(item_code, {"is_stock_item": 1}) + posting_date = nowdate() + posting_time = nowtime() + + for index in range(25): + se = make_stock_entry( + item_code=item_code, + qty=1, + to_warehouse=warehouse, + posting_date=posting_date, + posting_time=posting_time, + do_not_submit=True, + purpose="Material Receipt", + basic_rate=100, + ) + + se.append( + "items", + { + "item_code": item_code, + "item_name": se.items[0].item_name, + "description": se.items[0].description, + "t_warehouse": se.items[0].t_warehouse, + "basic_rate": 100, + "qty": 1, + "stock_qty": 1, + "conversion_factor": 1, + "expense_account": se.items[0].expense_account, + "cost_center": se.items[0].cost_center, + "uom": se.items[0].uom, + "stock_uom": se.items[0].stock_uom, + }, + ) + + se.remarks = f"The current number is {cstr(index)}" + + se.submit() + + sles = frappe.get_all( + "Stock Ledger Entry", + fields=[ + "posting_date", + "posting_time", + "actual_qty", + "qty_after_transaction", + "incoming_rate", + "stock_value_difference", + "stock_value", + ], + filters={"item_code": item_code, "warehouse": warehouse}, + order_by="creation", + ) + + self.assertEqual(len(sles), 50) + i = 0 + for sle in sles: + i += 1 + self.assertEqual(getdate(sle.posting_date), getdate(posting_date)) + self.assertEqual(get_time(sle.posting_time), get_time(posting_time)) + self.assertEqual(sle.actual_qty, 1) + self.assertEqual(sle.qty_after_transaction, i) + self.assertEqual(sle.incoming_rate, 100) + self.assertEqual(sle.stock_value_difference, 100) + self.assertEqual(sle.stock_value, 100 * i) + def make_serialized_item(**args): args = frappe._dict(args) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 319303dbbb0..cff1886d7fe 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -351,4 +351,3 @@ def on_doctype_update(): frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"]) frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"]) frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse") - frappe.db.add_index("Stock Ledger Entry", ["posting_datetime", "creation"]) diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 069192fdb16..587b25ea154 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1043,6 +1043,8 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): self.assertEqual(50, _get_stock_credit(final_consumption)) def test_tie_breaking(self): + from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_entries + frappe.flags.dont_execute_stock_reposts = True self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts") @@ -1085,6 +1087,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): self.assertEqual([10, 11], ordered_qty_after_transaction()) first.cancel() + repost_entries() self.assertEqual([1], ordered_qty_after_transaction()) backdated = make_stock_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 9808d2dfe72..8316488e253 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py @@ -255,7 +255,7 @@ class StockReservationEntry(Document): if self.has_batch_no else _("Warehouse"), frappe.bold(self.warehouse), - frappe.bold("Stock Reservation Entry"), + frappe.bold(_("Stock Reservation Entry")), ) frappe.throw(msg) @@ -497,7 +497,8 @@ def validate_stock_reservation_settings(voucher: object) -> None: if not frappe.db.get_single_value("Stock Settings", "enable_stock_reservation"): msg = _("Please enable {0} in the {1}.").format( - frappe.bold("Stock Reservation"), frappe.bold("Stock Settings") + frappe.bold(_("Stock Reservation")), + frappe.bold(_("Stock Settings")), ) frappe.throw(msg) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index c029b7bd1fb..229ff944750 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -175,7 +175,7 @@ class StockSettings(Document): if self.allow_negative_stock and self.enable_stock_reservation: frappe.throw( _("As {0} is enabled, you can not enable {1}.").format( - frappe.bold("Stock Reservation"), frappe.bold("Allow Negative Stock") + frappe.bold(_("Stock Reservation")), frappe.bold(_("Allow Negative Stock")) ) ) @@ -187,7 +187,7 @@ class StockSettings(Document): if self.allow_negative_stock: frappe.throw( _("As {0} is enabled, you can not enable {1}.").format( - frappe.bold("Allow Negative Stock"), frappe.bold("Stock Reservation") + frappe.bold(_("Allow Negative Stock")), frappe.bold(_("Stock Reservation")) ) ) @@ -207,7 +207,7 @@ class StockSettings(Document): if bin_with_negative_stock: frappe.throw( _("As there are negative stock, you can not enable {0}.").format( - frappe.bold("Stock Reservation") + frappe.bold(_("Stock Reservation")) ) ) @@ -221,7 +221,7 @@ class StockSettings(Document): if has_reserved_stock: frappe.throw( _("As there are reserved stock, you cannot disable {0}.").format( - frappe.bold("Stock Reservation") + frappe.bold(_("Stock Reservation")) ) ) diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 7b0cade3ca4..f2d157b0ae4 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -263,7 +263,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2024-01-24 16:27:28.299520", + "modified": "2024-08-14 16:08:15.733597", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", @@ -320,6 +320,5 @@ "sort_field": "modified", "sort_order": "DESC", "states": [], - "title_field": "warehouse_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 07e46312d3e..71e664dd066 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -221,7 +221,15 @@ class SerialBatchBundle: not self.sle.is_cancelled and not self.sle.serial_and_batch_bundle and self.item_details.has_batch_no == 1 - and self.item_details.create_new_batch + and ( + self.item_details.create_new_batch + or ( + frappe.db.get_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward" + ) + and self.sle.actual_qty < 0 + ) + ) ): self.make_serial_batch_no_bundle() elif not self.sle.is_cancelled: diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 693481dfba8..0961d9bd35e 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -647,6 +647,7 @@ class update_entries_after: and ( posting_datetime = %(posting_datetime)s ) + and creation = %(creation)s order by creation ASC for update @@ -1526,6 +1527,11 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc voucher_no = args.get("voucher_no") voucher_condition = f"and voucher_no != '{voucher_no}'" + elif args.get("creation"): + creation = args.get("creation") + operator = "<=" + voucher_condition = f"and creation < '{creation}'" + sle = frappe.db.sql( f""" select *, posting_datetime as "timestamp" diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index b140fdab51f..011a5bc371f 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -144,7 +144,7 @@ class ServiceLevelAgreement(Document): ): frappe.throw( _("{0} is not enabled in {1}").format( - frappe.bold("Track Service Level Agreement"), + frappe.bold(_("Track Service Level Agreement")), get_link_to_form("Support Settings", "Support Settings"), ) ) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 12b2854862c..b9d790d2428 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1095,7 +1095,7 @@ Guardian1 Name,Guardian1 Namen, Guardian2 Email ID,Guardian2 E-Mail-ID, Guardian2 Mobile No,Guardian2 Mobil Nein, Guardian2 Name,Guardian2 Namen, -HR Manager,Leiter der Personalabteilung, +HR Manager,Personalwesen Leiter, HSN,HSN, HSN/SAC,HSN / SAC, Half Yearly,Halbjährlich, @@ -3728,6 +3728,7 @@ Subdivision,Teilgebiet, Submit Review,Bewertung abschicken, Submitted,Gebucht, Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten, +Supplier Address Details,Vorschau Lieferantenadresse, Synchronize this account,Synchronisieren Sie dieses Konto, Tag,Etikett, Target Location is required while receiving Asset {0} from an employee,"Der Zielspeicherort ist erforderlich, wenn Asset {0} von einem Mitarbeiter empfangen wird", @@ -6027,10 +6028,10 @@ Hotel Room Reservation Item,Hotelzimmer-Reservierungselement, Hotel Settings,Hoteleinstellungen, Default Taxes and Charges,Standard-Steuern und -Abgaben, Default Invoice Naming Series,Standard-Rechnungsnummernkreis, -HR,HR, +HR,Personalwesen, Date on which this component is applied,"Datum, an dem diese Komponente angewendet wird", Salary Slip,Gehaltsabrechnung, -HR User,Nutzer Personalabteilung, +HR User,Personalwesen Benutzer, Job Applicant,Bewerber, Body,Körper, Appraisal Template,Bewertungsvorlage, @@ -9917,8 +9918,8 @@ Bank/Cash Account {0} doesn't belong to company {1},Das Bank- / Kassenkonto {0} Base Amount,Basisbetrag, Base Total Billed Amount,Insg. abgerechneter Betrag in Basiswährung, Base Total Costing Amount,Gesamtkosten in Basiswährung, -"Based on your HR Policy, select your leave allocation period's end date",Wählen Sie auf der Grundlage Ihrer HR-Richtlinien das Enddatum Ihres Abwesenheitskontingents aus, -"Based on your HR Policy, select your leave allocation period's start date",Wählen Sie auf der Grundlage Ihrer HR-Richtlinie das Startdatum Ihres Abwesenheitskontingents aus, +"Based on your HR Policy, select your leave allocation period's end date",Wählen Sie auf der Grundlage Ihrer Personal-Richtlinien das Enddatum Ihres Abwesenheitskontingents aus, +"Based on your HR Policy, select your leave allocation period's start date",Wählen Sie auf der Grundlage Ihrer Personal-Richtlinie das Startdatum Ihres Abwesenheitskontingents aus, Batch No is mandatory,Chargennummer ist obligatorisch, Batch No {0} does not exists,Charge Nr. {0} existiert nicht, Batch No {0} is linked with Item {1} which has serial no. Please scan serial no instead.,"Die Chargennummer {0} ist mit dem Artikel {1} verknüpft, der eine Seriennummer hat. Bitte scannen Sie stattdessen die Seriennummer.", @@ -11472,7 +11473,7 @@ Shipment Parcel,Versandpaket, Shipment Parcel Template,Versandpaketvorlage, Shipment Type,Sendungstyp, Shipment details,Sendungsdetails, -Shipping Address Details,Lieferadressendetails, +Shipping Address Details,Vorschau Lieferadresse, Shipping Address Template,Vorlage Lieferadresse, Show Balances in Chart Of Accounts,Saldo in Kontenplan anzeigen, Show Barcode Field in Stock Transactions,Barcode-Feld in Lagerbewegungen anzeigen, @@ -11815,3 +11816,103 @@ will be,wird sein, {} is a child company.,{} ist ein untergeordnetes Unternehmen., {} {} is already linked with another {},{} {} ist bereits mit einem anderen {} verknüpft, {} {} is already linked with {} {},{} {} ist bereits mit {} {} verknüpft, +A Transaction Deletion Document: {0} is triggered for {0},Eine Transaktion Löschungsdokument: {0} wird für {0} ausgelöst, +About Us Settings,"Einstellungen zu ""Über uns""", +Allow Internal Transfers at Arm's Length Price,Interne Übertragungen zum Fremdvergleichspreis zulassen, +Asset decapitalized after Asset Capitalization {0} was submitted,"Vermögenswert freigegeben, nachdem Anlagenaktivierung {0} gebucht wurde", +Auto Email Report,Auto Email-Bericht, +Auto close Opportunity Replied after the no. of days mentioned above,Automatische Schließungschaltung antwortete nach der oben genannten Anzahl von Tagen, +Avg Rate (Balance Stock),Durchschnittliche Rate (Lagerbestand), +Billing Interval in Subscription Plan must be Month to follow calendar months,"Abrechnungsintervall im Abonnementplan muss ""Monat"" sein, um Kalendermonate zu folgen", +Bulk Update,Massen-Update, +Can't disable batch wise valuation for active batches.,Sie können die chargenweise Bewertung für aktive Chargen nicht deaktivieren., +Can't disable batch wise valuation for items with FIFO valuation method.,Sie können die chargenweise Bewertung für Artikel mit FIFO-Bewertungsmethode nicht deaktivieren., +Cannot disable batch wise valuation for FIFO valuation method.,Sie können die chargenweise Bewertung für die FIFO-Bewertungsmethode nicht deaktivieren., +Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1},Mehrere Dokumente für ein Unternehmen können nicht in die Warteschlange gestellt werden. {0} ist bereits in die Warteschlange gestellt/wird für das Unternehmen ausgeführt: {1}, +Contact Us Settings,Einstellungen zu „Kontaktieren Sie uns“, +Create Journal Entries,Buchungssätze erstellen, +Create a variant with the template image.,Eine Variante mit dem Vorlagenbild erstellen., +Create in Draft Status,In Entwurfsstatus erstellen, +Custom delimiters,Benutzerdefinierte Trennzeichen, +Deleted Documents,Gelöschte Dokumente, +Delimiter options,Trennzeichenoptionen, +Dependent Task {0} is not a Template Task,Abhängige Aufgabe {0} ist keine Vorlage einer Aufgabe, +Depreciation Entry Posting Status,Buchungsstatus des Abschreibungseintrags, +Depreciation Schedule View,Ansicht Abschreibungsplan, +Depreciation cannot be calculated for fully depreciated assets,Für vollständig abgeschriebene Vermögensgegenstände kann keine Abschreibung berechnet werden, +Do Not Use Batch-wise Valuation,Keine chargenweise Bewertung verwenden, +Domain Settings,Domäneneinstellungen, +Email Domain,E-Mail-Domain, +Enable Immutable Ledger,Unveränderliches Hauptbuch aktivieren, +Enable it if users want to consider rejected materials to dispatch.,"Aktivieren Sie diese Option, wenn Benutzer zurückgewiesenes Material für den Versand berücksichtigen möchten.", +Excess Materials Consumed,Überschüssige Materialien verbraucht, +Excess Transfer,Überschuss-Übertragung, +FIFO Queue vs Qty After Transaction Comparison,Vergleich zwischen FIFO-Warteschlange und Menge nach Transaktion, +"For the {0}, the quantity is required to make the return entry","Für die {0} ist die Menge erforderlich, um die Retoure zu erstellen", +"If enabled, the item rate won't adjust to the valuation rate during internal transfers, but accounting will still use the valuation rate.","Falls aktiviert, wird der Artikelkurs bei internen Transfers nicht an den Bewertungskurs angepasst, aber die Buchhaltung verwendet weiterhin den Wertansatz.", +"If enabled, the system will use the moving average valuation method to calculate the valuation rate for the batched items and will not consider the individual batch-wise incoming rate.","Falls aktiviert, verwendet das System die Bewertungsmethode des gleitenden Durchschnitts zur Berechnung des Wertansatzes für die chargenweisen Artikel und berücksichtigt nicht den individuellen chargenweisen Eingangskurs.", +Job Worker,Unterauftragnehmer, +Job Worker Address,Unterauftragnehmer Adresse, +Job Worker Address Details,Vorschau Adresse Unterauftragnehmer, +Job Worker Contact,Vertrag des Unterauftragnehmers, +Job Worker Delivery Note,Lieferschein des Unterauftragnehmers, +Job Worker Name,Name des Unterauftragnehmer, +Job Worker Warehouse,Lagerhaus des Unterauftragnehmers, +"Learn about Common Party","Erfahren Sie mehr über die Verknüpfung von Kunden und Lieferanten", +Notification,Benachrichtigung, +Notification Settings,Benachrichtigungseinstellungen, +Offsetting for Accounting Dimension,Verrechnung für Buchhaltungsdimension, +Only Include Allocated Payments,Nur zugeordnete Zahlungen einbeziehen, +Only one {0} entry can be created against the Work Order {1},Nur ein {0} Eintrag kann gegen den Arbeitsauftrag {1} erstellt werden, +Over Picking Allowance,Überkommissionierzugabe, +Over Transfer Allowance,Überschlusstransferzugabe, +Overbilling of {} ignored because you have {} role.,"Überhöhte Abrechnung von {} wurde ignoriert, weil Sie die Rolle {} haben.", +Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.,Job für Zahlungsabgleich: {0} läuft für diese Partei. Kann jetzt nicht abgleichen., +Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.,"Eine Zahlungsanforderung, die aus einem Auftrag oder einer Bestellung erstellt wurde, wird im Entwurfsstatus sein. Wenn deaktiviert, wird das Dokument in ungespeichertem Zustand sein.", +Payment Request took too long to respond. Please try requesting for payment again.,"Zahlungsaufforderung hat zu lange gedauert, um zu antworten. Bitte versuchen Sie die Zahlung erneut anzufragen.", +Payment Terms Status for Sales Order,Status für Zahlungsbedingungen für Aufträge, +Pipeline By,Pipeline von, +Please enable Use Old Serial / Batch Fields to make_bundle,"Bitte aktivieren Sie ""Alte Serien-/Batchfelder verwenden"" für make_bundle", +Print Style,Druckstil, +Reconcile All Serial Nos / Batches,Alle Seriennummern/Chargen abgleichen, +Reset Company Default Values,Standardwerte des Unternehmens zurücksetzen, +Reset Raw Materials Table,Tabelle Rohstoffe zurücksetzen, +Return Against Subcontracting Receipt,Retoure gegen Unterauftragsbeleg, +Return Components,Komponenten zurückgeben, +Returned Against,Zurückgegeben gegen, +Returned exchange rate is neither integer not float.,Der zurückgegebene Wechselkurs ist weder Integer noch Float., +Round Off Tax Amount,Steuerbetrag abrunden, +Rounding Loss Allowance,Rundungsverlusttoleranz, +Rounding Loss Allowance should be between 0 and 1,Rundungsverlusttoleranz muss zwischen 0 und 1 sein, +Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1},Zeile #{0}: Ausschusslager ist für den abgelehnten Artikel {1} obligatorisch, +Row #{0}: You cannot use the inventory dimension '{1}' in Stock Reconciliation to modify the quantity or valuation rate. Stock reconciliation with inventory dimensions is intended solely for performing opening entries.,"Zeile #{0}: Sie können die Bestandsdimension '{1}' in der Bestandsabgleich nicht verwenden, um die Menge oder den Wertansatz zu ändern. Die Bestandsabgleich mit Bestandsdimensionen ist ausschließlich für die Durchführung von Eröffnungsbuchungen vorgesehen.", +Row {0}: Packed Qty must be equal to {1} Qty.,Zeile {0}: Verpackte Menge muss gleich der {1} Menge sein., +Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations,Zeile {0}: Die Gesamtzahl der Abschreibungen kann nicht kleiner oder gleich der Anzahl der gebuchten Abschreibungen zu Beginn sein, +SCO Supplied Item,Artikel beigestellt für Unterauftrag, +SLA Fulfilled On Status,SLA erfüllt am Status, +SLA will be applied if {1} is set as {2}{3},"SLA wird angewendet, wenn {1} als {2}{3} eingestellt ist", +SMS Settings,SMS-Einstellungen, +SO Total Qty,Kd.-Auftr.-Gesamtmenge, +"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}","Auftrag {0} existiert bereits für die Kundenbestellung {1}. Um mehrere Verkaufsaufträge zuzulassen, aktivieren Sie {2} in {3}", +"Scorecard variables can be used, as well as: +{total_score} (the total score from that period), +{period_number} (the number of periods to present day) +","Variablen der Bewertung können verwendet werden, sowie: +{total_score} (die Gesamtpunktzahl aus diesem Zeitraum), +{period_number} (die Anzahl der Zeiträume bis zum heutigen Tag) +", +Select Accounting Dimension.,Buchhaltungsdimension auswählen, +Select Corrective Operation,Nacharbeit auswählen, +Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff.,Wählen Sie Geburtsdatum. Damit wird das Alter der Mitarbeiter überprüft und die Einstellung von minderjährigen Mitarbeitern verhindert., +"Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases.",Wählen Sie Eintrittsdatum. Es wirkt sich auf die erste Gehaltsberechnung und die Zuteilung von Abwesenheiten auf Pro-rata-Basis aus., +Select Dimension,Dimension auswählen, +Select Items for Quality Inspection,Artikel für die Qualitätsprüfung auswählen, +Select Job Worker Address,Unterauftragnehmer Adresse auswählen, +Service Expenses,Wartungsaufwand, +Service Level Agreement for {0} {1} already exists.,Service Level Agreement für {0} {1} existiert bereits., +System Settings,Systemverwaltung, +Website Script,Webseiten-Skript, +Website Theme,Webseiten-Thema, +Workflow Action,Workflow-Aktion, +Workflow State,Workflow-Status, +{0} is not running. Cannot trigger events for this Document,{0} läuft nicht. Ereignisse für dieses Dokument können nicht ausgelöst werden,