diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index a48ce9b4c637..cd34bf7f8501 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -58,7 +58,7 @@ def build_conditions(process_type, account, company): ) if account: - conditions += f"AND {deferred_account}='{account}'" + conditions += f"AND {deferred_account}='{frappe.db.escape(account)}'" elif company: conditions += f"AND p.company = {frappe.db.escape(company)}" diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 92abb8cea89b..ac7883fce138 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -168,7 +168,7 @@ def get_payment_entries_for_bank_clearance( "Payment Entry" as payment_document, name as payment_entry, reference_no as cheque_number, reference_date as cheque_date, if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit, - if(paid_from=%(account)s, 0, received_amount) as debit, + if(paid_from=%(account)s, 0, received_amount + total_taxes_and_charges) as debit, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency from `tabPayment Entry` diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 593fa48e8560..47626492e845 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -259,7 +259,7 @@ def validate_depr_entry_voucher_type(self): frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation")) def validate_stock_accounts(self): - stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) + stock_accounts = get_stock_accounts(self.company, accounts=self.accounts) for account in stock_accounts: account_bal, stock_bal, warehouse_list = get_stock_and_account_balance( account, self.posting_date, self.company diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7885d8251298..b645d92cb64f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -2486,7 +2486,9 @@ def get_party_details(company, party_type, party, date, cost_center=None): account_balance = get_balance_on(party_account, date, cost_center=cost_center) _party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name" party_name = frappe.db.get_value(party_type, party, _party_name) - party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center) + party_balance = get_balance_on( + party_type=party_type, party=party, company=company, cost_center=cost_center + ) if party_type in ["Customer", "Supplier"]: party_bank_account = get_party_bank_account(party_type, party) bank_account = get_default_company_bank_account(company, party_type, party) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index fd07551897a8..462cae95ba5b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -20,6 +20,15 @@ from erpnext.accounts.utils import get_account_currency, get_currency_precision from erpnext.utilities import payment_app_import_guard +ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST = [ + "Sales Order", + "Purchase Order", + "Sales Invoice", + "Purchase Invoice", + "POS Invoice", + "Fees", +] + def _get_payment_gateway_controller(*args, **kwargs): with payment_app_import_guard(): @@ -525,6 +534,9 @@ def make_payment_request(**args): args = frappe._dict(args) + if args.dt not in ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST: + frappe.throw(_("Payment Requests cannot be created against: {0}").format(frappe.bold(args.dt))) + ref_doc = frappe.get_doc(args.dt, args.dn) gateway_account = get_gateway_details(args) or frappe._dict() diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index c171713dc611..5711b27da04b 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -80,8 +80,10 @@ frappe.ui.form.on("POS Closing Entry", { ) { reset_values(frm); frappe.run_serially([ + () => frappe.dom.freeze(__("Loading Invoices! Please Wait...")), () => frm.trigger("set_opening_amounts"), () => frm.trigger("get_pos_invoices"), + () => frappe.dom.unfreeze(), ]); } }, diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index 8707ee88860d..deb8bd7529db 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -57,6 +57,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex } onload_post_render(frm) { + super.onload_post_render(); this.pos_profile(frm); } diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index a74dfea2cca9..572529580e8e 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -728,14 +728,11 @@ def get_pricing_rule_items(pr_doc, other_items=False) -> list: def validate_coupon_code(coupon_name): coupon = frappe.get_doc("Coupon Code", coupon_name) - - if coupon.valid_from: - if coupon.valid_from > getdate(today()): - frappe.throw(_("Sorry, this coupon code's validity has not started")) - elif coupon.valid_upto: - if coupon.valid_upto < getdate(today()): - frappe.throw(_("Sorry, this coupon code's validity has expired")) - elif coupon.used >= coupon.maximum_use: + if coupon.valid_from and coupon.valid_from > getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has not started")) + elif coupon.valid_upto and coupon.valid_upto < getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has expired")) + elif coupon.maximum_use and coupon.used >= coupon.maximum_use: frappe.throw(_("Sorry, this coupon code is no longer valid")) 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 22be52992803..763607c22a16 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 @@ -25,6 +25,7 @@ "payment_terms_template", "sales_partner", "sales_person", + "show_remarks", "based_on_payment_terms", "section_break_3", "customer_collection", @@ -390,10 +391,16 @@ "fieldname": "ignore_cr_dr_notes", "fieldtype": "Check", "label": "Ignore System Generated Credit / Debit Notes" + }, + { + "default": "0", + "fieldname": "show_remarks", + "fieldtype": "Check", + "label": "Show Remarks" } ], "links": [], - "modified": "2024-08-13 10:41:18.381165", + "modified": "2024-10-18 17:51:39.108481", "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 509199ccae64..bc3ba26beb78 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 @@ -70,6 +70,7 @@ class ProcessStatementOfAccounts(Document): sales_person: DF.Link | None sender: DF.Link | None show_net_values_in_party_account: DF.Check + show_remarks: DF.Check start_date: DF.Date | None subject: DF.Data | None terms_and_conditions: DF.Link | None @@ -187,6 +188,7 @@ def get_common_filters(doc): "finance_book": doc.finance_book if doc.finance_book else None, "account": [doc.account] if doc.account else None, "cost_center": [cc.cost_center_name for cc in doc.cost_center], + "show_remarks": doc.show_remarks, } ) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 529086228de5..c680bd461308 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1359,14 +1359,15 @@ def make_item_gl_entries(self, gl_entries): else: if asset.calculate_depreciation: - notes = _( - "This schedule was created when Asset {0} was sold through Sales Invoice {1}." - ).format( - get_link_to_form(asset.doctype, asset.name), - get_link_to_form(self.doctype, self.get("name")), - ) - depreciate_asset(asset, self.posting_date, notes) - asset.reload() + if not asset.status == "Fully Depreciated": + notes = _( + "This schedule was created when Asset {0} was sold through Sales Invoice {1}." + ).format( + get_link_to_form(asset.doctype, asset.name), + get_link_to_form(self.doctype, self.get("name")), + ) + depreciate_asset(asset, self.posting_date, notes) + asset.reload() fixed_asset_gl_entries = get_gl_entries_on_asset_disposal( asset, diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 06e285e4adaf..65054aec53f9 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -881,16 +881,17 @@ def get_party_shipping_address(doctype: str, name: str) -> str | None: def get_partywise_advanced_payment_amount( party_type, posting_date=None, future_payment=0, company=None, party=None ): + account_type = frappe.get_cached_value("Party Type", party_type, "account_type") + ple = frappe.qb.DocType("Payment Ledger Entry") + acc = frappe.qb.DocType("Account") + query = ( frappe.qb.from_(ple) - .select(ple.party, Abs(Sum(ple.amount).as_("amount"))) - .where( - (ple.party_type.isin(party_type)) - & (ple.amount < 0) - & (ple.against_voucher_no == ple.voucher_no) - & (ple.delinked == 0) - ) + .inner_join(acc) + .on(ple.account == acc.name) + .select(ple.party) + .where((ple.party_type.isin(party_type)) & (acc.account_type == account_type) & (ple.delinked == 0)) .groupby(ple.party) ) @@ -909,9 +910,32 @@ def get_partywise_advanced_payment_amount( if invoice_doctypes := frappe.get_hooks("invoice_doctypes"): query = query.where(ple.voucher_type.notin(invoice_doctypes)) - data = query.run() - if data: - return frappe._dict(data) + # Get advance amount from Receivable / Payable Account + party_ledger = query.select(Abs(Sum(ple.amount).as_("amount"))) + party_ledger = party_ledger.where(ple.amount < 0) + party_ledger = party_ledger.where(ple.against_voucher_no == ple.voucher_no) + party_ledger = party_ledger.where( + acc.root_type == ("Liability" if account_type == "Payable" else "Asset") + ) + + data = party_ledger.run() + data = frappe._dict(data or {}) + + # Get advance amount from Advance Account + advance_ledger = query.select(Sum(ple.amount).as_("amount"), ple.account) + advance_ledger = advance_ledger.where( + acc.root_type == ("Asset" if account_type == "Payable" else "Liability") + ) + advance_ledger = advance_ledger.groupby(ple.account) + advance_ledger = advance_ledger.having(Sum(ple.amount) < 0) + + advance_data = advance_ledger.run() + + for row in advance_data: + data.setdefault(row[0], 0) + data[row[0]] += abs(row[1]) + + return data def get_default_contact(doctype: str, name: str) -> str | None: diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 274c8a7a3714..ab2f45d4f8bd 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -122,13 +122,13 @@ def get_provisional_profit_loss( for period in period_list: key = period if consolidated else period.key - total_assets = flt(asset[0].get(key)) + total_assets = flt(asset[-2].get(key)) effective_liability = 0.00 - if liability: - effective_liability += flt(liability[0].get(key)) - if equity: - effective_liability += flt(equity[0].get(key)) + if liability and liability[-1] == {}: + effective_liability += flt(liability[-2].get(key)) + if equity and equity[-1] == {}: + effective_liability += flt(equity[-2].get(key)) provisional_profit_loss[key] = total_assets - effective_liability total_row[key] = provisional_profit_loss[key] + effective_liability @@ -195,9 +195,9 @@ def get_report_summary( key = period if consolidated else period.key if asset: net_asset += asset[-2].get(key) - if liability: + if liability and liability[-1] == {}: net_liability += liability[-2].get(key) - if equity: + if equity and equity[-1] == {}: net_equity += equity[-2].get(key) if provisional_profit_loss: net_provisional_profit_loss += provisional_profit_loss.get(key) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index c6d9eac59662..377777ab2a35 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -122,21 +122,24 @@ def simulate_future_posting(self): """ simulate future posting by creating dummy gl entries. starts from the last posting date. """ - if self.service_start_date != self.service_end_date: - if add_days(self.last_entry_date, 1) < self.period_list[-1].to_date: - self.estimate_for_period_list = get_period_list( - self.filters.from_fiscal_year, - self.filters.to_fiscal_year, - add_days(self.last_entry_date, 1), - self.period_list[-1].to_date, - "Date Range", - "Monthly", - company=self.filters.company, - ) - for period in self.estimate_for_period_list: - amount = self.calculate_amount(period.from_date, period.to_date) - gle = self.make_dummy_gle(period.key, period.to_date, amount) - self.gle_entries.append(gle) + if ( + self.service_start_date != self.service_end_date + and add_days(self.last_entry_date, 1) < self.service_end_date + ): + self.estimate_for_period_list = get_period_list( + self.filters.from_fiscal_year, + self.filters.to_fiscal_year, + add_days(self.last_entry_date, 1), + self.service_end_date, + "Date Range", + "Monthly", + company=self.filters.company, + ) + + for period in self.estimate_for_period_list: + amount = self.calculate_amount(period.from_date, period.to_date) + gle = self.make_dummy_gle(period.key, period.to_date, amount) + self.gle_entries.append(gle) def calculate_item_revenue_expense_for_period(self): """ diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1d75fafc1a9d..91904059824f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1547,12 +1547,16 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle, precision): return matched -def get_stock_accounts(company, voucher_type=None, voucher_no=None): +def get_stock_accounts(company, voucher_type=None, voucher_no=None, accounts=None): stock_accounts = [ d.name for d in frappe.db.get_all("Account", {"account_type": "Stock", "company": company, "is_group": 0}) ] - if voucher_type and voucher_no: + + if accounts: + stock_accounts = [row.account for row in accounts if row.account in stock_accounts] + + elif voucher_type and voucher_no: if voucher_type == "Journal Entry": stock_accounts = [ d.account diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index b44164f2dae3..70ab7fdc8e84 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -144,6 +144,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): "has_certificate": task.certificate_required, "description": task.description, "assign_to_name": task.assign_to_name, + "task_assignee_email": task.assign_to, "periodicity": str(task.periodicity), "maintenance_type": task.maintenance_type, "due_date": task.next_due_date, diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 7d33176e2f37..c948630869b0 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -23,6 +23,7 @@ "column_break_6", "maintenance_status", "assign_to_name", + "task_assignee_email", "due_date", "completion_date", "description", @@ -168,15 +169,22 @@ "in_preview": 1, "label": "Task Name", "read_only": 1 + }, + { + "fieldname": "task_assignee_email", + "fieldtype": "Data", + "label": "Task Assignee Email", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-01-22 12:33:45.888124", + "modified": "2024-09-24 15:12:37.497853", "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance Log", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -199,4 +207,4 @@ "sort_order": "DESC", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py index 95d02714c5b1..140eb1af27eb 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py @@ -37,6 +37,7 @@ class AssetMaintenanceLog(Document): naming_series: DF.Literal["ACC-AML-.YYYY.-"] periodicity: DF.Data | None task: DF.Link | None + task_assignee_email: DF.Data | None task_name: DF.Data | None # end: auto-generated types diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 73923464ed96..188cbf0587c3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -393,12 +393,15 @@ def remove_serial_and_batch_bundle(self): def validate_return_against_account(self): if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against: cr_dr_account_field = "debit_to" if self.doctype == "Sales Invoice" else "credit_to" - cr_dr_account_label = "Debit To" if self.doctype == "Sales Invoice" else "Credit To" - cr_dr_account = self.get(cr_dr_account_field) - if frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) != cr_dr_account: + original_account = frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) + if original_account != self.get(cr_dr_account_field): frappe.throw( - _("'{0}' account: '{1}' should match the Return Against Invoice").format( - frappe.bold(cr_dr_account_label), frappe.bold(cr_dr_account) + _( + "Please set {0} to {1}, the same account that was used in the original invoice {2}." + ).format( + frappe.bold(_(self.meta.get_label(cr_dr_account_field), context=self.doctype)), + frappe.bold(original_account), + frappe.bold(self.return_against), ) ) @@ -3521,6 +3524,9 @@ def validate_fg_item_for_subcontracting(new_data, is_new): parent.update_billing_percentage() parent.set_status() + parent.validate_uom_is_integer("uom", "qty") + parent.validate_uom_is_integer("stock_uom", "stock_qty") + # Cancel and Recreate Stock Reservation Entries. if parent_doctype == "Sales Order": from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index f6f6742cc871..3c7f8b0ccca1 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -1235,6 +1235,17 @@ def add_items_in_ste(ste_doc, row, qty, rm_details, rm_detail_field="sco_rm_deta def make_return_stock_entry_for_subcontract( available_materials, order_doc, rm_details, order_doctype="Subcontracting Order" ): + def post_process(source_doc, target_doc): + target_doc.purpose = "Material Transfer" + + if source_doc.doctype == "Purchase Order": + target_doc.purchase_order = source_doc.name + else: + target_doc.subcontracting_order = source_doc.name + + target_doc.company = source_doc.company + target_doc.is_return = 1 + ste_doc = get_mapped_doc( order_doctype, order_doc.name, @@ -1245,18 +1256,13 @@ def make_return_stock_entry_for_subcontract( }, }, ignore_child_tables=True, + postprocess=post_process, ) - ste_doc.purpose = "Material Transfer" - if order_doctype == "Purchase Order": - ste_doc.purchase_order = order_doc.name rm_detail_field = "po_detail" else: - ste_doc.subcontracting_order = order_doc.name rm_detail_field = "sco_rm_detail" - ste_doc.company = order_doc.company - ste_doc.is_return = 1 for _key, value in available_materials.items(): if not value.qty: diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index e50cf9e4dd0b..022d1906e848 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -7,9 +7,9 @@ cur_frm.email_field = "email_id"; erpnext.LeadController = class LeadController extends frappe.ui.form.Controller { setup() { this.frm.make_methods = { - Customer: this.make_customer, - Quotation: this.make_quotation, - Opportunity: this.make_opportunity, + Customer: this.make_customer.bind(this), + Quotation: this.make_quotation.bind(this), + Opportunity: this.make_opportunity.bind(this), }; // For avoiding integration issues. diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index d2520d6b7eb6..06b9f1b07597 100644 --- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -1,4 +1,5 @@ { + "app": "erpnext", "charts": [], "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"YHCQG3wAGv\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Creator\",\"col\":3}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"Ubj6zXcmIQ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Plant Floor\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"PIQJYZOMnD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Manufacturing\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 17:11:37.032604", @@ -124,11 +125,86 @@ "onboard": 0, "type": "Link" }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bill of Materials", + "link_count": 6, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_count": 0, + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "Item", + "hidden": 0, + "is_query_report": 0, + "label": "Bill of Materials", + "link_count": 0, + "link_to": "BOM", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Workstation Type", + "link_count": 0, + "link_to": "Workstation Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Workstation", + "link_count": 0, + "link_to": "Workstation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Operation", + "link_count": 0, + "link_to": "Operation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Work Order", + "hidden": 0, + "is_query_report": 1, + "label": "Routing", + "link_count": 0, + "link_to": "Routing", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "hidden": 0, "is_query_report": 0, "label": "Reports", "link_count": 10, + "link_type": "DocType", "onboard": 0, "type": "Card Break" }, @@ -233,90 +309,16 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Work Order Consumed Materials", "link_count": 0, "link_to": "Work Order Consumed Materials", "link_type": "Report", "onboard": 0, "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Bill of Materials", - "link_count": 6, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Item", - "link_count": 0, - "link_to": "Item", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Item", - "hidden": 0, - "is_query_report": 0, - "label": "Bill of Materials", - "link_count": 0, - "link_to": "BOM", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Workstation Type", - "link_count": 0, - "link_to": "Workstation Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Workstation", - "link_count": 0, - "link_to": "Workstation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Operation", - "link_count": 0, - "link_to": "Operation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Work Order", - "hidden": 0, - "is_query_report": 1, - "label": "Routing", - "link_count": 0, - "link_to": "Routing", - "link_type": "DocType", - "onboard": 0, - "type": "Link" } ], - "modified": "2024-01-30 21:49:58.577218", + "modified": "2024-10-21 14:13:38.777556", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -398,5 +400,6 @@ "type": "Report" } ], - "title": "Manufacturing" + "title": "Manufacturing", + "type": "Workspace" } \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 333fca1d1da5..e59938909c7f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -378,3 +378,4 @@ erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter +erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log diff --git a/erpnext/patches/v15_0/update_task_assignee_email_field_in_asset_maintenance_log.py b/erpnext/patches/v15_0/update_task_assignee_email_field_in_asset_maintenance_log.py new file mode 100644 index 000000000000..a6eda9c2e17c --- /dev/null +++ b/erpnext/patches/v15_0/update_task_assignee_email_field_in_asset_maintenance_log.py @@ -0,0 +1,18 @@ +import frappe +from frappe.query_builder import DocType + + +def execute(): + if frappe.db.has_column("Asset Maintenance Log", "task_assignee_email"): + asset_maintenance_log = DocType("Asset Maintenance Log") + asset_maintenance_task = DocType("Asset Maintenance Task") + try: + ( + frappe.qb.update(asset_maintenance_log) + .set(asset_maintenance_log.task_assignee_email, asset_maintenance_task.assign_to) + .join(asset_maintenance_task) + .on(asset_maintenance_log.task == asset_maintenance_task.name) + .run() + ) + except Exception: + frappe.log_error("Failed to update Task Assignee Email Field.") diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index c6ee8a330c7b..de20f468ccb0 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -4,7 +4,8 @@ erpnext.accounts.unreconcile_payment = { add_unreconcile_btn(frm) { if (frm.doc.docstatus == 1) { if ( - (frm.doc.doctype == "Journal Entry" && frm.doc.voucher_type != "Journal Entry") || + (frm.doc.doctype == "Journal Entry" && + !["Journal Entry", "Bank Entry", "Cash Entry"].includes(frm.doc.voucher_type)) || !["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"].includes( frm.doc.doctype ) diff --git a/erpnext/setup/default_success_action.py b/erpnext/setup/default_success_action.py index 2b9e75c32652..dba205481843 100644 --- a/erpnext/setup/default_success_action.py +++ b/erpnext/setup/default_success_action.py @@ -11,14 +11,17 @@ def get_message(doctype): - return _("{0} has been submitted successfully").format(_(doctype)) + # Properly format the string with translated doctype + return _("{0} has been submitted successfully").format(doctype) def get_first_success_message(doctype): + # Reuse the get_message function for consistency return get_message(doctype) def get_default_success_action(): + # Loop through each doctype in the list and return formatted actions return [ { "doctype": "Success Action", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index cb442f6666dc..3e82ef51653d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1298,7 +1298,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle this.frm.cscript.toggle_enable_bom(); - if (doc.purpose == "Send to Subcontractor") { + if (erpnext.stock.is_subcontracting_or_return_transfer(doc)) { doc.customer = doc.customer_name = doc.customer_address = @@ -1364,6 +1364,10 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { }); }; +erpnext.stock.is_subcontracting_or_return_transfer = (doc) => { + return doc.purpose == "Send to Subcontractor" || (doc.purpose == "Material Transfer" && doc.is_return); +}; + function attach_bom_items(bom_no) { if (!bom_no) { return; diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 5ba9f2973a1b..f5fdfafe778c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -154,14 +154,14 @@ "search_index": 1 }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "purchase_order", "fieldtype": "Link", "label": "Purchase Order", "options": "Purchase Order" }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "subcontracting_order", "fieldtype": "Link", "label": "Subcontracting Order", @@ -427,13 +427,13 @@ }, { "collapsible": 1, - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "contact_section", "fieldtype": "Section Break", "label": "Supplier Details" }, { - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "supplier", "fieldtype": "Link", "label": "Supplier", @@ -445,7 +445,7 @@ }, { "bold": 1, - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "supplier_name", "fieldtype": "Data", "label": "Supplier Name", @@ -455,7 +455,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "supplier_address", "fieldtype": "Link", "label": "Supplier Address", diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py index 505399f4a5b9..dca5a0c7497d 100644 --- a/erpnext/templates/pages/order.py +++ b/erpnext/templates/pages/order.py @@ -4,6 +4,10 @@ import frappe from frappe import _ +from erpnext.accounts.doctype.payment_request.payment_request import ( + ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST, +) + def get_context(context): context.no_cache = 1 @@ -46,8 +50,10 @@ def get_context(context): ) context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points")) - context.show_pay_button = "payments" in frappe.get_installed_apps() and frappe.db.get_single_value( - "Buying Settings", "show_pay_button" + context.show_pay_button = ( + "payments" in frappe.get_installed_apps() + and frappe.db.get_single_value("Buying Settings", "show_pay_button") + and context.doc.doctype in ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST ) context.show_make_pi_button = False if context.doc.get("supplier"):