From b654dc0e288e3ecfdfbd0831c3ae15cc7036bb90 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 27 Aug 2019 22:32:33 +0530 Subject: [PATCH 01/49] fix: process allocation expiry --- .../leave_ledger_entry/leave_ledger_entry.py | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index c82114e6d5..228e226645 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -95,29 +95,28 @@ def process_expired_allocation(): 'expire_carry_forwarded_leaves_after_days': (">", 0) }, fieldname=['name']) - if leave_type_records: - leave_type = [record[0] for record in leave_type_records] + leave_type = [record[0] for record in leave_type_records] - expired_allocation = frappe.db.sql_list("""SELECT name - FROM `tabLeave Ledger Entry` - WHERE - `transaction_type`='Leave Allocation' - AND `is_expired`=1""") + expired_allocation = frappe.db.sql_list("""SELECT name + FROM `tabLeave Ledger Entry` + WHERE + `transaction_type`='Leave Allocation' + AND `is_expired`=1""") - expire_allocation = frappe.get_all("Leave Ledger Entry", - fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], - filters={ - 'to_date': ("<", today()), - 'transaction_type': 'Leave Allocation', - 'transaction_name': ('not in', expired_allocation) - }, - or_filters={ - 'is_carry_forward': 0, - 'leave_type': ('in', leave_type) - }) + expire_allocation = frappe.get_all("Leave Ledger Entry", + fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], + filters={ + 'to_date': ("<", today()), + 'transaction_type': 'Leave Allocation', + 'transaction_name': ('not in', expired_allocation) + }, + or_filters={ + 'is_carry_forward': 0, + 'leave_type': ('in', leave_type) + }) if expire_allocation: - create_expiry_ledger_entry(expire_allocation) + create_expiry_ledger_entry(expire_allocation) def create_expiry_ledger_entry(allocations): ''' Create ledger entry for expired allocation ''' From 36b30911a821424a50d86ae45cf7fffa254112b2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 29 Aug 2019 18:06:23 +0530 Subject: [PATCH 02/49] feat: customer credit limit --- .../selling/doctype/customer/customer.json | 1915 ++--------------- .../doctype/customer_credit_limit/__init__.py | 0 .../customer_credit_limit.json | 46 + .../customer_credit_limit.py | 10 + 4 files changed, 181 insertions(+), 1790 deletions(-) create mode 100644 erpnext/selling/doctype/customer_credit_limit/__init__.py create mode 100644 erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json create mode 100644 erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 8c30528960..8b5cca9320 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -1,2036 +1,475 @@ { - "allow_copy": 0, "allow_events_in_timeline": 1, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", - "beta": 0, "creation": "2013-06-11 14:26:44", - "custom": 0, "description": "Buyer of Goods and Services.", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "basic_info", + "naming_series", + "salutation", + "customer_name", + "gender", + "customer_type", + "default_bank_account", + "lead_name", + "image", + "column_break0", + "account_manager", + "customer_group", + "territory", + "tax_id", + "tax_category", + "disabled", + "is_internal_customer", + "represents_company", + "allowed_to_transact_section", + "companies", + "currency_and_price_list", + "default_currency", + "default_price_list", + "column_break_14", + "language", + "address_contacts", + "address_html", + "website", + "column_break1", + "contact_html", + "primary_address_and_contact_detail", + "customer_primary_contact", + "mobile_no", + "email_id", + "column_break_26", + "customer_primary_address", + "primary_address", + "default_receivable_accounts", + "accounts", + "credit_limit_section", + "payment_terms", + "credit_limit", + "more_info", + "customer_details", + "column_break_45", + "market_segment", + "industry", + "is_frozen", + "column_break_38", + "loyalty_program", + "loyalty_program_tier", + "sales_team_section_break", + "default_sales_partner", + "default_commission_rate", + "sales_team_section", + "sales_team", + "customer_pos_id" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "basic_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": "Name and Type", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-user", - "permlevel": 0, - "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 + "options": "fa fa-user" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, "fieldname": "naming_series", "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": "Series", - "length": 0, "no_copy": 1, "options": "CUST-.YYYY.-", - "permlevel": 0, - "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": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.customer_type!='Company'", - "fetch_if_empty": 0, "fieldname": "salutation", "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": "Salutation", - "length": 0, - "no_copy": 0, - "options": "Salutation", - "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 + "options": "Salutation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Full Name", - "length": 0, "no_copy": 1, "oldfieldname": "customer_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.customer_type != 'Company'", - "fetch_if_empty": 0, "fieldname": "gender", "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": "Gender", - "length": 0, - "no_copy": 0, - "options": "Gender", - "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 + "options": "Gender" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Company", - "fetch_if_empty": 0, "fieldname": "customer_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": "Type", - "length": 0, - "no_copy": 0, "oldfieldname": "customer_type", "oldfieldtype": "Select", "options": "Company\nIndividual", - "permlevel": 0, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default_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": "Default 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 + "options": "Bank Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "lead_name", "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": "From Lead", - "length": 0, "no_copy": 1, "oldfieldname": "lead_name", "oldfieldtype": "Link", "options": "Lead", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Image", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break0", "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, - "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, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "account_manager", "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 Manager", - "length": 0, - "no_copy": 0, - "options": "User", - "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 + "options": "User" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, "fieldname": "customer_group", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Customer Group", - "length": 0, - "no_copy": 0, "oldfieldname": "customer_group", "oldfieldtype": "Link", "options": "Customer Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, "fieldname": "territory", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Territory", - "length": 0, - "no_copy": 0, "oldfieldname": "territory", "oldfieldtype": "Link", "options": "Territory", - "permlevel": 0, "print_hide": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "tax_id", "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": "Tax ID", - "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 + "label": "Tax ID" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "tax_category", "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": "Tax Category", - "length": 0, - "no_copy": 0, - "options": "Tax Category", - "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 + "options": "Tax Category" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, "fieldname": "disabled", "fieldtype": "Check", - "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": "Disabled", - "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 + "label": "Disabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, "fieldname": "is_internal_customer", "fieldtype": "Check", - "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": "Is Internal Customer", - "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 + "label": "Is Internal Customer" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "is_internal_customer", - "fetch_if_empty": 0, "fieldname": "represents_company", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Represents Company", - "length": 0, - "no_copy": 0, "options": "Company", - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "represents_company", - "fetch_if_empty": 0, "fieldname": "allowed_to_transact_section", "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": "Allowed To Transact With", - "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 + "label": "Allowed To Transact With" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "represents_company", - "fetch_if_empty": 0, "fieldname": "companies", "fieldtype": "Table", - "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": "Allowed To Transact With", - "length": 0, - "no_copy": 0, - "options": "Allowed To Transact With", - "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 + "options": "Allowed To Transact With" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "currency_and_price_list", "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": "Currency and Price List", - "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 + "label": "Currency and Price List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default_currency", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Billing Currency", - "length": 0, "no_copy": 1, - "options": "Currency", - "permlevel": 0, - "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 + "options": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default_price_list", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "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 + "options": "Price List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_14", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "language", "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": "Print Language", - "length": 0, - "no_copy": 0, - "options": "Language", - "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 + "options": "Language" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:!doc.__islocal", - "fetch_if_empty": 0, "fieldname": "address_contacts", "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": "Address and Contact", - "length": 0, - "no_copy": 0, - "options": "fa fa-map-marker", - "permlevel": 0, - "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 + "options": "fa fa-map-marker" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "address_html", "fieldtype": "HTML", - "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": "Address HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "website", "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": "Website", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "label": "Website" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break1", "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, - "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, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "contact_html", "fieldtype": "HTML", - "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": "Contact HTML", - "length": 0, - "no_copy": 0, "oldfieldtype": "HTML", - "permlevel": 0, - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Select, to make the customer searchable with these fields", - "fetch_if_empty": 0, "fieldname": "primary_address_and_contact_detail", "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": "Primary Address and Contact Detail", - "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 + "label": "Primary Address and Contact Detail" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Reselect, if the chosen contact is edited after save", - "fetch_if_empty": 0, "fieldname": "customer_primary_contact", "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 Primary Contact", - "length": 0, - "no_copy": 0, - "options": "Contact", - "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 + "options": "Contact" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "customer_primary_contact.mobile_no", - "fetch_if_empty": 0, "fieldname": "mobile_no", "fieldtype": "Read Only", - "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": "Mobile No", - "length": 0, - "no_copy": 0, - "options": "", - "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 + "label": "Mobile No" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "customer_primary_contact.email_id", - "fetch_if_empty": 0, "fieldname": "email_id", "fieldtype": "Read Only", - "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": "Email Id", - "length": 0, - "no_copy": 0, - "options": "", - "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 + "label": "Email Id" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_26", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Reselect, if the chosen address is edited after save", - "fetch_if_empty": 0, "fieldname": "customer_primary_address", "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 Primary Address", - "length": 0, - "no_copy": 0, - "options": "Address", - "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 + "options": "Address" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, "fieldname": "primary_address", "fieldtype": "Read Only", - "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": "Primary Address", - "length": 0, - "no_copy": 0, - "options": "", - "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 + "label": "Primary Address" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "collapsible_depends_on": "", - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default_receivable_accounts", "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": "Accounting", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "label": "Accounting" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "description": "Mention if non-standard receivable account", - "fetch_if_empty": 0, "fieldname": "accounts", "fieldtype": "Table", - "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": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "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 + "options": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "collapsible_depends_on": "", - "columns": 0, - "fetch_if_empty": 0, "fieldname": "credit_limit_section", "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": "Credit Limit and Payment Terms", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "width": "" + "label": "Credit Limit and Payment Terms" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "credit_limit", - "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": "Credit Limit", - "length": 0, - "no_copy": 0, - "oldfieldname": "credit_limit", - "oldfieldtype": "Currency", - "options": "", - "permlevel": 1, - "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_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, - "fieldname": "bypass_credit_limit_check_at_sales_order", - "fieldtype": "Check", - "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": "Bypass credit limit check at Sales Order", - "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": "credit_limit", + "fieldtype": "Table", + "label": "Credit Limit", + "options": "Customer Credit Limit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_34", - "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "payment_terms", "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": "Default Payment Terms Template", - "length": 0, - "no_copy": 0, - "options": "Payment Terms Template", - "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 + "options": "Payment Terms Template" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, "collapsible_depends_on": "customer_details", - "columns": 0, - "fetch_if_empty": 0, "fieldname": "more_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": "More Information", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "permlevel": 0, - "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 + "options": "fa fa-file-text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Additional information regarding the customer.", - "fetch_if_empty": 0, "fieldname": "customer_details", "fieldtype": "Text", - "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 Details", - "length": 0, - "no_copy": 0, "oldfieldname": "customer_details", - "oldfieldtype": "Code", - "permlevel": 0, - "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 + "oldfieldtype": "Code" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_45", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "market_segment", "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": "Market Segment", - "length": 0, - "no_copy": 0, - "options": "Market Segment", - "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 + "options": "Market Segment" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "industry", "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": "Industry", - "length": 0, - "no_copy": 0, - "options": "Industry Type", - "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 + "options": "Industry Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_frozen", "fieldtype": "Check", - "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": "Is Frozen", - "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 + "label": "Is Frozen" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_38", "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": "Loyalty Points", - "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 + "label": "Loyalty Points" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "loyalty_program", "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": "Loyalty Program", - "length": 0, "no_copy": 1, - "options": "Loyalty Program", - "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 + "options": "Loyalty Program" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "loyalty_program_tier", "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": "Loyalty Program Tier", - "length": 0, "no_copy": 1, - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, "collapsible_depends_on": "default_sales_partner", - "columns": 0, - "fetch_if_empty": 0, "fieldname": "sales_team_section_break", "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": "Sales Partner and Commission", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-group", - "permlevel": 0, - "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 + "options": "fa fa-group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default_sales_partner", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Sales Partner", - "length": 0, - "no_copy": 0, "oldfieldname": "default_sales_partner", "oldfieldtype": "Link", - "options": "Sales Partner", - "permlevel": 0, - "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 + "options": "Sales Partner" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "default_commission_rate", "fieldtype": "Float", - "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": "Commission Rate", - "length": 0, - "no_copy": 0, "oldfieldname": "default_commission_rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "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 + "oldfieldtype": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, "collapsible_depends_on": "sales_team", - "columns": 0, - "fetch_if_empty": 0, "fieldname": "sales_team_section", "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": "Sales Team", - "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 + "label": "Sales Team" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "sales_team", "fieldtype": "Table", - "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": "Sales Team Details", - "length": 0, - "no_copy": 0, "oldfieldname": "sales_team", "oldfieldtype": "Table", - "options": "Sales Team", - "permlevel": 0, - "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 + "options": "Sales Team" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "customer_pos_id", "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": "Customer POS id", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 } ], - "has_web_view": 0, - "hide_toolbar": 0, "icon": "fa fa-user", "idx": 363, "image_field": "image", - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-04-12 08:45:39.357491", + "modified": "2019-08-28 17:38:09.709688", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -2038,184 +477,80 @@ "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales Manager" }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales Master Manager", "set_user_permissions": 1, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, "role": "Sales Master Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Stock User" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Stock Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Accounts User" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Accounts Manager" } ], "quick_entry": 1, - "read_only": 0, "search_fields": "customer_name,customer_group,territory, mobile_no,primary_address", "show_name_in_global_search": 1, "sort_order": "ASC", "title_field": "customer_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/selling/doctype/customer_credit_limit/__init__.py b/erpnext/selling/doctype/customer_credit_limit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json new file mode 100644 index 0000000000..bf99a58cb7 --- /dev/null +++ b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json @@ -0,0 +1,46 @@ +{ + "creation": "2019-08-28 17:29:42.115592", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "credit_limit", + "bypass_credit_limit_check", + "column_break_2", + "company" + ], + "fields": [ + { + "fieldname": "credit_limit", + "fieldtype": "Currency", + "label": "Credit Limit" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "default": "0", + "fieldname": "bypass_credit_limit_check", + "fieldtype": "Check", + "label": "Bypass credit limit_check" + } + ], + "istable": 1, + "modified": "2019-08-28 18:19:26.905239", + "modified_by": "Administrator", + "module": "Selling", + "name": "Customer Credit Limit", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py new file mode 100644 index 0000000000..60a4a9a5d2 --- /dev/null +++ b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class CustomerCreditLimit(Document): + pass From 79525327f07aa5c79c07545eb6fcecfcd814c061 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 29 Aug 2019 19:50:23 +0530 Subject: [PATCH 03/49] feat: bypass credit limit check --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 ++++-- erpnext/selling/doctype/sales_order/sales_order.py | 2 +- erpnext/stock/doctype/delivery_note/delivery_note.py | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 95c5dd5cd4..4f8d3bdd9a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -304,8 +304,10 @@ class SalesInvoice(SellingController): from erpnext.selling.doctype.customer.customer import check_credit_limit validate_against_credit_limit = False - bypass_credit_limit_check_at_sales_order = cint(frappe.get_cached_value("Customer", self.customer, - "bypass_credit_limit_check_at_sales_order")) + bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer Credit Limit", + filters={'parent': self.customer, 'company': self.company}, + fieldname=["bypass_credit_limit_check"]) + if bypass_credit_limit_check_at_sales_order: validate_against_credit_limit = True diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 4342af5e19..12e7b31a4c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -208,7 +208,7 @@ class SalesOrder(SellingController): def check_credit_limit(self): # if bypass credit limit check is set to true (1) at sales order level, # then we need not to check credit limit and vise versa - if not cint(frappe.get_cached_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order")): + if not cint(frappe.db.get_value("Customer Credit Limit", {'parent': self.customer, 'company': self.company}, "bypass_credit_limit_check")): check_credit_limit(self.customer, self.company) def check_nextdoc_docstatus(self): diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index f79d127984..e29cd1b792 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -234,8 +234,10 @@ class DeliveryNote(SellingController): extra_amount = 0 validate_against_credit_limit = False - bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", self.customer, - "bypass_credit_limit_check_at_sales_order")) + bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer Credit Limit", + filters={'parent': self.customer, 'company': self.company}, + fieldname="bypass_credit_limit_check")) + if bypass_credit_limit_check_at_sales_order: validate_against_credit_limit = True extra_amount = self.base_grand_total From 5755d2d32f7a17ca7d9df169249d661630e74750 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 29 Aug 2019 19:51:38 +0530 Subject: [PATCH 04/49] fix: customer credit limit report --- .../customer_credit_limit.json | 7 ++++--- .../customer_credit_balance.py | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json index bf99a58cb7..777feea054 100644 --- a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json +++ b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json @@ -13,6 +13,7 @@ { "fieldname": "credit_limit", "fieldtype": "Currency", + "in_list_view": 1, "label": "Credit Limit" }, { @@ -22,6 +23,7 @@ { "fieldname": "company", "fieldtype": "Link", + "in_list_view": 1, "label": "Company", "options": "Company" }, @@ -33,7 +35,7 @@ } ], "istable": 1, - "modified": "2019-08-28 18:19:26.905239", + "modified": "2019-08-29 18:27:10.323287", "modified_by": "Administrator", "module": "Selling", "name": "Customer Credit Limit", @@ -41,6 +43,5 @@ "permissions": [], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index ee0d72be7b..6b8b8581d2 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -29,7 +29,7 @@ def execute(filters=None): if customer_naming_type == "Naming Series": row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal, - d.bypass_credit_limit_check_at_sales_order, d.is_frozen, + d.bypass_credit_limit_check, d.is_frozen, d.disabled] else: row = [d.name, credit_limit, outstanding_amt, bal, @@ -60,9 +60,15 @@ def get_details(filters): conditions = "" if filters.get("customer"): - conditions += " where name = %(customer)s" - - return frappe.db.sql("""select name, customer_name, - bypass_credit_limit_check_at_sales_order, is_frozen, disabled from `tabCustomer` %s - """ % conditions, filters, as_dict=1) + conditions += " AND name = " + filters.get("customer") + return frappe.db.sql("""SELECT + c.name, c.customer_name, + ccl.bypass_credit_limit_check, + c.is_frozen, c.disabled + FROM `tabCustomer` c, `tabCustomer Credit Limit` ccl + WHERE + c.name = ccl.parent + AND ccl.company = %s + {0} + """.format(conditions), (filters.get("company")), as_dict=1) #nosec \ No newline at end of file From c059bc3e368a83022480395eae6c4c4b8228e049 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 29 Aug 2019 20:51:31 +0530 Subject: [PATCH 05/49] fix: grid view of customer credit limit --- erpnext/selling/doctype/customer/customer.py | 22 +++++++++++-------- .../customer_credit_limit.json | 11 ++++++---- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index c946c47c59..5edf06709d 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -167,13 +167,12 @@ class Customer(TransactionBase): frappe.throw(_("A Customer Group exists with same name please change the Customer name or rename the Customer Group"), frappe.NameError) def validate_credit_limit_on_change(self): - if self.get("__islocal") or not self.credit_limit \ - or self.credit_limit == frappe.db.get_value("Customer", self.name, "credit_limit"): + if self.get("__islocal") or not self.credit_limit: return - for company in frappe.get_all("Company"): - outstanding_amt = get_customer_outstanding(self.name, company.name) - if flt(self.credit_limit) < outstanding_amt: + for limit in frappe.get_all("Customer Credit Limit", {'parent': self.name}, ["credit_limit", "company"]): + outstanding_amt = get_customer_outstanding(self.name, limit.company) + if flt(limit.credit_limit) < outstanding_amt: frappe.throw(_("""New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0}""").format(outstanding_amt)) def on_trash(self): @@ -322,11 +321,16 @@ def get_credit_limit(customer, company): credit_limit = None if customer: - credit_limit, customer_group = frappe.get_cached_value("Customer", - customer, ["credit_limit", "customer_group"]) + credit_record = frappe.db.sql("""SELECT + customer_group, + credit_limit + FROM `tabCustomer`c , `tabCustomer Credit Limit` ccl + WHERE + c.name = ccl.parent + """, as_dict=1) - if not credit_limit: - credit_limit = frappe.get_cached_value("Customer Group", customer_group, "credit_limit") + if not credit_record.credit_limit: + credit_limit = frappe.get_cached_value("Customer Group", credit_record.customer_group, "credit_limit") if not credit_limit: credit_limit = frappe.get_cached_value('Company', company, "credit_limit") diff --git a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json index 777feea054..e26f798978 100644 --- a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json +++ b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json @@ -4,13 +4,14 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "credit_limit", - "bypass_credit_limit_check", + "company", "column_break_2", - "company" + "credit_limit", + "bypass_credit_limit_check" ], "fields": [ { + "columns": 4, "fieldname": "credit_limit", "fieldtype": "Currency", "in_list_view": 1, @@ -21,6 +22,7 @@ "fieldtype": "Column Break" }, { + "columns": 4, "fieldname": "company", "fieldtype": "Link", "in_list_view": 1, @@ -31,11 +33,12 @@ "default": "0", "fieldname": "bypass_credit_limit_check", "fieldtype": "Check", + "in_list_view": 1, "label": "Bypass credit limit_check" } ], "istable": 1, - "modified": "2019-08-29 18:27:10.323287", + "modified": "2019-08-29 20:46:36.073953", "modified_by": "Administrator", "module": "Selling", "name": "Customer Credit Limit", From dcd5be0d07cefc317b35124dd7ea6112645a68d4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 29 Aug 2019 20:53:51 +0530 Subject: [PATCH 06/49] patch: move credit limit in customer to child table --- erpnext/patches.txt | 1 + ...e_credit_limit_to_customer_credit_limit.py | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 07b8ee6ab4..2af12698c5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -632,3 +632,4 @@ execute:frappe.reload_doc('desk', 'doctype','dashboard_chart') erpnext.patches.v12_0.add_default_dashboards erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries +erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit \ No newline at end of file diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py new file mode 100644 index 0000000000..510d79f23d --- /dev/null +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -0,0 +1,34 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' Move credit limit and bypass credit limit to the child table of customer credit limit ''' + frappe.reload_doc("Selling", "doctype", "Customer Credit Limit") + frappe.reload_doc("Selling", "doctype", "Customer") + + if frappe.db.a_row_exists("Customer Credit Limit"): + return + + move_credit_limit_to_child_table() + +def move_credit_limit_to_child_table(): + ''' maps data from old field to the new field in the child table ''' + + credit_limit_data = frappe.db.sql(''' SELECT + name, credit_limit, + bypass_credit_limit_check_against_sales_order + FROM `tabCustomer`''', as_dict=1) + + default_company = frappe.db.get_single_value("Global Defaults", "default_company") + + for customer in credit_limit_data: + customer = frappe.get_doc("Customer", customer.name) + customer.append("credit_limit", { + 'credit_limit': customer.credit_limit, + 'bypass_credit_limit_check': customer.bypass_credit_limit_check_against_sales_order, + 'company': default_company + }) + customer.save() \ No newline at end of file From b3e5d11e1d10f4e6ea738c149d0161016b5df06a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 30 Aug 2019 18:18:48 +0530 Subject: [PATCH 07/49] fix: multiple fixes --- erpnext/patches.txt | 2 +- ...e_credit_limit_to_customer_credit_limit.py | 15 +++++++------ .../selling/doctype/customer/customer.json | 19 ++++++++-------- erpnext/selling/doctype/customer/customer.py | 22 ++++++++++++++----- .../selling/doctype/customer/test_customer.py | 8 +++---- ...es_order_with_bypass_credit_limit_check.js | 5 +++-- ...order_without_bypass_credit_limit_check.js | 6 +++-- 7 files changed, 46 insertions(+), 31 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2af12698c5..0c909bfa1b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -632,4 +632,4 @@ execute:frappe.reload_doc('desk', 'doctype','dashboard_chart') erpnext.patches.v12_0.add_default_dashboards erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries -erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit \ No newline at end of file +erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit # \ No newline at end of file diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 510d79f23d..3cfabe48ad 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -19,16 +19,17 @@ def move_credit_limit_to_child_table(): credit_limit_data = frappe.db.sql(''' SELECT name, credit_limit, - bypass_credit_limit_check_against_sales_order + bypass_credit_limit_check_at_sales_order FROM `tabCustomer`''', as_dict=1) - default_company = frappe.db.get_single_value("Global Defaults", "default_company") + companies = frappe.get_all("Company", 'name') for customer in credit_limit_data: customer = frappe.get_doc("Customer", customer.name) - customer.append("credit_limit", { - 'credit_limit': customer.credit_limit, - 'bypass_credit_limit_check': customer.bypass_credit_limit_check_against_sales_order, - 'company': default_company - }) + for company in companies: + customer.append("credit_limit_reference", { + 'credit_limit': customer.credit_limit, + 'bypass_credit_limit_check': customer.bypass_credit_limit_check_at_sales_order, + 'company': company.name + }) customer.save() \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 8b5cca9320..b83e284697 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -50,7 +50,7 @@ "accounts", "credit_limit_section", "payment_terms", - "credit_limit", + "credit_limit_reference", "more_info", "customer_details", "column_break_45", @@ -344,13 +344,6 @@ "fieldtype": "Section Break", "label": "Credit Limit and Payment Terms" }, - { - "default": "0", - "fieldname": "credit_limit", - "fieldtype": "Table", - "label": "Credit Limit", - "options": "Customer Credit Limit" - }, { "fieldname": "payment_terms", "fieldtype": "Link", @@ -464,12 +457,19 @@ "print_hide": 1, "read_only": 1, "report_hide": 1 + }, + { + "default": "0", + "fieldname": "credit_limit_reference", + "fieldtype": "Table", + "label": "Credit Limit", + "options": "Customer Credit Limit" } ], "icon": "fa fa-user", "idx": 363, "image_field": "image", - "modified": "2019-08-28 17:38:09.709688", + "modified": "2019-08-30 18:03:13.332934", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -550,6 +550,7 @@ "quick_entry": 1, "search_fields": "customer_name,customer_group,territory, mobile_no,primary_address", "show_name_in_global_search": 1, + "sort_field": "modified", "sort_order": "ASC", "title_field": "customer_name", "track_changes": 1 diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 5edf06709d..3e9a824dae 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -167,11 +167,17 @@ class Customer(TransactionBase): frappe.throw(_("A Customer Group exists with same name please change the Customer name or rename the Customer Group"), frappe.NameError) def validate_credit_limit_on_change(self): - if self.get("__islocal") or not self.credit_limit: + if self.get("__islocal") or not self.credit_limit_reference: return + company_record = [c.company for c in self.credit_limit_reference] + for limit in frappe.get_all("Customer Credit Limit", {'parent': self.name}, ["credit_limit", "company"]): outstanding_amt = get_customer_outstanding(self.name, limit.company) + company_record.append(limit.company) + if company_record.count(limit.company) >2: + frappe.throw(_("Credit limit is already defined for the Company {0}").format(limit.company, self.name)) + if flt(limit.credit_limit) < outstanding_amt: frappe.throw(_("""New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0}""").format(outstanding_amt)) @@ -322,14 +328,18 @@ def get_credit_limit(customer, company): if customer: credit_record = frappe.db.sql("""SELECT - customer_group, - credit_limit + c.customer_group, + ccl.credit_limit FROM `tabCustomer`c , `tabCustomer Credit Limit` ccl WHERE - c.name = ccl.parent - """, as_dict=1) + c.name = %s + AND c.name = ccl.parent + AND ccl.company = %s + """, (customer, company), as_dict=1) - if not credit_record.credit_limit: + credit_limit = credit_record.credit_limit + + if not credit_limit: credit_limit = frappe.get_cached_value("Customer Group", credit_record.customer_group, "credit_limit") if not credit_limit: diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index f261ef37fb..8ea4964047 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -25,7 +25,7 @@ class TestCustomer(unittest.TestCase): make_test_records('Item') def tearDown(self): - frappe.db.set_value("Customer", '_Test Customer', 'credit_limit', 0.0) + frappe.db.set_value("Customer", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', 0.0) def test_party_details(self): from erpnext.accounts.party import get_party_details @@ -226,7 +226,7 @@ class TestCustomer(unittest.TestCase): make_sales_order(qty=item_qty) if credit_limit == 0.0: - frappe.db.set_value("Customer", '_Test Customer', 'credit_limit', outstanding_amt - 50.0) + frappe.db.set_value("Customer", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', outstanding_amt - 50.0) # Sales Order so = make_sales_order(do_not_submit=True) @@ -241,7 +241,7 @@ class TestCustomer(unittest.TestCase): self.assertRaises(frappe.ValidationError, si.submit) if credit_limit > outstanding_amt: - frappe.db.set_value("Customer", '_Test Customer', 'credit_limit', credit_limit) + frappe.db.set_value("Customer", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', credit_limit) # Makes Sales invoice from Sales Order so.save(ignore_permissions=True) @@ -252,7 +252,7 @@ class TestCustomer(unittest.TestCase): def test_customer_credit_limit_on_change(self): outstanding_amt = self.get_customer_outstanding_amount() customer = frappe.get_doc("Customer", '_Test Customer') - customer.credit_limit = flt(outstanding_amt - 100) + customer.credit_limit_reference['credit_limit'] = flt(outstanding_amt - 100) self.assertRaises(frappe.ValidationError, customer.save) def test_customer_payment_terms(self): diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js index dbd58c19c5..1f73f91f61 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js @@ -10,8 +10,9 @@ QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) { () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), () => frappe.timeout(1), () => cur_frm.set_value("customer_name", "Test Customer 10"), - () => cur_frm.set_value("credit_limit", 100.00), - () => cur_frm.set_value("bypass_credit_limit_check_at_sales_order", 1), + () => cur_frm.add_child('credit_limit_reference', { + 'credit_limit': 1000, + 'bypass_credit_limit_check': 1}), // save form () => cur_frm.save(), () => frappe.timeout(1), diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js index 4e81fb065f..34560b6900 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js @@ -10,8 +10,10 @@ QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), () => frappe.timeout(1), () => cur_frm.set_value("customer_name", "Test Customer 11"), - () => cur_frm.set_value("credit_limit", 100.00), - () => cur_frm.set_value("bypass_credit_limit_check_at_sales_order", 0), + () => cur_frm.add_child('credit_limit_reference', { + 'credit_limit': 1000, + 'company': '_Test Company', + 'bypass_credit_limit_check': 1}), // save form () => cur_frm.save(), () => frappe.timeout(1), From 89433b4bea0bc9dd86696356f7fb58f13a513798 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 30 Aug 2019 22:57:51 +0530 Subject: [PATCH 08/49] fix: get credit limit --- .../v12_0/move_credit_limit_to_customer_credit_limit.py | 4 ++-- erpnext/selling/doctype/customer/customer.py | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 3cfabe48ad..344dd82958 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -18,8 +18,8 @@ def move_credit_limit_to_child_table(): ''' maps data from old field to the new field in the child table ''' credit_limit_data = frappe.db.sql(''' SELECT - name, credit_limit, - bypass_credit_limit_check_at_sales_order + name, credit_limit, + bypass_credit_limit_check_at_sales_order FROM `tabCustomer`''', as_dict=1) companies = frappe.get_all("Company", 'name') diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 3e9a824dae..940aa34e78 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -335,12 +335,13 @@ def get_credit_limit(customer, company): c.name = %s AND c.name = ccl.parent AND ccl.company = %s - """, (customer, company), as_dict=1) + """, (customer, company)) - credit_limit = credit_record.credit_limit + if credit_record: + customer_group, credit_limit = credit_record[0] - if not credit_limit: - credit_limit = frappe.get_cached_value("Customer Group", credit_record.customer_group, "credit_limit") + if not credit_limit and customer_group: + credit_limit = frappe.get_cached_value("Customer Group", customer_group, "credit_limit") if not credit_limit: credit_limit = frappe.get_cached_value('Company', company, "credit_limit") From f856b3b5595e8ccdadb4d2041d6ee762e0acfe47 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sat, 31 Aug 2019 01:40:58 +0530 Subject: [PATCH 09/49] fix: fetch customer group of the customer --- erpnext/selling/doctype/customer/customer.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 940aa34e78..0ecb41e98c 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -327,20 +327,10 @@ def get_credit_limit(customer, company): credit_limit = None if customer: - credit_record = frappe.db.sql("""SELECT - c.customer_group, - ccl.credit_limit - FROM `tabCustomer`c , `tabCustomer Credit Limit` ccl - WHERE - c.name = %s - AND c.name = ccl.parent - AND ccl.company = %s - """, (customer, company)) + credit_limit = frappe.db.get_value("Customer Credit Limit", {'parent': customer, 'company': company}, 'credit_limit') - if credit_record: - customer_group, credit_limit = credit_record[0] - - if not credit_limit and customer_group: + if not credit_limit: + customer_group = frappe.db.get_value("Customer", customer, 'credit_group') credit_limit = frappe.get_cached_value("Customer Group", customer_group, "credit_limit") if not credit_limit: From e3bb247868e749dde3e601f6879c550a3611740d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sat, 31 Aug 2019 13:16:14 +0530 Subject: [PATCH 10/49] fix: check column in credit limit --- .../v12_0/move_credit_limit_to_customer_credit_limit.py | 9 ++++++--- erpnext/selling/doctype/customer/customer.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 344dd82958..58ad337b34 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -17,10 +17,13 @@ def execute(): def move_credit_limit_to_child_table(): ''' maps data from old field to the new field in the child table ''' + if not frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'): + fields = ", bypass_credit_limit_check_at_sales_order" + credit_limit_data = frappe.db.sql(''' SELECT - name, credit_limit, - bypass_credit_limit_check_at_sales_order - FROM `tabCustomer`''', as_dict=1) + name, credit_limit + {0} + FROM `tabCustomer`'''.format(fields), as_dict=1) #nosec companies = frappe.get_all("Company", 'name') diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 0ecb41e98c..3d5fee9423 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -330,7 +330,7 @@ def get_credit_limit(customer, company): credit_limit = frappe.db.get_value("Customer Credit Limit", {'parent': customer, 'company': company}, 'credit_limit') if not credit_limit: - customer_group = frappe.db.get_value("Customer", customer, 'credit_group') + customer_group = frappe.db.get_value("Customer", customer, 'customer_group') credit_limit = frappe.get_cached_value("Customer Group", customer_group, "credit_limit") if not credit_limit: From dbc69da409d31bcefc8b15001fcda2260cbdd2a9 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sat, 31 Aug 2019 14:28:02 +0530 Subject: [PATCH 11/49] fix: minor fixes --- .../v12_0/move_credit_limit_to_customer_credit_limit.py | 3 ++- erpnext/selling/doctype/customer/test_customer.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 58ad337b34..0b3da20986 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -17,7 +17,8 @@ def execute(): def move_credit_limit_to_child_table(): ''' maps data from old field to the new field in the child table ''' - if not frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'): + fields="" + if frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'): fields = ", bypass_credit_limit_check_at_sales_order" credit_limit_data = frappe.db.sql(''' SELECT diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 8ea4964047..1ef2aabead 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -25,7 +25,7 @@ class TestCustomer(unittest.TestCase): make_test_records('Item') def tearDown(self): - frappe.db.set_value("Customer", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', 0.0) + frappe.db.set_value("Customer Credit Limit", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', 0.0) def test_party_details(self): from erpnext.accounts.party import get_party_details From 42d0da8f5a740f2fe3762c8411dbbc56a30e66db Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sat, 31 Aug 2019 14:28:10 +0530 Subject: [PATCH 12/49] Revert "fix: process allocation expiry" This reverts commit b654dc0e288e3ecfdfbd0831c3ae15cc7036bb90. --- .../leave_ledger_entry/leave_ledger_entry.py | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 228e226645..c82114e6d5 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -95,28 +95,29 @@ def process_expired_allocation(): 'expire_carry_forwarded_leaves_after_days': (">", 0) }, fieldname=['name']) - leave_type = [record[0] for record in leave_type_records] + if leave_type_records: + leave_type = [record[0] for record in leave_type_records] - expired_allocation = frappe.db.sql_list("""SELECT name - FROM `tabLeave Ledger Entry` - WHERE - `transaction_type`='Leave Allocation' - AND `is_expired`=1""") + expired_allocation = frappe.db.sql_list("""SELECT name + FROM `tabLeave Ledger Entry` + WHERE + `transaction_type`='Leave Allocation' + AND `is_expired`=1""") - expire_allocation = frappe.get_all("Leave Ledger Entry", - fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], - filters={ - 'to_date': ("<", today()), - 'transaction_type': 'Leave Allocation', - 'transaction_name': ('not in', expired_allocation) - }, - or_filters={ - 'is_carry_forward': 0, - 'leave_type': ('in', leave_type) - }) + expire_allocation = frappe.get_all("Leave Ledger Entry", + fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], + filters={ + 'to_date': ("<", today()), + 'transaction_type': 'Leave Allocation', + 'transaction_name': ('not in', expired_allocation) + }, + or_filters={ + 'is_carry_forward': 0, + 'leave_type': ('in', leave_type) + }) if expire_allocation: - create_expiry_ledger_entry(expire_allocation) + create_expiry_ledger_entry(expire_allocation) def create_expiry_ledger_entry(allocations): ''' Create ledger entry for expired allocation ''' From bb3cec15567b6ed8e48de7e3927f31945c590f90 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sat, 31 Aug 2019 14:32:07 +0530 Subject: [PATCH 13/49] test: fetch data from customer credit limit --- erpnext/selling/doctype/customer/test_customer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 1ef2aabead..e5771b2359 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -226,7 +226,7 @@ class TestCustomer(unittest.TestCase): make_sales_order(qty=item_qty) if credit_limit == 0.0: - frappe.db.set_value("Customer", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', outstanding_amt - 50.0) + frappe.db.set_value("Customer Credit Limit", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', outstanding_amt - 50.0) # Sales Order so = make_sales_order(do_not_submit=True) @@ -252,7 +252,7 @@ class TestCustomer(unittest.TestCase): def test_customer_credit_limit_on_change(self): outstanding_amt = self.get_customer_outstanding_amount() customer = frappe.get_doc("Customer", '_Test Customer') - customer.credit_limit_reference['credit_limit'] = flt(outstanding_amt - 100) + customer.credit_limit_reference[0].credit_limit = flt(outstanding_amt - 100) self.assertRaises(frappe.ValidationError, customer.save) def test_customer_payment_terms(self): From 86720deeae65adc00a25ce3ba514559bfb60f080 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 1 Sep 2019 10:15:23 +0530 Subject: [PATCH 14/49] fix: bypass credit limit check --- erpnext/patches.txt | 2 +- .../v12_0/move_credit_limit_to_customer_credit_limit.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0c909bfa1b..2af12698c5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -632,4 +632,4 @@ execute:frappe.reload_doc('desk', 'doctype','dashboard_chart') erpnext.patches.v12_0.add_default_dashboards erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries -erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit # \ No newline at end of file +erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit \ No newline at end of file diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 0b3da20986..6f59b46ee7 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -21,19 +21,19 @@ def move_credit_limit_to_child_table(): if frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'): fields = ", bypass_credit_limit_check_at_sales_order" - credit_limit_data = frappe.db.sql(''' SELECT + credit_limit_record = frappe.db.sql(''' SELECT name, credit_limit {0} FROM `tabCustomer`'''.format(fields), as_dict=1) #nosec companies = frappe.get_all("Company", 'name') - for customer in credit_limit_data: + for record in credit_limit_record: customer = frappe.get_doc("Customer", customer.name) for company in companies: customer.append("credit_limit_reference", { - 'credit_limit': customer.credit_limit, - 'bypass_credit_limit_check': customer.bypass_credit_limit_check_at_sales_order, + 'credit_limit': record.credit_limit, + 'bypass_credit_limit_check': record.bypass_credit_limit_check_at_sales_order, 'company': company.name }) customer.save() \ No newline at end of file From cf901da25d2e17657c1ad21823eb6a6e900b653e Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 2 Sep 2019 23:18:36 +0530 Subject: [PATCH 15/49] test: create customer credit limit on change --- .../v12_0/move_credit_limit_to_customer_credit_limit.py | 2 +- erpnext/selling/doctype/customer/test_customer.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 6f59b46ee7..ca01c604dd 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -29,7 +29,7 @@ def move_credit_limit_to_child_table(): companies = frappe.get_all("Company", 'name') for record in credit_limit_record: - customer = frappe.get_doc("Customer", customer.name) + customer = frappe.get_doc("Customer", record.name) for company in companies: customer.append("credit_limit_reference", { 'credit_limit': record.credit_limit, diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index e5771b2359..35d1b2cf1c 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -252,7 +252,10 @@ class TestCustomer(unittest.TestCase): def test_customer_credit_limit_on_change(self): outstanding_amt = self.get_customer_outstanding_amount() customer = frappe.get_doc("Customer", '_Test Customer') - customer.credit_limit_reference[0].credit_limit = flt(outstanding_amt - 100) + customer.append('credit_limit_reference', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'}) + + ''' define new credit limit for same company ''' + customer.append('credit_limit_reference', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'}) self.assertRaises(frappe.ValidationError, customer.save) def test_customer_payment_terms(self): From a5907e17d565852f09b771c146327fd8de541fa2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 4 Sep 2019 11:04:27 +0530 Subject: [PATCH 16/49] Update accounts_receivable.py --- .../report/accounts_receivable/accounts_receivable.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 88e4227837..b2bf3f90a7 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -492,7 +492,7 @@ class ReceivablePayableReport(object): elif party_type_field=="supplier": self.add_supplier_filters(conditions, values) - self.add_accounting_dimensions_filters() + self.add_accounting_dimensions_filters(conditions, values) return " and ".join(conditions), values @@ -697,4 +697,4 @@ class ReceivablePayableReport(object): 'datasets': rows }, "type": 'percentage' - } \ No newline at end of file + } From 1635967c4eb82975f96917aa220d966aff0211fb Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 5 Sep 2019 12:16:49 +0530 Subject: [PATCH 17/49] fix: attribute error when trying to fetch items (#18900) --- .../manufacturing/doctype/production_plan/production_plan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 650ab137a0..048ce0d6ef 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -99,7 +99,7 @@ class ProductionPlan(Document): self.get_mr_items() def get_so_items(self): - so_list = [d.sales_order for d in self.sales_orders if d.sales_order] + so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order] if not so_list: msgprint(_("Please enter Sales Orders in the above table")) return [] @@ -134,7 +134,7 @@ class ProductionPlan(Document): self.calculate_total_planned_qty() def get_mr_items(self): - mr_list = [d.material_request for d in self.material_requests if d.material_request] + mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request] if not mr_list: msgprint(_("Please enter Material Requests in the above table")) return [] From 805b8634da12f9659fecf63a30237ab25be11df5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 5 Sep 2019 12:18:33 +0530 Subject: [PATCH 18/49] fix: incorrect stock value difference when stock move from negative to positive (#18887) --- erpnext/stock/stock_ledger.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index f7deac3591..69a4b94b4e 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -181,10 +181,7 @@ class update_entries_after(object): # rounding as per precision self.stock_value = flt(self.stock_value, self.precision) - if self.prev_stock_value < 0 and self.stock_value >= 0 and sle.voucher_type != 'Stock Reconciliation': - stock_value_difference = sle.actual_qty * self.valuation_rate - else: - stock_value_difference = self.stock_value - self.prev_stock_value + stock_value_difference = self.stock_value - self.prev_stock_value self.prev_stock_value = self.stock_value From a2db94761addee2826e476a9808880039e882d1a Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 5 Sep 2019 12:19:50 +0530 Subject: [PATCH 19/49] fix: Honor Shopping Cart Price List (#18885) --- erpnext/shopping_cart/cart.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 0922f3d1a0..db2c3277d0 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -336,19 +336,20 @@ def set_price_list_and_rate(quotation, cart_settings): def _set_price_list(quotation, cart_settings): """Set price list based on customer or shopping cart default""" - if quotation.selling_price_list: - return + from erpnext.accounts.party import get_default_price_list # check if customer price list exists selling_price_list = None if quotation.party_name: - from erpnext.accounts.party import get_default_price_list - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list') # else check for territory based price list if not selling_price_list: selling_price_list = cart_settings.price_list + if not selling_price_list and quotation.party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + quotation.selling_price_list = selling_price_list def set_taxes(quotation, cart_settings): From 1e0b0da7ad8f6c20573006d650b9f1a0a276ee5b Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 5 Sep 2019 12:21:44 +0530 Subject: [PATCH 20/49] fix(patch): add company in filters to get proper parent account (#18905) --- erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py index 02203d29d4..9f4c445365 100644 --- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py +++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py @@ -82,7 +82,7 @@ def get_item_tax_template(item_tax_templates, rename_template_to_untitled, item_ account_name = " - ".join(parts[:-1]) company = frappe.db.get_value("Company", filters={"abbr": parts[-1]}) parent_account = frappe.db.get_value("Account", - filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0}, fieldname="parent_account") + filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account") frappe.get_doc({ "doctype": "Account", From 7834a0182b96cc372d50fc58eefcbdaeceb71e3d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Sep 2019 12:22:48 +0530 Subject: [PATCH 21/49] Update move_credit_limit_to_customer_credit_limit.py --- .../patches/v12_0/move_credit_limit_to_customer_credit_limit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index ca01c604dd..30acfe68ea 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -36,4 +36,4 @@ def move_credit_limit_to_child_table(): 'bypass_credit_limit_check': record.bypass_credit_limit_check_at_sales_order, 'company': company.name }) - customer.save() \ No newline at end of file + customer.db_insert() From c78c86663dfd170fb8f81ac8820852cbe456777c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Sep 2019 12:24:37 +0530 Subject: [PATCH 22/49] Update customer_credit_balance.py --- .../report/customer_credit_balance/customer_credit_balance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index 6b8b8581d2..cd50568cf9 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -60,7 +60,7 @@ def get_details(filters): conditions = "" if filters.get("customer"): - conditions += " AND name = " + filters.get("customer") + conditions += " AND c.name = " + filters.get("customer") return frappe.db.sql("""SELECT c.name, c.customer_name, @@ -71,4 +71,4 @@ def get_details(filters): c.name = ccl.parent AND ccl.company = %s {0} - """.format(conditions), (filters.get("company")), as_dict=1) #nosec \ No newline at end of file + """.format(conditions), (filters.get("company")), as_dict=1) #nosec From f4fde51bba4b62afb6a73eb70b25bb3439aba26c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Sep 2019 12:33:29 +0530 Subject: [PATCH 23/49] Update customer.py --- erpnext/selling/doctype/customer/customer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 5d0e8ed98c..4dd02da333 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -170,14 +170,14 @@ class Customer(TransactionBase): if self.get("__islocal") or not self.credit_limit_reference: return - company_record = [c.company for c in self.credit_limit_reference] - - for limit in frappe.get_all("Customer Credit Limit", {'parent': self.name}, ["credit_limit", "company"]): - outstanding_amt = get_customer_outstanding(self.name, limit.company) - company_record.append(limit.company) - if company_record.count(limit.company) >2: + company_record = [] + for limit in self.credit_limit_reference: + if limit.company in company_record: frappe.throw(_("Credit limit is already defined for the Company {0}").format(limit.company, self.name)) + else: + company_record.append(limit.company) + outstanding_amt = get_customer_outstanding(self.name, limit.company) if flt(limit.credit_limit) < outstanding_amt: frappe.throw(_("""New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0}""").format(outstanding_amt)) @@ -330,7 +330,7 @@ def get_credit_limit(customer, company): credit_limit = frappe.db.get_value("Customer Credit Limit", {'parent': customer, 'company': company}, 'credit_limit') if not credit_limit: - customer_group = frappe.db.get_value("Customer", customer, 'customer_group') + customer_group = frappe.get_cached_value("Customer", customer, 'customer_group') credit_limit = frappe.get_cached_value("Customer Group", customer_group, "credit_limit") if not credit_limit: From 16a7ec95dc8292efe98d4d817d3a0da1104dc0e7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Sep 2019 13:01:15 +0530 Subject: [PATCH 24/49] fix: tax category is optional argument --- erpnext/accounts/party.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index e42f4af2a5..26c2950f3a 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -365,7 +365,7 @@ def validate_due_date(posting_date, due_date, party_type, party, company=None, b .format(formatdate(default_due_date))) @frappe.whitelist() -def get_address_tax_category(tax_category, billing_address=None, shipping_address=None): +def get_address_tax_category(tax_category=None, billing_address=None, shipping_address=None): addr_tax_category_from = frappe.db.get_single_value("Accounts Settings", "determine_address_tax_category_from") if addr_tax_category_from == "Shipping Address": if shipping_address: From 5aaf15d145576b57a4e670166d21c5e49db51e4c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 5 Sep 2019 14:42:28 +0530 Subject: [PATCH 25/49] fix: healthcare practitioner not showing in the dropdown (#18929) --- .../healthcare_practitioner.py | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py index ed9eae3529..ad32e94631 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py @@ -69,33 +69,10 @@ def validate_service_item(item, msg): def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None): fields = ["name", "first_name", "mobile_phone"] - match_conditions = build_match_conditions("Healthcare Practitioner") - match_conditions = "and {}".format(match_conditions) if match_conditions else "" - if filters: - filter_conditions = get_filters_cond(doctype, filters, []) - match_conditions += "{}".format(filter_conditions) + filters = { + 'name': ("like", "%%%s%%" % txt) + } - return frappe.db.sql("""select %s from `tabHealthcare Practitioner` where docstatus < 2 - and (%s like %s or first_name like %s) - and active = 1 - {match_conditions} - order by - case when name like %s then 0 else 1 end, - case when first_name like %s then 0 else 1 end, - name, first_name limit %s, %s""".format( - match_conditions=match_conditions) % - ( - ", ".join(fields), - frappe.db.escape(searchfield), - "%s", "%s", "%s", "%s", "%s", "%s" - ), - ( - "%%%s%%" % frappe.db.escape(txt), - "%%%s%%" % frappe.db.escape(txt), - "%%%s%%" % frappe.db.escape(txt), - "%%%s%%" % frappe.db.escape(txt), - start, - page_len - ) - ) + return frappe.get_all("Healthcare Practitioner", fields = fields, + filters = filters, start=start, page_length=page_len, order_by="name, first_name", as_list=1) From f1fab871b8d66d51638117344ba1cbee5a84ec12 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 5 Sep 2019 14:47:43 +0530 Subject: [PATCH 26/49] fix: mismatch between warehouse tree value and warehouse based stock balance report value (#18879) --- erpnext/stock/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 6ea322872e..aec37d4e94 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -15,7 +15,7 @@ def get_stock_value_from_bin(warehouse=None, item_code=None): values = {} conditions = "" if warehouse: - conditions += """ and warehouse in ( + conditions += """ and `tabBin`.warehouse in ( select w2.name from `tabWarehouse` w1 join `tabWarehouse` w2 on w1.name = %(warehouse)s @@ -25,11 +25,12 @@ def get_stock_value_from_bin(warehouse=None, item_code=None): values['warehouse'] = warehouse if item_code: - conditions += " and item_code = %(item_code)s" + conditions += " and `tabBin`.item_code = %(item_code)s" values['item_code'] = item_code - query = "select sum(stock_value) from `tabBin` where 1 = 1 %s" % conditions + query = """select sum(stock_value) from `tabBin`, `tabItem` where 1 = 1 + and `tabItem`.name = `tabBin`.item_code and ifnull(`tabItem`.disabled, 0) = 0 %s""" % conditions stock_value = frappe.db.sql(query, values) From 7496548c3978b6991ff516021d4bd0cdd86ec932 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 5 Sep 2019 14:48:51 +0530 Subject: [PATCH 27/49] fix: not able to create invoice against patient (#18858) --- erpnext/healthcare/doctype/patient/patient.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index bf15cad5d5..e3eea96f85 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cint, cstr, getdate +from frappe.utils import cint, cstr, getdate, flt import dateutil from frappe.model.naming import set_name_by_naming_series from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account,send_registration_sms @@ -64,7 +64,7 @@ class Patient(Document): def invoice_patient_registration(self): frappe.db.set_value("Patient", self.name, "disabled", 0) send_registration_sms(self) - if(frappe.get_value("Healthcare Settings", None, "registration_fee")>0): + if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0): company = frappe.defaults.get_user_default('company') if not company: company = frappe.db.get_value("Global Defaults", None, "default_company") From c901d6322c45034d9240b1ac6284090d1b8c56d9 Mon Sep 17 00:00:00 2001 From: Govind S Menokee Date: Thu, 5 Sep 2019 14:50:23 +0530 Subject: [PATCH 28/49] fix(18837): Student creation error (#18838) Academic user not able to create student --- erpnext/education/doctype/student/student.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py index da25880c81..705c6e4e98 100644 --- a/erpnext/education/doctype/student/student.py +++ b/erpnext/education/doctype/student/student.py @@ -54,6 +54,7 @@ class Student(Document): 'send_welcome_email': 1, 'user_type': 'Website User' }) + student_user.flags.ignore_permissions = True student_user.add_roles("Student") student_user.save() update_password_link = student_user.reset_password() From 9d77e9f719b47f8c38fbabac8f20252249cc3d12 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 5 Sep 2019 14:51:22 +0530 Subject: [PATCH 29/49] fix: error while trying to get directions (#18827) --- erpnext/stock/doctype/delivery_trip/delivery_trip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index a253811b27..77d322ed28 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -238,7 +238,7 @@ class DeliveryTrip(Document): try: directions = maps_client.directions(**directions_data) except Exception as e: - frappe.throw(_(e.message)) + frappe.throw(_(e)) return directions[0] if directions else False From 77da3b4347ade309d1547e872c1fb9b89b71775f Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 5 Sep 2019 14:53:43 +0530 Subject: [PATCH 30/49] fix: pull project from task (#18776) --- erpnext/projects/doctype/timesheet/timesheet.js | 12 +++++++++--- erpnext/projects/doctype/timesheet/timesheet.py | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 101d903bed..3eea390ff3 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -147,6 +147,15 @@ frappe.ui.form.on("Timesheet Detail", { calculate_time_and_amount(frm); }, + task: (frm, cdt, cdn) => { + let row = frm.selected_doc; + if (row.task) { + frappe.db.get_value("Task", row.task, "project", (r) => { + frappe.model.set_value(cdt, cdn, "project", r.project); + }); + } + }, + from_time: function(frm, cdt, cdn) { calculate_end_time(frm, cdt, cdn); }, @@ -200,9 +209,6 @@ frappe.ui.form.on("Timesheet Detail", { }, activity_type: function(frm, cdt, cdn) { - frm.script_manager.copy_from_first_row('time_logs', frm.selected_doc, - 'project'); - frappe.call({ method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost", args: { diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 3c719227bd..9ee292796c 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -145,12 +145,17 @@ class Timesheet(Document): def validate_time_logs(self): for data in self.get('time_logs'): self.validate_overlap(data) + self.validate_task_project() def validate_overlap(self, data): settings = frappe.get_single('Projects Settings') self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap) self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap) + def validate_task_project(self): + for log in self.time_logs: + log.project = log.project or frappe.db.get_value("Task", log.task, "project") + def validate_overlap_for(self, fieldname, args, value, ignore_validation=False): if not value or ignore_validation: return From a7d448dcbaa11280fa973d08f912bec1a3a445cb Mon Sep 17 00:00:00 2001 From: John Clarke Date: Thu, 5 Sep 2019 03:25:35 -0600 Subject: [PATCH 31/49] fix: Error "TypeError: unorderable types: int() > str()" When Trying to Create Packing Slip (#18913) User report here https://discuss.erpnext.com/t/error-typeerror-unorderable-types-int-str-when-trying-to-create-packing-slip/52445 This has not been noted to github issues and is just a suggested fix, an expert may need to refactor more here? User and traceback does not state whether Python 2 or 3 but perhaps 3 since that apparently does not 'guess' when types do not match? See also https://stackoverflow.com/questions/14886881/unorderable-types-int-str --- erpnext/stock/doctype/packing_slip/packing_slip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.py b/erpnext/stock/doctype/packing_slip/packing_slip.py index b99215c426..4139a19abf 100644 --- a/erpnext/stock/doctype/packing_slip/packing_slip.py +++ b/erpnext/stock/doctype/packing_slip/packing_slip.py @@ -49,7 +49,7 @@ class PackingSlip(Document): frappe.msgprint(_("Please specify a valid 'From Case No.'"), raise_exception=1) elif not self.to_case_no: self.to_case_no = self.from_case_no - elif self.from_case_no > self.to_case_no: + elif cint(self.from_case_no) > cint(self.to_case_no): frappe.msgprint(_("'To Case No.' cannot be less than 'From Case No.'"), raise_exception=1) From 91596f2467ecc48166700627d1c4a9fcd24bbcbf Mon Sep 17 00:00:00 2001 From: Andy Zhu <30763348+AndyOverLord@users.noreply.github.com> Date: Thu, 5 Sep 2019 21:31:38 +1200 Subject: [PATCH 32/49] Change the field name for Payment Entry (#18874) In the Payment Entry Form, the field name for customer name is 'party' instead of 'party_name'. Need to change the customer_dashboard file accordingly. --- erpnext/selling/doctype/customer/customer_dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index 8e790bf9ce..654dd48c66 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -9,7 +9,7 @@ def get_data(): 'heatmap_message': _('This is based on transactions against this Customer. See timeline below for details'), 'fieldname': 'customer', 'non_standard_fieldnames': { - 'Payment Entry': 'party_name', + 'Payment Entry': 'party', 'Quotation': 'party_name', 'Opportunity': 'party_name' }, From 1d1427de60c3070f2fd6103a04c0037c45c669de Mon Sep 17 00:00:00 2001 From: Karthikeyan S Date: Thu, 5 Sep 2019 15:02:33 +0530 Subject: [PATCH 33/49] fix(auto attendance): handling None case for IN/OUT Logs (#18867) --- erpnext/hr/doctype/employee_checkin/employee_checkin.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index d7d6706140..86705121ac 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -142,8 +142,10 @@ def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type): elif check_in_out_type == 'Strictly based on Log Type in Employee Checkin': if working_hours_calc_type == 'First Check-in and Last Check-out': - first_in_log = logs[find_index_in_dict(logs, 'log_type', 'IN')] - last_out_log = logs[len(logs)-1-find_index_in_dict(reversed(logs), 'log_type', 'OUT')] + first_in_log_index = find_index_in_dict(logs, 'log_type', 'IN') + first_in_log = logs[first_in_log_index] if first_in_log_index or first_in_log_index == 0 else None + last_out_log_index = find_index_in_dict(reversed(logs), 'log_type', 'OUT') + last_out_log = logs[len(logs)-1-last_out_log_index] if last_out_log_index or last_out_log_index == 0 else None if first_in_log and last_out_log: in_time, out_time = first_in_log.time, last_out_log.time total_hours = time_diff_in_hours(in_time, out_time) From d114c8f88fde3ed412c071b7cf60876535389ea0 Mon Sep 17 00:00:00 2001 From: KanchanChauhan Date: Thu, 5 Sep 2019 15:11:43 +0530 Subject: [PATCH 34/49] fix(Purchase Order): Status updater (#18612) --- erpnext/buying/doctype/purchase_order/purchase_order_list.js | 4 ++-- erpnext/controllers/status_updater.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js index a67d69dc26..8413eb65c3 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js @@ -16,9 +16,9 @@ frappe.listview_settings['Purchase Order'] = { return [__("To Receive"), "orange", "per_received,<,100|per_billed,=,100|status,!=,Closed"]; } - } else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status !== "Closed") { + } else if (flt(doc.per_received, 2) >= 100 && flt(doc.per_billed, 2) < 100 && doc.status !== "Closed") { return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Closed"]; - } else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status !== "Closed") { + } else if (flt(doc.per_received, 2) >= 100 && flt(doc.per_billed, 2) == 100 && doc.status !== "Closed") { return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"]; } }, diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index b2057ca40f..64d49b4549 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -57,9 +57,9 @@ status_map = { "Purchase Order": [ ["Draft", None], ["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"], - ["To Bill", "eval:self.per_received == 100 and self.per_billed < 100 and self.docstatus == 1"], + ["To Bill", "eval:self.per_received >= 100 and self.per_billed < 100 and self.docstatus == 1"], ["To Receive", "eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1"], - ["Completed", "eval:self.per_received == 100 and self.per_billed == 100 and self.docstatus == 1"], + ["Completed", "eval:self.per_received >= 100 and self.per_billed == 100 and self.docstatus == 1"], ["Delivered", "eval:self.status=='Delivered'"], ["Cancelled", "eval:self.docstatus==2"], ["On Hold", "eval:self.status=='On Hold'"], From 867ac10ffd71a058c4dcafc29374d6a8f86e535d Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 5 Sep 2019 15:13:21 +0530 Subject: [PATCH 35/49] fix: handle for product bundle (#18420) * fix: handle for product bundle * fix: Requested changes and some general fixes --- .../doctype/sales_order/sales_order.py | 2 +- .../pending_so_items_for_purchase_request.py | 92 ++++++++++++++----- erpnext/stock/get_item_details.py | 2 +- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index c9aaab4b21..67fa0d43c8 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -518,7 +518,7 @@ def make_material_request(source_name, target_doc=None): "doctype": "Material Request Item", "field_map": { "parent": "sales_order", - "stock_uom": "uom" + "uom": "stock_uom" }, "postprocess": update_item }, diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py index 670b4e98bf..14d8031582 100644 --- a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py +++ b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cint,cstr +from frappe.utils import flt def execute(filters=None): columns = get_columns() @@ -100,7 +100,7 @@ def get_data(): so.transaction_date, so.customer, so.territory, - sum(so_item.qty) as net_qty, + sum(so_item.qty) as total_qty, so.company FROM `tabSales Order` so, `tabSales Order Item` so_item WHERE @@ -111,10 +111,15 @@ def get_data(): so.name,so_item.item_code """, as_dict = 1) + sales_orders = [row.name for row in sales_order_entry] mr_records = frappe.get_all("Material Request Item", - {"sales_order_item": ("!=",""), "docstatus": 1}, + {"sales_order": ("in", sales_orders), "docstatus": 1}, ["parent", "qty", "sales_order", "item_code"]) + bundled_item_map = get_packed_items(sales_orders) + + item_with_product_bundle = get_items_with_product_bundle([row.item_code for row in sales_order_entry]) + materials_request_dict = {} for record in mr_records: @@ -131,27 +136,64 @@ def get_data(): if record.parent not in details.get('material_requests'): details['material_requests'].append(record.parent) - pending_so=[] + pending_so = [] for so in sales_order_entry: - # fetch all the material request records for a sales order item - key = (so.name, so.item_code) - materials_request = materials_request_dict.get(key) or {} + if so.item_code not in item_with_product_bundle: + material_requests_against_so = materials_request_dict.get((so.name, so.item_code)) or {} + # check for pending sales order + if flt(so.total_qty) > flt(material_requests_against_so.get('qty')): + so_record = { + "item_code": so.item_code, + "item_name": so.item_name, + "description": so.description, + "sales_order_no": so.name, + "date": so.transaction_date, + "material_request": ','.join(material_requests_against_so.get('material_requests', [])), + "customer": so.customer, + "territory": so.territory, + "so_qty": so.total_qty, + "requested_qty": material_requests_against_so.get('qty'), + "pending_qty": so.total_qty - flt(material_requests_against_so.get('qty')), + "company": so.company + } + pending_so.append(so_record) + else: + for item in bundled_item_map.get((so.name, so.item_code)): + material_requests_against_so = materials_request_dict.get((so.name, item.item_code)) or {} + if flt(item.qty) > flt(material_requests_against_so.get('qty')): + so_record = { + "item_code": item.item_code, + "item_name": item.item_name, + "description": item.description, + "sales_order_no": so.name, + "date": so.transaction_date, + "material_request": ','.join(material_requests_against_so.get('material_requests', [])), + "customer": so.customer, + "territory": so.territory, + "so_qty": item.qty, + "requested_qty": material_requests_against_so.get('qty', 0), + "pending_qty": item.qty - flt(material_requests_against_so.get('qty', 0)), + "company": so.company + } + pending_so.append(so_record) - # check for pending sales order - if cint(so.net_qty) > cint(materials_request.get('qty')): - so_record = { - "item_code": so.item_code, - "item_name": so.item_name, - "description": so.description, - "sales_order_no": so.name, - "date": so.transaction_date, - "material_request": ','.join(materials_request.get('material_requests', [])), - "customer": so.customer, - "territory": so.territory, - "so_qty": so.net_qty, - "requested_qty": cint(materials_request.get('qty')), - "pending_qty": so.net_qty - cint(materials_request.get('qty')), - "company": so.company - } - pending_so.append(so_record) - return pending_so \ No newline at end of file + + return pending_so + +def get_items_with_product_bundle(item_list): + bundled_items = frappe.get_all("Product Bundle", filters = [ + ("new_item_code", "IN", item_list) + ], fields = ["new_item_code"]) + + return [d.new_item_code for d in bundled_items] + +def get_packed_items(sales_order_list): + packed_items = frappe.get_all("Packed Item", filters = [ + ("parent", "IN", sales_order_list) + ], fields = ["parent_item", "item_code", "qty", "item_name", "description", "parent"]) + + bundled_item_map = frappe._dict() + for d in packed_items: + bundled_item_map.setdefault((d.parent, d.parent_item), []).append(d) + + return bundled_item_map diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index bf5a8180bd..35c0bb6165 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -639,7 +639,7 @@ def validate_price_list(args): if not frappe.db.get_value("Price List", {"name": args.price_list, args.transaction_type: 1, "enabled": 1}): throw(_("Price List {0} is disabled or does not exist").format(args.price_list)) - elif not args.get("supplier"): + elif args.get("customer"): throw(_("Price List not selected")) def validate_conversion_rate(args, meta): From c257ce82c5a8897247a2ee8c132110329a96858f Mon Sep 17 00:00:00 2001 From: hendrik Date: Thu, 5 Sep 2019 16:44:09 +0700 Subject: [PATCH 36/49] Party accounts if don't have default company (#18771) Add check for get_default company --- erpnext/accounts/party.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 26c2950f3a..59936d5116 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -292,8 +292,11 @@ def validate_party_accounts(doc): party_account_currency = frappe.db.get_value("Account", account.account, "account_currency", cache=True) existing_gle_currency = get_party_gle_currency(doc.doctype, doc.name, account.company) - company_default_currency = frappe.get_cached_value('Company', - frappe.db.get_default("Company"), "default_currency") + if frappe.db.get_default("Company"): + company_default_currency = frappe.get_cached_value('Company', + frappe.db.get_default("Company"), "default_currency") + else: + company_default_currency = frappe.db.get_value('Company', account.company, "default_currency") if existing_gle_currency and party_account_currency != existing_gle_currency: frappe.throw(_("Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.").format(existing_gle_currency, account.company)) @@ -607,4 +610,4 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None): .format(("credit") if party_type == "Customer" else "debit", cond) , party_type) if data: - return frappe._dict(data) \ No newline at end of file + return frappe._dict(data) From 48d1463825578fa288ec4e895e7c35dcbb0b44b9 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 5 Sep 2019 04:45:28 -0500 Subject: [PATCH 37/49] fix(CoA): SKR04 (#18820) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - move debitors into receivables - move creditors into payables - rename "C - Verb." to "C-Verbindlichkeiten" for consitency - move "Erlösschmälerungen" into a root of type "Expense" - move "Erhaltene Boni" and "Erhaltene Rabatte" into a root of type "Income" --- ..._kontenplan_SKR04_with_account_number.json | 286 +++++++++--------- 1 file changed, 143 insertions(+), 143 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json index b042bcc420..adfa9f89e2 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json @@ -406,7 +406,11 @@ "is_group": 1, "Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": { "account_number": "9960" - }, + }, + "Debitoren": { + "is_group": 1, + "account_number": "10000" + }, "Forderungen aus Lieferungen und Leistungen": { "account_number": "1200", "account_type": "Receivable" @@ -1077,7 +1081,7 @@ } } }, - "C - Verb.": { + "C - Verbindlichkeiten": { "account_type": "Payable", "1 - Anleihen": { "is_group": 1, @@ -1193,7 +1197,15 @@ "is_group": 1, "Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": { "account_number": "9964" - }, + }, + "Kreditoren": { + "account_number": "70000", + "is_group": 1, + "Wareneingangs-­Verrechnungskonto" : { + "account_number": "70001", + "account_type": "Stock Received But Not Billed" + } + }, "Verb. aus Lieferungen und Leistungen": { "account_number": "3300", "account_type": "Payable" @@ -1682,90 +1694,6 @@ "account_type": "Income Account" } }, - "Erl\u00f6sschm\u00e4lerungen (Gruppe)": { - "is_group": 1, - "Erl\u00f6sschm\u00e4lerungen": { - "account_number": "4700" - }, - "Erl\u00f6sschm\u00e4lerungen aus steuerfreien Ums\u00e4tzen \u00a7 4 Nr. 1a UStG": { - "account_number": "4705" - }, - "Erl\u00f6sschm\u00e4lerungen 7 % USt": { - "account_number": "4710" - }, - "Erl\u00f6sschm\u00e4lerungen 19 % USt": { - "account_number": "4720" - }, - "Erl\u00f6sschm\u00e4lerungen 16 % USt": { - "account_number": "4723" - }, - "Erl\u00f6sschm\u00e4lerungen aus steuerfreien innergem. Lieferungen": { - "account_number": "4724" - }, - "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 7 % USt": { - "account_number": "4725" - }, - "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 19 % USt": { - "account_number": "4726" - }, - "Erl\u00f6sschm\u00e4lerungen aus im anderen EU-Land steuerpfl. Lieferungen": { - "account_number": "4727" - }, - "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": { - "account_number": "4729" - }, - "Gew\u00e4hrte Skonti (Gruppe)": { - "is_group": 1, - "Gew. Skonti": { - "account_number": "4730" - }, - "Gew. Skonti 7 % USt": { - "account_number": "4731" - }, - "Gew. Skonti 19 % USt": { - "account_number": "4736" - }, - "Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": { - "account_number": "4738" - }, - "Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": { - "account_number": "4741" - }, - "Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": { - "account_number": "4742" - }, - "Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": { - "account_number": "4743" - }, - "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": { - "account_number": "4745" - }, - "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": { - "account_number": "4746" - }, - "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": { - "account_number": "4748" - } - }, - "Gew\u00e4hrte Boni 7 % USt": { - "account_number": "4750" - }, - "Gew\u00e4hrte Boni 19 % USt": { - "account_number": "4760" - }, - "Gew\u00e4hrte Boni": { - "account_number": "4769" - }, - "Gew\u00e4hrte Rabatte": { - "account_number": "4770" - }, - "Gew\u00e4hrte Rabatte 7 % USt": { - "account_number": "4780" - }, - "Gew\u00e4hrte Rabatte 19 % USt": { - "account_number": "4790" - } - }, "Grundst\u00fccksertr\u00e4ge (Gruppe)": { "is_group": 1, "Grundst\u00fccksertr\u00e4ge": { @@ -2049,48 +1977,6 @@ "Erh. Skonti aus Erwerb Waren als letzter Abnehmer innerh. Dreiecksgesch. 19% Vorst. u. 19% Ust.": { "account_number": "5793" } - }, - "Erhaltene Boni (Gruppe)": { - "is_group": 1, - "Erhaltene Boni 7 % Vorsteuer": { - "account_number": "5750" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { - "account_number": "5753" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { - "account_number": "5754" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { - "account_number": "5755" - }, - "Erhaltene Boni 19 % Vorsteuer": { - "account_number": "5760" - }, - "Erhaltene Boni": { - "account_number": "5769" - } - }, - "Erhaltene Rabatte (Gruppe)": { - "is_group": 1, - "Erhaltene Rabatte": { - "account_number": "5770" - }, - "Erhaltene Rabatte 7 % Vorsteuer": { - "account_number": "5780" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { - "account_number": "5783" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { - "account_number": "5784" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { - "account_number": "5785" - }, - "Erhaltene Rabatte 19 % Vorsteuer": { - "account_number": "5790" - } } }, "Bezugsnebenkosten (Gruppe)": { @@ -2409,7 +2295,49 @@ }, "6 - sonstige betriebliche Ertr\u00e4ge": { "root_type": "Income", - "is_group": 1, + "is_group": 1, + "Erhaltene Boni (Gruppe)": { + "is_group": 1, + "Erhaltene Boni 7 % Vorsteuer": { + "account_number": "5750" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { + "account_number": "5753" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { + "account_number": "5754" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { + "account_number": "5755" + }, + "Erhaltene Boni 19 % Vorsteuer": { + "account_number": "5760" + }, + "Erhaltene Boni": { + "account_number": "5769" + } + }, + "Erhaltene Rabatte (Gruppe)": { + "is_group": 1, + "Erhaltene Rabatte": { + "account_number": "5770" + }, + "Erhaltene Rabatte 7 % Vorsteuer": { + "account_number": "5780" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { + "account_number": "5783" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { + "account_number": "5784" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { + "account_number": "5785" + }, + "Erhaltene Rabatte 19 % Vorsteuer": { + "account_number": "5790" + } + }, "Andere aktivierte Eigenleistungen": { "account_number": "4820" }, @@ -2732,7 +2660,91 @@ }, "7 - sonstige betriebliche Aufwendungen": { "root_type": "Expense", - "is_group": 1, + "is_group": 1, + "Erl\u00f6sschm\u00e4lerungen (Gruppe)": { + "is_group": 1, + "Erl\u00f6sschm\u00e4lerungen": { + "account_number": "4700" + }, + "Erl\u00f6sschm\u00e4lerungen aus steuerfreien Ums\u00e4tzen \u00a7 4 Nr. 1a UStG": { + "account_number": "4705" + }, + "Erl\u00f6sschm\u00e4lerungen 7 % USt": { + "account_number": "4710" + }, + "Erl\u00f6sschm\u00e4lerungen 19 % USt": { + "account_number": "4720" + }, + "Erl\u00f6sschm\u00e4lerungen 16 % USt": { + "account_number": "4723" + }, + "Erl\u00f6sschm\u00e4lerungen aus steuerfreien innergem. Lieferungen": { + "account_number": "4724" + }, + "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 7 % USt": { + "account_number": "4725" + }, + "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 19 % USt": { + "account_number": "4726" + }, + "Erl\u00f6sschm\u00e4lerungen aus im anderen EU-Land steuerpfl. Lieferungen": { + "account_number": "4727" + }, + "Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": { + "account_number": "4729" + }, + "Gew\u00e4hrte Skonti (Gruppe)": { + "is_group": 1, + "Gew. Skonti": { + "account_number": "4730" + }, + "Gew. Skonti 7 % USt": { + "account_number": "4731" + }, + "Gew. Skonti 19 % USt": { + "account_number": "4736" + }, + "Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": { + "account_number": "4738" + }, + "Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": { + "account_number": "4741" + }, + "Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": { + "account_number": "4742" + }, + "Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": { + "account_number": "4743" + }, + "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": { + "account_number": "4745" + }, + "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": { + "account_number": "4746" + }, + "Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": { + "account_number": "4748" + } + }, + "Gew\u00e4hrte Boni 7 % USt": { + "account_number": "4750" + }, + "Gew\u00e4hrte Boni 19 % USt": { + "account_number": "4760" + }, + "Gew\u00e4hrte Boni": { + "account_number": "4769" + }, + "Gew\u00e4hrte Rabatte": { + "account_number": "4770" + }, + "Gew\u00e4hrte Rabatte 7 % USt": { + "account_number": "4780" + }, + "Gew\u00e4hrte Rabatte 19 % USt": { + "account_number": "4790" + } + }, "Sonstige betriebliche Aufwendungen": { "account_number": "6300" }, @@ -3609,18 +3621,6 @@ "Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen f. sonstige Steuern": { "account_number": "7694" } - }, - "Debitoren": { - "root_type": "Asset", - "is_group": 1 - }, - "Kreditoren": { - "root_type": "Liability", - "is_group": 1, - "Wareneingangs-­Verrechnungskonto" : { - "account_number": "70001", - "account_type": "Stock Received But Not Billed" - } } } } From cffe577028ef63628c86bedab1c939ccb14f9707 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 5 Sep 2019 16:03:15 +0530 Subject: [PATCH 38/49] fix: Add UOM in anlytics report when viewing based on item (#18902) --- .../report/sales_analytics/sales_analytics.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 8a5e50a61e..72767f0689 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -40,6 +40,16 @@ class Analytics(object): "fieldtype": "Data", "width": 140 }) + + if self.filters.tree_type == "Item": + self.columns.append({ + "label": _("UOM"), + "fieldname": 'stock_uom', + "fieldtype": "Link", + "options": "UOM", + "width": 100 + }) + for end_date in self.periodic_daterange: period = self.get_period(end_date) self.columns.append({ @@ -129,7 +139,7 @@ class Analytics(object): value_field = 'qty' self.entries = frappe.db.sql(""" - select i.item_code as entity, i.item_name as entity_name, i.{value_field} as value_field, s.{date_field} + select i.item_code as entity, i.item_name as entity_name, i.stock_uom, i.{value_field} as value_field, s.{date_field} from `tab{doctype} Item` i , `tab{doctype}` s where s.name = i.parent and i.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s @@ -198,6 +208,10 @@ class Analytics(object): total += amount row["total"] = total + + if self.filters.tree_type == "Item": + row["stock_uom"] = period_data.get("stock_uom") + self.data.append(row) def get_rows_by_group(self): @@ -232,6 +246,9 @@ class Analytics(object): self.entity_periodic_data.setdefault(d.entity, frappe._dict()).setdefault(period, 0.0) self.entity_periodic_data[d.entity][period] += flt(d.value_field) + if self.filters.tree_type == "Item": + self.entity_periodic_data[d.entity]['stock_uom'] = d.stock_uom + def get_period(self, posting_date): if self.filters.range == 'Weekly': period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year) From 6298da44586d1fd34c117812819240a3469b7165 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 5 Sep 2019 16:12:33 +0530 Subject: [PATCH 39/49] fix: treeview fixes (#18803) --- .../doctype/quality_procedure/quality_procedure.py | 14 +++++++++++++- .../quality_procedure/quality_procedure_tree.js | 4 +--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index 4d3c522956..b4de03e1e4 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -52,7 +52,19 @@ class QualityProcedure(NestedSet): def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): if parent is None or parent == "All Quality Procedures": parent = "" - return frappe.get_all(doctype, fields=["name as value", "is_group as expandable"], filters={"parent_quality_procedure": parent}) + + return frappe.db.sql(""" + select + name as value, + is_group as expandable + from + `tab{doctype}` + where + ifnull(parent_quality_procedure, "")={parent} + """.format( + doctype = doctype, + parent=frappe.db.escape(parent) + ), as_dict=1) @frappe.whitelist() def add_node(): diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index 8fd785f205..dbdbbab392 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -1,5 +1,3 @@ -frappe.provide("frappe.treeview_settings"); - frappe.treeview_settings["Quality Procedure"] = { ignore_fields:["parent_quality_procedure"], get_tree_nodes: 'erpnext.quality_management.doctype.quality_procedure.quality_procedure.get_children', @@ -19,7 +17,7 @@ frappe.treeview_settings["Quality Procedure"] = { ], breadcrumb: "Setup", root_label: "All Quality Procedures", - get_tree_root: true, + get_tree_root: false, menu_items: [ { label: __("New Quality Procedure"), From b5bd91417d4a394cbfbc7e76d44b270ca54bc62d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Sep 2019 16:43:20 +0530 Subject: [PATCH 40/49] fix: Print/PDF of AR/AP report after refactoring (#18931) --- .../accounts_receivable.html | 498 +++++++++--------- 1 file changed, 246 insertions(+), 252 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index d00bcf643e..791f3f8008 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -1,275 +1,269 @@ + .print-format { + padding: 4mm; + font-size: 8.0pt !important; + } + .print-format td { + vertical-align:middle !important; + } + -

{%= __(report.report_name) %}

-

- {% if (filters.customer_name) { %} - {%= filters.customer_name %} - {% } else { %} - {%= filters.customer || filters.supplier %} - {% } %} -

-
- {% if (filters.tax_id) { %} - {%= __("Tax Id: ")%} {%= filters.tax_id %} +

{%= __(report.report_name) %}

+

+ {% if (filters.customer_name) { %} + {%= filters.customer_name %} + {% } else { %} + {%= filters.customer || filters.supplier %} {% } %} -

-
- {%= __(filters.ageing_based_on) %} - {%= __("Until") %} - {%= frappe.datetime.str_to_user(filters.report_date) %} -
+ +
+ {% if (filters.tax_id) { %} + {%= __("Tax Id: ")%} {%= filters.tax_id %} + {% } %} +
+
+ {%= __(filters.ageing_based_on) %} + {%= __("Until") %} + {%= frappe.datetime.str_to_user(filters.report_date) %} +
-
-
- {% if(filters.payment_terms) { %} - {%= __("Payment Terms") %}: {%= filters.payment_terms %} - {% } %} +
+
+ {% if(filters.payment_terms) { %} + {%= __("Payment Terms") %}: {%= filters.payment_terms %} + {% } %} +
+
+ {% if(filters.credit_limit) { %} + {%= __("Credit Limit") %}: {%= format_currency(filters.credit_limit) %} + {% } %} +
-
- {% if(filters.credit_limit) { %} - {%= __("Credit Limit") %}: {%= format_currency(filters.credit_limit) %} + + {% if(filters.show_future_payments) { %} + {% var balance_row = data.slice(-1).pop(); + var range1 = report.columns[11].label; + var range2 = report.columns[12].label; + var range3 = report.columns[13].label; + var range4 = report.columns[14].label; + var range5 = report.columns[15].label; + %} + {% if(balance_row) { %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
(Amount in {%= data[0]["currency"] || "" %})
{%= __(" ") %}{%= __(range1) %}{%= __(range2) %}{%= __(range3) %}{%= __(range4) %}{%= __(range5) %}{%= __("Total") %}
{%= __("Total Outstanding") %}{%= format_number(balance_row["range1"], null, 2) %}{%= format_currency(balance_row["range2"]) %}{%= format_currency(balance_row["range3"]) %}{%= format_currency(balance_row["range4"]) %}{%= format_currency(balance_row["range5"]) %} + {%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %} +
{%= __("Future Payments") %} + {%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %} +
+ {% } %} {% } %} -
-
- -{% if(filters.show_future_payments) { %} - {% var balance_row = data.slice(-1).pop(); - var range1 = report.columns[11].label; - var range2 = report.columns[12].label; - var range3 = report.columns[13].label; - var range4 = report.columns[14].label; - var range5 = report.columns[15].label; - var range6 = report.columns[16].label; - %} - {% if(balance_row) { %} - - - - - - - - - - - - - - +
(Amount in {%= data[0][__("currency")] || "" %})
- - - - - - - - + {% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %} + + + + {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} + + + {% } else { %} + + {% } %} + {% if(!filters.show_future_payments) { %} + + {% } %} + + {% if(!filters.show_future_payments) { %} + + + {% } %} + + {% if(filters.show_future_payments) { %} + {% if(report.report_name === "Accounts Receivable") { %} + + {% } %} + + + + {% } %} + {% } else { %} + + + + + + {% } %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{%= __(" ") %}{%= __(range1) %}{%= __(range2) %}{%= __(range3) %}{%= __(range4) %}{%= __(range5) %}{%= __(range6) %}{%= __("Total") %}{%= __("Date") %}{%= __("Age (Days)") %}{%= __("Reference") %}{%= __("Sales Person") %}{%= __("Reference") %}{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}{%= __("Invoiced Amount") %}{%= __("Paid Amount") %}{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}{%= __("Outstanding Amount") %}{%= __("Customer LPO No.") %}{%= __("Future Payment Ref") %}{%= __("Future Payment Amount") %}{%= __("Remaining Balance") %}{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}{%= __("Total Invoiced Amount") %}{%= __("Total Paid Amount") %}{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}{%= __("Total Outstanding Amount") %}
{%= __("Total Outstanding") %}{%= format_number(balance_row[range1], null, 2) %}{%= format_currency(balance_row[range2]) %}{%= format_currency(balance_row[range3]) %}{%= format_currency(balance_row[range4]) %}{%= format_currency(balance_row[range5]) %}{%= format_currency(balance_row[range6]) %} - {%= format_currency(flt(balance_row[("outstanding_amount")]), data[data.length-1]["currency"]) %} -
{%= __("PDC/LC") %} - {%= format_currency(flt(balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %} -
- {% } %} -{% } %} - - - - {% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %} - - - - {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} - - - {% } else { %} - - {% } %} - {% if(!filters.show_future_payments) { %} - - {% } %} - - {% if(!filters.show_future_payments) { %} - - - {% } %} - - {% if(filters.show_future_payments) { %} - {% if(report.report_name === "Accounts Receivable") { %} - - {% } %} - - - - {% } %} - {% } else { %} - - - - - - {% } %} - - - - {% for(var i=0, l=data.length; i - {% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %} - {% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %} - - - - - {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} - - {% } %} - - {% if(!filters.show_future_payments) { %} - + + + + {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} + {% } %} -
- {% if data[i][__("Remarks")] %} - {%= __("Remarks") %}: - {%= data[i][__("Remarks")] %} - {% } %} -
- - {% } %} - - - {% if(!filters.show_future_payments) { %} - - - {% } %} - - - {% if(filters.show_future_payments) { %} - {% if(report.report_name === "Accounts Receivable") { %} - - {% } %} - - - - {% } %} - {% } else { %} - - {% if(!filters.show_future_payments) { %} - - {% } %} - {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} - - {% } %} - - - - - {% if(!filters.show_future_payments) { %} - - - {% } %} - - - {% if(filters.show_future_payments) { %} - {% if(report.report_name === "Accounts Receivable") { %} - - {% } %} - - - - {% } %} - {% } %} - {% } else { %} - {% if(data[i][__("Customer")] || data[i][__("Supplier")]|| " ") { %} - {% if((data[i][__("Customer")] || data[i][__("Supplier")]) != __("'Total'")) { %} + {% if(!filters.show_future_payments) { %} + {% } %} + + + + {% if(!filters.show_future_payments) { %} + + + {% } %} + + + {% if(filters.show_future_payments) { %} + {% if(report.report_name === "Accounts Receivable") { %} + + {% } %} + + + + {% } %} {% } else { %} - + + {% if(!filters.show_future_payments) { %} + + {% } %} + {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} + + {% } %} + + + + + {% if(!filters.show_future_payments) { %} + + + {% } %} + + + {% if(filters.show_future_payments) { %} + {% if(report.report_name === "Accounts Receivable") { %} + + {% } %} + + + + {% } %} + {% } %} + {% } else { %} + {% if(data[i]["party"]|| " ") { %} + {% if((data[i]["party"]) != __("'Total'")) { %} + + {% } else { %} + + {% } %} + + + + {% } %} - - - - {% } %} + {% } %} - - {% } %} - -
{%= __("Date") %}{%= __("Age (Days)") %}{%= __("Reference") %}{%= __("Sales Person") %}{%= __("Reference") %}{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}{%= __("Invoiced Amount") %}{%= __("Paid Amount") %}{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}{%= __("Outstanding Amount") %}{%= __("Customer LPO No.") %}{%= __("PDC/LC Ref") %}{%= __("PDC/LC Amount") %}{%= __("Remaining Balance") %}{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}{%= __("Total Invoiced Amount") %}{%= __("Total Paid Amount") %}{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}{%= __("Total Outstanding Amount") %}
{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}{%= data[i][__("Age (Days)")] %} - {% if(!filters.show_future_payments) { %} - {%= data[i]["voucher_type"] %} -
- {% } %} - {%= data[i]["voucher_no"] %} -
{%= data[i]["sales_person"] %} - {% if(!(filters.customer || filters.supplier)) { %} - {%= data[i][__("Customer")] || data[i][__("Supplier")] %} - {% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %} -
{%= data[i][__("Customer Name")] %} - {% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %} -
{%= data[i][__("Supplier Name")] %} + {% for(var i=0, l=data.length; i + {% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %} + {% if(data[i]["party"]) { %} +
{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}{%= data[i]["age"] %} + {% if(!filters.show_future_payments) { %} + {%= data[i]["voucher_type"] %} +
{% } %} + {%= data[i]["voucher_no"] %} +
{%= data[i]["sales_person"] %} - {%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %} - {%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %} - {%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} - {%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %} - {%= data[i]["po_no"] %}{%= data[i][("pdc/lc_ref")] %}{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}{%= __("Total") %} - {%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %} - {%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} - {%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %} - {%= data[i][__("Customer LPO")] %}{%= data[i][("pdc/lc_ref")] %}{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %} {% if(!(filters.customer || filters.supplier)) { %} - {%= data[i][__("Customer")] || data[i][__("Supplier")] %} - {% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %} -
{%= data[i][__("Customer Name")] %} - {% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %} -
{%= data[i][__("Supplier Name")] %} + {%= data[i]["party"] %} + {% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %} +
{%= data[i]["customer_name"] %} + {% } else if(data[i]["supplier_name"] != data[i]["party"]) { %} +
{%= data[i]["supplier_name"] %} {% } %} {% } %} -
{%= __("Remarks") %}: - {%= data[i][__("Remarks")] %} +
+ {% if data[i]["remarks"] %} + {%= __("Remarks") %}: + {%= data[i]["remarks"] %} + {% } %} +
+ {%= format_currency(data[i]["invoiced"], data[i]["currency"]) %} + {%= format_currency(data[i]["paid"], data[i]["currency"]) %} + {%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} + {%= format_currency(data[i]["outstanding"], data[i]["currency"]) %} + {%= data[i]["po_no"] %}{%= data[i]["future_ref"] %}{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}{%= __("Total") %}{%= __("Total") %} + {%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %} + {%= format_currency(data[i]["paid"], data[i]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} + {%= format_currency(data[i]["outstanding"], data[i]["currency"]) %} + {%= data[i]["po_no"] %}{%= data[i]["future_ref"] %}{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %} + {% if(!(filters.customer || filters.supplier)) { %} + {%= data[i]["party"] %} + {% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %} +
{%= data[i]["customer_name"] %} + {% } else if(data[i]["supplier_name"] != data[i]["party"]) { %} +
{%= data[i]["supplier_name"] %} + {% } %} + {% } %} +
{%= __("Remarks") %}: + {%= data[i]["remarks"] %} +
{%= __("Total") %}{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}{%= format_currency(data[i]["paid"], data[i]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}{%= format_currency(data[i][("total_invoiced_amt")], data[i]["currency"]) %}{%= format_currency(data[i][("total_paid_amt")], data[i]["currency"]) %}{%= report.report_name === "Accounts Receivable Summary" ? format_currency(data[i][__("credit_note_amt")], data[i]["currency"]) : format_currency(data[i][__("debit_note_amt")], data[i]["currency"]) %}{%= format_currency(data[i][("total_outstanding_amt")], data[i]["currency"]) %}
-

{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}

+ + +

{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}

From ac2b5ed84f4cfcaf7f7fef059ae03d53dcd608e5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Sep 2019 16:57:26 +0530 Subject: [PATCH 41/49] fix: credit limit patch --- .../move_credit_limit_to_customer_credit_limit.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 30acfe68ea..3bcdb8fc7f 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -21,10 +21,10 @@ def move_credit_limit_to_child_table(): if frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'): fields = ", bypass_credit_limit_check_at_sales_order" - credit_limit_record = frappe.db.sql(''' SELECT - name, credit_limit - {0} - FROM `tabCustomer`'''.format(fields), as_dict=1) #nosec + credit_limit_record = frappe.db.sql(''' + SELECT name, credit_limit {0} + FROM `tabCustomer` where credit_limit > 0 + '''.format(fields), as_dict=1) #nosec companies = frappe.get_all("Company", 'name') @@ -36,4 +36,5 @@ def move_credit_limit_to_child_table(): 'bypass_credit_limit_check': record.bypass_credit_limit_check_at_sales_order, 'company': company.name }) - customer.db_insert() + for row in customer.credit_limit_reference: + row.db_insert() From 0906bc8430ef28d159adb96872bb6fe3ff47b8f3 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 5 Sep 2019 17:50:44 +0550 Subject: [PATCH 42/49] bumped to version 12.1.1 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ee37c8d780..106384ade0 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.1.0' +__version__ = '12.1.1' def get_default_company(user=None): '''Get default company for user''' From ac0c1edd23946073c4f1ca53f8196fba45d125b9 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 6 Sep 2019 12:10:10 +0530 Subject: [PATCH 43/49] feat: added date filter based on billing date and based date (#18936) --- erpnext/controllers/trends.py | 4 +- erpnext/public/js/purchase_trends_filters.js | 39 +++++++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py index 28a8fddfac..0404a1e6b0 100644 --- a/erpnext/controllers/trends.py +++ b/erpnext/controllers/trends.py @@ -39,7 +39,6 @@ def validate_filters(filters): frappe.throw(_("'Based On' and 'Group By' can not be same")) def get_data(filters, conditions): - data = [] inc, cond= '','' query_details = conditions["based_on_select"] + conditions["period_wise_select"] @@ -47,10 +46,11 @@ def get_data(filters, conditions): posting_date = 't1.transaction_date' if conditions.get('trans') in ['Sales Invoice', 'Purchase Invoice', 'Purchase Receipt', 'Delivery Note']: posting_date = 't1.posting_date' + if filters.period_based_on: + posting_date = 't1.'+filters.period_based_on if conditions["based_on_select"] in ["t1.project,", "t2.project,"]: cond = ' and '+ conditions["based_on_select"][:-1] +' IS Not NULL' - if conditions.get('trans') in ['Sales Order', 'Purchase Order']: cond += " and t1.status != 'Closed'" diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js index 595a138f12..cd767f5d16 100644 --- a/erpnext/public/js/purchase_trends_filters.js +++ b/erpnext/public/js/purchase_trends_filters.js @@ -3,6 +3,14 @@ erpnext.get_purchase_trends_filters = function() { return [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, { "fieldname":"period", "label": __("Period"), @@ -15,6 +23,23 @@ erpnext.get_purchase_trends_filters = function() { ], "default": "Monthly" }, + { + "fieldname":"fiscal_year", + "label": __("Fiscal Year"), + "fieldtype": "Link", + "options":'Fiscal Year', + "default": frappe.sys_defaults.fiscal_year + }, + { + "fieldname":"period_based_on", + "label": __("Period based On"), + "fieldtype": "Select", + "options": [ + { "value": "posting_date", "label": __("Posting Date") }, + { "value": "bill_date", "label": __("Billing Date") }, + ], + "default": "posting_date" + }, { "fieldname":"based_on", "label": __("Based On"), @@ -39,19 +64,5 @@ erpnext.get_purchase_trends_filters = function() { ], "default": "" }, - { - "fieldname":"fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options":'Fiscal Year', - "default": frappe.sys_defaults.fiscal_year - }, - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") - }, ]; } From 4ce38059aca818da540a6dd3a02940678f169e83 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 6 Sep 2019 14:33:10 +0530 Subject: [PATCH 44/49] feat: Company wise credit limit --- .../doctype/sales_invoice/sales_invoice.py | 2 +- ...e_credit_limit_to_customer_credit_limit.py | 44 +- .../selling/doctype/customer/customer.json | 6 +- erpnext/selling/doctype/customer/customer.py | 10 +- .../selling/doctype/customer/test_customer.py | 29 +- .../doctype/sales_order/sales_order.py | 4 +- ...es_order_with_bypass_credit_limit_check.js | 13 +- ...order_without_bypass_credit_limit_check.js | 18 +- .../customer_group/customer_group.json | 447 ++---------------- .../doctype/delivery_note/delivery_note.py | 2 +- 10 files changed, 122 insertions(+), 453 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4f8d3bdd9a..abbac77783 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -305,7 +305,7 @@ class SalesInvoice(SellingController): validate_against_credit_limit = False bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer Credit Limit", - filters={'parent': self.customer, 'company': self.company}, + filters={'parent': self.customer, 'parenttype': 'Customer', 'company': self.company}, fieldname=["bypass_credit_limit_check"]) if bypass_credit_limit_check_at_sales_order: diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py index 3bcdb8fc7f..c9293b9b63 100644 --- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py +++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py @@ -8,6 +8,7 @@ def execute(): ''' Move credit limit and bypass credit limit to the child table of customer credit limit ''' frappe.reload_doc("Selling", "doctype", "Customer Credit Limit") frappe.reload_doc("Selling", "doctype", "Customer") + frappe.reload_doc("Setup", "doctype", "Customer Group") if frappe.db.a_row_exists("Customer Credit Limit"): return @@ -17,24 +18,29 @@ def execute(): def move_credit_limit_to_child_table(): ''' maps data from old field to the new field in the child table ''' - fields="" - if frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'): - fields = ", bypass_credit_limit_check_at_sales_order" - - credit_limit_record = frappe.db.sql(''' - SELECT name, credit_limit {0} - FROM `tabCustomer` where credit_limit > 0 - '''.format(fields), as_dict=1) #nosec - companies = frappe.get_all("Company", 'name') + for doctype in ("Customer", "Customer Group"): + fields = "" + if doctype == "Customer" \ + and frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'): + fields = ", bypass_credit_limit_check_at_sales_order" - for record in credit_limit_record: - customer = frappe.get_doc("Customer", record.name) - for company in companies: - customer.append("credit_limit_reference", { - 'credit_limit': record.credit_limit, - 'bypass_credit_limit_check': record.bypass_credit_limit_check_at_sales_order, - 'company': company.name - }) - for row in customer.credit_limit_reference: - row.db_insert() + credit_limit_records = frappe.db.sql(''' + SELECT name, credit_limit {0} + FROM `tab{1}` where credit_limit > 0 + '''.format(fields, doctype), as_dict=1) #nosec + + for record in credit_limit_records: + doc = frappe.get_doc(doctype, record.name) + for company in companies: + row = frappe._dict({ + 'credit_limit': record.credit_limit, + 'company': company.name + }) + if doctype == "Customer": + row.bypass_credit_limit_check = record.bypass_credit_limit_check_at_sales_order + + doc.append("credit_limits", row) + + for row in doc.credit_limits: + row.db_insert() diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index b83e284697..c2c8c1971a 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -50,7 +50,7 @@ "accounts", "credit_limit_section", "payment_terms", - "credit_limit_reference", + "credit_limits", "more_info", "customer_details", "column_break_45", @@ -460,7 +460,7 @@ }, { "default": "0", - "fieldname": "credit_limit_reference", + "fieldname": "credit_limits", "fieldtype": "Table", "label": "Credit Limit", "options": "Customer Credit Limit" @@ -469,7 +469,7 @@ "icon": "fa fa-user", "idx": 363, "image_field": "image", - "modified": "2019-08-30 18:03:13.332934", + "modified": "2019-09-06 12:40:31.801424", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 972a6102ae..4ca5af51a4 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -167,11 +167,11 @@ class Customer(TransactionBase): frappe.throw(_("A Customer Group exists with same name please change the Customer name or rename the Customer Group"), frappe.NameError) def validate_credit_limit_on_change(self): - if self.get("__islocal") or not self.credit_limit_reference: + if self.get("__islocal") or not self.credit_limits: return company_record = [] - for limit in self.credit_limit_reference: + for limit in self.credit_limits: if limit.company in company_record: frappe.throw(_("Credit limit is already defined for the Company {0}").format(limit.company, self.name)) else: @@ -327,11 +327,13 @@ def get_credit_limit(customer, company): credit_limit = None if customer: - credit_limit = frappe.db.get_value("Customer Credit Limit", {'parent': customer, 'company': company}, 'credit_limit') + credit_limit = frappe.db.get_value("Customer Credit Limit", + {'parent': customer, 'parenttype': 'Customer', 'company': company}, 'credit_limit') if not credit_limit: customer_group = frappe.get_cached_value("Customer", customer, 'customer_group') - credit_limit = frappe.get_cached_value("Customer Group", customer_group, "credit_limit") + credit_limit = frappe.db.get_value("Customer Credit Limit", + {'parent': customer_group, 'parenttype': 'Customer Group', 'company': company}, 'credit_limit') if not credit_limit: credit_limit = frappe.get_cached_value('Company', company, "credit_limit") diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 35d1b2cf1c..87fdaa366f 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -25,7 +25,7 @@ class TestCustomer(unittest.TestCase): make_test_records('Item') def tearDown(self): - frappe.db.set_value("Customer Credit Limit", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', 0.0) + set_credit_limit('_Test Customer', '_Test Company', 0) def test_party_details(self): from erpnext.accounts.party import get_party_details @@ -225,8 +225,8 @@ class TestCustomer(unittest.TestCase): item_qty = int((abs(outstanding_amt) + 200)/100) make_sales_order(qty=item_qty) - if credit_limit == 0.0: - frappe.db.set_value("Customer Credit Limit", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', outstanding_amt - 50.0) + if not credit_limit: + set_credit_limit('_Test Customer', '_Test Company', outstanding_amt - 50) # Sales Order so = make_sales_order(do_not_submit=True) @@ -241,7 +241,7 @@ class TestCustomer(unittest.TestCase): self.assertRaises(frappe.ValidationError, si.submit) if credit_limit > outstanding_amt: - frappe.db.set_value("Customer", {'parent': '_Test Customer', 'company': '_Test Company'}, 'credit_limit', credit_limit) + set_credit_limit('_Test Customer', '_Test Company', credit_limit) # Makes Sales invoice from Sales Order so.save(ignore_permissions=True) @@ -252,10 +252,10 @@ class TestCustomer(unittest.TestCase): def test_customer_credit_limit_on_change(self): outstanding_amt = self.get_customer_outstanding_amount() customer = frappe.get_doc("Customer", '_Test Customer') - customer.append('credit_limit_reference', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'}) + customer.append('credit_limits', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'}) ''' define new credit limit for same company ''' - customer.append('credit_limit_reference', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'}) + customer.append('credit_limits', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'}) self.assertRaises(frappe.ValidationError, customer.save) def test_customer_payment_terms(self): @@ -295,3 +295,20 @@ def get_customer_dict(customer_name): "doctype": "Customer", "territory": "_Test Territory" } + +def set_credit_limit(customer, company, credit_limit): + customer = frappe.get_doc("Customer", customer) + existing_row = None + for d in customer.credit_limits: + if d.company == company: + existing_row = d + d.credit_limit = credit_limit + d.db_update() + break + + if not existing_row: + customer.append('credit_limits', { + 'company': company, + 'credit_limit': credit_limit + }) + customer.credit_limits[-1].db_insert() diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 7d2eaf599e..12b9a8e96d 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -208,7 +208,9 @@ class SalesOrder(SellingController): def check_credit_limit(self): # if bypass credit limit check is set to true (1) at sales order level, # then we need not to check credit limit and vise versa - if not cint(frappe.db.get_value("Customer Credit Limit", {'parent': self.customer, 'company': self.company}, "bypass_credit_limit_check")): + if not cint(frappe.db.get_value("Customer Credit Limit", + {'parent': self.customer, 'parenttype': 'Customer', 'company': self.company}, + "bypass_credit_limit_check")): check_credit_limit(self.customer, self.company) def check_nextdoc_docstatus(self): diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js index 1f73f91f61..79d798b944 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js @@ -1,7 +1,7 @@ QUnit.module('Sales Order'); QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) { -//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com +//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com assert.expect(2); let done = assert.async(); frappe.run_serially([ @@ -10,7 +10,8 @@ QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) { () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), () => frappe.timeout(1), () => cur_frm.set_value("customer_name", "Test Customer 10"), - () => cur_frm.add_child('credit_limit_reference', { + () => cur_frm.add_child('credit_limits', { + 'company': cur_frm.doc.company || '_Test Company' 'credit_limit': 1000, 'bypass_credit_limit_check': 1}), // save form @@ -23,10 +24,10 @@ QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) { () => frappe.timeout(1), () => cur_frm.set_value("item_code", "Test Product 10"), () => cur_frm.set_value("item_group", "Products"), - () => cur_frm.set_value("standard_rate", 100), + () => cur_frm.set_value("standard_rate", 100), // save form () => cur_frm.save(), - () => frappe.timeout(1), + () => frappe.timeout(1), () => { return frappe.tests.make('Sales Order', [ @@ -47,11 +48,11 @@ QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) { () => frappe.tests.click_button('Yes'), () => frappe.timeout(3), () => { - + assert.ok(cur_frm.doc.status=="To Deliver and Bill", "It is submited. Credit limit is NOT checked for sales order"); - }, + }, () => done() ]); }); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js index 34560b6900..8de39f9aa3 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js @@ -1,7 +1,7 @@ QUnit.module('Sales Order'); QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert) { -//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com +//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com assert.expect(2); let done = assert.async(); frappe.run_serially([ @@ -10,7 +10,7 @@ QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), () => frappe.timeout(1), () => cur_frm.set_value("customer_name", "Test Customer 11"), - () => cur_frm.add_child('credit_limit_reference', { + () => cur_frm.add_child('credit_limits', { 'credit_limit': 1000, 'company': '_Test Company', 'bypass_credit_limit_check': 1}), @@ -23,10 +23,10 @@ QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert () => frappe.click_link('Edit in full page'), () => cur_frm.set_value("item_code", "Test Product 11"), () => cur_frm.set_value("item_group", "Products"), - () => cur_frm.set_value("standard_rate", 100), + () => cur_frm.set_value("standard_rate", 100), // save form () => cur_frm.save(), - () => frappe.timeout(1), + () => frappe.timeout(1), () => { return frappe.tests.make('Sales Order', [ @@ -47,14 +47,14 @@ QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert () => frappe.tests.click_button('Yes'), () => frappe.timeout(3), () => { - - if (cur_dialog.body.innerText.match(/^Credit limit has been crossed for customer.*$/)) - { + + if (cur_dialog.body.innerText.match(/^Credit limit has been crossed for customer.*$/)) + { /*Match found */ assert.ok(true, "Credit Limit crossed message received"); } - - + + }, () => cur_dialog.cancel(), () => done() diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index c130e79541..3565b4b38a 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -1,553 +1,194 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "_comments": "[]", "allow_import": 1, "allow_rename": 1, "autoname": "field:customer_group_name", - "beta": 0, "creation": "2013-01-10 16:34:23", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "field_order": [ + "customer_group_name", + "parent_customer_group", + "is_group", + "cb0", + "default_price_list", + "payment_terms", + "lft", + "rgt", + "old_parent", + "default_receivable_account", + "accounts", + "credit_limit_section", + "credit_limits" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "customer_group_name", "fieldtype": "Data", - "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": "Customer Group Name", - "length": 0, "no_copy": 1, "oldfieldname": "customer_group_name", "oldfieldtype": "Data", - "permlevel": 0, - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "parent_customer_group", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent Customer Group", - "length": 0, - "no_copy": 0, "oldfieldname": "parent_customer_group", "oldfieldtype": "Link", - "options": "Customer Group", - "permlevel": 0, - "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 + "options": "Customer Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "Only leaf nodes are allowed in transaction", "fieldname": "is_group", "fieldtype": "Check", - "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": "Is Group", - "length": 0, - "no_copy": 0, "oldfieldname": "is_group", - "oldfieldtype": "Select", - "options": "", - "permlevel": 0, - "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 + "oldfieldtype": "Select" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "cb0", - "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, - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_price_list", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "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 + "options": "Price List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "fieldname": "payment_terms", "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": "Default Payment Terms Template", - "length": 0, - "no_copy": 0, - "options": "Payment Terms Template", - "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 + "options": "Payment Terms Template" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "credit_limit", - "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": "Credit Limit", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "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_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "lft", "fieldtype": "Int", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "lft", - "length": 0, "no_copy": 1, "oldfieldname": "lft", "oldfieldtype": "Int", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "rgt", "fieldtype": "Int", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "rgt", - "length": 0, "no_copy": 1, "oldfieldname": "rgt", "oldfieldtype": "Int", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "old_parent", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "old_parent", - "length": 0, "no_copy": 1, "oldfieldname": "old_parent", "oldfieldtype": "Data", "options": "Customer Group", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_receivable_account", "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": "Default Receivable Account", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "label": "Default Receivable Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:!doc.__islocal", "description": "Mention if non-standard receivable account applicable", "fieldname": "accounts", "fieldtype": "Table", - "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": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "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 + "options": "Party Account" + }, + { + "fieldname": "credit_limit_section", + "fieldtype": "Section Break", + "label": "Credit Limits" + }, + { + "fieldname": "credit_limits", + "fieldtype": "Table", + "label": "Credit Limit", + "options": "Customer Credit Limit" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-sitemap", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:26:05.935871", + "modified": "2019-09-06 12:40:14.954697", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales Master Manager", "set_user_permissions": 1, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, "role": "Sales Master Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales Manager" } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "search_fields": "parent_customer_group", "show_name_in_global_search": 1, - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index e29cd1b792..bdb49e4919 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -235,7 +235,7 @@ class DeliveryNote(SellingController): extra_amount = 0 validate_against_credit_limit = False bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer Credit Limit", - filters={'parent': self.customer, 'company': self.company}, + filters={'parent': self.customer, 'parenttype': 'Customer', 'company': self.company}, fieldname="bypass_credit_limit_check")) if bypass_credit_limit_check_at_sales_order: From ddfbb07c3bbd5078c3e3459cfb7f2dfb7e093632 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Fri, 6 Sep 2019 15:38:06 +0530 Subject: [PATCH 45/49] fix: fix accounts balance timeline dashboard chart source (#18942) * fix: fix accounts balance timeline dashboard chart source * fix: pass chart_name to get function instead of chart object --- .../account_balance_timeline/account_balance_timeline.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py index 648cc68dac..bc07b6d807 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py @@ -12,11 +12,14 @@ from frappe.utils.nestedset import get_descendants_of @frappe.whitelist() @cache_source -def get(chart_name=None, from_date = None, to_date = None): - chart = frappe.get_doc('Dashboard Chart', chart_name) +def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_date = None): + if chart_name: + chart = frappe.get_doc('Dashboard Chart', chart_name) + else: + chart = frappe._dict(frappe.parse_json(chart)) timespan = chart.timespan timegrain = chart.time_interval - filters = json.loads(chart.filters_json) + filters = frappe.parse_json(chart.filters_json) account = filters.get("account") company = filters.get("company") From c69cc13c8564682f3e4c011ea3e122622bc2b603 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 6 Sep 2019 15:40:15 +0530 Subject: [PATCH 46/49] fix: updated footer message (#18949) --- erpnext/templates/includes/footer/footer_powered.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index 8d5523ff97..d4deaaef68 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -1,3 +1,3 @@ {% set domains = frappe.get_doc("Domain Settings").active_domains %} -Powered by ERPNext - {{ domains[0].domain if domains else 'Open Source' }} ERP Software +Powered by ERPNext - ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }} From 4726fc4893073b0ae0cc0ca42700aaa4e8b1d2dd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 6 Sep 2019 15:45:07 +0530 Subject: [PATCH 47/49] fix: Deleted unwanted production order files --- .../production_order/production_order.py | 648 ------------------ 1 file changed, 648 deletions(-) delete mode 100644 erpnext/manufacturing/doctype/production_order/production_order.py diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py deleted file mode 100644 index 67930629f5..0000000000 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ /dev/null @@ -1,648 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -import json -from six import text_type -from frappe import _ -from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate -from frappe.model.document import Document -from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict -from dateutil.relativedelta import relativedelta -from erpnext.stock.doctype.item.item import validate_end_of_life -from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError -from erpnext.projects.doctype.timesheet.timesheet import OverlapError -from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs -from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations -from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty -from frappe.utils.csvutils import getlink -from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty -from erpnext.utilities.transaction_base import validate_uom_is_integer - -class OverProductionError(frappe.ValidationError): pass -class StockOverProductionError(frappe.ValidationError): pass -class OperationTooLongError(frappe.ValidationError): pass -class ItemHasVariantError(frappe.ValidationError): pass - -form_grid_templates = { - "operations": "templates/form_grid/production_order_grid.html" -} - -class ProductionOrder(Document): - def validate(self): - self.validate_production_item() - if self.bom_no: - validate_bom_no(self.production_item, self.bom_no) - - self.validate_sales_order() - self.set_default_warehouse() - self.validate_warehouse_belongs_to_company() - self.calculate_operating_cost() - self.validate_qty() - self.validate_operation_time() - self.status = self.get_status() - - validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"]) - - self.set_required_items(reset_only_qty = len(self.get("required_items"))) - - def validate_sales_order(self): - if self.sales_order: - so = frappe.db.sql(""" - select so.name, so_item.delivery_date, so.project - from `tabSales Order` so - inner join `tabSales Order Item` so_item on so_item.parent = so.name - left join `tabProduct Bundle Item` pk_item on so_item.item_code = pk_item.parent - where so.name=%s and so.docstatus = 1 and ( - so_item.item_code=%s or - pk_item.item_code=%s ) - """, (self.sales_order, self.production_item, self.production_item), as_dict=1) - - if not so: - so = frappe.db.sql(""" - select - so.name, so_item.delivery_date, so.project - from - `tabSales Order` so, `tabSales Order Item` so_item, `tabPacked Item` packed_item - where so.name=%s - and so.name=so_item.parent - and so.name=packed_item.parent - and so_item.item_code = packed_item.parent_item - and so.docstatus = 1 and packed_item.item_code=%s - """, (self.sales_order, self.production_item), as_dict=1) - - if len(so): - if not self.expected_delivery_date: - self.expected_delivery_date = so[0].delivery_date - - if so[0].project: - self.project = so[0].project - - if not self.material_request: - self.validate_production_order_against_so() - else: - frappe.throw(_("Sales Order {0} is not valid").format(self.sales_order)) - - def set_default_warehouse(self): - if not self.wip_warehouse: - self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") - if not self.fg_warehouse: - self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse") - - def validate_warehouse_belongs_to_company(self): - warehouses = [self.fg_warehouse, self.wip_warehouse] - for d in self.get("required_items"): - if d.source_warehouse not in warehouses: - warehouses.append(d.source_warehouse) - - for wh in warehouses: - validate_warehouse_company(wh, self.company) - - def calculate_operating_cost(self): - self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0 - for d in self.get("operations"): - d.planned_operating_cost = flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0) - d.actual_operating_cost = flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0) - - self.planned_operating_cost += flt(d.planned_operating_cost) - self.actual_operating_cost += flt(d.actual_operating_cost) - - variable_cost = self.actual_operating_cost if self.actual_operating_cost \ - else self.planned_operating_cost - self.total_operating_cost = flt(self.additional_operating_cost) + flt(variable_cost) - - def validate_production_order_against_so(self): - # already ordered qty - ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order` - where production_item = %s and sales_order = %s and docstatus < 2 and name != %s""", - (self.production_item, self.sales_order, self.name))[0][0] - - total_qty = flt(ordered_qty_against_so) + flt(self.qty) - - # get qty from Sales Order Item table - so_item_qty = frappe.db.sql("""select sum(stock_qty) from `tabSales Order Item` - where parent = %s and item_code = %s""", - (self.sales_order, self.production_item))[0][0] - # get qty from Packing Item table - dnpi_qty = frappe.db.sql("""select sum(qty) from `tabPacked Item` - where parent = %s and parenttype = 'Sales Order' and item_code = %s""", - (self.sales_order, self.production_item))[0][0] - # total qty in SO - so_qty = flt(so_item_qty) + flt(dnpi_qty) - - allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", - "over_production_allowance_percentage")) - - if total_qty > so_qty + (allowance_percentage/100 * so_qty): - frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}") - .format(self.production_item, so_qty), OverProductionError) - - def update_status(self, status=None): - '''Update status of production order if unknown''' - if status != "Stopped": - status = self.get_status(status) - - if status != self.status: - self.db_set("status", status) - - self.update_required_items() - - return status - - def get_status(self, status=None): - '''Return the status based on stock entries against this production order''' - if not status: - status = self.status - - if self.docstatus==0: - status = 'Draft' - elif self.docstatus==1: - if status != 'Stopped': - stock_entries = frappe._dict(frappe.db.sql("""select purpose, sum(fg_completed_qty) - from `tabStock Entry` where production_order=%s and docstatus=1 - group by purpose""", self.name)) - - status = "Not Started" - if stock_entries: - status = "In Process" - produced_qty = stock_entries.get("Manufacture") - if flt(produced_qty) == flt(self.qty): - status = "Completed" - else: - status = 'Cancelled' - - return status - - def update_production_order_qty(self): - """Update **Manufactured Qty** and **Material Transferred for Qty** in Production Order - based on Stock Entry""" - - for purpose, fieldname in (("Manufacture", "produced_qty"), - ("Material Transfer for Manufacture", "material_transferred_for_manufacturing")): - qty = flt(frappe.db.sql("""select sum(fg_completed_qty) - from `tabStock Entry` where production_order=%s and docstatus=1 - and purpose=%s""", (self.name, purpose))[0][0]) - - if qty > self.qty: - frappe.throw(_("{0} ({1}) cannot be greater than planned quanitity ({2}) in Production Order {3}").format(\ - self.meta.get_label(fieldname), qty, self.qty, self.name), StockOverProductionError) - - self.db_set(fieldname, qty) - - def before_submit(self): - self.make_time_logs() - - def on_submit(self): - if not self.wip_warehouse: - frappe.throw(_("Work-in-Progress Warehouse is required before Submit")) - if not self.fg_warehouse: - frappe.throw(_("For Warehouse is required before Submit")) - - self.update_reserved_qty_for_production() - self.update_completed_qty_in_material_request() - self.update_planned_qty() - - def on_cancel(self): - self.validate_cancel() - - frappe.db.set(self,'status', 'Cancelled') - self.delete_timesheet() - self.update_completed_qty_in_material_request() - self.update_planned_qty() - self.update_reserved_qty_for_production() - - def validate_cancel(self): - if self.status == "Stopped": - frappe.throw(_("Stopped Production Order cannot be cancelled, Unstop it first to cancel")) - - # Check whether any stock entry exists against this Production Order - stock_entry = frappe.db.sql("""select name from `tabStock Entry` - where production_order = %s and docstatus = 1""", self.name) - if stock_entry: - frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0])) - - def update_planned_qty(self): - update_bin_qty(self.production_item, self.fg_warehouse, { - "planned_qty": get_planned_qty(self.production_item, self.fg_warehouse) - }) - - if self.material_request: - mr_obj = frappe.get_doc("Material Request", self.material_request) - mr_obj.update_requested_qty([self.material_request_item]) - - def update_completed_qty_in_material_request(self): - if self.material_request: - frappe.get_doc("Material Request", self.material_request).update_completed_qty([self.material_request_item]) - - def set_production_order_operations(self): - """Fetch operations from BOM and set in 'Production Order'""" - self.set('operations', []) - - if not self.bom_no \ - or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")): - return - - if self.use_multi_level_bom: - bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree() - else: - bom_list = [self.bom_no] - - operations = frappe.db.sql(""" - select - operation, description, workstation, idx, - base_hour_rate as hour_rate, time_in_mins, - "Pending" as status, parent as bom - from - `tabBOM Operation` - where - parent in (%s) order by idx - """ % ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1) - - self.set('operations', operations) - self.calculate_time() - - def calculate_time(self): - bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity") - - for d in self.get("operations"): - d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * flt(self.qty) - - self.calculate_operating_cost() - - def get_holidays(self, workstation): - holiday_list = frappe.db.get_value("Workstation", workstation, "holiday_list") - - holidays = {} - - if holiday_list not in holidays: - holiday_list_days = [getdate(d[0]) for d in frappe.get_all("Holiday", fields=["holiday_date"], - filters={"parent": holiday_list}, order_by="holiday_date", limit_page_length=0, as_list=1)] - - holidays[holiday_list] = holiday_list_days - - return holidays[holiday_list] - - def make_time_logs(self, open_new=False): - """Capacity Planning. Plan time logs based on earliest availablity of workstation after - Planned Start Date. Time logs will be created and remain in Draft mode and must be submitted - before manufacturing entry can be made.""" - - if not self.operations: - return - - timesheets = [] - plan_days = frappe.db.get_single_value("Manufacturing Settings", "capacity_planning_for_days") or 30 - - timesheet = make_timesheet(self.name, self.company) - timesheet.set('time_logs', []) - - for i, d in enumerate(self.operations): - - if d.status != 'Completed': - self.set_start_end_time_for_workstation(d, i) - - args = self.get_operations_data(d) - - add_timesheet_detail(timesheet, args) - original_start_time = d.planned_start_time - - # validate operating hours if workstation [not mandatory] is specified - try: - timesheet.validate_time_logs() - except OverlapError: - if frappe.message_log: frappe.message_log.pop() - timesheet.schedule_for_production_order(d.idx) - except WorkstationHolidayError: - if frappe.message_log: frappe.message_log.pop() - timesheet.schedule_for_production_order(d.idx) - - from_time, to_time = self.get_start_end_time(timesheet, d.name) - - if date_diff(from_time, original_start_time) > cint(plan_days): - frappe.throw(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation)) - break - - d.planned_start_time = from_time - d.planned_end_time = to_time - d.db_update() - - if timesheet and open_new: - return timesheet - - if timesheet and timesheet.get("time_logs"): - timesheet.save() - timesheets.append(getlink("Timesheet", timesheet.name)) - - self.planned_end_date = self.operations[-1].planned_end_time - if timesheets: - frappe.local.message_log = [] - frappe.msgprint(_("Timesheet created:") + "\n" + "\n".join(timesheets)) - - def get_operations_data(self, data): - return { - 'from_time': get_datetime(data.planned_start_time), - 'hours': data.time_in_mins / 60.0, - 'to_time': get_datetime(data.planned_end_time), - 'project': self.project, - 'operation': data.operation, - 'operation_id': data.name, - 'workstation': data.workstation, - 'completed_qty': flt(self.qty) - flt(data.completed_qty) - } - - def set_start_end_time_for_workstation(self, data, index): - """Set start and end time for given operation. If first operation, set start as - `planned_start_date`, else add time diff to end time of earlier operation.""" - - if index == 0: - data.planned_start_time = self.planned_start_date - else: - data.planned_start_time = get_datetime(self.operations[index-1].planned_end_time)\ - + get_mins_between_operations() - - data.planned_end_time = get_datetime(data.planned_start_time) + relativedelta(minutes = data.time_in_mins) - - if data.planned_start_time == data.planned_end_time: - frappe.throw(_("Capacity Planning Error")) - - def get_start_end_time(self, timesheet, operation_id): - for data in timesheet.time_logs: - if data.operation_id == operation_id: - return data.from_time, data.to_time - - def check_operation_fits_in_working_hours(self, d): - """Raises expection if operation is longer than working hours in the given workstation.""" - from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours - check_if_within_operating_hours(d.workstation, d.operation, d.planned_start_time, d.planned_end_time) - - def update_operation_status(self): - for d in self.get("operations"): - if not d.completed_qty: - d.status = "Pending" - elif flt(d.completed_qty) < flt(self.qty): - d.status = "Work in Progress" - elif flt(d.completed_qty) == flt(self.qty): - d.status = "Completed" - else: - frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'")) - - def set_actual_dates(self): - self.actual_start_date = None - self.actual_end_date = None - if self.get("operations"): - actual_start_dates = [d.actual_start_time for d in self.get("operations") if d.actual_start_time] - if actual_start_dates: - self.actual_start_date = min(actual_start_dates) - - actual_end_dates = [d.actual_end_time for d in self.get("operations") if d.actual_end_time] - if actual_end_dates: - self.actual_end_date = max(actual_end_dates) - - def delete_timesheet(self): - for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}): - frappe.delete_doc("Timesheet", timesheet.name) - - def validate_production_item(self): - if frappe.db.get_value("Item", self.production_item, "has_variants"): - frappe.throw(_("Production Order cannot be raised against a Item Template"), ItemHasVariantError) - - if self.production_item: - validate_end_of_life(self.production_item) - - def validate_qty(self): - if not self.qty > 0: - frappe.throw(_("Quantity to Manufacture must be greater than 0.")) - - def validate_operation_time(self): - for d in self.operations: - if not d.time_in_mins > 0: - frappe.throw(_("Operation Time must be greater than 0 for Operation {0}".format(d.operation))) - - def update_required_items(self): - ''' - update bin reserved_qty_for_production - called from Stock Entry for production, after submit, cancel - ''' - if self.docstatus==1: - # calculate transferred qty based on submitted stock entries - self.update_transaferred_qty_for_required_items() - - # update in bin - self.update_reserved_qty_for_production() - - def update_reserved_qty_for_production(self, items=None): - '''update reserved_qty_for_production in bins''' - for d in self.required_items: - if d.source_warehouse: - stock_bin = get_bin(d.item_code, d.source_warehouse) - stock_bin.update_reserved_qty_for_production() - - def get_items_and_operations_from_bom(self): - self.set_required_items() - self.set_production_order_operations() - - return check_if_scrap_warehouse_mandatory(self.bom_no) - - def set_available_qty(self): - for d in self.get("required_items"): - if d.source_warehouse: - d.available_qty_at_source_warehouse = get_latest_stock_qty(d.item_code, d.source_warehouse) - - if self.wip_warehouse: - d.available_qty_at_wip_warehouse = get_latest_stock_qty(d.item_code, self.wip_warehouse) - - def set_required_items(self, reset_only_qty=False): - '''set required_items for production to keep track of reserved qty''' - if not reset_only_qty: - self.required_items = [] - - if self.bom_no and self.qty: - item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=self.qty, - fetch_exploded = self.use_multi_level_bom) - - if reset_only_qty: - for d in self.get("required_items"): - if item_dict.get(d.item_code): - d.required_qty = item_dict.get(d.item_code).get("qty") - else: - for item in sorted(item_dict.values(), key=lambda d: d['idx']): - self.append('required_items', { - 'item_code': item.item_code, - 'item_name': item.item_name, - 'description': item.description, - 'required_qty': item.qty, - 'source_warehouse': item.source_warehouse or item.default_warehouse - }) - - self.set_available_qty() - - def update_transaferred_qty_for_required_items(self): - '''update transferred qty from submitted stock entries for that item against - the production order''' - - for d in self.required_items: - transferred_qty = frappe.db.sql('''select sum(qty) - from `tabStock Entry` entry, `tabStock Entry Detail` detail - where - entry.production_order = %s - and entry.purpose = "Material Transfer for Manufacture" - and entry.docstatus = 1 - and detail.parent = entry.name - and detail.item_code = %s''', (self.name, d.item_code))[0][0] - - d.db_set('transferred_qty', flt(transferred_qty), update_modified = False) - - -@frappe.whitelist() -def get_item_details(item, project = None): - res = frappe.db.sql(""" - select stock_uom, description - from `tabItem` - where disabled=0 - and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s) - and name=%s - """, (nowdate(), item), as_dict=1) - - if not res: - return {} - - res = res[0] - - filters = {"item": item, "is_default": 1} - - if project: - filters = {"item": item, "project": project} - - res["bom_no"] = frappe.db.get_value("BOM", filters = filters) - - if not res["bom_no"]: - variant_of= frappe.db.get_value("Item", item, "variant_of") - - if variant_of: - res["bom_no"] = frappe.db.get_value("BOM", filters={"item": variant_of, "is_default": 1}) - - if not res["bom_no"]: - if project: - res = get_item_details(item) - frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project), alert=1) - else: - frappe.throw(_("Default BOM for {0} not found").format(item)) - - res['project'] = project or frappe.db.get_value('BOM', res['bom_no'], 'project') - res.update(check_if_scrap_warehouse_mandatory(res["bom_no"])) - - return res - -@frappe.whitelist() -def check_if_scrap_warehouse_mandatory(bom_no): - res = {"set_scrap_wh_mandatory": False } - if bom_no: - bom = frappe.get_doc("BOM", bom_no) - - if len(bom.scrap_items) > 0: - res["set_scrap_wh_mandatory"] = True - - return res - -@frappe.whitelist() -def set_production_order_ops(name): - po = frappe.get_doc('Production Order', name) - po.set_production_order_operations() - po.save() - -@frappe.whitelist() -def make_stock_entry(production_order_id, purpose, qty=None): - production_order = frappe.get_doc("Production Order", production_order_id) - if not frappe.db.get_value("Warehouse", production_order.wip_warehouse, "is_group") \ - and not production_order.skip_transfer: - wip_warehouse = production_order.wip_warehouse - else: - wip_warehouse = None - - stock_entry = frappe.new_doc("Stock Entry") - stock_entry.purpose = purpose - stock_entry.production_order = production_order_id - stock_entry.company = production_order.company - stock_entry.from_bom = 1 - stock_entry.bom_no = production_order.bom_no - stock_entry.use_multi_level_bom = production_order.use_multi_level_bom - stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty)) - stock_entry.set_stock_entry_type() - - if purpose=="Material Transfer for Manufacture": - stock_entry.to_warehouse = wip_warehouse - stock_entry.project = production_order.project - else: - stock_entry.from_warehouse = wip_warehouse - stock_entry.to_warehouse = production_order.fg_warehouse - additional_costs = get_additional_costs(production_order, fg_qty=stock_entry.fg_completed_qty) - stock_entry.project = production_order.project - stock_entry.set("additional_costs", additional_costs) - - stock_entry.get_items() - return stock_entry.as_dict() - -@frappe.whitelist() -def make_timesheet(production_order, company): - timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "" - timesheet.production_order = production_order - timesheet.company = company - return timesheet - -@frappe.whitelist() -def add_timesheet_detail(timesheet, args): - if isinstance(timesheet, text_type): - timesheet = frappe.get_doc('Timesheet', timesheet) - - if isinstance(args, text_type): - args = json.loads(args) - - timesheet.append('time_logs', args) - return timesheet - -@frappe.whitelist() -def get_default_warehouse(): - wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", - "default_wip_warehouse") - fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", - "default_fg_warehouse") - return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse} - -@frappe.whitelist() -def make_new_timesheet(source_name, target_doc=None): - po = frappe.get_doc('Production Order', source_name) - ts = po.make_time_logs(open_new=True) - - if not ts or not ts.get('time_logs'): - frappe.throw(_("Already completed")) - - return ts - -@frappe.whitelist() -def stop_unstop(production_order, status): - """ Called from client side on Stop/Unstop event""" - - if not frappe.has_permission("Production Order", "write"): - frappe.throw(_("Not permitted"), frappe.PermissionError) - - pro_order = frappe.get_doc("Production Order", production_order) - pro_order.update_status(status) - pro_order.update_planned_qty() - frappe.msgprint(_("Production Order has been {0}").format(status)) - pro_order.notify_update() - - return pro_order.status - -@frappe.whitelist() -def query_sales_order(production_item): - out = frappe.db.sql_list(""" - select distinct so.name from `tabSales Order` so, `tabSales Order Item` so_item - where so_item.parent=so.name and so_item.item_code=%s and so.docstatus=1 - union - select distinct so.name from `tabSales Order` so, `tabPacked Item` pi_item - where pi_item.parent=so.name and pi_item.item_code=%s and so.docstatus=1 - """, (production_item, production_item)) - - return out From 8e67a3a8a84e22844c05f750fa309b703c84c8a6 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Sat, 7 Sep 2019 12:29:29 +0530 Subject: [PATCH 48/49] fix: check if 'All Item Group' exists before settings it as parent_item_group (#18956) * fix: check if 'All Item Group' exists before settings it as parent_item_group * feat: get cached value to avoid db call on each insert --- erpnext/setup/doctype/item_group/item_group.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index bb357d8bf8..b309b504e0 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe import copy +from frappe import _ from frappe.utils import nowdate, cint, cstr from frappe.utils.nestedset import NestedSet from frappe.website.website_generator import WebsiteGenerator @@ -28,7 +29,8 @@ class ItemGroup(NestedSet, WebsiteGenerator): super(ItemGroup, self).validate() if not self.parent_item_group and not frappe.flags.in_test: - self.parent_item_group = 'All Item Groups' + if frappe.db.exists("Item Group", _('All Item Groups'), cache=True): + self.parent_item_group = _('All Item Groups') self.make_route() From 3b366c30a83e2e5e6e65d076e8b15fc11955e7e1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 7 Sep 2019 12:31:07 +0530 Subject: [PATCH 49/49] feat: Added default Leave Approver in Employee (#18953) --- erpnext/hr/doctype/employee/employee.json | 10 +++++++++- .../doctype/leave_application/leave_application.py | 14 ++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 5202218ed3..ee0b2a265a 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -7,6 +7,7 @@ "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "basic_information", "employee", @@ -54,6 +55,7 @@ "column_break_44", "holiday_list", "default_shift", + "leave_approver", "salary_information", "salary_mode", "bank_name", @@ -767,12 +769,18 @@ "fieldtype": "Link", "label": "Default Shift", "options": "Shift Type" + }, + { + "fieldname": "leave_approver", + "fieldtype": "Link", + "label": "Leave Approver", + "options": "User" } ], "icon": "fa fa-user", "idx": 24, "image_field": "image", - "modified": "2019-06-01 16:05:55.132180", + "modified": "2019-09-06 15:54:36.735147", "modified_by": "Administrator", "module": "HR", "name": "Employee", diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 0aa8849e87..d4da9c1ee6 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -745,10 +745,12 @@ def get_approved_leaves_for_period(employee, leave_type, from_date, to_date): return leave_days @frappe.whitelist() -def get_leave_approver(employee, department=None): - if not department: - department = frappe.db.get_value('Employee', employee, 'department') +def get_leave_approver(employee): + leave_approver, department = frappe.db.get_value("Employee", + employee, ["leave_approver", "department"]) - if department: - return frappe.db.get_value('Department Approver', {'parent': department, - 'parentfield': 'leave_approvers', 'idx': 1}, 'approver') \ No newline at end of file + if not leave_approver and department: + leave_approver = frappe.db.get_value('Department Approver', {'parent': department, + 'parentfield': 'leave_approvers', 'idx': 1}, 'approver') + + return leave_approver \ No newline at end of file