diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.json b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.json index 67c47176be5..dc5413e973c 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.json +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.json @@ -1,1056 +1,260 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "ACC-BG-.YYYY.-.#####", - "beta": 0, - "creation": "2016-12-17 10:43:35.731631", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "ACC-BG-.YYYY.-.#####", + "creation": "2016-12-17 10:43:35.731631", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "bg_type", + "reference_doctype", + "reference_docname", + "customer", + "supplier", + "project", + "column_break_6", + "amount", + "start_date", + "validity", + "end_date", + "bank_account_info", + "bank", + "bank_account", + "account", + "bank_account_no", + "column_break_17", + "iban", + "branch_code", + "swift_number", + "section_break_14", + "more_information", + "margin_details", + "bank_guarantee_number", + "name_of_beneficiary", + "column_break_19", + "margin_money", + "charges", + "fixed_deposit_number", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bg_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Guarantee Type", - "length": 0, - "no_copy": 0, - "options": "\nReceiving\nProviding", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bg_type", + "fieldtype": "Select", + "label": "Bank Guarantee Type", + "options": "\nReceiving\nProviding", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Document Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_docname", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Document Name", - "length": 0, - "no_copy": 0, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_docname", + "fieldtype": "Dynamic Link", + "label": "Reference Document Name", + "options": "reference_doctype" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.bg_type == \"Receiving\"", - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.bg_type == \"Receiving\"", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.bg_type == \"Providing\"", - "fieldname": "supplier", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Supplier", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.bg_type == \"Providing\"", + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "validity", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Validity in Days", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "validity", + "fieldtype": "Int", + "label": "Validity in Days" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account Info", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank_account_info", + "fieldtype": "Section Break", + "label": "Bank Account Info" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank", - "length": 0, - "no_copy": 0, - "options": "Bank", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank", + "fieldtype": "Link", + "label": "Bank", + "options": "Bank" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank_account", + "fieldtype": "Link", + "label": "Bank Account", + "options": "Bank Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account No", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank_account_no", + "fieldtype": "Data", + "label": "Bank Account No", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "iban", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "IBAN", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "iban", + "fieldtype": "Data", + "label": "IBAN", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "branch_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Branch Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "branch_code", + "fieldtype": "Data", + "label": "Branch Code", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "swift_number", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "SWIFT number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "swift_number", + "fieldtype": "Data", + "label": "SWIFT number", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_14", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_14", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "more_information", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Clauses and Conditions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "more_information", + "fieldtype": "Text Editor", + "label": "Clauses and Conditions" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "margin_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Other Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "margin_details", + "fieldtype": "Section Break", + "label": "Other Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_guarantee_number", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Guarantee Number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "bank_guarantee_number", + "fieldtype": "Data", + "label": "Bank Guarantee Number", "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "name_of_beneficiary", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Name of Beneficiary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "name_of_beneficiary", + "fieldtype": "Data", + "label": "Name of Beneficiary" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_19", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_19", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "margin_money", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Margin Money", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "margin_money", + "fieldtype": "Currency", + "label": "Margin Money" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "", - "fieldname": "charges", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Charges Incurred", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "charges", + "fieldtype": "Currency", + "label": "Charges Incurred" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "fixed_deposit_number", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fixed Deposit Number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "fixed_deposit_number", + "fieldtype": "Data", + "label": "Fixed Deposit Number" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Bank Guarantee", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Bank Guarantee", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:54.741746", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Guarantee", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2025-09-25 23:44:12.473583", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank Guarantee", + "naming_rule": "Expression (old style)", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "customer", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "customer", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "row_format": "Dynamic", + "search_fields": "customer", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "customer" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json index 62033126060..37fbd261898 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json @@ -116,7 +116,7 @@ { "allow_on_submit": 1, "fieldname": "reference_number", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Reference Number" }, { @@ -238,7 +238,7 @@ "grid_page_length": 50, "is_submittable": 1, "links": [], - "modified": "2025-06-18 17:24:57.044666", + "modified": "2025-09-26 17:06:29.207673", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Transaction", diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 39ea5fde777..16c84ac2a60 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -36,7 +36,7 @@ class BankTransaction(Document): party: DF.DynamicLink | None party_type: DF.Link | None payment_entries: DF.Table[BankTransactionPayments] - reference_number: DF.Data | None + reference_number: DF.SmallText | None status: DF.Literal["", "Pending", "Settled", "Unreconciled", "Reconciled", "Cancelled"] transaction_id: DF.Data | None transaction_type: DF.Data | None diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 46690a1a29a..510e6b1dcbc 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -253,7 +253,7 @@ class GLEntry(Document): ) def validate_cost_center(self): - if not self.cost_center: + if not self.cost_center or self.is_cancelled: return is_group, company = frappe.get_cached_value("Cost Center", self.cost_center, ["is_group", "company"]) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 7f71454d11d..82132ce70be 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -189,8 +189,8 @@ class JournalEntry(AccountsController): def on_submit(self): self.validate_cheque_info() - self.check_credit_limit() self.make_gl_entries() + self.check_credit_limit() self.update_asset_value() self.update_inter_company_jv() self.update_invoice_discounting() diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 0c9a20882c6..f8fde0b54bc 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -11,6 +11,7 @@ from frappe.utils import flt, nowdate from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.journal_entry.journal_entry import StockAccountInvalidTransaction from erpnext.exceptions import InvalidAccountCurrency +from erpnext.selling.doctype.customer.test_customer import make_customer, set_credit_limit class TestJournalEntry(unittest.TestCase): @@ -592,6 +593,15 @@ class TestJournalEntry(unittest.TestCase): self.assertEqual(jv.pay_to_recd_from, "_Test Receiver 2") + def test_credit_limit_for_customer(self): + customer = make_customer("_Test New Customer") + set_credit_limit("_Test New Customer", "_Test Company", 50) + jv = make_journal_entry(account1="Debtors - _TC", account2="_Test Cash - _TC", amount=100, save=False) + jv.accounts[0].party_type = "Customer" + jv.accounts[0].party = customer + jv.save() + self.assertRaises(frappe.ValidationError, jv.submit) + def make_journal_entry( account1, diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 03dfd724229..d36313dc6da 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -601,6 +601,7 @@ frappe.ui.form.on("Payment Entry", { if (frm.doc.payment_type == "Pay") { frm.events.paid_amount(frm); } + frm.events.paid_from_account_currency(frm); } ); }, @@ -624,6 +625,7 @@ frappe.ui.form.on("Payment Entry", { frm.events.received_amount(frm); } } + frm.events.paid_to_account_currency(frm); } ); }, diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 1a400878ba5..e6f2b2733dd 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -127,7 +127,13 @@ class PaymentRequest(Document): existing_payment_request_amount = flt(get_existing_payment_request_amount(ref_doc)) - if existing_payment_request_amount + flt(self.grand_total) > ref_amount: + if ( + flt( + existing_payment_request_amount + flt(self.grand_total, self.precision("grand_total")), + get_currency_precision(), + ) + > ref_amount + ): frappe.throw( _("Total Payment Request amount cannot be greater than {0} amount").format( self.reference_doctype diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 0863c68009a..1bc16992479 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -5,28 +5,33 @@ frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statement erpnext.utils.add_dimensions("Balance Sheet", 10); -frappe.query_reports["Balance Sheet"]["filters"].push({ - fieldname: "selected_view", - label: __("Select View"), - fieldtype: "Select", - options: [ - { value: "Report", label: __("Report View") }, - { value: "Growth", label: __("Growth View") }, - ], - default: "Report", - reqd: 1, -}); - -frappe.query_reports["Balance Sheet"]["filters"].push({ - fieldname: "accumulated_values", - label: __("Accumulated Values"), - fieldtype: "Check", - default: 1, -}); - -frappe.query_reports["Balance Sheet"]["filters"].push({ - fieldname: "include_default_book_entries", - label: __("Include Default FB Entries"), - fieldtype: "Check", - default: 1, -}); +frappe.query_reports["Balance Sheet"]["filters"].push( + { + fieldname: "selected_view", + label: __("Select View"), + fieldtype: "Select", + options: [ + { value: "Report", label: __("Report View") }, + { value: "Growth", label: __("Growth View") }, + ], + default: "Report", + reqd: 1, + }, + { + fieldname: "accumulated_values", + label: __("Accumulated Values"), + fieldtype: "Check", + default: 1, + }, + { + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, + }, + { + fieldname: "show_zero_values", + label: __("Show zero values"), + fieldtype: "Check", + } +); diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index bf46c9b07c6..89f36364826 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -212,7 +212,7 @@ def get_data( company_currency, accumulated_values=filters.accumulated_values, ) - out = filter_out_zero_value_rows(out, parent_children_map) + out = filter_out_zero_value_rows(out, parent_children_map, filters.show_zero_values) if out and total: add_total_row(out, root_type, balance_must_be, period_list, company_currency) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index add7a90b74b..5081c450151 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -677,7 +677,9 @@ class GrossProfitGenerator: si.name = si_item.parent and si.docstatus = 1 and si.is_return = 1 + and si.posting_date between %(from_date)s and %(to_date)s """, + {"from_date": self.filters.from_date, "to_date": self.filters.to_date}, as_dict=1, ) diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index c6c8a0266ef..88a614074e0 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -1,9 +1,9 @@ import frappe from frappe import qb from frappe.tests.utils import FrappeTestCase -from frappe.utils import flt, nowdate +from frappe.utils import add_days, flt, get_first_day, get_last_day, nowdate -from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note +from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note, make_sales_return from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.report.gross_profit.gross_profit import execute from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice @@ -392,7 +392,6 @@ class TestGrossProfit(FrappeTestCase): """ Item Qty for Sales Invoices with multiple instances of same item go in the -ve. Ideally, the credit noteshould cancel out the invoice items. """ - from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return # Invoice with an item added twice sinv = self.create_sales_invoice(qty=1, rate=100, posting_date=nowdate(), do_not_submit=True) @@ -635,3 +634,42 @@ class TestGrossProfit(FrappeTestCase): self.assertEqual(total.buying_amount, 0.0) self.assertEqual(total.gross_profit, 100.0) self.assertEqual(total.get("gross_profit_%"), 100.0) + + def test_profit_for_later_period_return(self): + month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) + + # create sales invoice on month start date + sinv = self.create_sales_invoice(qty=1, rate=100, do_not_save=True, do_not_submit=True) + sinv.set_posting_time = 1 + sinv.posting_date = month_start_date + sinv.save().submit() + + # create credit note on next month start date + cr_note = make_sales_return(sinv.name) + cr_note.set_posting_time = 1 + cr_note.posting_date = add_days(month_end_date, 1) + cr_note.save().submit() + + # apply filters for invoiced period + filters = frappe._dict( + company=self.company, from_date=month_start_date, to_date=month_end_date, group_by="Invoice" + ) + + _, data = execute(filters=filters) + total = data[-1] + + self.assertEqual(total.selling_amount, 100.0) + self.assertEqual(total.buying_amount, 0.0) + self.assertEqual(total.gross_profit, 100.0) + self.assertEqual(total.get("gross_profit_%"), 100.0) + + # extend filters upto returned period + filters.update(to_date=add_days(month_end_date, 1)) + + _, data = execute(filters=filters) + total = data[-1] + + self.assertEqual(total.selling_amount, 0.0) + self.assertEqual(total.buying_amount, 0.0) + self.assertEqual(total.gross_profit, 0.0) + self.assertEqual(total.get("gross_profit_%"), 0.0) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 98c6656ca3f..84ffcaf4a36 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -5,29 +5,34 @@ frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financi erpnext.utils.add_dimensions("Profit and Loss Statement", 10); -frappe.query_reports["Profit and Loss Statement"]["filters"].push({ - fieldname: "selected_view", - label: __("Select View"), - fieldtype: "Select", - options: [ - { value: "Report", label: __("Report View") }, - { value: "Growth", label: __("Growth View") }, - { value: "Margin", label: __("Margin View") }, - ], - default: "Report", - reqd: 1, -}); - -frappe.query_reports["Profit and Loss Statement"]["filters"].push({ - fieldname: "accumulated_values", - label: __("Accumulated Values"), - fieldtype: "Check", - default: 1, -}); - -frappe.query_reports["Profit and Loss Statement"]["filters"].push({ - fieldname: "include_default_book_entries", - label: __("Include Default FB Entries"), - fieldtype: "Check", - default: 1, -}); +frappe.query_reports["Profit and Loss Statement"]["filters"].push( + { + fieldname: "selected_view", + label: __("Select View"), + fieldtype: "Select", + options: [ + { value: "Report", label: __("Report View") }, + { value: "Growth", label: __("Growth View") }, + { value: "Margin", label: __("Margin View") }, + ], + default: "Report", + reqd: 1, + }, + { + fieldname: "accumulated_values", + label: __("Accumulated Values"), + fieldtype: "Check", + default: 1, + }, + { + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, + }, + { + fieldname: "show_zero_values", + label: __("Show zero values"), + fieldtype: "Check", + } +); diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 9e1dd1a57c3..3f75f1e2b4c 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -216,7 +216,7 @@ def get_opening_balance( ignore_is_opening=0, ): closing_balance = frappe.qb.DocType(doctype) - account = frappe.qb.DocType("Account") + accounts = frappe.db.get_all("Account", filters={"report_type": report_type}, pluck="name") opening_balance = ( frappe.qb.from_(closing_balance) @@ -228,14 +228,7 @@ def get_opening_balance( Sum(closing_balance.debit_in_account_currency).as_("debit_in_account_currency"), Sum(closing_balance.credit_in_account_currency).as_("credit_in_account_currency"), ) - .where( - (closing_balance.company == filters.company) - & ( - closing_balance.account.isin( - frappe.qb.from_(account).select("name").where(account.report_type == report_type) - ) - ) - ) + .where((closing_balance.company == filters.company) & (closing_balance.account.isin(accounts))) .groupby(closing_balance.account) ) @@ -290,21 +283,24 @@ def get_opening_balance( if filters.project: opening_balance = opening_balance.where(closing_balance.project == filters.project) - if filters.get("include_default_book_entries"): - company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") + if frappe.db.count("Finance Book"): + if filters.get("include_default_book_entries"): + company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") - if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): - frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'")) + if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): + frappe.throw( + _("To use a different finance book, please uncheck 'Include Default FB Entries'") + ) - opening_balance = opening_balance.where( - (closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) - | (closing_balance.finance_book.isnull()) - ) - else: - opening_balance = opening_balance.where( - (closing_balance.finance_book.isin([cstr(filters.finance_book), ""])) - | (closing_balance.finance_book.isnull()) - ) + opening_balance = opening_balance.where( + (closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) + | (closing_balance.finance_book.isnull()) + ) + else: + opening_balance = opening_balance.where( + (closing_balance.finance_book.isin([cstr(filters.finance_book), ""])) + | (closing_balance.finance_book.isnull()) + ) if accounting_dimensions: for dimension in accounting_dimensions: diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 599221185d9..a45b5813584 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -200,7 +200,11 @@ def validate_quantity(doc, key, args, ref, valid_items, already_returned_items): current_stock_qty = args.get(column) elif args.get("return_qty_from_rejected_warehouse"): reference_qty = ref.get("rejected_qty") * ref.get("conversion_factor", 1.0) - current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0) + current_stock_qty = ( + args.get(column) * args.get("conversion_factor", 1.0) + if column != "stock_qty" + else args.get(column) + ) else: reference_qty = ref.get(column) * ref.get("conversion_factor", 1.0) current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index ab5c1b3c69d..da03f907e3b 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -12,6 +12,8 @@ from frappe.utils import cint, flt, get_link_to_form from erpnext.controllers.stock_controller import StockController from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import ( + get_auto_batch_nos, + get_available_serial_nos, get_voucher_wise_serial_batch_from_bundle, ) from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -52,9 +54,42 @@ class SubcontractingController(StockController): if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]: self.validate_items() self.create_raw_materials_supplied() + self.set_valuation_rate_for_rm() else: super().validate() + def set_valuation_rate_for_rm(self): + rate_changed = False + if self.doctype == "Subcontracting Receipt": + for row in self.supplied_items: + kwargs = frappe._dict( + { + "item_code": row.rm_item_code, + "warehouse": self.supplier_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": flt(row.consumed_qty) * (-1 if not self.is_return else 1), + "voucher_type": self.doctype, + "voucher_no": self.name, + "company": self.company, + "serial_and_batch_bundle": row.serial_and_batch_bundle, + "voucher_detail_no": row.name, + "batch_no": row.batch_no, + "serial_no": row.serial_no, + "use_serial_batch_fields": row.use_serial_batch_fields, + } + ) + + rate = get_incoming_rate(kwargs) + precision = frappe.get_precision("Subcontracting Receipt Supplied Item", "rate") + if flt(rate, precision) != flt(row.rate, precision): + row.rate = rate + row.amount = flt(row.consumed_qty) * flt(rate) + rate_changed = True + + if rate_changed: + self.calculate_items_qty_and_amount() + def validate_rejected_warehouse(self): for item in self.get("items"): if flt(item.rejected_qty) and not item.rejected_warehouse: @@ -610,6 +645,64 @@ class SubcontractingController(StockController): self.set_rate_for_supplied_items(rm_obj, item_row) elif self.backflush_based_on == "BOM": self.update_rate_for_supplied_items() + self.set_batch_for_supplied_items() + + def set_batch_for_supplied_items(self): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos_for_outward + from erpnext.stock.get_item_details import get_filtered_serial_nos + + for row in self.supplied_items: + item_details = frappe.get_cached_value( + "Item", row.rm_item_code, ["has_batch_no", "has_serial_no"], as_dict=1 + ) + + if not item_details.has_batch_no and not item_details.has_serial_no: + continue + + if not row.use_serial_batch_fields: + continue + + kwargs = frappe._dict( + { + "item_code": row.rm_item_code, + "warehouse": self.supplier_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": flt(row.consumed_qty), + } + ) + + if item_details.has_serial_no and not row.serial_and_batch_bundle and not row.serial_no: + serial_nos = get_available_serial_nos(kwargs) + if serial_nos: + serial_nos = [sn.get("serial_no") for sn in serial_nos] + serial_nos = get_filtered_serial_nos(serial_nos, self, "supplied_items") + row.serial_no = "\n".join(serial_nos) + + elif item_details.has_batch_no and not row.serial_and_batch_bundle and not row.batch_no: + batches = get_auto_batch_nos(kwargs) + if batches: + consumed_qty = row.consumed_qty + for index, d in enumerate(batches): + if consumed_qty <= 0: + break + + if index == 0: + row.batch_no = d.get("batch_no") + row.consumed_qty = d.get("qty") + consumed_qty -= d.get("qty") + else: + new_row = self.append("supplied_items", {}) + new_row.update(frappe.copy_doc(row).as_dict()) + new_row.update( + { + "consumed_qty": d.get("qty"), + "batch_no": d.get("batch_no"), + "rate": row.rate, + "amount": flt(d.get("qty")) * flt(row.rate), + } + ) + consumed_qty -= d.get("qty") def update_rate_for_supplied_items(self): if self.doctype != "Subcontracting Receipt": diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index 3b11a0f8029..dc80a23198a 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -1308,6 +1308,7 @@ def make_subcontracted_items(): "Subcontracted Item SA7": {}, "Subcontracted Item SA8": {}, "Subcontracted Item SA9": {"stock_uom": "Litre"}, + "Subcontracted Item SA10": {}, } for item, properties in sub_contracted_items.items(): @@ -1329,6 +1330,7 @@ def make_raw_materials(): "Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"}, "Subcontracted SRM Item 8": {}, "Subcontracted SRM Item 9": {"stock_uom": "Litre"}, + "Subcontracted SRM Item 10": {}, } for item, properties in raw_materials.items(): @@ -1357,6 +1359,7 @@ def make_service_items(): "Subcontracted Service Item 7": {}, "Subcontracted Service Item 8": {}, "Subcontracted Service Item 9": {}, + "Subcontracted Service Item 10": {}, } for item, properties in service_items.items(): @@ -1381,6 +1384,7 @@ def make_bom_for_subcontracted_items(): "Subcontracted Item SA6": ["Subcontracted SRM Item 3"], "Subcontracted Item SA7": ["Subcontracted SRM Item 1"], "Subcontracted Item SA8": ["Subcontracted SRM Item 8"], + "Subcontracted Item SA10": ["Subcontracted SRM Item 10"], } for item_code, raw_materials in boms.items(): diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 68360b696c0..fb8c8039d98 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1631,7 +1631,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d } ) - sales_order = doc.get("sales_order") + sales_order = data.get("sales_order") for item_code, details in item_details.items(): so_item_details.setdefault(sales_order, frappe._dict()) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ed7e4bf623b..0af61025653 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2137,16 +2137,22 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.frm.doc.name).options, "master_name": this.frm.doc.taxes_and_charges }, - callback: function(r) { - if(!r.exc) { - if(me.frm.doc.shipping_rule && me.frm.doc.taxes) { - for (let tax of r.message) { + callback: function (r) { + if (!r.exc) { + let taxes = r.message; + taxes.forEach((tax) => { + if (me.frm.doc?.cost_center && !tax.cost_center) { + tax.cost_center = me.frm.doc.cost_center; + } + }); + if (me.frm.doc.shipping_rule && me.frm.doc.taxes) { + for (let tax of taxes) { me.frm.add_child("taxes", tax); } refresh_field("taxes"); } else { - me.frm.set_value("taxes", r.message); + me.frm.set_value("taxes", taxes); me.calculate_taxes_and_totals(); } } diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 90051673e70..dfb4a5b4445 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -443,3 +443,14 @@ def create_internal_customer(customer_name=None, represents_company=None, allowe customer_name = frappe.db.get_value("Customer", customer_name) return customer_name + + +def make_customer(customer_name): + if not frappe.db.exists("Customer", customer_name): + customer = frappe.new_doc("Customer") + customer.customer_name = customer_name + customer.customer_type = "Individual" + customer.insert() + return customer.name + else: + return customer_name diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 6ebe5320bbf..38cede3df47 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -74,8 +74,11 @@ class PickList(TransactionBase): if self.has_reserved_stock(): self.set_onload("has_reserved_stock", True) - for item in self.get("locations"): - item.update(get_item_details(item.item_code, item.uom, item.warehouse, self.company)) + if self.docstatus.is_draft() and not hasattr(self, "_action"): + company = self.company + + for item in self.get("locations"): + item.update(get_item_details(item.item_code, item.uom, item.warehouse, company)) def validate(self): self.validate_expired_batches() diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index aff309e5f80..9c69d856d45 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -311,7 +311,7 @@ class SerialandBatchBundle(Document): def throw_error_message(self, message, exception=frappe.ValidationError): frappe.throw(_(message), exception, title=_("Error")) - def set_incoming_rate(self, parent=None, row=None, save=False, allow_negative_stock=False): + def set_incoming_rate(self, parent=None, row=None, save=False, allow_negative_stock=False, prev_sle=None): if self.type_of_transaction not in ["Inward", "Outward"] or self.voucher_type in [ "Installation Note", "Job Card", @@ -321,15 +321,15 @@ class SerialandBatchBundle(Document): return if return_against := self.get_return_against(parent=parent): - self.set_valuation_rate_for_return_entry(return_against, row, save) + self.set_valuation_rate_for_return_entry(return_against, row, save, prev_sle=prev_sle) elif self.type_of_transaction == "Outward": self.set_incoming_rate_for_outward_transaction( row, save, allow_negative_stock=allow_negative_stock ) else: - self.set_incoming_rate_for_inward_transaction(row, save) + self.set_incoming_rate_for_inward_transaction(row, save, prev_sle=prev_sle) - def set_valuation_rate_for_return_entry(self, return_against, row, save=False): + def set_valuation_rate_for_return_entry(self, return_against, row, save=False, prev_sle=None): if valuation_details := self.get_valuation_rate_for_return_entry(return_against): for row in self.entries: if valuation_details: @@ -361,7 +361,7 @@ class SerialandBatchBundle(Document): ) elif self.type_of_transaction == "Inward": - self.set_incoming_rate_for_inward_transaction(row, save) + self.set_incoming_rate_for_inward_transaction(row, save, prev_sle=prev_sle) def validate_returned_serial_batch_no(self, return_against, row, original_inv_details): if frappe.flags.through_repost_item_valuation: @@ -529,7 +529,11 @@ class SerialandBatchBundle(Document): if save: d.db_set( - {"incoming_rate": d.incoming_rate, "stock_value_difference": d.stock_value_difference} + { + "incoming_rate": d.incoming_rate, + "stock_value_difference": d.stock_value_difference, + "stock_queue": d.get("stock_queue"), + } ) def validate_negative_batch(self, batch_no, available_qty): @@ -606,7 +610,11 @@ class SerialandBatchBundle(Document): return return_against - def set_incoming_rate_for_inward_transaction(self, row=None, save=False): + def set_incoming_rate_for_inward_transaction(self, row=None, save=False, prev_sle=None): + from erpnext.stock.utils import get_valuation_method + + valuation_method = get_valuation_method(self.item_code) + valuation_field = "valuation_rate" if self.voucher_type in ["Sales Invoice", "Delivery Note", "Quotation"]: valuation_field = "incoming_rate" @@ -630,19 +638,42 @@ class SerialandBatchBundle(Document): if not rate and self.voucher_detail_no and self.voucher_no: rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field) + stock_queue = [] + batches = [] + if prev_sle and prev_sle.stock_queue: + batches = frappe.get_all( + "Batch", + filters={ + "name": ("in", [d.batch_no for d in self.entries if d.batch_no]), + "use_batchwise_valuation": 0, + }, + pluck="name", + ) + + if batches and valuation_method == "FIFO": + stock_queue = parse_json(prev_sle.stock_queue) + for d in self.entries: if self.is_rejected: rate = 0.0 - elif (d.incoming_rate == rate) and d.qty and d.stock_value_difference: + elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference: continue d.incoming_rate = flt(rate) if d.qty: d.stock_value_difference = flt(d.qty) * d.incoming_rate + if stock_queue and valuation_method == "FIFO" and d.batch_no in batches: + stock_queue.append([d.qty, d.incoming_rate]) + d.stock_queue = json.dumps(stock_queue) + if save: d.db_set( - {"incoming_rate": d.incoming_rate, "stock_value_difference": d.stock_value_difference} + { + "incoming_rate": d.incoming_rate, + "stock_value_difference": d.stock_value_difference, + "stock_queue": d.get("stock_queue"), + } ) def set_serial_and_batch_values(self, parent, row, qty_field=None): 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 b81fbc8bc55..eec91b2c282 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 @@ -285,6 +285,25 @@ class TestSerialandBatchBundle(FrappeTestCase): self.assertEqual(flt(sle.stock_value_difference), 1000.00 * -1) self.assertEqual(json.loads(sle.stock_queue), [[20, 200]]) + se = make_stock_entry( + item_code=batch_item_code, + target="_Test Warehouse - _TC", + qty=10, + rate=100, + batch_no=batch_id, + use_serial_batch_fields=True, + ) + + sle = frappe.db.get_value( + "Serial and Batch Entry", + {"parent": se.items[0].serial_and_batch_bundle, "docstatus": 1}, + ["stock_value_difference", "stock_queue"], + as_dict=True, + ) + + self.assertEqual(flt(sle.stock_value_difference), 1000.00) + self.assertEqual(json.loads(sle.stock_queue), [[20, 200], [10, 100]]) + se = make_stock_entry( item_code=batch_item_code, target="_Test Warehouse - _TC", @@ -301,7 +320,7 @@ class TestSerialandBatchBundle(FrappeTestCase): ) self.assertEqual(flt(sle.stock_value_difference), 1000.00) - self.assertEqual(json.loads(sle.stock_queue), [[20, 200]]) + self.assertEqual(json.loads(sle.stock_queue), [[20, 200], [10, 100]]) se = make_stock_entry( item_code=batch_item_code, @@ -319,6 +338,24 @@ class TestSerialandBatchBundle(FrappeTestCase): self.assertEqual(flt(sle.stock_value_difference), 5000.00 * -1) self.assertFalse(json.loads(sle.stock_queue or "[]")) + self.assertEqual(flt(sle.stock_value), 1000.0) + + se = make_stock_entry( + item_code=batch_item_code, + source="_Test Warehouse - _TC", + qty=10, + use_serial_batch_fields=False, + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"item_code": batch_item_code, "is_cancelled": 0, "voucher_no": se.name}, + ["stock_value_difference", "stock_queue", "stock_value"], + as_dict=True, + ) + + self.assertEqual(flt(sle.stock_value_difference), 1000.00 * -1) + self.assertFalse(json.loads(sle.stock_queue or "[]")) self.assertEqual(flt(sle.stock_value), 0.0) def test_old_serial_no_valuation(self): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4ff31b66c19..1fe9134e42d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2041,6 +2041,9 @@ class StockEntry(StockController): # in case of BOM to_warehouse = item.get("default_warehouse") + expense_account = item.get("expense_account") + if not expense_account: + expense_account = frappe.get_cached_value("Company", self.company, "stock_adjustment_account") args = { "to_warehouse": to_warehouse, "from_warehouse": "", @@ -2048,7 +2051,7 @@ class StockEntry(StockController): "item_name": item.item_name, "description": item.description, "stock_uom": item.stock_uom, - "expense_account": item.get("expense_account"), + "expense_account": expense_account, "cost_center": item.get("buying_cost_center"), "is_finished_item": 1, } diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py index 50f39817fff..8b47cd88df6 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py @@ -84,7 +84,7 @@ def get_reposting_entries(): def get_stock_ledgers(vouchers): return frappe.get_all( "Stock Ledger Entry", - fields=["item_code", "warehouse", "posting_date"], + fields=["item_code", "warehouse", "posting_date", "posting_time", "posting_datetime"], filters={"voucher_no": ("in", vouchers)}, ) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index c912c529b22..81ecbf363fa 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -260,10 +260,13 @@ def filter_batches(batches, doc): del batches[row.get("batch_no")] -def get_filtered_serial_nos(serial_nos, doc): +def get_filtered_serial_nos(serial_nos, doc, table=None): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - for row in doc.get("items"): + if not table: + table = "items" + + for row in doc.get(table): if row.get("serial_no"): for serial_no in get_serial_nos(row.get("serial_no")): if serial_no in serial_nos: diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 67e340d0f70..0db7e40b77f 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -4,10 +4,11 @@ import frappe from frappe import _ -from frappe.utils import get_link_to_form, parse_json +from frappe.utils import get_datetime, get_link_to_form, parse_json import erpnext from erpnext.accounts.utils import get_currency_precision, get_stock_accounts +from erpnext.stock.doctype.stock_reposting_settings.stock_reposting_settings import get_stock_ledgers from erpnext.stock.doctype.warehouse.warehouse import get_warehouses_based_on_account @@ -141,18 +142,29 @@ def create_reposting_entries(rows, company): rows = parse_json(rows) entries = [] - for row in rows: - row = frappe._dict(row) + item_wh = frappe._dict() + vouchers = [row.get("voucher_no") for row in rows] + sles = get_stock_ledgers(vouchers) + for sle in sles: + key = (sle.item_code, sle.warehouse) + if key not in item_wh: + item_wh[key] = sle + elif get_datetime(item_wh.get(key).posting_datetime) > get_datetime(sle.posting_datetime): + item_wh[key] = sle + + for key, sle in item_wh.items(): + item_code, warehouse = key try: doc = frappe.get_doc( { "doctype": "Repost Item Valuation", - "based_on": "Transaction", + "based_on": "Item and Warehouse", "status": "Queued", - "voucher_type": row.voucher_type, - "voucher_no": row.voucher_no, - "posting_date": row.posting_date, + "item_code": item_code, + "warehouse": warehouse, + "posting_date": sle.posting_date, + "posting_time": sle.posting_time, "company": company, "allow_nagative_stock": 1, } diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index e9dcf3996dd..40e576987f8 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -732,7 +732,6 @@ class update_entries_after: elif dependant_sle.voucher_type == "Stock Entry" and is_transfer_stock_entry( dependant_sle.voucher_no ): - print(dependant_sle.voucher_no) self.distinct_item_warehouses[key] = val self.new_items_found = True @@ -1020,7 +1019,9 @@ class update_entries_after: ) else: doc = frappe.get_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle) - doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock) + doc.set_incoming_rate( + save=True, allow_negative_stock=self.allow_negative_stock, prev_sle=self.wh_data + ) doc.calculate_qty_and_amount(save=True) if stock_queue := frappe.get_all( diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index e9848c88952..2f9a04e7e93 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -252,14 +252,18 @@ class SubcontractingOrder(SubcontractingController): if si.fg_item: item = frappe.get_doc("Item", si.fg_item) - po_item = frappe.get_doc("Purchase Order Item", si.purchase_order_item) - available_qty = po_item.qty - po_item.subcontracted_quantity + qty, subcontracted_quantity, fg_item_qty = frappe.db.get_value( + "Purchase Order Item", + si.purchase_order_item, + ["qty", "subcontracted_quantity", "fg_item_qty"], + ) + available_qty = flt(qty) - flt(subcontracted_quantity) if available_qty == 0: continue si.qty = available_qty - conversion_factor = po_item.qty / po_item.fg_item_qty + conversion_factor = flt(qty) / flt(fg_item_qty) si.fg_item_qty = flt( available_qty / conversion_factor, frappe.get_precision("Purchase Order Item", "qty") ) @@ -338,13 +342,24 @@ class SubcontractingOrder(SubcontractingController): def update_subcontracted_quantity_in_po(self, cancel=False): for service_item in self.service_items: - doc = frappe.get_doc("Purchase Order Item", service_item.purchase_order_item) - doc.subcontracted_quantity = ( - (doc.subcontracted_quantity + service_item.qty) - if not cancel - else (doc.subcontracted_quantity - service_item.qty) + subcontracted_quantity = flt( + frappe.db.get_value( + "Purchase Order Item", service_item.purchase_order_item, "subcontracted_quantity" + ) + ) + + subcontracted_quantity = ( + (subcontracted_quantity + service_item.qty) + if not cancel + else (subcontracted_quantity - service_item.qty) + ) + + frappe.db.set_value( + "Purchase Order Item", + service_item.purchase_order_item, + "subcontracted_quantity", + subcontracted_quantity, ) - doc.save() @frappe.whitelist() diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 23a1ab9ebf4..f5dd1c4f16c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -116,7 +116,13 @@ class SubcontractingReceipt(SubcontractingController): self.validate_items_qty() self.set_items_bom() self.set_items_cost_center() - self.set_items_expense_account() + + if self.company: + default_expense_account = self.get_company_default( + "default_expense_account", ignore_validation=True + ) + self.set_service_expense_account(default_expense_account) + self.set_expense_account_for_subcontracted_items(default_expense_account) def validate(self): self.reset_supplied_items() @@ -196,6 +202,39 @@ class SubcontractingReceipt(SubcontractingController): if item.subcontracting_order: check_on_hold_or_closed_status("Subcontracting Order", item.subcontracting_order) + def set_service_expense_account(self, default_expense_account): + for row in self.get("items"): + if not row.service_expense_account and row.purchase_order_item: + service_item = frappe.db.get_value( + "Purchase Order Item", row.purchase_order_item, "item_code" + ) + + if service_item: + if default := ( + get_item_defaults(service_item, self.company) + or get_item_group_defaults(service_item, self.company) + or get_brand_defaults(service_item, self.company) + ): + if service_expense_account := default.get("expense_account"): + row.service_expense_account = service_expense_account + + if not row.service_expense_account: + row.service_expense_account = default_expense_account + + def set_expense_account_for_subcontracted_items(self, default_expense_account): + for row in self.get("items"): + if not row.expense_account: + if default := ( + get_item_defaults(row.item_code, self.company) + or get_item_group_defaults(row.item_code, self.company) + or get_brand_defaults(row.item_code, self.company) + ): + if expense_account := default.get("expense_account"): + row.expense_account = expense_account + + if not row.expense_account: + row.expense_account = default_expense_account + def validate_items_qty(self): for item in self.items: if not (item.qty or item.rejected_qty): @@ -242,14 +281,6 @@ class SubcontractingReceipt(SubcontractingController): self.company, ) - def set_items_expense_account(self): - if self.company: - expense_account = self.get_company_default("default_expense_account", ignore_validation=True) - - for item in self.items: - if not item.expense_account: - item.expense_account = expense_account - def set_supplied_items_expense_account(self): for item in self.supplied_items: if not item.expense_account: @@ -599,13 +630,17 @@ class SubcontractingReceipt(SubcontractingController): project=item.project, item=item, ) + + service_cost = flt( + item.service_cost_per_qty, item.precision("service_cost_per_qty") + ) * flt(item.qty, item.precision("qty")) # Expense Account (Credit) self.add_gl_entry( gl_entries=gl_entries, account=item.expense_account, cost_center=item.cost_center, debit=0.0, - credit=stock_value_diff, + credit=flt(stock_value_diff) - service_cost, remarks=remarks, against_account=accepted_warehouse_account, account_currency=get_account_currency(item.expense_account), @@ -613,6 +648,21 @@ class SubcontractingReceipt(SubcontractingController): item=item, ) + service_account = item.service_expense_account or item.expense_account + # Expense Account (Credit) + self.add_gl_entry( + gl_entries=gl_entries, + account=service_account, + cost_center=item.cost_center, + debit=0.0, + credit=service_cost, + remarks=remarks, + against_account=accepted_warehouse_account, + account_currency=get_account_currency(service_account), + project=item.project, + item=item, + ) + if flt(item.rm_supp_cost) and supplier_warehouse_account: for rm_item in supplied_items_details.get(item.name): # Supplier Warehouse Account (Credit) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 55e950856bc..d5e30bd9368 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -422,6 +422,79 @@ class TestSubcontractingReceipt(FrappeTestCase): self.assertEqual(expected_values[gle.account][0], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.credit) + def test_subcontracting_receipt_for_service_expense_account(self): + service_expense_account = ( + frappe.get_doc( + { + "doctype": "Account", + "account_name": "_Test Service Expense", + "account_type": "Expense Account", + "company": "_Test Company with perpetual inventory", + "is_group": 0, + "parent_account": "Indirect Expenses - TCP1", + } + ) + .insert(ignore_if_duplicate=True) + .name + ) + + service_item_doc = frappe.get_doc("Item", "Subcontracted Service Item 10") + service_item_doc.append( + "item_defaults", + { + "company": "_Test Company with perpetual inventory", + "expense_account": service_expense_account, + "default_warehouse": "Stores - TCP1", + }, + ) + + service_item_doc.save() + + service_items = [ + { + "warehouse": "Stores - TCP1", + "item_code": "Subcontracted Service Item 10", + "qty": 10, + "rate": 100, + "fg_item": "Subcontracted Item SA10", + "fg_item_qty": 10, + }, + ] + sco = get_subcontracting_order( + company="_Test Company with perpetual inventory", + warehouse="Stores - TCP1", + supplier_warehouse="Work In Progress - TCP1", + service_items=service_items, + ) + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + scr = make_subcontracting_receipt(sco.name) + scr.submit() + + for item in scr.items: + self.assertEqual(item.service_expense_account, service_expense_account) + + gl_entries = get_gl_entries("Subcontracting Receipt", scr.name) + self.assertTrue(gl_entries) + + fg_warehouse_ac = get_inventory_account(scr.company, scr.items[0].warehouse) + expense_account = scr.items[0].expense_account + expected_values = { + fg_warehouse_ac: [2000, 1000], + expense_account: [1000, 1000], + service_expense_account: [0, 1000], + } + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account][0], gle.debit) + self.assertEqual(expected_values[gle.account][1], gle.credit) + @change_settings("Stock Settings", {"use_serial_batch_fields": 0}) def test_subcontracting_receipt_with_zero_service_cost(self): warehouse = "Stores - TCP1" @@ -740,13 +813,13 @@ class TestSubcontractingReceipt(FrappeTestCase): for row in scr.supplied_items: self.assertEqual(row.rate, 300.00) self.assertTrue(row.serial_and_batch_bundle) - auto_created_serial_batch = frappe.db.get_value( + serial_and_batch_bundle = frappe.db.get_value( "Stock Ledger Entry", {"voucher_no": scr.name, "voucher_detail_no": row.name}, - "auto_created_serial_and_batch_bundle", + "serial_and_batch_bundle", ) - self.assertTrue(auto_created_serial_batch) + self.assertTrue(serial_and_batch_bundle) self.assertEqual(scr.items[0].rm_cost_per_qty, 900) self.assertEqual(scr.items[0].service_cost_per_qty, 100) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index 23a7e69669d..5a02ab91bf0 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -64,6 +64,8 @@ "manufacturer_part_no", "accounting_details_section", "expense_account", + "column_break_exht", + "service_expense_account", "accounting_dimensions_section", "cost_center", "dimension_col_break", @@ -580,12 +582,22 @@ "fieldname": "add_serial_batch_for_rejected_qty", "fieldtype": "Button", "label": "Add Serial / Batch No (Rejected Qty)" + }, + { + "fieldname": "column_break_exht", + "fieldtype": "Column Break" + }, + { + "fieldname": "service_expense_account", + "fieldtype": "Link", + "label": "Service Expense Account", + "options": "Account" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2024-12-06 15:23:58.680169", + "modified": "2025-09-26 12:00:38.877638", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py index 69f7ae73e7a..8d5265f3a0f 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py @@ -55,6 +55,7 @@ class SubcontractingReceiptItem(Document): serial_and_batch_bundle: DF.Link | None serial_no: DF.SmallText | None service_cost_per_qty: DF.Currency + service_expense_account: DF.Link | None stock_uom: DF.Link subcontracting_order: DF.Link | None subcontracting_order_item: DF.Data | None