diff --git a/erpnext/accounts/doctype/bank_account/bank_account.js b/erpnext/accounts/doctype/bank_account/bank_account.js index 5ac41e44520..a7b5891cff9 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.js +++ b/erpnext/accounts/doctype/bank_account/bank_account.js @@ -12,6 +12,11 @@ frappe.ui.form.on('Bank Account', { } }; }); + frm.set_query("party_type", function() { + return { + query: "erpnext.setup.doctype.party_type.party_type.get_party_type", + }; + }); }, refresh: function(frm) { frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank Account' } diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 84a8e6857c3..10db0d77a0b 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -1,729 +1,189 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:account_name", - "beta": 0, - "creation": "2017-05-29 21:35:13.136357", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:account_name", + "creation": "2017-05-29 21:35:13.136357", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "account_name", + "account", + "bank", + "is_company_account", + "company", + "column_break_7", + "is_default", + "bank_account_no", + "iban", + "branch_code", + "swift_number", + "section_break_11", + "party_type", + "column_break_14", + "party", + "address_and_contact", + "address_html", + "website", + "column_break_12", + "contact_html" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Account Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "account_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Account Name", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank", - "length": 0, - "no_copy": 0, - "options": "Bank", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank", + "fieldtype": "Link", + "label": "Bank", + "options": "Bank", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_company_account", - "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 Company Account", - "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": "is_company_account", + "fieldtype": "Check", + "label": "Is Company Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_company_account", - "fieldname": "company", - "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": "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": 0 - }, + "depends_on": "is_company_account", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "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, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_default", - "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 Default", - "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": "is_default", + "fieldtype": "Check", + "label": "Is Default" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account No", - "length": 30, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank_account_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Bank Account No", + "length": 30 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "iban", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "IBAN", - "length": 30, - "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": "iban", + "fieldtype": "Data", + "in_list_view": 1, + "label": "IBAN", + "length": 30 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "branch_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Branch Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "branch_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Branch Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "swift_number", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "SWIFT number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "swift_number", + "fieldtype": "Data", + "label": "SWIFT number" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.is_company_account", - "fieldname": "section_break_11", - "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, - "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 - }, + "depends_on": "eval:!doc.is_company_account", + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_type", - "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": "Party Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "party_type", + "fieldtype": "Link", + "label": "Party Type", + "options": "DocType" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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 - }, + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party", - "length": 0, - "no_copy": 0, - "options": "party_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 - }, + "fieldname": "party", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "party_type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_and_contact", - "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, - "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": "address_and_contact", + "fieldtype": "Section Break", + "label": "Address and Contact", + "options": "fa fa-map-marker" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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, - "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": "address_html", + "fieldtype": "HTML", + "label": "Address HTML" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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, - "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": "website", + "fieldtype": "Data", + "label": "Website" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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, - "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": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-05 17:56:05.103238", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Account", - "name_case": "", - "owner": "Administrator", + ], + "modified": "2019-04-25 22:10:07.951351", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank Account", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "bank,account", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "search_fields": "bank,account", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/c_form/c_form.js b/erpnext/accounts/doctype/c_form/c_form.js index 92cdb635da3..3d0fc0a42ce 100644 --- a/erpnext/accounts/doctype/c_form/c_form.js +++ b/erpnext/accounts/doctype/c_form/c_form.js @@ -4,24 +4,38 @@ //c-form js file // ----------------------------- +frappe.ui.form.on('C-Form', { + setup(frm) { + frm.fields_dict.invoices.grid.get_field("invoice_no").get_query = function(doc) { + return { + filters: { + "docstatus": 1, + "customer": doc.customer, + "company": doc.company, + "c_form_applicable": 'Yes', + "c_form_no": '' + } + }; + } -cur_frm.fields_dict.invoices.grid.get_field("invoice_no").get_query = function(doc) { - return { - filters: { - "docstatus": 1, - "customer": doc.customer, - "company": doc.company, - "c_form_applicable": 'Yes', - "c_form_no": '' + frm.fields_dict.state.get_query = function() { + return { + filters: { + country: "India" + } + }; } } -} +}); -cur_frm.fields_dict.state.get_query = function(doc) { - return {filters: { country: "India"}} -} +frappe.ui.form.on('C-Form Invoice Detail', { + invoice_no(frm, cdt, cdn) { + let d = frappe.get_doc(cdt, cdn); -cur_frm.cscript.invoice_no = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return get_server_fields('get_invoice_details', d.invoice_no, 'invoices', doc, cdt, cdn, 1); -} \ No newline at end of file + frm.call('get_invoice_details', { + invoice_no: d.invoice_no + }).then(r => { + frappe.model.set_value(cdt, cdn, r.message); + }); + } +}); diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index 3df4da52efc..96ec57dcb0b 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -22,6 +22,28 @@ frappe.ui.form.on('Cost Center', { frm.trigger("update_cost_center_number"); }); } + + let intro_txt = ''; + let doc = frm.doc; + frm.toggle_display('cost_center_name', doc.__islocal); + frm.toggle_enable(['is_group', 'company'], doc.__islocal); + + if(!doc.__islocal && doc.is_group==1) { + intro_txt += __('Note: This Cost Center is a Group. Cannot make accounting entries against groups.'); + } + + frm.events.hide_unhide_group_ledger(frm); + + frm.toggle_display('sb1', doc.is_group==0); + frm.set_intro(intro_txt); + + if(!frm.doc.__islocal) { + frm.add_custom_button(__('Chart of Cost Centers'), + function() { frappe.set_route("Tree", "Cost Center"); }); + + frm.add_custom_button(__('Budget'), + function() { frappe.set_route("List", "Budget", {'cost_center': frm.doc.name}); }); + } }, update_cost_center_number: function(frm) { var d = new frappe.ui.Dialog({ @@ -64,62 +86,38 @@ frappe.ui.form.on('Cost Center', { primary_action_label: __('Update') }); d.show(); + }, + + parent_cost_center(frm) { + if(!frm.doc.company) { + frappe.msgprint(__('Please enter company name first')); + } + }, + + hide_unhide_group_ledger(frm) { + let doc = frm.doc; + if (doc.is_group == 1) { + frm.add_custom_button(__('Convert to Non-Group'), + () => frm.events.convert_to_ledger(frm)); + } else if (doc.is_group == 0) { + frm.add_custom_button(__('Convert to Group'), + () => frm.events.convert_to_group(frm)); + } + }, + + convert_to_group(frm) { + frm.call('convert_ledger_to_group').then(r => { + if(r.message === 1) { + frm.refresh(); + } + }); + }, + + convert_to_ledger(frm) { + frm.call('convert_group_to_ledger').then(r => { + if(r.message === 1) { + frm.refresh(); + } + }); } }); - -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - var intro_txt = ''; - cur_frm.toggle_display('cost_center_name', doc.__islocal); - cur_frm.toggle_enable(['is_group', 'company'], doc.__islocal); - - if(!doc.__islocal && doc.is_group==1) { - intro_txt += __('Note: This Cost Center is a Group. Cannot make accounting entries against groups.'); - } - - cur_frm.cscript.hide_unhide_group_ledger(doc); - - cur_frm.toggle_display('sb1', doc.is_group==0) - cur_frm.set_intro(intro_txt); - - if(!cur_frm.doc.__islocal) { - cur_frm.add_custom_button(__('Chart of Cost Centers'), - function() { frappe.set_route("Tree", "Cost Center"); }); - - cur_frm.add_custom_button(__('Budget'), - function() { frappe.set_route("List", "Budget", {'cost_center': cur_frm.doc.name}); }); - } -} - -cur_frm.cscript.parent_cost_center = function(doc, cdt, cdn) { - if(!doc.company){ - frappe.msgprint(__('Please enter company name first')); - } -} - -cur_frm.cscript.hide_unhide_group_ledger = function(doc) { - if (doc.is_group == 1) { - cur_frm.add_custom_button(__('Convert to Non-Group'), - function() { cur_frm.cscript.convert_to_ledger(); }, "fa fa-retweet", - "btn-default") - } else if (doc.is_group == 0) { - cur_frm.add_custom_button(__('Convert to Group'), - function() { cur_frm.cscript.convert_to_group(); }, "fa fa-retweet", - "btn-default") - } -} - -cur_frm.cscript.convert_to_ledger = function(doc, cdt, cdn) { - return $c_obj(cur_frm.doc,'convert_group_to_ledger','',function(r,rt) { - if(r.message == 1) { - cur_frm.refresh(); - } - }); -} - -cur_frm.cscript.convert_to_group = function(doc, cdt, cdn) { - return $c_obj(cur_frm.doc,'convert_ledger_to_group','',function(r,rt) { - if(r.message == 1) { - cur_frm.refresh(); - } - }); -} diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js index 4dc64338ae4..152e17dbc88 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js @@ -1,37 +1,31 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -$.extend(cur_frm.cscript, { - onload: function() { - if(cur_frm.doc.__islocal) { - cur_frm.set_value("year_start_date", +frappe.ui.form.on('Fiscal Year', { + onload: function(frm) { + if(frm.doc.__islocal) { + frm.set_value("year_start_date", frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)); } }, - refresh: function (doc, dt, dn) { - var me = this; - this.frm.toggle_enable('year_start_date', doc.__islocal) - this.frm.toggle_enable('year_end_date', doc.__islocal) + refresh: function (frm) { + let doc = frm.doc; + frm.toggle_enable('year_start_date', doc.__islocal); + frm.toggle_enable('year_end_date', doc.__islocal); if (!doc.__islocal && (doc.name != frappe.sys_defaults.fiscal_year)) { - this.frm.add_custom_button(__("Default"), - this.frm.cscript.set_as_default, "fa fa-star"); - this.frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'")); + frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm)); + frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'")); } else { - this.frm.set_intro(""); + frm.set_intro(""); } }, - set_as_default: function() { - return frappe.call({ - doc: cur_frm.doc, - method: "set_as_default" - }); + set_as_default: function(frm) { + return frm.call('set_as_default'); }, - year_start_date: function(doc, dt, dn) { - var me = this; - - var year_end_date = - frappe.datetime.add_days(frappe.datetime.add_months(this.frm.doc.year_start_date, 12), -1); - this.frm.set_value("year_end_date", year_end_date); + year_start_date: function(frm) { + let year_end_date = + frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1); + frm.set_value("year_end_date", year_end_date); }, }); diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js index 7b489de0a35..569f0084c6a 100644 --- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js +++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js @@ -1,16 +1,16 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -cur_frm.cscript.onload = function(doc,cdt,cdn){ - if(doc.__islocal){ - var callback1 = function(r,rt){ - refresh_field('percentages'); +frappe.ui.form.on('Monthly Distribution', { + onload(frm) { + if(frm.doc.__islocal) { + return frm.call('get_months').then(() => { + frm.refresh_field('percentages'); + }); } + }, - return $c('runserverobj', {'method':'get_months', 'docs':doc}, callback1); + refresh(frm) { + frm.toggle_display('distribution_id', frm.doc.__islocal); } -} - -cur_frm.cscript.refresh = function(doc,cdt,cdn){ - cur_frm.toggle_display('distribution_id', doc.__islocal); -} +}); diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js index e1fe5a16a39..8f09bc36912 100644 --- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js +++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js @@ -1,6 +1,10 @@ -cur_frm.cscript.refresh = function(doc, dt, dn){ - if(!doc.__islocal){ - var df = frappe.meta.get_docfield(doc.doctype, "payment_gateway", doc.name); - df.read_only = 1; +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.ui.form.on('Payment Gateway Account', { + refresh(frm) { + if(!frm.doc.__islocal) { + frm.set_df_property('payment_gateway', 'read_only', 1); + } } -} \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js index e5b63367556..c92b58b5809 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js @@ -1,108 +1,6 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on("Pricing Rule", "refresh", function(frm) { - var help_content = - ` - - -
-

- - ${__('Notes')} -

-
    -
  • - ${__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")} -
  • -
  • - ${__("If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.")} -
  • -
  • - ${__('Discount Percentage can be applied either against a Price List or for all Price List.')} -
  • -
  • - ${__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')} -
  • -
-
-

- ${__('How Pricing Rule is applied?')} -

-
    -
  1. - ${__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")} -
  2. -
  3. - ${__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Group, Campaign, Sales Partner etc.")} -
  4. -
  5. - ${__('Pricing Rules are further filtered based on quantity.')} -
  6. -
  7. - ${__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.')} -
  8. -
  9. - ${__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')} -
      -
    • - ${__('Item Code > Item Group > Brand')} -
    • -
    • - ${__('Customer > Customer Group > Territory')} -
    • -
    • - ${__('Supplier > Supplier Group')} -
    • -
    -
  10. -
  11. - ${__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')} -
  12. -
-
`; - - set_field_options("pricing_rule_help", help_content); - - cur_frm.cscript.set_options_for_applicable_for(); -}); - -cur_frm.cscript.set_options_for_applicable_for = function() { - var options = [""]; - var applicable_for = cur_frm.doc.applicable_for; - - if(cur_frm.doc.selling) { - options = $.merge(options, ["Customer", "Customer Group", "Territory", "Sales Partner", "Campaign"]); - } - if(cur_frm.doc.buying) { - $.merge(options, ["Supplier", "Supplier Group"]); - } - - set_field_options("applicable_for", options.join("\n")); - - if(!in_list(options, applicable_for)) applicable_for = null; - cur_frm.set_value("applicable_for", applicable_for) -} - -cur_frm.cscript.selling = function() { - cur_frm.cscript.set_options_for_applicable_for(); -} - -cur_frm.cscript.buying = function() { - cur_frm.cscript.set_options_for_applicable_for(); -} - -//Dynamically change the description based on type of margin -cur_frm.cscript.margin_type = function(doc){ - cur_frm.set_df_property('margin_rate_or_amount', 'description', doc.margin_type=='Percentage'?'In Percentage %':'In Amount') -} - -frappe.ui.form.on('Pricing Rule', 'rate_or_discount', function(frm){ - if(frm.doc.rate_or_discount == 'Rate') { - frm.set_value('for_price_list', "") - } -}) - frappe.ui.form.on('Pricing Rule', { setup: function(frm) { frm.fields_dict["for_price_list"].get_query = function(doc){ @@ -199,7 +97,7 @@ frappe.ui.form.on('Pricing Rule', { `; - set_field_options("pricing_rule_help", help_content); + frm.set_df_property('pricing_rule_help', 'options', help_content); frm.events.set_options_for_applicable_for(frm); frm.trigger("toggle_reqd_apply_on"); }, @@ -256,5 +154,4 @@ frappe.ui.form.on('Pricing Rule', { if(!in_list(options, applicable_for)) applicable_for = null; frm.set_value("applicable_for", applicable_for); } - }); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 7ab7f149021..d224961bd8e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -285,6 +285,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ is_paid: function() { hide_fields(this.frm.doc); if(cint(this.frm.doc.is_paid)) { + this.frm.set_value("allocate_advances_automatically", 0); if(!this.frm.doc.company) { this.frm.set_value("is_paid", 0) frappe.msgprint(__("Please specify Company to proceed")); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 0af273c5180..95d49a4421a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -21,8 +21,8 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled from frappe.model.mapper import get_mapped_doc from six import iteritems -from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\ - unlink_inter_company_invoice +from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ + unlink_inter_company_doc from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from erpnext.accounts.deferred_revenue import validate_service_stop_date @@ -348,7 +348,7 @@ class PurchaseInvoice(BuyingController): self.make_gl_entries() self.update_project() - update_linked_invoice(self.doctype, self.name, self.inter_company_invoice_reference) + update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): if not self.grand_total: @@ -778,7 +778,7 @@ class PurchaseInvoice(BuyingController): self.update_project() frappe.db.set(self, 'status', 'Cancelled') - unlink_inter_company_invoice(self.doctype, self.name, self.inter_company_invoice_reference) + unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference) def update_project(self): project_list = [] @@ -917,5 +917,5 @@ def block_invoice(name, hold_comment): @frappe.whitelist() def make_inter_company_sales_invoice(source_name, target_doc=None): - from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_invoice - return make_inter_company_invoice("Purchase Invoice", source_name, target_doc) + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction + return make_inter_company_transaction("Purchase Invoice", source_name, target_doc) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 44af743ebf8..f21fbd97946 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -369,6 +369,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte set_pos_data: function() { if(this.frm.doc.is_pos) { + this.frm.set_value("allocate_advances_automatically", 0); if(!this.frm.doc.company) { this.frm.set_value("is_pos", 0); frappe.msgprint(__("Please specify Company to proceed")); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 2089f15c7c1..31a9c66f6f2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -179,7 +179,7 @@ class SalesInvoice(SellingController): if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') == "Each Transaction": update_company_current_month_sales(self.company) self.update_project() - update_linked_invoice(self.doctype, self.name, self.inter_company_invoice_reference) + update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) # create the loyalty point ledger entry if the customer is enrolled in any loyalty program if not self.is_return and self.loyalty_program: @@ -243,7 +243,7 @@ class SalesInvoice(SellingController): against_si_doc.delete_loyalty_point_entry() against_si_doc.make_loyalty_point_entry() - unlink_inter_company_invoice(self.doctype, self.name, self.inter_company_invoice_reference) + unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference) # Healthcare Service Invoice. domain_settings = frappe.get_doc('Domain Settings') @@ -1165,21 +1165,29 @@ class SalesInvoice(SellingController): self.set_missing_values(for_validate = True) -def validate_inter_company_party(doctype, party, company, inter_company_invoice_reference): - if doctype == "Sales Invoice": +def validate_inter_company_party(doctype, party, company, inter_company_reference): + if doctype in ["Sales Invoice", "Sales Order"]: partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer" - ref_doc = "Purchase Invoice" + + if doctype == "Sales Invoice": + ref_doc = "Purchase Invoice" + else: + ref_doc = "Purchase Order" else: partytype, ref_partytype, internal = "Supplier", "Customer", "is_internal_supplier" - ref_doc = "Sales Invoice" - if inter_company_invoice_reference: - doc = frappe.get_doc(ref_doc, inter_company_invoice_reference) - ref_party = doc.supplier if doctype == "Sales Invoice" else doc.customer + if doctype == "Purchase Invoice": + ref_doc = "Sales Invoice" + else: + ref_doc = "Sales Order" + + if inter_company_reference: + doc = frappe.get_doc(ref_doc, inter_company_reference) + ref_party = doc.supplier if doctype in ["Sales Invoice", "Sales Order"] else doc.customer if not frappe.db.get_value(partytype, {"represents_company": doc.company}, "name") == party: - frappe.throw(_("Invalid {0} for Inter Company Invoice.").format(partytype)) + frappe.throw(_("Invalid {0} for Inter Company Transaction.").format(partytype)) if not frappe.get_cached_value(ref_partytype, ref_party, "represents_company") == company: - frappe.throw(_("Invalid Company for Inter Company Invoice.")) + frappe.throw(_("Invalid Company for Inter Company Transaction.")) elif frappe.db.get_value(partytype, {"name": party, internal: 1}, "name") == party: companies = frappe.db.sql("""select company from `tabAllowed To Transact With` @@ -1188,18 +1196,29 @@ def validate_inter_company_party(doctype, party, company, inter_company_invoice_ if not company in companies: frappe.throw(_("{0} not allowed to transact with {1}. Please change the Company.").format(partytype, company)) -def update_linked_invoice(doctype, name, inter_company_invoice_reference): - if inter_company_invoice_reference: - frappe.db.set_value(doctype, inter_company_invoice_reference,\ - "inter_company_invoice_reference", name) +def update_linked_doc(doctype, name, inter_company_reference): -def unlink_inter_company_invoice(doctype, name, inter_company_invoice_reference): - ref_doc = "Purchase Invoice" if doctype == "Sales Invoice" else "Sales Invoice" - if inter_company_invoice_reference: - frappe.db.set_value(doctype, name,\ - "inter_company_invoice_reference", "") - frappe.db.set_value(ref_doc, inter_company_invoice_reference,\ - "inter_company_invoice_reference", "") + if doctype in ["Sales Invoice", "Purchase Invoice"]: + ref_field = "inter_company_invoice_reference" + else: + ref_field = "inter_company_order_reference" + + if inter_company_reference: + frappe.db.set_value(doctype, inter_company_reference,\ + ref_field, name) + +def unlink_inter_company_doc(doctype, name, inter_company_reference): + + if doctype in ["Sales Invoice", "Purchase Invoice"]: + ref_doc = "Purchase Invoice" if doctype == "Sales Invoice" else "Sales Invoice" + ref_field = "inter_company_invoice_reference" + else: + ref_doc = "Purchase Order" if doctype == "Sales Order" else "Sales Order" + ref_field = "inter_company_order_reference" + + if inter_company_reference: + frappe.db.set_value(doctype, name, ref_field, "") + frappe.db.set_value(ref_doc, inter_company_reference, ref_field, "") def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context @@ -1299,7 +1318,7 @@ def set_account_for_mode_of_payment(self): data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account") def get_inter_company_details(doc, doctype): - if doctype == "Sales Invoice": + if doctype in ["Sales Invoice", "Sales Order"]: party = frappe.db.get_value("Supplier", {"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company}, "name") company = frappe.get_cached_value("Customer", doc.customer, "represents_company") else: @@ -1312,21 +1331,21 @@ def get_inter_company_details(doc, doctype): } -def validate_inter_company_invoice(doc, doctype): +def validate_inter_company_transaction(doc, doctype): details = get_inter_company_details(doc, doctype) - price_list = doc.selling_price_list if doctype == "Sales Invoice" else doc.buying_price_list + price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order"] else doc.buying_price_list valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1}) if not valid_price_list: frappe.throw(_("Selected Price List should have buying and selling fields checked.")) party = details.get("party") if not party: - partytype = "Supplier" if doctype == "Sales Invoice" else "Customer" + partytype = "Supplier" if doctype in ["Sales Invoice", "Sales Order"] else "Customer" frappe.throw(_("No {0} found for Inter Company Transactions.").format(partytype)) company = details.get("company") - default_currency = frappe.get_cached_value('Company', company, "default_currency") + default_currency = frappe.get_cached_value('Company', company, "default_currency") if default_currency != doc.currency: frappe.throw(_("Company currencies of both the companies should match for Inter Company Transactions.")) @@ -1334,17 +1353,17 @@ def validate_inter_company_invoice(doc, doctype): @frappe.whitelist() def make_inter_company_purchase_invoice(source_name, target_doc=None): - return make_inter_company_invoice("Sales Invoice", source_name, target_doc) + return make_inter_company_transaction("Sales Invoice", source_name, target_doc) -def make_inter_company_invoice(doctype, source_name, target_doc=None): - if doctype == "Sales Invoice": - source_doc = frappe.get_doc("Sales Invoice", source_name) - target_doctype = "Purchase Invoice" +def make_inter_company_transaction(doctype, source_name, target_doc=None): + if doctype in ["Sales Invoice", "Sales Order"]: + source_doc = frappe.get_doc(doctype, source_name) + target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order" else: - source_doc = frappe.get_doc("Purchase Invoice", source_name) - target_doctype = "Sales Invoice" + source_doc = frappe.get_doc(doctype, source_name) + target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order" - validate_inter_company_invoice(source_doc, doctype) + validate_inter_company_transaction(source_doc, doctype) details = get_inter_company_details(source_doc, doctype) def set_missing_values(source, target): @@ -1352,7 +1371,7 @@ def make_inter_company_invoice(doctype, source_name, target_doc=None): def update_details(source_doc, target_doc, source_parent): target_doc.inter_company_invoice_reference = source_doc.name - if target_doc.doctype == "Purchase Invoice": + if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]: target_doc.company = details.get("company") target_doc.supplier = details.get("party") target_doc.buying_price_list = source_doc.selling_price_list diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 74c5f0ec1be..47c60839b36 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -18,6 +18,7 @@ from erpnext.accounts.doctype.account.test_account import get_inventory_account, from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data from erpnext.stock.doctype.item.test_item import create_item from six import iteritems +from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction class TestSalesInvoice(unittest.TestCase): def make(self): w = frappe.copy_doc(test_records[0]) @@ -1625,6 +1626,61 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(expected_gle[i][2], gle.credit) self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) + def test_inter_company_transaction(self): + + if not frappe.db.exists("Customer", "_Test Internal Customer"): + customer = frappe.get_doc({ + "customer_group": "_Test Customer Group", + "customer_name": "_Test Internal Customer", + "customer_type": "Individual", + "doctype": "Customer", + "territory": "_Test Territory", + "is_internal_customer": 1, + "represents_company": "_Test Company 1" + }) + + customer.append("companies", { + "company": "Wind Power LLC" + }) + + customer.insert() + + if not frappe.db.exists("Supplier", "_Test Internal Supplier"): + supplier = frappe.get_doc({ + "supplier_group": "_Test Supplier Group", + "supplier_name": "_Test Internal Supplier", + "doctype": "Supplier", + "is_internal_supplier": 1, + "represents_company": "Wind Power LLC" + }) + + supplier.append("companies", { + "company": "_Test Company 1" + }) + + supplier.insert() + + si = create_sales_invoice( + company = "Wind Power LLC", + customer = "_Test Internal Customer", + debit_to = "Debtors - WP", + warehouse = "Stores - WP", + income_account = "Sales - WP", + expense_account = "Cost of Goods Sold - WP", + cost_center = "Main - WP", + currency = "USD", + do_not_save = 1 + ) + + si.selling_price_list = "_Test Price List Rest of the World" + si.submit() + + target_doc = make_inter_company_transaction("Sales Invoice", si.name) + target_doc.submit() + + self.assertEqual(target_doc.company, "_Test Company 1") + self.assertEqual(target_doc.supplier, "_Test Internal Supplier") + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") args = frappe._dict(args) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index f358b4a205f..48663cac7ea 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -359,7 +359,8 @@ def set_gl_entries_by_account( "from_date": from_date, "to_date": to_date, "cost_center": filters.cost_center, - "project": filters.project + "project": filters.project, + "finance_book": filters.get("finance_book") }, as_dict=True) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index e9a0f70b332..2d78d2693d0 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -296,6 +296,12 @@ frappe.ui.form.on('Asset', { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); }, + gross_purchase_amount: function(frm) { + frm.doc.finance_books.forEach(d => { + frm.events.set_depreciation_rate(frm, d); + }) + }, + set_depreciation_rate: function(frm, row) { if (row.total_number_of_depreciations && row.frequency_of_depreciation) { frappe.call({ diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 8011038b1b1..72f5c627a71 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -101,7 +101,7 @@ class Asset(AccountsController): def set_depreciation_rate(self): for d in self.get("finance_books"): - d.rate_of_depreciation = self.get_depreciation_rate(d) + d.rate_of_depreciation = self.get_depreciation_rate(d, on_validate=True) def make_depreciation_schedule(self): depreciation_method = [d.depreciation_method for d in self.finance_books] @@ -125,7 +125,7 @@ class Asset(AccountsController): no_of_depreciations * cint(d.frequency_of_depreciation)) total_days = date_diff(end_date, self.available_for_use_date) - rate_per_day = value_after_depreciation / total_days + rate_per_day = (value_after_depreciation - d.get("expected_value_after_useful_life")) / total_days number_of_pending_depreciations = cint(d.total_number_of_depreciations) - \ cint(self.number_of_depreciations_booked) @@ -291,8 +291,8 @@ class Asset(AccountsController): def validate_expected_value_after_useful_life(self): for row in self.get('finance_books'): - accumulated_depreciation_after_full_schedule = \ - max([d.accumulated_depreciation_amount for d in self.get("schedules") if d.finance_book_id == row.idx]) + accumulated_depreciation_after_full_schedule = max([d.accumulated_depreciation_amount + for d in self.get("schedules") if cint(d.finance_book_id) == row.idx]) asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule), @@ -403,7 +403,7 @@ class Asset(AccountsController): make_gl_entries(gl_entries) self.db_set('booked_fixed_asset', 1) - def get_depreciation_rate(self, args): + def get_depreciation_rate(self, args, on_validate=False): if isinstance(args, string_types): args = json.loads(args) @@ -420,7 +420,10 @@ class Asset(AccountsController): if args.get("depreciation_method") == 'Double Declining Balance': return 200.0 / args.get("total_number_of_depreciations") - if args.get("depreciation_method") == "Written Down Value" and not args.get("rate_of_depreciation"): + if args.get("depreciation_method") == "Written Down Value": + if args.get("rate_of_depreciation") and on_validate: + return args.get("rate_of_depreciation") + no_of_years = flt(args.get("total_number_of_depreciations") * flt(args.get("frequency_of_depreciation"))) / 12 value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 985097b447d..ef85ffa1cb8 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -102,9 +102,9 @@ class TestAsset(unittest.TestCase): asset.save() self.assertEqual(asset.status, "Draft") expected_schedules = [ - ["2020-06-06", 163.93, 163.93], - ["2021-04-06", 49836.07, 50000.0], - ["2022-02-06", 40000.0, 90000.00] + ["2020-06-06", 147.54, 147.54], + ["2021-04-06", 44852.46, 45000.0], + ["2022-02-06", 45000.0, 90000.00] ] schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] @@ -130,8 +130,8 @@ class TestAsset(unittest.TestCase): self.assertEqual(asset.status, "Draft") asset.save() expected_schedules = [ - ["2020-06-06", 197.37, 40197.37], - ["2021-04-06", 49802.63, 90000.00] + ["2020-06-06", 164.47, 40164.47], + ["2021-04-06", 49835.53, 90000.00] ] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount] for d in asset.get("schedules")] @@ -266,8 +266,8 @@ class TestAsset(unittest.TestCase): self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR") expected_gle = ( - ("_Test Accumulated Depreciations - _TC", 0.0, 35699.15), - ("_Test Depreciations - _TC", 35699.15, 0.0) + ("_Test Accumulated Depreciations - _TC", 0.0, 32129.24), + ("_Test Depreciations - _TC", 32129.24, 0.0) ) gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 28ceab59181..e63ef605b0b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -138,6 +138,20 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( erpnext.utils.make_subscription(doc.doctype, doc.name) }, __('Create')) } + + if (doc.docstatus === 1 && !doc.inter_company_order_reference) { + let me = this; + frappe.model.with_doc("Supplier", me.frm.doc.supplier, () => { + let supplier = frappe.model.get_doc("Supplier", me.frm.doc.supplier); + let internal = supplier.is_internal_supplier; + let disabled = supplier.disabled; + if (internal === 1 && disabled === 0) { + me.frm.add_custom_button("Inter Company Order", function() { + me.make_inter_company_order(me.frm); + }, __('Create')); + } + }); + } } if(flt(doc.per_billed)==0) { this.frm.add_custom_button(__('Payment Request'), @@ -296,6 +310,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( }); }, + make_inter_company_order: function(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.buying.doctype.purchase_order.purchase_order.make_inter_company_sales_order", + frm: frm + }); + }, + make_purchase_receipt: function() { frappe.model.open_mapped_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 46f48fb2d7b..13a097a0d7e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -298,6 +298,7 @@ "collapsible": 0, "columns": 0, "default": "Today", + "fetch_if_empty": 0, "fieldname": "transaction_date", "fieldtype": "Date", "hidden": 0, @@ -332,6 +333,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "schedule_date", "fieldtype": "Date", "hidden": 0, @@ -365,6 +367,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.docstatus===1", + "fetch_if_empty": 0, "fieldname": "order_confirmation_no", "fieldtype": "Data", "hidden": 0, @@ -398,6 +401,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.order_confirmation_no", + "fetch_if_empty": 0, "fieldname": "order_confirmation_date", "fieldtype": "Date", "hidden": 0, @@ -430,6 +434,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -465,6 +470,7 @@ "collapsible": 0, "collapsible_depends_on": "", "columns": 0, + "fetch_if_empty": 0, "fieldname": "drop_ship", "fieldtype": "Section Break", "hidden": 0, @@ -498,6 +504,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "customer", "fieldtype": "Link", "hidden": 0, @@ -532,6 +539,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "customer_name", "fieldtype": "Data", "hidden": 0, @@ -564,6 +572,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_19", "fieldtype": "Column Break", "hidden": 0, @@ -596,6 +605,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "customer_contact_person", "fieldtype": "Link", "hidden": 0, @@ -629,6 +639,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, @@ -661,6 +672,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, @@ -693,6 +705,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_contact_email", "fieldtype": "Code", "hidden": 1, @@ -726,6 +739,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_addresses", "fieldtype": "Section Break", "hidden": 0, @@ -758,6 +772,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "supplier_address", "fieldtype": "Link", "hidden": 0, @@ -790,6 +805,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_person", "fieldtype": "Link", "hidden": 0, @@ -823,6 +839,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "address_display", "fieldtype": "Small Text", "hidden": 0, @@ -854,6 +871,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_display", "fieldtype": "Small Text", "hidden": 0, @@ -885,6 +903,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_mobile", "fieldtype": "Small Text", "hidden": 0, @@ -916,6 +935,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_email", "fieldtype": "Small Text", "hidden": 0, @@ -947,6 +967,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break_address", "fieldtype": "Column Break", "hidden": 0, @@ -979,6 +1000,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "shipping_address", "fieldtype": "Link", "hidden": 0, @@ -1012,6 +1034,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "shipping_address_display", "fieldtype": "Small Text", "hidden": 0, @@ -1044,6 +1067,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "hidden": 0, @@ -1076,6 +1100,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", "hidden": 0, @@ -1111,6 +1136,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "conversion_rate", "fieldtype": "Float", "hidden": 0, @@ -1145,6 +1171,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "cb_price_list", "fieldtype": "Column Break", "hidden": 0, @@ -1175,6 +1202,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "buying_price_list", "fieldtype": "Link", "hidden": 0, @@ -1207,6 +1235,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "price_list_currency", "fieldtype": "Link", "hidden": 0, @@ -1239,6 +1268,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "plc_conversion_rate", "fieldtype": "Float", "hidden": 0, @@ -1271,6 +1301,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "hidden": 0, @@ -1302,6 +1333,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sec_warehouse", "fieldtype": "Section Break", "hidden": 0, @@ -1333,6 +1365,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "set_warehouse", "fieldtype": "Link", "hidden": 0, @@ -1366,6 +1399,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break_warehouse", "fieldtype": "Column Break", "hidden": 0, @@ -1398,6 +1432,7 @@ "collapsible": 0, "columns": 0, "default": "No", + "fetch_if_empty": 0, "fieldname": "is_subcontracted", "fieldtype": "Select", "hidden": 0, @@ -1431,6 +1466,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.is_subcontracted==\"Yes\"", + "fetch_if_empty": 0, "fieldname": "supplier_warehouse", "fieldtype": "Link", "hidden": 0, @@ -1464,6 +1500,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "items_section", "fieldtype": "Section Break", "hidden": 0, @@ -1497,6 +1534,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "scan_barcode", "fieldtype": "Data", "hidden": 0, @@ -1529,6 +1567,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "items", "fieldtype": "Table", "hidden": 0, @@ -1563,6 +1602,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_48", "fieldtype": "Section Break", "hidden": 0, @@ -1596,6 +1636,7 @@ "collapsible": 0, "collapsible_depends_on": "", "columns": 0, + "fetch_if_empty": 0, "fieldname": "pricing_rules", "fieldtype": "Table", "hidden": 0, @@ -1630,6 +1671,7 @@ "collapsible": 0, "collapsible_depends_on": "supplied_items", "columns": 0, + "fetch_if_empty": 0, "fieldname": "raw_material_details", "fieldtype": "Section Break", "hidden": 0, @@ -1663,6 +1705,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "supplied_items", "fieldtype": "Table", "hidden": 0, @@ -1697,6 +1740,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sb_last_purchase", "fieldtype": "Section Break", "hidden": 0, @@ -1727,6 +1771,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_qty", "fieldtype": "Float", "hidden": 0, @@ -1759,6 +1804,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_total", "fieldtype": "Currency", "hidden": 0, @@ -1792,6 +1838,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_net_total", "fieldtype": "Currency", "hidden": 0, @@ -1826,6 +1873,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_26", "fieldtype": "Column Break", "hidden": 0, @@ -1856,6 +1904,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total", "fieldtype": "Currency", "hidden": 0, @@ -1889,6 +1938,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "net_total", "fieldtype": "Currency", "hidden": 0, @@ -1923,6 +1973,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_net_weight", "fieldtype": "Float", "hidden": 0, @@ -1955,6 +2006,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_section", "fieldtype": "Section Break", "hidden": 0, @@ -1989,6 +2041,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "taxes_and_charges", "fieldtype": "Link", "hidden": 0, @@ -2023,6 +2076,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_50", "fieldtype": "Column Break", "hidden": 0, @@ -2054,6 +2108,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "shipping_rule", "fieldtype": "Link", "hidden": 0, @@ -2087,6 +2142,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_52", "fieldtype": "Section Break", "hidden": 0, @@ -2118,6 +2174,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes", "fieldtype": "Table", "hidden": 0, @@ -2152,6 +2209,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", "hidden": 0, @@ -2184,6 +2242,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "other_charges_calculation", "fieldtype": "Text", "hidden": 0, @@ -2216,6 +2275,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "totals", "fieldtype": "Section Break", "hidden": 0, @@ -2249,6 +2309,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_taxes_and_charges_added", "fieldtype": "Currency", "hidden": 0, @@ -2283,6 +2344,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_taxes_and_charges_deducted", "fieldtype": "Currency", "hidden": 0, @@ -2317,6 +2379,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_total_taxes_and_charges", "fieldtype": "Currency", "hidden": 0, @@ -2351,6 +2414,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_39", "fieldtype": "Column Break", "hidden": 0, @@ -2382,6 +2446,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_and_charges_added", "fieldtype": "Currency", "hidden": 0, @@ -2416,6 +2481,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_and_charges_deducted", "fieldtype": "Currency", "hidden": 0, @@ -2450,6 +2516,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "hidden": 0, @@ -2484,6 +2551,7 @@ "collapsible": 1, "collapsible_depends_on": "discount_amount", "columns": 0, + "fetch_if_empty": 0, "fieldname": "discount_section", "fieldtype": "Section Break", "hidden": 0, @@ -2517,6 +2585,7 @@ "collapsible": 0, "columns": 0, "default": "Grand Total", + "fetch_if_empty": 0, "fieldname": "apply_discount_on", "fieldtype": "Select", "hidden": 0, @@ -2550,6 +2619,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_discount_amount", "fieldtype": "Currency", "hidden": 0, @@ -2583,6 +2653,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_45", "fieldtype": "Column Break", "hidden": 0, @@ -2614,6 +2685,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "additional_discount_percentage", "fieldtype": "Float", "hidden": 0, @@ -2646,6 +2718,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "discount_amount", "fieldtype": "Currency", "hidden": 0, @@ -2679,6 +2752,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "totals_section", "fieldtype": "Section Break", "hidden": 0, @@ -2710,6 +2784,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_grand_total", "fieldtype": "Currency", "hidden": 0, @@ -2744,6 +2819,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "hidden": 0, @@ -2778,6 +2854,7 @@ "collapsible": 0, "columns": 0, "description": "In Words will be visible once you save the Purchase Order.", + "fetch_if_empty": 0, "fieldname": "base_in_words", "fieldtype": "Data", "hidden": 0, @@ -2811,6 +2888,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_rounded_total", "fieldtype": "Currency", "hidden": 0, @@ -2845,6 +2923,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break4", "fieldtype": "Column Break", "hidden": 0, @@ -2876,6 +2955,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "grand_total", "fieldtype": "Currency", "hidden": 0, @@ -2910,6 +2990,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "rounding_adjustment", "fieldtype": "Currency", "hidden": 0, @@ -2943,6 +3024,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "rounded_total", "fieldtype": "Currency", "hidden": 0, @@ -2975,6 +3057,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "disable_rounded_total", "fieldtype": "Check", "hidden": 0, @@ -3007,6 +3090,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "in_words", "fieldtype": "Data", "hidden": 0, @@ -3040,6 +3124,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "advance_paid", "fieldtype": "Currency", "hidden": 0, @@ -3072,6 +3157,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "hidden": 0, @@ -3104,6 +3190,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_terms_template", "fieldtype": "Link", "hidden": 0, @@ -3137,6 +3224,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_schedule", "fieldtype": "Table", "hidden": 0, @@ -3171,6 +3259,7 @@ "collapsible": 1, "collapsible_depends_on": "terms", "columns": 0, + "fetch_if_empty": 0, "fieldname": "terms_section_break", "fieldtype": "Section Break", "hidden": 0, @@ -3204,6 +3293,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "tc_name", "fieldtype": "Link", "hidden": 0, @@ -3238,6 +3328,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "terms", "fieldtype": "Text Editor", "hidden": 0, @@ -3271,6 +3362,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "more_info", "fieldtype": "Section Break", "hidden": 0, @@ -3304,6 +3396,7 @@ "collapsible": 0, "columns": 0, "default": "Draft", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -3338,6 +3431,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "ref_sq", "fieldtype": "Data", "hidden": 1, @@ -3371,6 +3465,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "party_account_currency", "fieldtype": "Link", "hidden": 1, @@ -3404,6 +3499,41 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "inter_company_order_reference", + "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": "Inter Company Order Reference", + "length": 0, + "no_copy": 0, + "options": "Sales Order", + "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, + "fetch_if_empty": 0, "fieldname": "column_break_74", "fieldtype": "Column Break", "hidden": 0, @@ -3437,6 +3567,7 @@ "columns": 0, "depends_on": "eval:!doc.__islocal", "description": "", + "fetch_if_empty": 0, "fieldname": "per_received", "fieldtype": "Percent", "hidden": 0, @@ -3472,6 +3603,7 @@ "columns": 0, "depends_on": "eval:!doc.__islocal", "description": "", + "fetch_if_empty": 0, "fieldname": "per_billed", "fieldtype": "Percent", "hidden": 0, @@ -3505,6 +3637,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break5", "fieldtype": "Section Break", "hidden": 0, @@ -3906,17 +4039,15 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-file-text", "idx": 105, - "image_view": 0, "in_create": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-02-14 19:36:49.390935", + "modified": "2019-04-18 19:43:17.239390", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -4001,7 +4132,6 @@ ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 1, "search_fields": "status, transaction_date, supplier,grand_total", "show_name_in_global_search": 1, "sort_field": "modified", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 6a4c818b5b8..e5156a32b7f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -17,6 +17,8 @@ from erpnext.accounts.party import get_party_account_currency from six import string_types from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ + unlink_inter_company_doc form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -56,6 +58,7 @@ class PurchaseOrder(BuyingController): self.validate_bom_for_subcontracting_items() self.create_raw_materials_supplied("supplied_items") self.set_received_qty_for_drop_ship_items() + validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_order_reference) def validate_with_previous_doc(self): super(PurchaseOrder, self).validate_with_previous_doc({ @@ -219,6 +222,7 @@ class PurchaseOrder(BuyingController): self.update_blanket_order() + update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) def on_cancel(self): super(PurchaseOrder, self).on_cancel() @@ -244,6 +248,7 @@ class PurchaseOrder(BuyingController): self.update_blanket_order() + unlink_inter_company_doc(self.doctype, self.name, self.inter_company_order_reference) def on_update(self): pass @@ -490,3 +495,9 @@ def update_status(status, name): po = frappe.get_doc("Purchase Order", name) po.update_status(status) po.update_delivered_qty_in_sales_order() + +@frappe.whitelist() +def make_inter_company_sales_order(source_name, target_doc=None): + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction + return make_inter_company_transaction("Purchase Order", source_name, target_doc) + diff --git a/erpnext/config/education.py b/erpnext/config/education.py index d5f9e2dca41..4efaaa65cdc 100644 --- a/erpnext/config/education.py +++ b/erpnext/config/education.py @@ -33,6 +33,10 @@ def get_data(): "type": "doctype", "name": "Student Applicant" }, + { + "type": "doctype", + "name": "Web Academy Applicant" + }, { "type": "doctype", "name": "Student Admission" @@ -180,6 +184,10 @@ def get_data(): { "label": _("Masters"), "items": [ + { + "type": "doctype", + "name": "Program", + }, { "type": "doctype", "name": "Course", @@ -187,7 +195,7 @@ def get_data(): }, { "type": "doctype", - "name": "Program" + "name": "Topic", }, { "type": "doctype", @@ -201,6 +209,40 @@ def get_data(): } ] }, + { + "label": _("Content Masters"), + "items": [ + { + "type": "doctype", + "name": "Article" + }, + { + "type": "doctype", + "name": "Video" + }, + { + "type": "doctype", + "name": "Quiz" + } + ] + }, + { + "label": _("LMS Activity"), + "items": [ + { + "type": "doctype", + "name": "Course Enrollment" + }, + { + "type": "doctype", + "name": "Course Activity" + }, + { + "type": "doctype", + "name": "Quiz Activity" + } + ] + }, { "label": _("Settings"), "items": [ diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 63ea2591c5e..155a996a15a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -89,7 +89,8 @@ class AccountsController(TransactionBase): self.validate_paid_amount() if self.doctype in ['Purchase Invoice', 'Sales Invoice']: - if cint(self.allocate_advances_automatically): + pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid" + if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)): self.set_advances() if self.is_return: diff --git a/erpnext/demo/user/accounts.py b/erpnext/demo/user/accounts.py index 18855041edd..6206dfd2d03 100644 --- a/erpnext/demo/user/accounts.py +++ b/erpnext/demo/user/accounts.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals +import erpnext import frappe import random from frappe.utils import random_string @@ -72,8 +73,10 @@ def work(): make_pos_invoice() def make_payment_entries(ref_doctype, report): - outstanding_invoices = list(set([r[3] for r in query_report.run(report, - {"report_date": frappe.flags.current_date })["result"] if r[2]==ref_doctype])) + outstanding_invoices = list(set([r[3] for r in query_report.run(report, { + "report_date": frappe.flags.current_date, + "company": erpnext.get_default_company() + })["result"] if r[2]==ref_doctype])) # make Payment Entry for inv in outstanding_invoices[:random.randint(1, 2)]: diff --git a/erpnext/demo/user/manufacturing.py b/erpnext/demo/user/manufacturing.py index a28d061f382..bece0798faa 100644 --- a/erpnext/demo/user/manufacturing.py +++ b/erpnext/demo/user/manufacturing.py @@ -102,10 +102,18 @@ def submit_job_cards(): for operation in work_order.operations: job = job_map[operation.operation] - job.actual_start_date = start_date + job_time_log = frappe.new_doc("Job Card Time Log") + job_time_log.from_time = start_date minutes = operation.get("time_in_mins") - random_minutes = random.randint(int(minutes/2), minutes) - job.actual_end_date = job.actual_start_date + timedelta(minutes=random_minutes) - start_date = job.actual_end_date - job.save() + job_time_log.time_in_mins = random.randint(int(minutes/2), minutes) + job_time_log.to_time = job_time_log.from_time + \ + timedelta(minutes=job_time_log.time_in_mins) + job_time_log.parent = job.name + job_time_log.parenttype = 'Job Card' + job_time_log.parentfield = 'time_logs' + job_time_log.completed_qty = work_order.qty + job_time_log.save(ignore_permissions=True) + job.time_logs.append(job_time_log) + job.save(ignore_permissions=True) job.submit() + start_date = job_time_log.to_time diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py index 69ba9007a61..d4b55e8e2c5 100644 --- a/erpnext/demo/user/sales.py +++ b/erpnext/demo/user/sales.py @@ -21,17 +21,26 @@ def work(domain="Manufacturing"): if random.random() < 0.5: make_quotation(domain) + try: + lost_reason = frappe.get_doc({ + "doctype": "Opportunity Lost Reason", + "lost_reason": "Did not ask" + }) + lost_reason.save(ignore_permissions=True) + except frappe.exceptions.DuplicateEntryError: + pass + # lost quotations / inquiries if random.random() < 0.3: for i in range(random.randint(1,3)): quotation = get_random('Quotation', doc=True) if quotation and quotation.status == 'Submitted': - quotation.declare_order_lost('Did not ask') + quotation.declare_order_lost([{'lost_reason': 'Did not ask'}]) for i in range(random.randint(1,3)): opportunity = get_random('Opportunity', doc=True) if opportunity and opportunity.status in ('Open', 'Replied'): - opportunity.declare_enquiry_lost('Did not ask') + opportunity.declare_enquiry_lost([{'lost_reason': 'Did not ask'}]) for i in range(random.randint(1,3)): if random.random() < 0.6: diff --git a/erpnext/demo/user/stock.py b/erpnext/demo/user/stock.py index 60b1edcac59..f95a6b83315 100644 --- a/erpnext/demo/user/stock.py +++ b/erpnext/demo/user/stock.py @@ -73,13 +73,13 @@ def make_stock_reconciliation(): stock_reco = frappe.new_doc("Stock Reconciliation") stock_reco.posting_date = frappe.flags.current_date stock_reco.company = erpnext.get_default_company() - stock_reco.get_items_for("Stores - WP") + stock_reco.get_items_for("Stores - WPL") if stock_reco.items: for item in stock_reco.items: if item.qty: item.qty = item.qty - round(random.randint(1, item.qty)) try: - stock_reco.insert() + stock_reco.insert(ignore_permissions=True) stock_reco.submit() frappe.db.commit() except OpeningEntryAccountError: diff --git a/erpnext/domains/education.py b/erpnext/domains/education.py index c6405764575..55e4eed801a 100644 --- a/erpnext/domains/education.py +++ b/erpnext/domains/education.py @@ -14,7 +14,7 @@ data = { 'Student Attendance Tool', 'Student Applicant' ], - 'default_portal_role': 'Guardian', + 'default_portal_role': 'LMS User', 'restricted_roles': [ 'Student', 'Instructor', diff --git a/erpnext/education/api.py b/erpnext/education/api.py index 30d55880732..1a19716b508 100644 --- a/erpnext/education/api.py +++ b/erpnext/education/api.py @@ -38,7 +38,7 @@ def enroll_student(source_name): program_enrollment.student = student.name program_enrollment.student_name = student.title program_enrollment.program = frappe.db.get_value("Student Applicant", source_name, "program") - frappe.publish_realtime('enroll_student_progress', {"progress": [4, 4]}, user=frappe.session.user) + frappe.publish_realtime('enroll_student_progress', {"progress": [2, 4]}, user=frappe.session.user) return program_enrollment @@ -69,7 +69,7 @@ def mark_attendance(students_present, students_absent, course_schedule=None, stu present = json.loads(students_present) absent = json.loads(students_absent) - + for d in present: make_attendance_records(d["student"], d["student_name"], "Present", course_schedule, student_group, date) @@ -89,7 +89,7 @@ def make_attendance_records(student, student_name, status, course_schedule=None, :param status: Status (Present/Absent) """ student_attendance = frappe.get_doc({ - "doctype": "Student Attendance", + "doctype": "Student Attendance", "student": student, "course_schedule": course_schedule, "student_group": student_group, @@ -112,7 +112,7 @@ def get_student_guardians(student): :param student: Student. """ - guardians = frappe.get_list("Student Guardian", fields=["guardian"] , + guardians = frappe.get_list("Student Guardian", fields=["guardian"] , filters={"parent": student}) return guardians @@ -353,7 +353,7 @@ def update_email_group(doctype, name): for guard in get_student_guardians(stud.student): email = frappe.db.get_value("Guardian", guard.guardian, "email_address") if email: - email_list.append(email) + email_list.append(email) add_subscribers(name, email_list) @frappe.whitelist() diff --git a/erpnext/education/doctype/article/__init__.py b/erpnext/education/doctype/article/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/article/article.js b/erpnext/education/doctype/article/article.js new file mode 100644 index 00000000000..4c9c6f01f05 --- /dev/null +++ b/erpnext/education/doctype/article/article.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Article', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/article/article.json b/erpnext/education/doctype/article/article.json new file mode 100644 index 00000000000..c30cd189d6b --- /dev/null +++ b/erpnext/education/doctype/article/article.json @@ -0,0 +1,230 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-10-17 05:45:38.471670", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "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": "Title", + "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": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "author", + "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": "Author", + "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, + "fieldname": "content", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Content", + "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, + "fieldname": "publish_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Publish Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:06:56.016865", + "modified_by": "Administrator", + "module": "Education", + "name": "Article", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/article/article.py b/erpnext/education/doctype/article/article.py new file mode 100644 index 00000000000..7dc850be37c --- /dev/null +++ b/erpnext/education/doctype/article/article.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 Article(Document): + + + def get_article(self): + pass + + diff --git a/erpnext/education/doctype/article/test_article.js b/erpnext/education/doctype/article/test_article.js new file mode 100644 index 00000000000..9dbf063e844 --- /dev/null +++ b/erpnext/education/doctype/article/test_article.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Article", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Article + () => frappe.tests.make('Article', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/article/test_article.py b/erpnext/education/doctype/article/test_article.py new file mode 100644 index 00000000000..2fce07f82c1 --- /dev/null +++ b/erpnext/education/doctype/article/test_article.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestArticle(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/content_activity/__init__.py b/erpnext/education/doctype/content_activity/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/content_activity/content_activity.json b/erpnext/education/doctype/content_activity/content_activity.json new file mode 100644 index 00000000000..b4c95dad9c3 --- /dev/null +++ b/erpnext/education/doctype/content_activity/content_activity.json @@ -0,0 +1,141 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-16 03:55:53.283893", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "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": "Content", + "length": 0, + "no_copy": 0, + "options": "Content", + "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, + "fetch_from": "content.content_type", + "fieldname": "content_type", + "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": "Content Type", + "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, + "fieldname": "last_activity", + "fieldtype": "Datetime", + "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": "Last Activity ", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-16 03:55:58.202436", + "modified_by": "Administrator", + "module": "Education", + "name": "Content Activity", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/content_activity/content_activity.py b/erpnext/education/doctype/content_activity/content_activity.py new file mode 100644 index 00000000000..2ae7a5c94c9 --- /dev/null +++ b/erpnext/education/doctype/content_activity/content_activity.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 ContentActivity(Document): + pass diff --git a/erpnext/education/doctype/content_question/__init__.py b/erpnext/education/doctype/content_question/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/content_question/content_question.js b/erpnext/education/doctype/content_question/content_question.js new file mode 100644 index 00000000000..7615f5efcf6 --- /dev/null +++ b/erpnext/education/doctype/content_question/content_question.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Content Question', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/content_question/content_question.json b/erpnext/education/doctype/content_question/content_question.json new file mode 100644 index 00000000000..d390e8ef70f --- /dev/null +++ b/erpnext/education/doctype/content_question/content_question.json @@ -0,0 +1,76 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-15 14:35:40.728454", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "question_link", + "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": 0, + "label": "Question Link", + "length": 0, + "no_copy": 0, + "options": "Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-15 14:41:31.729083", + "modified_by": "Administrator", + "module": "Education", + "name": "Content Question", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/content_question/content_question.py b/erpnext/education/doctype/content_question/content_question.py new file mode 100644 index 00000000000..b239d211a3f --- /dev/null +++ b/erpnext/education/doctype/content_question/content_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 ContentQuestion(Document): + pass diff --git a/erpnext/education/doctype/content_question/test_content_question.js b/erpnext/education/doctype/content_question/test_content_question.js new file mode 100644 index 00000000000..cc869a87fcf --- /dev/null +++ b/erpnext/education/doctype/content_question/test_content_question.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Content Question", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Content Question + () => frappe.tests.make('Content Question', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/content_question/test_content_question.py b/erpnext/education/doctype/content_question/test_content_question.py new file mode 100644 index 00000000000..268b9be2e7a --- /dev/null +++ b/erpnext/education/doctype/content_question/test_content_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestContentQuestion(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/course/course.json b/erpnext/education/doctype/course/course.json index 15360f8d582..072e8b4afba 100644 --- a/erpnext/education/doctype/course/course.json +++ b/erpnext/education/doctype/course/course.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -15,10 +16,12 @@ "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": "course_name", "fieldtype": "Data", "hidden": 0, @@ -41,14 +44,17 @@ "reqd": 1, "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, + "fetch_if_empty": 0, "fieldname": "department", "fieldtype": "Link", "hidden": 0, @@ -72,14 +78,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "parent_course", "fieldtype": "Data", "hidden": 0, @@ -102,14 +111,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -131,14 +143,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "course_code", "fieldtype": "Data", "hidden": 0, @@ -161,14 +176,17 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "unique": 0 + "translatable": 0, + "unique": 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": "course_abbreviation", "fieldtype": "Data", "hidden": 0, @@ -191,14 +209,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "section_break_6", "fieldtype": "Section Break", "hidden": 0, @@ -220,16 +241,53 @@ "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, + "fetch_if_empty": 0, + "fieldname": "topics", + "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": "Topics", + "length": 0, + "no_copy": 0, + "options": "Course Topic", + "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, + "fetch_if_empty": 0, "fieldname": "course_intro", - "fieldtype": "Text Editor", + "fieldtype": "Small Text", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -250,14 +308,50 @@ "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, + "fetch_if_empty": 0, + "fieldname": "hero_image", + "fieldtype": "Attach Image", + "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": "Hero Image", + "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, + "fetch_if_empty": 0, "fieldname": "assessment", "fieldtype": "Section Break", "hidden": 0, @@ -280,14 +374,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "default_grading_scale", "fieldtype": "Link", "hidden": 0, @@ -311,14 +408,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "assessment_criteria", "fieldtype": "Table", "hidden": 0, @@ -342,21 +442,20 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "idx": 0, - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-11-10 19:06:28.909585", + "modified": "2019-04-09 11:35:27.354877", "modified_by": "Administrator", "module": "Education", "name": "Course", @@ -365,7 +464,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -385,7 +483,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -397,7 +494,7 @@ "print": 1, "read": 1, "report": 1, - "role": "HR Manager", + "role": "Instructor", "set_user_permissions": 0, "share": 1, "submit": 0, @@ -406,12 +503,12 @@ ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Education", "search_fields": "course_name", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py index 69d2fca63e8..987823a01e9 100644 --- a/erpnext/education/doctype/course/course.py +++ b/erpnext/education/doctype/course/course.py @@ -10,7 +10,7 @@ from frappe import _ class Course(Document): def validate(self): self.validate_assessment_criteria() - + def validate_assessment_criteria(self): if self.assessment_criteria: total_weightage = 0 @@ -18,3 +18,11 @@ class Course(Document): total_weightage += criteria.weightage or 0 if total_weightage != 100: frappe.throw(_("Total Weightage of all Assessment Criteria must be 100%")) + + def get_topics(self): + try: + topic_list = self.get_all_children() + topic_data = [frappe.get_doc("Topic", topic.topic) for topic in topic_list] + except frappe.DoesNotExistError: + return None + return topic_data \ No newline at end of file diff --git a/erpnext/education/doctype/course/test_course.py b/erpnext/education/doctype/course/test_course.py index b18f4a94be7..a24ba8a5276 100644 --- a/erpnext/education/doctype/course/test_course.py +++ b/erpnext/education/doctype/course/test_course.py @@ -2,6 +2,7 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt from __future__ import unicode_literals +from erpnext.education.doctype.topic.test_topic import make_topic import frappe import unittest @@ -9,4 +10,35 @@ import unittest # test_records = frappe.get_test_records('Course') class TestCourse(unittest.TestCase): - pass + def setUp(self): + make_course_and_linked_topic("_Test Course 1", ["_Test Topic 1", "_Test Topic 2"]) + + def test_get_topics(self): + course = frappe.get_doc("Course", "_Test Course 1") + topics = course.get_topics() + self.assertEqual(topics[0].name, "_Test Topic 1") + self.assertEqual(topics[1].name, "_Test Topic 2") + frappe.db.rollback() + +def make_course(name): + try: + course = frappe.get_doc("Course", name) + except frappe.DoesNotExistError: + course = frappe.get_doc({ + "doctype": "Course", + "course_name": name, + "course_code": name + }).insert() + return course.name + +def make_course_and_linked_topic(course_name, topic_name_list): + try: + course = frappe.get_doc("Course", course_name) + except frappe.DoesNotExistError: + make_course(course_name) + course = frappe.get_doc("Course", course_name) + topic_list = [make_topic(topic_name) for topic_name in topic_name_list] + for topic in topic_list: + course.append("topics", {"topic": topic}) + course.save() + return course diff --git a/erpnext/education/doctype/course_activity/__init__.py b/erpnext/education/doctype/course_activity/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/course_activity/course_activity.js b/erpnext/education/doctype/course_activity/course_activity.js new file mode 100644 index 00000000000..5115fc415e1 --- /dev/null +++ b/erpnext/education/doctype/course_activity/course_activity.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Activity', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_activity/course_activity.json b/erpnext/education/doctype/course_activity/course_activity.json new file mode 100644 index 00000000000..99ae9aee561 --- /dev/null +++ b/erpnext/education/doctype/course_activity/course_activity.json @@ -0,0 +1,301 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "format:EDU-CA-{YYYY}-{#####}", + "beta": 1, + "creation": "2018-10-01 17:35:54.391413", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enrollment", + "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": "Enrollment", + "length": 0, + "no_copy": 0, + "options": "Course Enrollment", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "enrollment.course", + "fieldname": "course", + "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": 1, + "label": "Course", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "enrollment.student", + "fieldname": "student", + "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": 1, + "label": "Student", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content_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": "Content Type", + "length": 0, + "no_copy": 0, + "options": "\nArticle\nVideo", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "fieldtype": "Dynamic 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": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "options": "content_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": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "activity_date", + "fieldtype": "Datetime", + "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": "Activity Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-12-06 11:53:08.006123", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Activity", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 1, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_activity/course_activity.py b/erpnext/education/doctype/course_activity/course_activity.py new file mode 100644 index 00000000000..054b1920976 --- /dev/null +++ b/erpnext/education/doctype/course_activity/course_activity.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document + +class CourseActivity(Document): + def validate(self): + self.check_if_enrolled() + + + def check_if_enrolled(self): + if frappe.db.exists("Course Enrollment", self.enrollment): + return True + else: + frappe.throw(_("Course Enrollment {0} does not exists".format(self.enrollment))) \ No newline at end of file diff --git a/erpnext/education/doctype/course_activity/test_course_activity.js b/erpnext/education/doctype/course_activity/test_course_activity.js new file mode 100644 index 00000000000..c89c89e5d3c --- /dev/null +++ b/erpnext/education/doctype/course_activity/test_course_activity.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Activity", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Activity + () => frappe.tests.make('Course Activity', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_activity/test_course_activity.py b/erpnext/education/doctype/course_activity/test_course_activity.py new file mode 100644 index 00000000000..5269a6b71a3 --- /dev/null +++ b/erpnext/education/doctype/course_activity/test_course_activity.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestCourseActivity(unittest.TestCase): + pass + +def make_course_activity(enrollment, content_type, content): + activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment, 'content_type': content_type, 'content': content}) + try: + activity = frappe.get_doc("Course Activity", activity[0]['name']) + except (IndexError, frappe.DoesNotExistError): + activity = frappe.get_doc({ + "doctype": "Course Activity", + "enrollment": enrollment, + "content_type": content_type, + "content": content, + "activity_date": frappe.utils.datetime.datetime.now() + }).insert() + return activity diff --git a/erpnext/education/doctype/course_content/__init__.py b/erpnext/education/doctype/course_content/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/course_content/course_content.js b/erpnext/education/doctype/course_content/course_content.js new file mode 100644 index 00000000000..b9faf99aafa --- /dev/null +++ b/erpnext/education/doctype/course_content/course_content.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Content', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_content/course_content.json b/erpnext/education/doctype/course_content/course_content.json new file mode 100644 index 00000000000..378e56005f4 --- /dev/null +++ b/erpnext/education/doctype/course_content/course_content.json @@ -0,0 +1,142 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-01 13:04:09.313771", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "", + "fieldname": "content_type", + "fieldtype": "Select", + "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": "Content Type", + "length": 0, + "no_copy": 0, + "options": "\nArticle\nVideo\nQuiz", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "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, + "label": "", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "fieldtype": "Dynamic 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": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "options": "content_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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-17 07:36:04.029818", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Content", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_content/course_content.py b/erpnext/education/doctype/course_content/course_content.py new file mode 100644 index 00000000000..0d2f85ab500 --- /dev/null +++ b/erpnext/education/doctype/course_content/course_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 CourseContent(Document): + pass diff --git a/erpnext/education/doctype/course_content/test_course_content.js b/erpnext/education/doctype/course_content/test_course_content.js new file mode 100644 index 00000000000..786e67e9a37 --- /dev/null +++ b/erpnext/education/doctype/course_content/test_course_content.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Content", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Content + () => frappe.tests.make('Course Content', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_content/test_course_content.py b/erpnext/education/doctype/course_content/test_course_content.py new file mode 100644 index 00000000000..9be4b1f5ce3 --- /dev/null +++ b/erpnext/education/doctype/course_content/test_course_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestCourseContent(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/course_enrollment/__init__.py b/erpnext/education/doctype/course_enrollment/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.js b/erpnext/education/doctype/course_enrollment/course_enrollment.js new file mode 100644 index 00000000000..b5d3cc56e57 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/course_enrollment.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Enrollment', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.json b/erpnext/education/doctype/course_enrollment/course_enrollment.json new file mode 100644 index 00000000000..6286ec1e662 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/course_enrollment.json @@ -0,0 +1,233 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "format:EDU-CE-{YYYY}-{#####}", + "beta": 1, + "creation": "2018-10-15 15:35:39.375161", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "program_enrollment", + "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": "Program Enrollment", + "length": 0, + "no_copy": 0, + "options": "Program Enrollment", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student", + "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": 1, + "label": "Student", + "length": 0, + "no_copy": 0, + "options": "Student", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "course", + "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": 0, + "label": "Course", + "length": 0, + "no_copy": 0, + "options": "Course", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enrollment_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Enrollment Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 18:59:01.742377", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Enrollment", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.py b/erpnext/education/doctype/course_enrollment/course_enrollment.py new file mode 100644 index 00000000000..9508636d2ff --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/course_enrollment.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document +from functools import reduce + +class CourseEnrollment(Document): + def get_progress(self, student): + """ + Returns Progress of given student for a particular course enrollment + + :param self: Course Enrollment Object + :param student: Student Object + """ + course = frappe.get_doc('Course', self.course) + topics = course.get_topics() + progress = [] + for topic in topics: + progress.append(student.get_topic_progress(self.name, topic)) + return reduce(lambda x,y: x+y, progress) # Flatten out the List + + def validate_duplication(self): + enrollment = frappe.get_all("Course Enrollment", filters={ + "student": self.student, + "course": self.course, + "program_enrollment": self.program_enrollment + }) + if enrollment: + frappe.throw(_("Student is already enrolled.")) + + def add_quiz_activity(self, quiz_name, quiz_response,answers, score, status): + result = {k: ('Correct' if v else 'Wrong') for k,v in answers.items()} + result_data = [] + for key in answers: + item = {} + item['question'] = key + item['quiz_result'] = result[key] + try: + if isinstance(quiz_response[key], list): + item['selected_option'] = ', '.join(frappe.get_value('Options', res, 'option') for res in quiz_response[key]) + else: + item['selected_option'] = frappe.get_value('Options', quiz_response[key], 'option') + except KeyError: + item['selected_option'] = "Unattempted" + result_data.append(item) + + quiz_activity = frappe.get_doc({ + "doctype": "Quiz Activity", + "enrollment": self.name, + "quiz": quiz_name, + "activity_date": frappe.utils.datetime.datetime.now(), + "result": result_data, + "score": score, + "status": status + }).insert() + + def add_activity(self, content_type, content): + if check_activity_exists(self.name, content_type, content): + pass + else: + activity = frappe.get_doc({ + "doctype": "Course Activity", + "enrollment": self.name, + "content_type": content_type, + "content": content, + "activity_date": frappe.utils.datetime.datetime.now() + }) + activity.insert() + +def check_activity_exists(enrollment, content_type, content): + activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment, 'content_type': content_type, 'content': content}) + return bool(activity) \ No newline at end of file diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.js b/erpnext/education/doctype/course_enrollment/test_course_enrollment.js new file mode 100644 index 00000000000..216cc307991 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Enrollment", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Enrollment + () => frappe.tests.make('Course Enrollment', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py new file mode 100644 index 00000000000..5ecace2a603 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + + +from erpnext.education.doctype.student.test_student import create_student +from erpnext.education.doctype.student.test_student import get_student +from erpnext.education.doctype.program.test_program import setup_program +from erpnext.education.doctype.course_activity.test_course_activity import make_course_activity + +class TestCourseEnrollment(unittest.TestCase): + def setUp(self): + setup_program() + student = create_student({"first_name": "_Test First", "last_name": "_Test Last", "email": "_test_student_1@example.com"}) + program_enrollment = student.enroll_in_program("_Test Program") + course_enrollment = student.enroll_in_course("_Test Course 1", program_enrollment.name) + make_course_activity(course_enrollment.name, "Article", "_Test Article 1-1") + + def test_get_progress(self): + student = get_student("_test_student_1@example.com") + program_enrollment_name = frappe.get_list("Program Enrollment", filters={"student": student.name, "Program": "_Test Program"})[0].name + course_enrollment_name = frappe.get_list("Course Enrollment", filters={"student": student.name, "course": "_Test Course 1", "program_enrollment": program_enrollment_name})[0].name + course_enrollment = frappe.get_doc("Course Enrollment", course_enrollment_name) + progress = course_enrollment.get_progress(student) + finished = {'content': '_Test Article 1-1', 'content_type': 'Article', 'is_complete': True} + self.assertTrue(finished in progress) + frappe.db.rollback() + + + diff --git a/erpnext/education/doctype/course_topic/__init__.py b/erpnext/education/doctype/course_topic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/course_topic/course_topic.js b/erpnext/education/doctype/course_topic/course_topic.js new file mode 100644 index 00000000000..7d03ba32ff2 --- /dev/null +++ b/erpnext/education/doctype/course_topic/course_topic.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Topic', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_topic/course_topic.json b/erpnext/education/doctype/course_topic/course_topic.json new file mode 100644 index 00000000000..3fcddc169e1 --- /dev/null +++ b/erpnext/education/doctype/course_topic/course_topic.json @@ -0,0 +1,109 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-12-12 11:51:25.952740", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "topic", + "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": 0, + "label": "Topic", + "length": 0, + "no_copy": 0, + "options": "Topic", + "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, + "fetch_from": "topic.topic_name", + "fieldname": "topic_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": "Topic Name", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-12-12 13:01:58.960425", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Topic", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_topic/course_topic.py b/erpnext/education/doctype/course_topic/course_topic.py new file mode 100644 index 00000000000..2364f17a49c --- /dev/null +++ b/erpnext/education/doctype/course_topic/course_topic.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 CourseTopic(Document): + pass diff --git a/erpnext/education/doctype/course_topic/test_course_topic.js b/erpnext/education/doctype/course_topic/test_course_topic.js new file mode 100644 index 00000000000..d8d154fb9c8 --- /dev/null +++ b/erpnext/education/doctype/course_topic/test_course_topic.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Topic", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Topic + () => frappe.tests.make('Course Topic', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_topic/test_course_topic.py b/erpnext/education/doctype/course_topic/test_course_topic.py new file mode 100644 index 00000000000..7ce46d28adf --- /dev/null +++ b/erpnext/education/doctype/course_topic/test_course_topic.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestCourseTopic(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json index c1eaa112848..3be4988d217 100644 --- a/erpnext/education/doctype/education_settings/education_settings.json +++ b/erpnext/education/doctype/education_settings/education_settings.json @@ -1,359 +1,492 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-04-05 13:33:04.519313", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-04-05 13:33:04.519313", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_academic_year", - "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": "Current Academic Year", - "length": 0, - "no_copy": 0, - "options": "Academic Year", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "current_academic_year", + "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": "Current Academic Year", + "length": 0, + "no_copy": 0, + "options": "Academic Year", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_academic_term", - "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": "Current Academic Term", - "length": 0, - "no_copy": 0, - "options": "Academic Term", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "current_academic_term", + "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": "Current Academic Term", + "length": 0, + "no_copy": 0, + "options": "Academic Term", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attendance_freeze_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Attendance Freeze Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "attendance_freeze_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Attendance Freeze Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_4", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.", - "fieldname": "validate_batch", - "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": "Validate Batch for Students in Student Group", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.", + "fieldname": "validate_batch", + "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": "Validate Batch for Students in Student Group", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.", - "fieldname": "validate_course", - "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": "Validate Enrolled Course for Students in Student Group", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.", + "fieldname": "validate_course", + "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": "Validate Enrolled Course for Students in Student Group", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "description": "If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.", - "fieldname": "academic_term_reqd", - "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": "Make Academic Term Mandatory", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "description": "If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.", + "fieldname": "academic_term_reqd", + "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": "Make Academic Term Mandatory", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "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, - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_7", + "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, + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Full Name", - "fieldname": "instructor_created_by", - "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": "Instructor Records to be created by", - "length": 0, - "no_copy": 0, - "options": "Full Name\nNaming Series\nEmployee Number", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Full Name", + "fieldname": "instructor_created_by", + "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": "Instructor Records to be created by", + "length": 0, + "no_copy": 0, + "options": "Full Name\nNaming Series\nEmployee Number", + "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, + "fieldname": "web_academy_settings_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": "LMS Settings", + "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, + "fieldname": "portal_title", + "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": "Portal Title", + "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, + "fieldname": "description", + "fieldtype": "Small 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": "Description", + "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 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-26 04:43:35.406690", - "modified_by": "Administrator", - "module": "Education", - "name": "Education Settings", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2018-12-11 15:49:15.045116", + "modified_by": "Administrator", + "module": "Education", + "name": "Education Settings", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Education Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Education Manager", + "set_user_permissions": 0, + "share": 1, + "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": 0, + "role": "Guest", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Education", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/education/doctype/options/__init__.py b/erpnext/education/doctype/options/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/options/options.json b/erpnext/education/doctype/options/options.json new file mode 100644 index 00000000000..59deab7837d --- /dev/null +++ b/erpnext/education/doctype/options/options.json @@ -0,0 +1,107 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-15 14:05:28.601274", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "option", + "fieldtype": "Small Text", + "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": "Option", + "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, + "fieldname": "is_correct", + "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 Correct", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-15 14:16:18.303156", + "modified_by": "Administrator", + "module": "Education", + "name": "Options", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/options/options.py b/erpnext/education/doctype/options/options.py new file mode 100644 index 00000000000..a11d77afb23 --- /dev/null +++ b/erpnext/education/doctype/options/options.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 Options(Document): + pass diff --git a/erpnext/education/doctype/program/program.js b/erpnext/education/doctype/program/program.js index 5146a193221..98263b55a1f 100644 --- a/erpnext/education/doctype/program/program.js +++ b/erpnext/education/doctype/program/program.js @@ -4,40 +4,5 @@ cur_frm.add_fetch('fee_structure', 'total_amount', 'amount'); frappe.ui.form.on("Program", "refresh", function(frm) { - if(!frm.doc.__islocal) { - frm.add_custom_button(__("Student Applicant"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Student Applicant"); - }); - - frm.add_custom_button(__("Program Enrollment"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Program Enrollment"); - }); - - frm.add_custom_button(__("Student Group"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Student Group"); - }); - - frm.add_custom_button(__("Fee Structure"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Fee Structure"); - }); - - frm.add_custom_button(__("Fees"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Fees"); - }); - } + }); \ No newline at end of file diff --git a/erpnext/education/doctype/program/program.json b/erpnext/education/doctype/program/program.json index 05e35a2a539..cb8d7786e1d 100644 --- a/erpnext/education/doctype/program/program.json +++ b/erpnext/education/doctype/program/program.json @@ -1,277 +1,627 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:program_code", - "beta": 0, - "creation": "2015-09-07 12:54:03.609282", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:program_code", + "beta": 0, + "creation": "2015-09-07 12:54:03.609282", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_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": "Program Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "program_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": "Program Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "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": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "department", + "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": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "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, + "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_3", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Program Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "program_code", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Program Code", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 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": "program_abbreviation", + "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": "Program Abbreviation", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_abbreviation", - "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": "Program Abbreviation", - "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, + "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": "section_break_5", + "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": "Portal Settings", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "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": "Course", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "courses", + "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": "Courses", + "length": 0, + "no_copy": 0, + "options": "Program Course", + "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "courses", - "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": "Courses", - "length": 0, - "no_copy": 0, - "options": "Program Course", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_9", + "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, + "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, + "fetch_if_empty": 0, + "fieldname": "description", + "fieldtype": "Small 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": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "intro_video", + "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": "Intro Video", + "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, + "fetch_if_empty": 0, + "fieldname": "hero_image", + "fieldtype": "Attach Image", + "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": "Hero Image", + "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, + "fetch_if_empty": 0, + "fieldname": "column_break_11", + "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, + "default": "0", + "fetch_if_empty": 0, + "fieldname": "is_published", + "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 Published", + "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, + "default": "0", + "fetch_if_empty": 0, + "fieldname": "is_featured", + "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 Featured", + "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 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2017-11-10 18:56:18.413911", - "modified_by": "Administrator", - "module": "Education", - "name": "Program", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "menu_index": 0, + "modified": "2019-03-18 15:26:56.737903", + "modified_by": "Administrator", + "module": "Education", + "name": "Program", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Guest", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Student", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "search_fields": "program_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Education", + "route": "", + "search_fields": "program_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/education/doctype/program/program.py b/erpnext/education/doctype/program/program.py index f626880b2af..dbeda40775f 100644 --- a/erpnext/education/doctype/program/program.py +++ b/erpnext/education/doctype/program/program.py @@ -7,4 +7,8 @@ import frappe from frappe.model.document import Document class Program(Document): - pass \ No newline at end of file + + def get_course_list(self): + program_course_list = self.get_all_children() + course_list = [frappe.get_doc("Course", program_course.course) for program_course in program_course_list] + return course_list \ No newline at end of file diff --git a/erpnext/education/doctype/program/program_dashboard.py b/erpnext/education/doctype/program/program_dashboard.py new file mode 100644 index 00000000000..cb8f74207ee --- /dev/null +++ b/erpnext/education/doctype/program/program_dashboard.py @@ -0,0 +1,20 @@ +from frappe import _ + +def get_data(): + return { + 'fieldname': 'program', + 'transactions': [ + { + 'label': _('Admission and Enrollment'), + 'items': ['Student Applicant', 'Program Enrollment'] + }, + { + 'label': _('Student Activity'), + 'items': ['Student Group' ] + }, + { + 'label': _('Fee'), + 'items': ['Fees','Fee Structure'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/program/test_program.py b/erpnext/education/doctype/program/test_program.py index a4accdaaf7c..3bcca3a7f18 100644 --- a/erpnext/education/doctype/program/test_program.py +++ b/erpnext/education/doctype/program/test_program.py @@ -2,11 +2,85 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt from __future__ import unicode_literals +from erpnext.education.doctype.course.test_course import make_course +from erpnext.education.doctype.topic.test_topic import make_topic_and_linked_content +from erpnext.education.doctype.course.test_course import make_course_and_linked_topic import frappe import unittest -# test_records = frappe.get_test_records('Program') +test_data = { + "program_name": "_Test Program", + "description": "_Test Program", + "course": [{ + "course_name": "_Test Course 1", + "topic": [{ + "topic_name": "_Test Topic 1-1", + "content": [{ + "type": "Article", + "name": "_Test Article 1-1" + }, { + "type": "Article", + "name": "_Test Article 1-2" + }] + }, + { + "topic_name": "_Test Topic 1-2", + "content": [{ + "type": "Article", + "name": "_Test Article 1-3" + }, { + "type": "Article", + "name": "_Test Article 1-4" + }] + } + ] + }] +} class TestProgram(unittest.TestCase): - pass + def setUp(self): + make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"]) + + def test_get_course_list(self): + program = frappe.get_doc("Program", "_Test Program 1") + course = program.get_course_list() + self.assertEqual(course[0].name, "_Test Course 1") + self.assertEqual(course[1].name, "_Test Course 2") + frappe.db.rollback() + +def make_program(name): + program = frappe.get_doc({ + "doctype": "Program", + "program_name": name, + "program_code": name, + "description": "_test description", + "is_published": True, + "is_featured": True, + }).insert() + return program.name + +def make_program_and_linked_courses(program_name, course_name_list): + try: + program = frappe.get_doc("Program", program_name) + except frappe.DoesNotExistError: + make_program(program_name) + program = frappe.get_doc("Program", program_name) + course_list = [make_course(course_name) for course_name in course_name_list] + for course in course_list: + program.append("courses", {"course": course}) + program.save() + return program + +def setup_program(): + topic_list = [course['topic'] for course in test_data['course']] + for topic in topic_list[0]: + make_topic_and_linked_content(topic['topic_name'], topic['content']) + + all_courses_list = [{'course': course['course_name'], 'topic': [topic['topic_name'] for topic in course['topic']]} for course in test_data['course']] # returns [{'course': 'Applied Math', 'topic': ['Trignometry', 'Geometry']}] + for course in all_courses_list: + make_course_and_linked_topic(course['course'], course['topic']) + + course_list = [course['course_name'] for course in test_data['course']] + program = make_program_and_linked_courses(test_data['program_name'], course_list) + return program \ No newline at end of file diff --git a/erpnext/education/doctype/program/test_records.json b/erpnext/education/doctype/program/test_records.json index e5eda70982f..7901db32250 100644 --- a/erpnext/education/doctype/program/test_records.json +++ b/erpnext/education/doctype/program/test_records.json @@ -1,12 +1,14 @@ [ { - "program_name": "_Test Program", + "program_name": "_Test Program 1", "program_code": "_TP1", + "description": "Test Description", "program_abbreviation": "TP1" }, { "program_name": "_Test Program 2", "program_code": "_TP2", + "description": "Test Description", "program_abbreviation": "TP2" } -] +] \ No newline at end of file diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.json b/erpnext/education/doctype/program_enrollment/program_enrollment.json index 5f4621f368e..1d8a4344a7b 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.json +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, @@ -714,7 +715,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-21 16:15:35.014952", + "modified": "2018-11-07 21:13:06.502279", "modified_by": "Administrator", "module": "Education", "name": "Program Enrollment", @@ -739,6 +740,25 @@ "share": 1, "submit": 1, "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 } ], "quick_entry": 0, diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py index 320a58a9249..22cca86fcf5 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py @@ -8,6 +8,7 @@ from frappe import msgprint, _ from frappe.model.document import Document from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import comma_and +import erpnext.www.lms as lms class ProgramEnrollment(Document): def validate(self): @@ -16,11 +17,12 @@ class ProgramEnrollment(Document): self.student_name = frappe.db.get_value("Student", self.student, "title") if not self.courses: self.extend("courses", self.get_courses()) - + def on_submit(self): self.update_student_joining_date() self.make_fee_records() - + self.create_course_enrollments() + def validate_duplication(self): enrollment = frappe.get_all("Program Enrollment", filters={ "student": self.student, @@ -31,11 +33,11 @@ class ProgramEnrollment(Document): }) if enrollment: frappe.throw(_("Student is already enrolled.")) - + def update_student_joining_date(self): date = frappe.db.sql("select min(enrollment_date) from `tabProgram Enrollment` where student= %s", self.student) frappe.db.set_value("Student", self.student, "joining_date", date) - + def make_fee_records(self): from erpnext.education.api import get_fee_components fee_list = [] @@ -54,7 +56,7 @@ class ProgramEnrollment(Document): "program_enrollment": self.name, "components": fee_components }) - + fees.save() fees.submit() fee_list.append(fees.name) @@ -66,6 +68,56 @@ class ProgramEnrollment(Document): def get_courses(self): return frappe.db.sql('''select course, course_name from `tabProgram Course` where parent = %s and required = 1''', (self.program), as_dict=1) + def create_course_enrollments(self): + student = frappe.get_doc("Student", self.student) + program = frappe.get_doc("Program", self.program) + course_list = [course.course for course in program.get_all_children()] + for course_name in course_list: + student.enroll_in_course(course_name=course_name, program_enrollment=self.name) + + def get_all_course_enrollments(self): + course_enrollment_names = frappe.get_list("Course Enrollment", filters={'program_enrollment': self.name}) + return [frappe.get_doc('Course Enrollment', course_enrollment.name) for course_enrollment in course_enrollment_names] + + def get_quiz_progress(self): + student = frappe.get_doc("Student", self.student) + quiz_progress = frappe._dict() + progress_list = [] + for course_enrollment in self.get_all_course_enrollments(): + course_progress = course_enrollment.get_progress(student) + for progress_item in course_progress: + if progress_item['content_type'] == "Quiz": + progress_item['course'] = course_enrollment.course + progress_list.append(progress_item) + if not progress_list: + return None + quiz_progress.quiz_attempt = progress_list + quiz_progress.name = self.program + quiz_progress.program = self.program + return quiz_progress + + def get_program_progress(self): + import math + program = frappe.get_doc("Program", self.program) + program_progress = {} + progress = [] + for course in program.get_all_children(): + course_progress = lms.get_student_course_details(course.course, self.program) + is_complete = False + if course_progress['flag'] == "Completed": + is_complete = True + progress.append({'course_name': course.course_name, 'name': course.course, 'is_complete': is_complete}) + + program_progress['progress'] = progress + program_progress['name'] = self.program + program_progress['program'] = frappe.get_value("Program", self.program, 'program_name') + + try: + program_progress['percentage'] = math.ceil((sum([item['is_complete'] for item in progress] * 100)/len(progress))) + except ZeroDivisionError: + program_progress['percentage'] = 0 + + return program_progress @frappe.whitelist() def get_program_courses(doctype, txt, searchfield, start, page_len, filters): @@ -102,11 +154,11 @@ def get_students(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select name, title from tabStudent - where + where name not in (%s) - and + and `%s` LIKE %s - order by + order by idx desc, name limit %s, %s"""%( ", ".join(['%s']*len(students)), searchfield, "%s", "%s", "%s"), diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py index c26e89920b0..c6cbee1b75a 100644 --- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py @@ -6,7 +6,21 @@ from __future__ import unicode_literals import frappe import unittest -# test_records = frappe.get_test_records('Program Enrollment') +from erpnext.education.doctype.student.test_student import create_student +from erpnext.education.doctype.student.test_student import get_student +from erpnext.education.doctype.program.test_program import make_program_and_linked_courses +from erpnext.education.doctype.course_activity.test_course_activity import make_course_activity class TestProgramEnrollment(unittest.TestCase): - pass + + def setUp(self): + create_student({"first_name": "_Test Name", "last_name": "_Test Last Name", "email": "_test_student@example.com"}) + make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"]) + + def test_create_course_enrollments(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + course_enrollments = student.get_all_course_enrollments() + self.assertTrue("_Test Course 1" in course_enrollments.keys()) + self.assertTrue("_Test Course 2" in course_enrollments.keys()) + frappe.db.rollback() \ No newline at end of file diff --git a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py index db23ac744d8..9f8f9f4dc00 100644 --- a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py +++ b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py @@ -48,7 +48,7 @@ class ProgramEnrollmentTool(Document): return students else: frappe.throw(_("No students Found")) - + def enroll_students(self): total = len(self.students) for i, stud in enumerate(self.students): diff --git a/erpnext/education/doctype/question/__init__.py b/erpnext/education/doctype/question/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/question/question.js b/erpnext/education/doctype/question/question.js new file mode 100644 index 00000000000..01b3091b58d --- /dev/null +++ b/erpnext/education/doctype/question/question.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Question', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/question/question.json b/erpnext/education/doctype/question/question.json new file mode 100644 index 00000000000..14a9f3ce922 --- /dev/null +++ b/erpnext/education/doctype/question/question.json @@ -0,0 +1,167 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "format:QUESTION-{#####}", + "beta": 0, + "creation": "2018-10-01 15:58:00.696815", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "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": "question", + "fieldtype": "Small Text", + "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": "Question", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "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": "options", + "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": "Options", + "length": 0, + "no_copy": 0, + "options": "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": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-04-22 14:02:08.140652", + "modified_by": "Administrator", + "module": "Education", + "name": "Question", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py new file mode 100644 index 00000000000..8cd23983ce9 --- /dev/null +++ b/erpnext/education/doctype/question/question.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document + +class Question(Document): + + def validate(self): + self.check_at_least_one_option() + self.check_minimum_one_correct_answer() + + def check_at_least_one_option(self): + if len(self.options) <= 1: + frappe.throw(_("A question must have more than one options")) + else: + pass + + def check_minimum_one_correct_answer(self): + correct_options = [option.is_correct for option in self.options] + if bool(sum(correct_options)): + pass + else: + frappe.throw(_("A qustion must have at least one correct options")) + + def get_answer(self): + options = self.options + answers = [item.name for item in options if item.is_correct == True] + if len(answers) == 0: + frappe.throw("No correct answer is set for {0}".format(self.name)) + return None + elif len(answers) == 1: + return answers[0] + else: + return answers \ No newline at end of file diff --git a/erpnext/education/doctype/question/test_question.js b/erpnext/education/doctype/question/test_question.js new file mode 100644 index 00000000000..509939c6b56 --- /dev/null +++ b/erpnext/education/doctype/question/test_question.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Question", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Question + () => frappe.tests.make('Question', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/question/test_question.py b/erpnext/education/doctype/question/test_question.py new file mode 100644 index 00000000000..552872e15fb --- /dev/null +++ b/erpnext/education/doctype/question/test_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuestion(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/quiz/__init__.py b/erpnext/education/doctype/quiz/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/quiz/quiz.js b/erpnext/education/doctype/quiz/quiz.js new file mode 100644 index 00000000000..122cf37818b --- /dev/null +++ b/erpnext/education/doctype/quiz/quiz.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quiz', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/quiz/quiz.json b/erpnext/education/doctype/quiz/quiz.json new file mode 100644 index 00000000000..f91bc0f0219 --- /dev/null +++ b/erpnext/education/doctype/quiz/quiz.json @@ -0,0 +1,299 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-10-17 05:52:50.149904", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "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": "Title", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "question", + "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": "Question", + "length": 0, + "no_copy": 0, + "options": "Quiz Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "quiz_configuration_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": "Quiz Configuration", + "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, + "fieldname": "passing_score", + "fieldtype": "Float", + "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": "Passing Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "description": "Enter 0 to waive limit", + "fieldname": "max_attempts", + "fieldtype": "Int", + "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": "Max Attempts", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Last Highest Score", + "fieldname": "grading_basis", + "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": "Grading Basis", + "length": 0, + "no_copy": 0, + "options": "\nLast Attempt\nLast Highest Score", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:07:36.190116", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py new file mode 100644 index 00000000000..6da50a6e258 --- /dev/null +++ b/erpnext/education/doctype/quiz/quiz.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 Quiz(Document): + + def validate_quiz_attempts(self, enrollment, quiz_name): + if self.max_attempts > 0: + try: + if len(frappe.get_all("Quiz Activity", {'enrollment': enrollment.name, 'quiz': quiz_name})) >= self.max_attempts: + frappe.throw('Maximum attempts reached!') + except Exception as e: + pass + + + def evaluate(self, response_dict, quiz_name): + # self.validate_quiz_attempts(enrollment, quiz_name) + questions = [frappe.get_doc('Question', question.question_link) for question in self.question] + answers = {q.name:q.get_answer() for q in questions} + correct_answers = {} + for key in answers: + try: + if isinstance(response_dict[key], list): + result = compare_list_elementwise(response_dict[key], answers[key]) + else: + result = (response_dict[key] == answers[key]) + except: + result = False + correct_answers[key] = result + score = (sum(correct_answers.values()) * 100 ) / len(answers) + if score >= self.passing_score: + status = "Pass" + else: + status = "Fail" + return correct_answers, score, status + + + def get_questions(self): + quiz_question = self.get_all_children() + if quiz_question: + questions = [frappe.get_doc('Question', question.question_link).as_dict() for question in quiz_question] + for question in questions: + correct_options = [option.is_correct for option in question.options] + if sum(correct_options) > 1: + question['type'] = "MultipleChoice" + else: + question['type'] = "SingleChoice" + return questions + else: + return None + +def compare_list_elementwise(*args): + try: + if all(len(args[0]) == len(_arg) for _arg in args[1:]): + return all(all([element in (item) for element in args[0]]) for item in args[1:]) + else: + return False + except TypeError: + frappe.throw("Compare List function takes on list arguments") + diff --git a/erpnext/education/doctype/quiz/test_quiz.js b/erpnext/education/doctype/quiz/test_quiz.js new file mode 100644 index 00000000000..147d13952ab --- /dev/null +++ b/erpnext/education/doctype/quiz/test_quiz.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Quiz", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Quiz + () => frappe.tests.make('Quiz', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/quiz/test_quiz.py b/erpnext/education/doctype/quiz/test_quiz.py new file mode 100644 index 00000000000..344fd544ee8 --- /dev/null +++ b/erpnext/education/doctype/quiz/test_quiz.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuiz(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/quiz_activity/__init__.py b/erpnext/education/doctype/quiz_activity/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.js b/erpnext/education/doctype/quiz_activity/quiz_activity.js new file mode 100644 index 00000000000..f6ba12ca4dc --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/quiz_activity.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quiz Activity', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.json b/erpnext/education/doctype/quiz_activity/quiz_activity.json new file mode 100644 index 00000000000..e78db42f7d3 --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/quiz_activity.json @@ -0,0 +1,490 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "format:EDU-QA-{YYYY}-{#####}", + "beta": 1, + "creation": "2018-10-15 15:48:40.482821", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enrollment", + "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": "Enrollment", + "length": 0, + "no_copy": 0, + "options": "Course Enrollment", + "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": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "enrollment.student", + "fieldname": "student", + "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": 0, + "label": "Student", + "length": 0, + "no_copy": 0, + "options": "Student", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "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, + "fetch_from": "enrollment.course", + "fieldname": "course", + "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": "Course", + "length": 0, + "no_copy": 0, + "options": "Course", + "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": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_5", + "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, + "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, + "fieldname": "quiz", + "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": 0, + "label": "Quiz", + "length": 0, + "no_copy": 0, + "options": "Quiz", + "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": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_7", + "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, + "fieldname": "status", + "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": "Status", + "length": 0, + "no_copy": 0, + "options": "\nPass\nFail", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_9", + "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, + "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, + "fieldname": "result", + "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": "Result", + "length": 0, + "no_copy": 0, + "options": "Quiz Result", + "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": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "activity_date", + "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": "Activity Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "score", + "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": "Score", + "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": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:05:52.434437", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz Activity", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.py b/erpnext/education/doctype/quiz_activity/quiz_activity.py new file mode 100644 index 00000000000..24c7175397d --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/quiz_activity.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 QuizActivity(Document): + pass diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.js b/erpnext/education/doctype/quiz_activity/test_quiz_activity.js new file mode 100644 index 00000000000..94b5ab796ac --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/test_quiz_activity.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Quiz Activity", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Quiz Activity + () => frappe.tests.make('Quiz Activity', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.py b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py new file mode 100644 index 00000000000..fb0425d8096 --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuizActivity(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/quiz_question/__init__.py b/erpnext/education/doctype/quiz_question/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/quiz_question/quiz_question.json b/erpnext/education/doctype/quiz_question/quiz_question.json new file mode 100644 index 00000000000..3857c5ca8d3 --- /dev/null +++ b/erpnext/education/doctype/quiz_question/quiz_question.json @@ -0,0 +1,110 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-17 06:13:00.098883", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "question_link", + "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": 0, + "label": "Question Link", + "length": 0, + "no_copy": 0, + "options": "Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "question_link.question", + "fieldname": "question", + "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": "Question", + "length": 0, + "no_copy": 0, + "options": "", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-18 15:35:12.195250", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz Question", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz_question/quiz_question.py b/erpnext/education/doctype/quiz_question/quiz_question.py new file mode 100644 index 00000000000..317e75b2cb2 --- /dev/null +++ b/erpnext/education/doctype/quiz_question/quiz_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 QuizQuestion(Document): + pass diff --git a/erpnext/education/doctype/quiz_result/__init__.py b/erpnext/education/doctype/quiz_result/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/quiz_result/quiz_result.js b/erpnext/education/doctype/quiz_result/quiz_result.js new file mode 100644 index 00000000000..a018749e698 --- /dev/null +++ b/erpnext/education/doctype/quiz_result/quiz_result.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quiz Result', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/quiz_result/quiz_result.json b/erpnext/education/doctype/quiz_result/quiz_result.json new file mode 100644 index 00000000000..86505ac756a --- /dev/null +++ b/erpnext/education/doctype/quiz_result/quiz_result.json @@ -0,0 +1,145 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-15 15:52:25.766374", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "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": "question", + "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": 0, + "label": "Question", + "length": 0, + "no_copy": 0, + "options": "Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "selected_option", + "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": "Selected Option", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "quiz_result", + "fieldtype": "Select", + "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": "Result", + "length": 0, + "no_copy": 0, + "options": "\nCorrect\nWrong", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-03-27 17:58:54.388848", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz Result", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz_result/quiz_result.py b/erpnext/education/doctype/quiz_result/quiz_result.py new file mode 100644 index 00000000000..a4fd9f062f3 --- /dev/null +++ b/erpnext/education/doctype/quiz_result/quiz_result.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 QuizResult(Document): + pass diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.js b/erpnext/education/doctype/quiz_result/test_quiz_result.js new file mode 100644 index 00000000000..43f53a1dc77 --- /dev/null +++ b/erpnext/education/doctype/quiz_result/test_quiz_result.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Quiz Result", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Quiz Result + () => frappe.tests.make('Quiz Result', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.py b/erpnext/education/doctype/quiz_result/test_quiz_result.py new file mode 100644 index 00000000000..86ee52d87d3 --- /dev/null +++ b/erpnext/education/doctype/quiz_result/test_quiz_result.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuizResult(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/student/student.json b/erpnext/education/doctype/student/student.json index af6c8b16bc7..bee915e91d3 100644 --- a/erpnext/education/doctype/student/student.json +++ b/erpnext/education/doctype/student/student.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_1", "fieldtype": "Section Break", "hidden": 0, @@ -52,6 +54,7 @@ "collapsible": 0, "columns": 0, "default": "1", + "fetch_if_empty": 0, "fieldname": "enabled", "fieldtype": "Check", "hidden": 0, @@ -84,6 +87,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_3", "fieldtype": "Section Break", "hidden": 0, @@ -115,6 +119,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "first_name", "fieldtype": "Data", "hidden": 0, @@ -147,6 +152,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "middle_name", "fieldtype": "Data", "hidden": 0, @@ -179,6 +185,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "last_name", "fieldtype": "Data", "hidden": 0, @@ -211,6 +218,41 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "user", + "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": "User ID", + "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 + }, + { + "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_4", "fieldtype": "Column Break", "hidden": 0, @@ -243,6 +285,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 0, @@ -276,6 +319,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "student_email_id", "fieldtype": "Data", "hidden": 0, @@ -295,7 +339,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -308,6 +352,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "student_mobile_number", "fieldtype": "Data", "hidden": 0, @@ -342,6 +387,7 @@ "collapsible": 0, "columns": 0, "default": "Today", + "fetch_if_empty": 0, "fieldname": "joining_date", "fieldtype": "Date", "hidden": 0, @@ -374,6 +420,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, @@ -408,6 +455,7 @@ "collapsible": 0, "collapsible_depends_on": "", "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_7", "fieldtype": "Section Break", "hidden": 0, @@ -440,6 +488,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "date_of_birth", "fieldtype": "Date", "hidden": 0, @@ -472,6 +521,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "blood_group", "fieldtype": "Select", "hidden": 0, @@ -505,6 +555,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -536,6 +587,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "gender", "fieldtype": "Select", "hidden": 0, @@ -569,6 +621,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "nationality", "fieldtype": "Data", "hidden": 0, @@ -602,6 +655,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "student_applicant", "fieldtype": "Link", "hidden": 0, @@ -635,6 +689,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_22", "fieldtype": "Section Break", "hidden": 0, @@ -667,6 +722,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "address_line_1", "fieldtype": "Data", "hidden": 0, @@ -699,6 +755,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "address_line_2", "fieldtype": "Data", "hidden": 0, @@ -731,6 +788,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "pincode", "fieldtype": "Data", "hidden": 0, @@ -763,6 +821,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_20", "fieldtype": "Column Break", "hidden": 0, @@ -794,6 +853,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "city", "fieldtype": "Data", "hidden": 0, @@ -826,6 +886,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "state", "fieldtype": "Data", "hidden": 0, @@ -858,6 +919,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_18", "fieldtype": "Section Break", "hidden": 0, @@ -890,6 +952,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "guardians", "fieldtype": "Table", "hidden": 0, @@ -923,6 +986,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_20", "fieldtype": "Section Break", "hidden": 0, @@ -956,6 +1020,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "siblings", "fieldtype": "Table", "hidden": 0, @@ -989,6 +1054,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "exit", "fieldtype": "Section Break", "hidden": 0, @@ -1021,6 +1087,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "date_of_leaving", "fieldtype": "Date", "hidden": 0, @@ -1053,6 +1120,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "leaving_certificate_number", "fieldtype": "Data", "hidden": 0, @@ -1085,6 +1153,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_31", "fieldtype": "Column Break", "hidden": 0, @@ -1116,6 +1185,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "reason_for_leaving", "fieldtype": "Text", "hidden": 0, @@ -1149,6 +1219,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -1176,18 +1247,16 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_field": "image", - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-21 14:44:35.278833", + "modified": "2019-04-10 17:46:26.893020", "modified_by": "Administrator", "module": "Education", "name": "Student", @@ -1231,11 +1300,48 @@ "share": 1, "submit": 0, "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Student", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 } ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Education", "show_name_in_global_search": 1, "sort_field": "modified", diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py index 53bf6f72730..cf8407c4eac 100644 --- a/erpnext/education/doctype/student/student.py +++ b/erpnext/education/doctype/student/student.py @@ -7,7 +7,7 @@ import frappe from frappe.model.document import Document from frappe import _ from frappe.desk.form.linked_with import get_linked_doctypes - +from erpnext.education.utils import check_content_completion, check_quiz_completion class Student(Document): def validate(self): self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) @@ -39,11 +39,99 @@ class Student(Document): if student: frappe.throw(_("Student {0} exist against student applicant {1}").format(student[0][0], self.student_applicant)) + def after_insert(self): + self.create_student_user() + + def create_student_user(self): + """Create a website user for student creation if not already exists""" + if not frappe.db.exists("User", self.student_email_id): + student_user = frappe.get_doc({ + 'doctype':'User', + 'first_name': self.first_name, + 'last_name': self.last_name, + 'email': self.student_email_id, + 'gender': self.gender, + 'send_welcome_email': 1, + 'user_type': 'Website User' + }) + student_user.add_roles("Student", "LMS User") + student_user.save() + update_password_link = student_user.reset_password() + def update_applicant_status(self): """Updates Student Applicant status to Admitted""" if self.student_applicant: frappe.db.set_value("Student Applicant", self.student_applicant, "application_status", "Admitted") + def get_all_course_enrollments(self): + """Returns a list of course enrollments linked with the current student""" + course_enrollments = frappe.get_all("Course Enrollment", filters={"student": self.name}, fields=['course', 'name']) + if not course_enrollments: + return None + else: + enrollments = {item['course']:item['name'] for item in course_enrollments} + return enrollments + + def get_program_enrollments(self): + """Returns a list of course enrollments linked with the current student""" + program_enrollments = frappe.get_all("Program Enrollment", filters={"student": self.name}, fields=['program']) + if not program_enrollments: + return None + else: + enrollments = [item['program'] for item in program_enrollments] + return enrollments + + def get_topic_progress(self, course_enrollment_name, topic): + """ + Get Progress Dictionary of a student for a particular topic + :param self: Student Object + :param course_enrollment_name: Name of the Course Enrollment + :param topic: Topic DocType Object + """ + contents = topic.get_contents() + progress = [] + for content in contents: + if content.doctype in ('Article', 'Video'): + status = check_content_completion(content.name, content.doctype, course_enrollment_name) + progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status}) + elif content.doctype == 'Quiz': + status, score, result = check_quiz_completion(content, course_enrollment_name) + progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status, 'score': score, 'result': result}) + return progress + + def enroll_in_program(self, program_name): + try: + enrollment = frappe.get_doc({ + "doctype": "Program Enrollment", + "student": self.name, + "academic_year": frappe.get_last_doc("Academic Year").name, + "program": program_name, + "enrollment_date": frappe.utils.datetime.datetime.now() + }) + enrollment.save(ignore_permissions=True) + except frappe.exceptions.ValidationError: + enrollment_name = frappe.get_list("Program Enrollment", filters={"student": self.name, "Program": program_name})[0].name + return frappe.get_doc("Program Enrollment", enrollment_name) + else: + enrollment.submit() + return enrollment + + def enroll_in_course(self, course_name, program_enrollment, enrollment_date=frappe.utils.datetime.datetime.now()): + try: + enrollment = frappe.get_doc({ + "doctype": "Course Enrollment", + "student": self.name, + "course": course_name, + "program_enrollment": program_enrollment, + "enrollment_date": enrollment_date + }) + enrollment.save(ignore_permissions=True) + except frappe.exceptions.ValidationError: + enrollment_name = frappe.get_list("Course Enrollment", filters={"student": self.name, "course": course_name, "program_enrollment": program_enrollment})[0].name + return frappe.get_doc("Program Enrollment", enrollment_name) + else: + return enrollment + def get_timeline_data(doctype, name): '''Return timeline for attendance''' return dict(frappe.db.sql('''select unix_timestamp(`date`), count(*) diff --git a/erpnext/education/doctype/student/student_dashboard.py b/erpnext/education/doctype/student/student_dashboard.py index d86f4f231cb..0cbd17b8a4a 100644 --- a/erpnext/education/doctype/student/student_dashboard.py +++ b/erpnext/education/doctype/student/student_dashboard.py @@ -9,7 +9,7 @@ def get_data(): 'transactions': [ { 'label': _('Admission'), - 'items': ['Program Enrollment'] + 'items': ['Program Enrollment', 'Course Enrollment'] }, { 'label': _('Student Activity'), @@ -19,6 +19,10 @@ def get_data(): 'label': _('Assessment'), 'items': ['Assessment Result'] }, + { + 'label': _('Student LMS Activity'), + 'items': ['Course Activity', 'Quiz Activity' ] + }, { 'label': _('Attendance'), 'items': ['Student Attendance', 'Student Leave Application'] diff --git a/erpnext/education/doctype/student/test_records.json b/erpnext/education/doctype/student/test_records.json index 6acc4b688f6..8ad3afa6a89 100644 --- a/erpnext/education/doctype/student/test_records.json +++ b/erpnext/education/doctype/student/test_records.json @@ -6,6 +6,7 @@ "program": "TC101", "date_of_birth": "2000-01-01", "gender": "Male", + "student_email_id": "_test_student@example.com", "blood_group": "A+" }, @@ -16,6 +17,7 @@ "program": "TC101", "date_of_birth": "2000-01-01", "gender": "Male", + "student_email_id": "_test_student_1@example.com", "blood_group": "A+" }, @@ -25,27 +27,8 @@ "last_name": "Name 2", "program": "TC101", "date_of_birth": "2000-01-01", - "gender": "Male", - "blood_group": "A+" - - }, - { - "first_name": "_Test", - "middle_name": "Student", - "last_name": "Name 3", - "program": "TC101", - "date_of_birth": "2000-01-01", - "gender": "Male", - "blood_group": "A+" - - }, - { - "first_name": "_Test", - "middle_name": "Student", - "last_name": "Name 4", - "program": "TC101", - "date_of_birth": "2000-01-01", - "gender": "Male", + "gender": "Female", + "student_email_id": "_test_student_2@example.com", "blood_group": "A+" } diff --git a/erpnext/education/doctype/student/test_student.py b/erpnext/education/doctype/student/test_student.py index cc6537f8654..8610edbf282 100644 --- a/erpnext/education/doctype/student/test_student.py +++ b/erpnext/education/doctype/student/test_student.py @@ -2,11 +2,60 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt from __future__ import unicode_literals +from frappe.test_runner import make_test_records +from erpnext.education.doctype.program.test_program import make_program_and_linked_courses +from erpnext.education.doctype.course.test_course import make_course import frappe import unittest -# test_records = frappe.get_test_records('Student') - +test_records = frappe.get_test_records('Student') class TestStudent(unittest.TestCase): - pass + def setUp(self): + create_student({"first_name": "_Test Name", "last_name": "_Test Last Name", "email": "_test_student@example.com"}) + make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"]) + + def test_create_student_user(self): + self.assertTrue(bool(frappe.db.exists("User", "_test_student@example.com"))) + frappe.db.rollback() + + def test_enroll_in_program(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + test_enrollment = frappe.get_all("Program Enrollment", filters={"student": student.name, "Program": "_Test Program 1"}) + self.assertTrue(len(test_enrollment)) + self.assertEqual(test_enrollment[0]['name'], enrollment.name) + frappe.db.rollback() + + def test_get_program_enrollments(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + program_enrollments = student.get_program_enrollments() + self.assertTrue("_Test Program 1" in program_enrollments) + frappe.db.rollback() + + def test_get_all_course_enrollments(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + course_enrollments = student.get_all_course_enrollments() + self.assertTrue("_Test Course 1" in course_enrollments.keys()) + self.assertTrue("_Test Course 2" in course_enrollments.keys()) + frappe.db.rollback() + +def create_student(student_dict): + student = get_student(student_dict['email']) + if not student: + student = frappe.get_doc({ + "doctype": "Student", + "first_name": student_dict['first_name'], + "last_name": student_dict['last_name'], + "student_email_id": student_dict['email'] + }).insert() + return student + +def get_student(email): + try: + student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name + return frappe.get_doc("Student", student_id) + except IndexError: + return None \ No newline at end of file diff --git a/erpnext/education/doctype/student_applicant/student_applicant.json b/erpnext/education/doctype/student_applicant/student_applicant.json index 297821f7535..71134e0907d 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.json +++ b/erpnext/education/doctype/student_applicant/student_applicant.json @@ -143,6 +143,39 @@ "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", + "fieldname": "lms_only", + "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": "LMS Only", + "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, @@ -627,7 +660,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -1160,7 +1193,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-21 14:44:30.146264", + "modified": "2018-10-05 13:15:59.283862", "modified_by": "Administrator", "module": "Education", "name": "Student Applicant", diff --git a/erpnext/education/doctype/topic/__init__.py b/erpnext/education/doctype/topic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/topic/test_topic.js b/erpnext/education/doctype/topic/test_topic.js new file mode 100644 index 00000000000..4460b794786 --- /dev/null +++ b/erpnext/education/doctype/topic/test_topic.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Topic", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Topic + () => frappe.tests.make('Topic', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/topic/test_topic.py b/erpnext/education/doctype/topic/test_topic.py new file mode 100644 index 00000000000..d03db1cb93a --- /dev/null +++ b/erpnext/education/doctype/topic/test_topic.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestTopic(unittest.TestCase): + def setUp(self): + make_topic_and_linked_content("_Test Topic 1", [{"type":"Article", "name": "_Test Article 1"}]) + + def test_get_contents(self): + topic = frappe.get_doc("Topic", "_Test Topic 1") + contents = topic.get_contents() + self.assertEqual(contents[0].doctype, "Article") + self.assertEqual(contents[0].name, "_Test Article 1") + frappe.db.rollback() + +def make_topic(name): + try: + topic = frappe.get_doc("Topic", name) + except frappe.DoesNotExistError: + topic = frappe.get_doc({ + "doctype": "Topic", + "topic_name": name, + "topic_code": name, + }).insert() + return topic.name + +def make_topic_and_linked_content(topic_name, content_dict_list): + try: + topic = frappe.get_doc("Topic", topic_name) + except frappe.DoesNotExistError: + make_topic(topic_name) + topic = frappe.get_doc("Topic", topic_name) + content_list = [make_content(content['type'], content['name']) for content in content_dict_list] + for content in content_list: + topic.append("topic_content", {"content": content.title, "content_type": content.doctype}) + topic.save() + return topic + + +def make_content(type, name): + try: + content = frappe.get_doc(type, name) + except frappe.DoesNotExistError: + content = frappe.get_doc({"doctype": type, "title": name}).insert() + return content diff --git a/erpnext/education/doctype/topic/topic.js b/erpnext/education/doctype/topic/topic.js new file mode 100644 index 00000000000..695c17476c9 --- /dev/null +++ b/erpnext/education/doctype/topic/topic.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Topic', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/topic/topic.json b/erpnext/education/doctype/topic/topic.json new file mode 100644 index 00000000000..f47b10d7804 --- /dev/null +++ b/erpnext/education/doctype/topic/topic.json @@ -0,0 +1,297 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:topic_code", + "beta": 0, + "creation": "2018-12-12 11:37:39.917760", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "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": "topic_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": "Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "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_2", + "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, + "fetch_if_empty": 0, + "fieldname": "topic_code", + "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": "Code", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 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": "section_break_4", + "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, + "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, + "fetch_if_empty": 0, + "fieldname": "topic_content", + "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": "Topic Content", + "length": 0, + "no_copy": 0, + "options": "Topic Content", + "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, + "fetch_if_empty": 0, + "fieldname": "hero_image", + "fieldtype": "Attach Image", + "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": "Hero Image", + "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 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "menu_index": 0, + "modified": "2019-04-09 11:35:34.137040", + "modified_by": "Administrator", + "module": "Education", + "name": "Topic", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/topic/topic.py b/erpnext/education/doctype/topic/topic.py new file mode 100644 index 00000000000..339fc7d8871 --- /dev/null +++ b/erpnext/education/doctype/topic/topic.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 Topic(Document): + def get_contents(self): + try: + topic_content_list = self.get_all_children() + content_data = [frappe.get_doc(topic_content.content_type, topic_content.content) for topic_content in topic_content_list] + except Exception as e: + frappe.log_error(frappe.get_traceback()) + return None + return content_data \ No newline at end of file diff --git a/erpnext/education/doctype/topic_content/__init__.py b/erpnext/education/doctype/topic_content/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/topic_content/test_topic_content.js b/erpnext/education/doctype/topic_content/test_topic_content.js new file mode 100644 index 00000000000..bf9a62d0376 --- /dev/null +++ b/erpnext/education/doctype/topic_content/test_topic_content.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Topic Content", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Topic Content + () => frappe.tests.make('Topic Content', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/topic_content/test_topic_content.py b/erpnext/education/doctype/topic_content/test_topic_content.py new file mode 100644 index 00000000000..cf304f60bc3 --- /dev/null +++ b/erpnext/education/doctype/topic_content/test_topic_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestTopicContent(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/topic_content/topic_content.js b/erpnext/education/doctype/topic_content/topic_content.js new file mode 100644 index 00000000000..9cda0cabb2a --- /dev/null +++ b/erpnext/education/doctype/topic_content/topic_content.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Topic Content', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/topic_content/topic_content.json b/erpnext/education/doctype/topic_content/topic_content.json new file mode 100644 index 00000000000..52207882e0c --- /dev/null +++ b/erpnext/education/doctype/topic_content/topic_content.json @@ -0,0 +1,140 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-12-12 11:42:57.987434", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content_type", + "fieldtype": "Select", + "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": "Content Type", + "length": 0, + "no_copy": 0, + "options": "\nArticle\nVideo\nQuiz", + "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, + "fieldname": "column_break_2", + "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, + "fieldname": "content", + "fieldtype": "Dynamic 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": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "options": "content_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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-12-12 11:46:46.112018", + "modified_by": "Administrator", + "module": "Education", + "name": "Topic Content", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/topic_content/topic_content.py b/erpnext/education/doctype/topic_content/topic_content.py new file mode 100644 index 00000000000..9b2c90bb4f2 --- /dev/null +++ b/erpnext/education/doctype/topic_content/topic_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 TopicContent(Document): + pass diff --git a/erpnext/education/doctype/video/__init__.py b/erpnext/education/doctype/video/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/education/doctype/video/test_video.js b/erpnext/education/doctype/video/test_video.js new file mode 100644 index 00000000000..a82a221319c --- /dev/null +++ b/erpnext/education/doctype/video/test_video.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Video", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Video + () => frappe.tests.make('Video', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/video/test_video.py b/erpnext/education/doctype/video/test_video.py new file mode 100644 index 00000000000..ecb09a2f9eb --- /dev/null +++ b/erpnext/education/doctype/video/test_video.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestVideo(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/video/video.js b/erpnext/education/doctype/video/video.js new file mode 100644 index 00000000000..c35c19b0c1e --- /dev/null +++ b/erpnext/education/doctype/video/video.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Video', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/video/video.json b/erpnext/education/doctype/video/video.json new file mode 100644 index 00000000000..cc8f718ba4e --- /dev/null +++ b/erpnext/education/doctype/video/video.json @@ -0,0 +1,262 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-10-17 05:47:13.087395", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "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": "Title", + "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": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Description", + "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, + "fieldname": "duration", + "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": "Duration", + "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, + "fieldname": "url", + "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": "URL", + "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, + "fieldname": "publish_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Publish Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:07:17.134288", + "modified_by": "Administrator", + "module": "Education", + "name": "Video", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/video/video.py b/erpnext/education/doctype/video/video.py new file mode 100644 index 00000000000..b19f81258cf --- /dev/null +++ b/erpnext/education/doctype/video/video.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 Video(Document): + + + def get_video(self): + pass diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py index 1b93c9d71ee..bf766adc099 100644 --- a/erpnext/education/utils.py +++ b/erpnext/education/utils.py @@ -2,7 +2,7 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For lice -from __future__ import unicode_literals +from __future__ import unicode_literals, division import frappe from frappe import _ @@ -10,19 +10,19 @@ class OverlapError(frappe.ValidationError): pass def validate_overlap_for(doc, doctype, fieldname, value=None): """Checks overlap for specified field. - - :param fieldname: Checks Overlap for this field + + :param fieldname: Checks Overlap for this field """ - + existing = get_overlap_for(doc, doctype, fieldname, value) if existing: frappe.throw(_("This {0} conflicts with {1} for {2} {3}").format(doc.doctype, existing.name, doc.meta.get_label(fieldname) if not value else fieldname , value or doc.get(fieldname)), OverlapError) - + def get_overlap_for(doc, doctype, fieldname, value=None): """Returns overlaping document for specified field. - - :param fieldname: Checks Overlap for this field + + :param fieldname: Checks Overlap for this field """ existing = frappe.db.sql("""select name, from_time, to_time from `tab{0}` @@ -42,7 +42,8 @@ def get_overlap_for(doc, doctype, fieldname, value=None): }, as_dict=True) return existing[0] if existing else None - + + def validate_duplicate_student(students): unique_students= [] for stud in students: @@ -51,3 +52,93 @@ def validate_duplicate_student(students): .format(stud.student, stud.student_name, unique_students.index(stud.student)+1, stud.idx)) else: unique_students.append(stud.student) + + return None + +# LMS Utils +def get_current_student(): + """ + Returns student user name, example EDU-STU-2018-00001 (Based on the naming series). + Takes email from from frappe.session.user + """ + email = frappe.session.user + if email in ('Administrator', 'Guest'): + return None + try: + student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name + return frappe.get_doc("Student", student_id) + except (IndexError, frappe.DoesNotExistError): + return None + +def check_super_access(): + current_user = frappe.get_doc('User', frappe.session.user) + roles = set([role.role for role in current_user.roles]) + return bool(roles & {'Administrator', 'Instructor', 'Education Manager', 'System Manager', 'Academic User'}) + +def get_program_enrollment(program_name): + """ + Function to get program enrollments for a particular student for a program + """ + student = get_current_student() + if not student: + return None + else: + enrollment = frappe.get_all("Program Enrollment", filters={'student':student.name, 'program': program_name}) + if enrollment: + return enrollment[0].name + else: + return None + +def get_program_and_enrollment_status(program_name): + program = frappe.get_doc('Program', program_name) + is_enrolled = bool(get_program_enrollment(program_name)) or check_super_access() + return {'program': program, 'is_enrolled': is_enrolled} + +def get_course_enrollment(course_name): + student = get_current_student() + if not student: + return None + enrollment_name = frappe.get_all("Course Enrollment", filters={'student': student.name, 'course':course_name}) + try: + name = enrollment_name[0].name + enrollment = frappe.get_doc("Course Enrollment", name) + return enrollment + except: + return None + +def create_student_from_current_user(): + user = frappe.get_doc("User", frappe.session.user) + student = frappe.get_doc({ + "doctype": "Student", + "first_name": user.first_name, + "last_name": user.last_name, + "student_email_id": user.email, + "user": frappe.session.user + }) + student.save(ignore_permissions=True) + return student + +def enroll_in_course(course_name, program_name): + student = get_current_student() + return student.enroll_in_course(course_name=course_name, program_enrollment=get_program_enrollment(program_name)) + +def check_content_completion(content_name, content_type, enrollment_name): + activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment_name, 'content_type': content_type, 'content': content_name}) + if activity: + return True + else: + return False + +def check_quiz_completion(quiz, enrollment_name): + attempts = frappe.get_all("Quiz Activity", filters={'enrollment': enrollment_name, 'quiz': quiz.name}, fields=["name", "activity_date", "score", "status"]) + status = False if quiz.max_attempts == 0 else bool(len(attempts) == quiz.max_attempts) + score = None + result = None + if attempts: + if quiz.grading_basis == 'Last Highest Score': + attempts = sorted(attempts, key = lambda i: int(i.score), reverse=True) + score = attempts[0]['score'] + result = attempts[0]['status'] + if result == 'Pass': + status = True + return status, score, result \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/__init__.py b/erpnext/erpnext_integrations/doctype/tally_migration/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js new file mode 100644 index 00000000000..104ac570c69 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -0,0 +1,50 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Tally Migration', { + onload: function(frm) { + frappe.realtime.on("tally_migration_progress_update", function (data) { + frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message); + if (data.count == data.total) { + window.setTimeout(title => frm.dashboard.hide_progress(title), 1500, data.title); + } + }); + }, + refresh: function(frm) { + if (frm.doc.master_data && !frm.doc.is_master_data_imported) { + if (frm.doc.is_master_data_processed) { + if (frm.doc.status != "Importing Master Data") { + frm.events.add_button(frm, __("Import Master Data"), "import_master_data"); + } + } else { + if (frm.doc.status != "Processing Master Data") { + frm.events.add_button(frm, __("Process Master Data"), "process_master_data"); + } + } + } + if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) { + if (frm.doc.is_day_book_data_processed) { + if (frm.doc.status != "Importing Day Book Data") { + frm.events.add_button(frm, __("Import Day Book Data"), "import_day_book_data"); + } + } else { + if (frm.doc.status != "Processing Day Book Data") { + frm.events.add_button(frm, __("Process Day Book Data"), "process_day_book_data"); + } + } + } + }, + add_button: function(frm, label, method) { + frm.add_custom_button( + label, + () => frm.call({ + doc: frm.doc, + method: method, + freeze: true, + callback: () => { + frm.remove_custom_button(label); + } + }) + ); + } +}); diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json new file mode 100644 index 00000000000..26415caf8d3 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -0,0 +1,219 @@ +{ + "beta": 1, + "creation": "2019-02-01 14:27:09.485238", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status", + "master_data", + "is_master_data_processed", + "is_master_data_imported", + "column_break_2", + "tally_creditors_account", + "tally_debtors_account", + "company_section", + "tally_company", + "column_break_8", + "erpnext_company", + "processed_files_section", + "chart_of_accounts", + "parties", + "addresses", + "column_break_17", + "uoms", + "items", + "vouchers", + "accounts_section", + "default_warehouse", + "round_off_account", + "column_break_21", + "default_cost_center", + "day_book_section", + "day_book_data", + "column_break_27", + "is_day_book_data_processed", + "is_day_book_data_imported" + ], + "fields": [ + { + "fieldname": "status", + "fieldtype": "Data", + "hidden": 1, + "label": "Status" + }, + { + "fieldname": "master_data", + "fieldtype": "Attach", + "in_list_view": 1, + "label": "Master Data" + }, + { + "default": "Sundry Creditors", + "fieldname": "tally_creditors_account", + "fieldtype": "Data", + "label": "Tally Creditors Account", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Sundry Debtors", + "fieldname": "tally_debtors_account", + "fieldtype": "Data", + "label": "Tally Debtors Account", + "reqd": 1 + }, + { + "depends_on": "is_master_data_processed", + "fieldname": "company_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "tally_company", + "fieldtype": "Data", + "label": "Tally Company", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "erpnext_company", + "fieldtype": "Data", + "label": "ERPNext Company" + }, + { + "fieldname": "processed_files_section", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Processed Files" + }, + { + "fieldname": "chart_of_accounts", + "fieldtype": "Attach", + "label": "Chart of Accounts" + }, + { + "fieldname": "parties", + "fieldtype": "Attach", + "label": "Parties" + }, + { + "fieldname": "addresses", + "fieldtype": "Attach", + "label": "Addresses" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "uoms", + "fieldtype": "Attach", + "label": "UOMs" + }, + { + "fieldname": "items", + "fieldtype": "Attach", + "label": "Items" + }, + { + "fieldname": "vouchers", + "fieldtype": "Attach", + "label": "Vouchers" + }, + { + "depends_on": "is_master_data_imported", + "fieldname": "accounts_section", + "fieldtype": "Section Break", + "label": "Accounts" + }, + { + "fieldname": "default_warehouse", + "fieldtype": "Link", + "label": "Default Warehouse", + "options": "Warehouse" + }, + { + "fieldname": "round_off_account", + "fieldtype": "Link", + "label": "Round Off Account", + "options": "Account" + }, + { + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_cost_center", + "fieldtype": "Link", + "label": "Default Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "is_master_data_processed", + "fieldtype": "Check", + "label": "Is Master Data Processed", + "read_only": 1 + }, + { + "fieldname": "is_day_book_data_processed", + "fieldtype": "Check", + "label": "Is Day Book Data Processed", + "read_only": 1 + }, + { + "fieldname": "is_day_book_data_imported", + "fieldtype": "Check", + "label": "Is Day Book Data Imported", + "read_only": 1 + }, + { + "fieldname": "is_master_data_imported", + "fieldtype": "Check", + "label": "Is Master Data Imported", + "read_only": 1 + }, + { + "depends_on": "is_master_data_imported", + "fieldname": "day_book_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "fieldname": "day_book_data", + "fieldtype": "Attach", + "in_list_view": 1, + "label": "Day Book Data" + } + ], + "modified": "2019-04-29 05:46:54.394967", + "modified_by": "Administrator", + "module": "ERPNext Integrations", + "name": "Tally Migration", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py new file mode 100644 index 00000000000..12b646dad77 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -0,0 +1,512 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals + +from decimal import Decimal +import json +import re +import traceback +import zipfile +import frappe +from frappe import _ +from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.model.document import Document +from frappe.model.naming import getseries, revert_series_if_last +from frappe.utils.data import format_datetime +from bs4 import BeautifulSoup as bs +from erpnext import encode_company_abbr +from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts + +PRIMARY_ACCOUNT = "Primary" +VOUCHER_CHUNK_SIZE = 500 + + +class TallyMigration(Document): + def autoname(self): + if not self.name: + self.name = "Tally Migration on " + format_datetime(self.creation) + + def get_collection(self, data_file): + def sanitize(string): + return re.sub("", "", string) + + def emptify(string): + string = re.sub(r"<\w+/>", "", string) + string = re.sub(r"<([\w.]+)>\s*<\/\1>", "", string) + string = re.sub(r"\r\n", "", string) + return string + + master_file = frappe.get_doc("File", {"file_url": data_file}) + + with zipfile.ZipFile(master_file.get_full_path()) as zf: + encoded_content = zf.read(zf.namelist()[0]) + try: + content = encoded_content.decode("utf-8-sig") + except UnicodeDecodeError: + content = encoded_content.decode("utf-16") + + master = bs(sanitize(emptify(content)), "xml") + collection = master.BODY.IMPORTDATA.REQUESTDATA + return collection + + def dump_processed_data(self, data): + for key, value in data.items(): + f = frappe.get_doc({ + "doctype": "File", + "file_name": key + ".json", + "attached_to_doctype": self.doctype, + "attached_to_name": self.name, + "content": json.dumps(value) + }).insert() + setattr(self, key, f.file_url) + + def _process_master_data(self): + def get_company_name(collection): + return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string + + def get_coa_customers_suppliers(collection): + root_type_map = { + "Application of Funds (Assets)": "Asset", + "Expenses": "Expense", + "Income": "Income", + "Source of Funds (Liabilities)": "Liability" + } + roots = set(root_type_map.keys()) + accounts = list(get_groups(collection.find_all("GROUP"))) + list(get_ledgers(collection.find_all("LEDGER"))) + children, parents = get_children_and_parent_dict(accounts) + group_set = [acc[1] for acc in accounts if acc[2]] + children, customers, suppliers = remove_parties(parents, children, group_set) + coa = traverse({}, children, roots, roots, group_set) + + for account in coa: + coa[account]["root_type"] = root_type_map[account] + + return coa, customers, suppliers + + def get_groups(accounts): + for account in accounts: + if account["NAME"] in (self.tally_creditors_account, self.tally_debtors_account): + yield get_parent(account), account["NAME"], 0 + else: + yield get_parent(account), account["NAME"], 1 + + def get_ledgers(accounts): + for account in accounts: + # If Ledger doesn't have PARENT field then don't create Account + # For example "Profit & Loss A/c" + if account.PARENT: + yield account.PARENT.string, account["NAME"], 0 + + def get_parent(account): + if account.PARENT: + return account.PARENT.string + return { + ("Yes", "No"): "Application of Funds (Assets)", + ("Yes", "Yes"): "Expenses", + ("No", "Yes"): "Income", + ("No", "No"): "Source of Funds (Liabilities)", + }[(account.ISDEEMEDPOSITIVE.string, account.ISREVENUE.string)] + + def get_children_and_parent_dict(accounts): + children, parents = {}, {} + for parent, account, is_group in accounts: + children.setdefault(parent, set()).add(account) + parents.setdefault(account, set()).add(parent) + parents[account].update(parents.get(parent, [])) + return children, parents + + def remove_parties(parents, children, group_set): + customers, suppliers = set(), set() + for account in parents: + if self.tally_creditors_account in parents[account]: + children.pop(account, None) + if account not in group_set: + suppliers.add(account) + elif self.tally_debtors_account in parents[account]: + children.pop(account, None) + if account not in group_set: + customers.add(account) + return children, customers, suppliers + + def traverse(tree, children, accounts, roots, group_set): + for account in accounts: + if account in group_set or account in roots: + if account in children: + tree[account] = traverse({}, children, children[account], roots, group_set) + else: + tree[account] = {"is_group": 1} + else: + tree[account] = {} + return tree + + def get_parties_addresses(collection, customers, suppliers): + parties, addresses = [], [] + for account in collection.find_all("LEDGER"): + party_type = None + if account.NAME.string in customers: + party_type = "Customer" + parties.append({ + "doctype": party_type, + "customer_name": account.NAME.string, + "tax_id": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None, + "customer_group": "All Customer Groups", + "territory": "All Territories", + "customer_type": "Individual", + }) + elif account.NAME.string in suppliers: + party_type = "Supplier" + parties.append({ + "doctype": party_type, + "supplier_name": account.NAME.string, + "pan": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None, + "supplier_group": "All Supplier Groups", + "supplier_type": "Individual", + }) + if party_type: + address = "\n".join([a.string for a in account.find_all("ADDRESS")]) + addresses.append({ + "doctype": "Address", + "address_line1": address[:140].strip(), + "address_line2": address[140:].strip(), + "country": account.COUNTRYNAME.string if account.COUNTRYNAME else None, + "state": account.LEDSTATENAME.string if account.LEDSTATENAME else None, + "gst_state": account.LEDSTATENAME.string if account.LEDSTATENAME else None, + "pin_code": account.PINCODE.string if account.PINCODE else None, + "mobile": account.LEDGERPHONE.string if account.LEDGERPHONE else None, + "phone": account.LEDGERPHONE.string if account.LEDGERPHONE else None, + "gstin": account.PARTYGSTIN.string if account.PARTYGSTIN else None, + "links": [{"link_doctype": party_type, "link_name": account["NAME"]}], + }) + return parties, addresses + + def get_stock_items_uoms(collection): + uoms = [] + for uom in collection.find_all("UNIT"): + uoms.append({"doctype": "UOM", "uom_name": uom.NAME.string}) + + items = [] + for item in collection.find_all("STOCKITEM"): + items.append({ + "doctype": "Item", + "item_code" : item.NAME.string, + "stock_uom": item.BASEUNITS.string, + "is_stock_item": 0, + "item_group": "All Item Groups", + "item_defaults": [{"company": self.erpnext_company}] + }) + return items, uoms + + + self.publish("Process Master Data", _("Reading Uploaded File"), 1, 5) + collection = self.get_collection(self.master_data) + + company = get_company_name(collection) + self.tally_company = company + self.erpnext_company = company + + self.publish("Process Master Data", _("Processing Chart of Accounts and Parties"), 2, 5) + chart_of_accounts, customers, suppliers = get_coa_customers_suppliers(collection) + self.publish("Process Master Data", _("Processing Party Addresses"), 3, 5) + parties, addresses = get_parties_addresses(collection, customers, suppliers) + self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5) + items, uoms = get_stock_items_uoms(collection) + data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms} + self.publish("Process Master Data", _("Done"), 5, 5) + + self.dump_processed_data(data) + self.is_master_data_processed = 1 + self.status = "" + self.save() + + def publish(self, title, message, count, total): + frappe.publish_realtime("tally_migration_progress_update", {"title": title, "message": message, "count": count, "total": total}) + + def _import_master_data(self): + def create_company_and_coa(coa_file_url): + coa_file = frappe.get_doc("File", {"file_url": coa_file_url}) + frappe.local.flags.ignore_chart_of_accounts = True + company = frappe.get_doc({ + "doctype": "Company", + "company_name": self.erpnext_company, + "default_currency": "INR", + "enable_perpetual_inventory": 0, + }).insert() + frappe.local.flags.ignore_chart_of_accounts = False + create_charts(company.name, custom_chart=json.loads(coa_file.get_content())) + company.create_default_warehouses() + + def create_parties_and_addresses(parties_file_url, addresses_file_url): + parties_file = frappe.get_doc("File", {"file_url": parties_file_url}) + for party in json.loads(parties_file.get_content()): + try: + frappe.get_doc(party).insert() + except: + self.log(party) + addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url}) + for address in json.loads(addresses_file.get_content()): + try: + frappe.get_doc(address).insert(ignore_mandatory=True) + except: + try: + gstin = address.pop("gstin", None) + frappe.get_doc(address).insert(ignore_mandatory=True) + self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)}) + except: + self.log(address) + + + def create_items_uoms(items_file_url, uoms_file_url): + uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) + for uom in json.loads(uoms_file.get_content()): + if not frappe.db.exists(uom): + try: + frappe.get_doc(uom).insert() + except: + self.log(uom) + + items_file = frappe.get_doc("File", {"file_url": items_file_url}) + for item in json.loads(items_file.get_content()): + try: + frappe.get_doc(item).insert() + except: + self.log(item) + + self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) + create_company_and_coa(self.chart_of_accounts) + self.publish("Import Master Data", _("Importing Parties and Addresses"), 2, 4) + create_parties_and_addresses(self.parties, self.addresses) + self.publish("Import Master Data", _("Importing Items and UOMs"), 3, 4) + create_items_uoms(self.items, self.uoms) + self.publish("Import Master Data", _("Done"), 4, 4) + self.status = "" + self.is_master_data_imported = 1 + self.save() + + def _process_day_book_data(self): + def get_vouchers(collection): + vouchers = [] + for voucher in collection.find_all("VOUCHER"): + if voucher.ISCANCELLED.string == "Yes": + continue + inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST") + if voucher.VOUCHERTYPENAME.string not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries: + function = voucher_to_invoice + else: + function = voucher_to_journal_entry + try: + vouchers.append(function(voucher)) + except: + self.log(voucher) + return vouchers + + def voucher_to_journal_entry(voucher): + accounts = [] + ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST") + for entry in ledger_entries: + account = {"account": encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company), "cost_center": self.default_cost_center} + if entry.ISPARTYLEDGER.string == "Yes": + party_details = get_party(entry.LEDGERNAME.string) + if party_details: + party_type, party_account = party_details + account["party_type"] = party_type + account["account"] = party_account + account["party"] = entry.LEDGERNAME.string + amount = Decimal(entry.AMOUNT.string) + if amount > 0: + account["credit_in_account_currency"] = str(abs(amount)) + else: + account["debit_in_account_currency"] = str(abs(amount)) + accounts.append(account) + + journal_entry = { + "doctype": "Journal Entry", + "tally_guid": voucher.GUID.string, + "posting_date": voucher.DATE.string, + "company": self.erpnext_company, + "accounts": accounts, + } + return journal_entry + + def voucher_to_invoice(voucher): + if voucher.VOUCHERTYPENAME.string in ["Sales", "Credit Note"]: + doctype = "Sales Invoice" + party_field = "customer" + account_field = "debit_to" + account_name = encode_company_abbr(self.tally_debtors_account, self.erpnext_company) + price_list_field = "selling_price_list" + elif voucher.VOUCHERTYPENAME.string in ["Purchase", "Debit Note"]: + doctype = "Purchase Invoice" + party_field = "supplier" + account_field = "credit_to" + account_name = encode_company_abbr(self.tally_creditors_account, self.erpnext_company) + price_list_field = "buying_price_list" + + invoice = { + "doctype": doctype, + party_field: voucher.PARTYNAME.string, + "tally_guid": voucher.GUID.string, + "posting_date": voucher.DATE.string, + "due_date": voucher.DATE.string, + "items": get_voucher_items(voucher, doctype), + "taxes": get_voucher_taxes(voucher), + account_field: account_name, + price_list_field: "Tally Price List", + "set_posting_time": 1, + "disable_rounded_total": 1, + "company": self.erpnext_company, + } + return invoice + + def get_voucher_items(voucher, doctype): + inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST") + if doctype == "Sales Invoice": + account_field = "income_account" + elif doctype == "Purchase Invoice": + account_field = "expense_account" + items = [] + for entry in inventory_entries: + qty, uom = entry.ACTUALQTY.string.strip().split() + items.append({ + "item_code": entry.STOCKITEMNAME.string, + "description": entry.STOCKITEMNAME.string, + "qty": qty.strip(), + "uom": uom.strip(), + "conversion_factor": 1, + "price_list_rate": entry.RATE.string.split("/")[0], + "cost_center": self.default_cost_center, + "warehouse": self.default_warehouse, + account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string, self.erpnext_company), + }) + return items + + def get_voucher_taxes(voucher): + ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST") + taxes = [] + for entry in ledger_entries: + if entry.ISPARTYLEDGER.string == "No": + tax_account = encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company) + taxes.append({ + "charge_type": "Actual", + "account_head": tax_account, + "description": tax_account, + "tax_amount": entry.AMOUNT.string, + "cost_center": self.default_cost_center, + }) + return taxes + + def get_party(party): + if frappe.db.exists({"doctype": "Supplier", "supplier_name": party}): + return "Supplier", encode_company_abbr(self.tally_creditors_account, self.erpnext_company) + elif frappe.db.exists({"doctype": "Customer", "customer_name": party}): + return "Customer", encode_company_abbr(self.tally_debtors_account, self.erpnext_company) + + self.publish("Process Day Book Data", _("Reading Uploaded File"), 1, 3) + collection = self.get_collection(self.day_book_data) + self.publish("Process Day Book Data", _("Processing Vouchers"), 2, 3) + vouchers = get_vouchers(collection) + self.publish("Process Day Book Data", _("Done"), 3, 3) + self.dump_processed_data({"vouchers": vouchers}) + self.status = "" + self.is_day_book_data_processed = 1 + self.save() + + def _import_day_book_data(self): + def create_fiscal_years(vouchers): + from frappe.utils.data import add_years, getdate + earliest_date = getdate(min(voucher["posting_date"] for voucher in vouchers)) + oldest_year = frappe.get_all("Fiscal Year", fields=["year_start_date", "year_end_date"], order_by="year_start_date")[0] + while earliest_date < oldest_year.year_start_date: + new_year = frappe.get_doc({"doctype": "Fiscal Year"}) + new_year.year_start_date = add_years(oldest_year.year_start_date, -1) + new_year.year_end_date = add_years(oldest_year.year_end_date, -1) + if new_year.year_start_date.year == new_year.year_end_date.year: + new_year.year = new_year.year_start_date.year + else: + new_year.year = "{}-{}".format(new_year.year_start_date.year, new_year.year_end_date.year) + new_year.save() + oldest_year = new_year + + def create_custom_fields(doctypes): + for doctype in doctypes: + df = { + "fieldtype": "Data", + "fieldname": "tally_guid", + "read_only": 1, + "label": "Tally GUID" + } + create_custom_field(doctype, df) + + def create_price_list(): + frappe.get_doc({ + "doctype": "Price List", + "price_list_name": "Tally Price List", + "selling": 1, + "buying": 1, + "enabled": 1, + "currency": "INR" + }).insert() + + frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") + frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") + frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) + + vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) + vouchers = json.loads(vouchers_file.get_content()) + + create_fiscal_years(vouchers) + create_price_list() + create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"]) + + total = len(vouchers) + is_last = False + for index in range(0, total, VOUCHER_CHUNK_SIZE): + if index + VOUCHER_CHUNK_SIZE >= total: + is_last = True + frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last) + + def _import_vouchers(self, start, total, is_last=False): + frappe.flags.in_migrate = True + vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) + vouchers = json.loads(vouchers_file.get_content()) + chunk = vouchers[start: start + VOUCHER_CHUNK_SIZE] + + for index, voucher in enumerate(chunk, start=start): + try: + doc = frappe.get_doc(voucher).insert() + doc.submit() + self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) + except: + self.log(voucher) + + if is_last: + self.status = "" + self.is_day_book_data_imported = 1 + self.save() + frappe.db.set_value("Price List", "Tally Price List", "enabled", 0) + frappe.flags.in_migrate = False + + def process_master_data(self): + self.status = "Processing Master Data" + self.save() + frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600) + + def import_master_data(self): + self.status = "Importing Master Data" + self.save() + frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600) + + def process_day_book_data(self): + self.status = "Processing Day Book Data" + self.save() + frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600) + + def import_day_book_data(self): + self.status = "Importing Day Book Data" + self.save() + frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) + + def log(self, data=None): + message = "\n".join(["Data", json.dumps(data, default=str, indent=4), "Exception", traceback.format_exc()]) + return frappe.log_error(title="Tally Migration Error", message=message) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js new file mode 100644 index 00000000000..433c5e2cda8 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Tally Migration", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Tally Migration + () => frappe.tests.make('Tally Migration', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py new file mode 100644 index 00000000000..9f67e55ca16 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestTallyMigration(unittest.TestCase): + pass diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 608e8b2225e..85f2804f27d 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -11,6 +11,7 @@ app_email = "info@erpnext.com" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" + develop_version = '12.x.x-develop' error_report_email = "support@erpnext.com" @@ -23,7 +24,8 @@ web_include_css = "assets/css/erpnext-web.css" doctype_js = { "Communication": "public/js/communication.js", "Event": "public/js/event.js", - "Website Theme": "public/js/website_theme.js" + "Website Theme": "public/js/website_theme.js", + "Newsletter": "public/js/newsletter.js" } welcome_email = "erpnext.setup.utils.welcome_email" diff --git a/erpnext/hr/doctype/designation/designation.json b/erpnext/hr/doctype/designation/designation.json index 1d4a3cf7b79..4c3888be4aa 100644 --- a/erpnext/hr/doctype/designation/designation.json +++ b/erpnext/hr/doctype/designation/designation.json @@ -1,119 +1,194 @@ { - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:designation_name", - "beta": 0, - "creation": "2013-01-10 16:34:13", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:designation_name", + "beta": 0, + "creation": "2013-01-10 16:34:13", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "designation_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": "Designation", - "length": 0, - "no_copy": 0, - "oldfieldname": "designation_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, - "unique": 0 - }, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "designation_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": "Designation", + "length": 0, + "no_copy": 0, + "oldfieldname": "designation_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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "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": "Description", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "description", + "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": "Description", + "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, + "fetch_if_empty": 0, + "fieldname": "required_skills_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": "Required Skills", + "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, + "fetch_if_empty": 0, + "fieldname": "skills", + "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": "Skills", + "length": 0, + "no_copy": 0, + "options": "Designation Skill", + "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 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-bookmark", - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-02-17 16:53:43.895882", - "modified_by": "Administrator", - "module": "HR", - "name": "Designation", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_toolbar": 0, + "icon": "fa fa-bookmark", + "idx": 1, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "menu_index": 0, + "modified": "2019-04-16 10:02:23.277734", + "modified_by": "Administrator", + "module": "HR", + "name": "Designation", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 1, + "sort_order": "ASC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/designation_skill/__init__.py b/erpnext/hr/doctype/designation_skill/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/designation_skill/designation_skill.json b/erpnext/hr/doctype/designation_skill/designation_skill.json new file mode 100644 index 00000000000..30e23d0f02a --- /dev/null +++ b/erpnext/hr/doctype/designation_skill/designation_skill.json @@ -0,0 +1,74 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-04-16 10:01:05.259881", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "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": "skill", + "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": 0, + "label": "Skill", + "length": 0, + "no_copy": 0, + "options": "Skill", + "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 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-04-16 13:42:10.760449", + "modified_by": "Administrator", + "module": "HR", + "name": "Designation Skill", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/designation_skill/designation_skill.py b/erpnext/hr/doctype/designation_skill/designation_skill.py new file mode 100644 index 00000000000..c37d21f454e --- /dev/null +++ b/erpnext/hr/doctype/designation_skill/designation_skill.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 DesignationSkill(Document): + pass diff --git a/erpnext/hr/doctype/employee_skill/__init__.py b/erpnext/hr/doctype/employee_skill/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/employee_skill/employee_skill.json b/erpnext/hr/doctype/employee_skill/employee_skill.json new file mode 100644 index 00000000000..4b1419e1ecf --- /dev/null +++ b/erpnext/hr/doctype/employee_skill/employee_skill.json @@ -0,0 +1,141 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-04-16 09:57:52.751635", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "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": "skill", + "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": 0, + "label": "Skill", + "length": 0, + "no_copy": 0, + "options": "Skill", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "proficiency", + "fieldtype": "Rating", + "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": "Proficiency", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fetch_if_empty": 0, + "fieldname": "evaluation_date", + "fieldtype": "Date", + "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": "Evaluation Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-04-16 14:13:17.111035", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Skill", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_skill/employee_skill.py b/erpnext/hr/doctype/employee_skill/employee_skill.py new file mode 100644 index 00000000000..ac05fba624b --- /dev/null +++ b/erpnext/hr/doctype/employee_skill/employee_skill.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 EmployeeSkill(Document): + pass diff --git a/erpnext/hr/doctype/employee_skill_map/__init__.py b/erpnext/hr/doctype/employee_skill_map/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.js b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.js new file mode 100644 index 00000000000..b82b18d43b1 --- /dev/null +++ b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.js @@ -0,0 +1,21 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Skill Map', { + // refresh: function(frm) { + + // } + designation: (frm) => { + frm.set_value('employee_skills', null); + if (frm.doc.designation) { + frappe.db.get_doc('Designation', frm.doc.designation).then((designation) => { + designation.skills.forEach(designation_skill => { + let row = frappe.model.add_child(frm.doc, 'Employee Skill', 'employee_skills'); + row.skill = designation_skill.skill; + row.proficiency = 1; + }); + refresh_field('employee_skills'); + }); + } + } +}); diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json new file mode 100644 index 00000000000..624145f23a9 --- /dev/null +++ b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json @@ -0,0 +1,298 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:employee", + "beta": 0, + "creation": "2019-04-16 10:07:48.303426", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "engine": "InnoDB", + "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": "employee", + "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": "Employee", + "length": 0, + "no_copy": 0, + "options": "Employee", + "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, + "fetch_from": "employee.employee_name", + "fetch_if_empty": 0, + "fieldname": "employee_name", + "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": "Employee Name", + "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, + "fetch_from": "employee.designation", + "fetch_if_empty": 0, + "fieldname": "designation", + "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": "Designation", + "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, + "fetch_if_empty": 0, + "fieldname": "skills_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": "Skills", + "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, + "fetch_if_empty": 0, + "fieldname": "employee_skills", + "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": "Employee Skills", + "length": 0, + "no_copy": 0, + "options": "Employee Skill", + "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, + "fetch_if_empty": 0, + "fieldname": "trainings_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": "Trainings", + "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, + "fetch_if_empty": 0, + "fieldname": "trainings", + "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": "Trainings", + "length": 0, + "no_copy": 0, + "options": "Employee Training", + "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 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-04-16 16:16:40.058429", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Skill Map", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "employee_name", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py new file mode 100644 index 00000000000..073f02fa258 --- /dev/null +++ b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.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 EmployeeSkillMap(Document): + pass diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json index 7b2804b3262..66fac5bee5c 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json +++ b/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "max_amount", "fieldtype": "Currency", "hidden": 0, @@ -29,7 +31,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Max Amount", + "label": "Max Exemption Amount", "length": 0, "no_copy": 0, "permlevel": 0, @@ -39,7 +41,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -52,6 +54,8 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "1", + "fetch_if_empty": 0, "fieldname": "is_active", "fieldtype": "Check", "hidden": 0, @@ -88,7 +92,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-19 16:33:48.419267", + "modified": "2019-04-25 13:20:31.367158", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Category", @@ -159,6 +163,7 @@ "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js index 9560df53e13..a827eca1c46 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js @@ -10,6 +10,7 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { } } }); + frm.set_query('payroll_period', function() { const fields = {'employee': 'Employee', 'company': 'Company'}; @@ -27,6 +28,7 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { } } }); + frm.set_query('exemption_sub_category', 'declarations', function() { return { filters: { @@ -34,5 +36,16 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { } } }); + }, + + refresh: function(frm) { + if(frm.doc.docstatus==1) { + frm.add_custom_button(__('Submit Proof'), function() { + frappe.model.open_mapped_doc({ + method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + frm: frm + }); + }).addClass("btn-primary"); + } } }); diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index 865e8219b31..8891b97a5de 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "employee", "fieldtype": "Link", "hidden": 0, @@ -53,9 +55,10 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", + "fetch_from": "employee.employee_name", + "fetch_if_empty": 0, + "fieldname": "employee_name", + "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -63,104 +66,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "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": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "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, - "fieldname": "payroll_period", - "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": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_exemption_amount", - "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": "Total Exemption Amount", + "label": "Employee Name", "length": 0, "no_copy": 0, "permlevel": 0, @@ -184,6 +90,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "employee.department", + "fetch_if_empty": 0, "fieldname": "department", "fieldtype": "Link", "hidden": 0, @@ -217,6 +124,108 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_2", + "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, + "fetch_if_empty": 0, + "fieldname": "payroll_period", + "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": 0, + "label": "Payroll Period", + "length": 0, + "no_copy": 0, + "options": "Payroll Period", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.company", + "fetch_if_empty": 0, + "fieldname": "company", + "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": "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": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -249,6 +258,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_8", "fieldtype": "Section Break", "hidden": 0, @@ -280,6 +290,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "declarations", "fieldtype": "Table", "hidden": 0, @@ -300,7 +311,137 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "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, + "fetch_if_empty": 0, + "fieldname": "section_break_10", + "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, + "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, + "fetch_if_empty": 0, + "fieldname": "total_declared_amount", + "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": "Total Declared Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "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_12", + "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, + "fetch_if_empty": 0, + "fieldname": "total_exemption_amount", + "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": "Total Exemption Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -317,7 +458,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:49.363307", + "modified": "2019-04-25 16:38:05.847925", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Declaration", diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index 186b2e10526..cbdfcf8ecdf 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -6,28 +6,61 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from erpnext.hr.utils import validate_tax_declaration, calculate_annual_eligible_hra_exemption +from frappe.utils import flt +from frappe.model.mapper import get_mapped_doc +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_annual_eligible_hra_exemption + +class DuplicateDeclarationError(frappe.ValidationError): pass class EmployeeTaxExemptionDeclaration(Document): def validate(self): validate_tax_declaration(self.declarations) - self.total_exemption_amount = 0 + self.validate_duplicate() + self.set_total_declared_amount() + self.set_total_exemption_amount() self.calculate_hra_exemption() - for item in self.declarations: - self.total_exemption_amount += item.amount - def before_submit(self): - if frappe.db.exists({"doctype": "Employee Tax Exemption Declaration", - "employee": self.employee, - "payroll_period": self.payroll_period, - "docstatus": 1}): - frappe.throw(_("Tax Declaration of {0} for period {1} already submitted.")\ - .format(self.employee, self.payroll_period), frappe.DocstatusTransitionError) + def validate_duplicate(self): + duplicate = frappe.db.get_value("Employee Tax Exemption Declaration", + filters = { + "employee": self.employee, + "payroll_period": self.payroll_period, + "name": ["!=", self.name] + } + ) + if duplicate: + frappe.throw(_("Duplicate Tax Declaration of {0} for period {1}") + .format(self.employee, self.payroll_period), DuplicateDeclarationError) + + def set_total_declared_amount(self): + self.total_declared_amount = 0.0 + for d in self.declarations: + self.total_declared_amount += flt(d.amount) + + def set_total_exemption_amount(self): + self.total_exemption_amount = get_total_exemption_amount(self.declarations) def calculate_hra_exemption(self): - hra_exemption = calculate_annual_eligible_hra_exemption(self) - if hra_exemption: - self.total_exemption_amount += hra_exemption["annual_exemption"] - self.salary_structure_hra = hra_exemption["hra_amount"] - self.annual_hra_exemption = hra_exemption["annual_exemption"] - self.monthly_hra_exemption = hra_exemption["monthly_exemption"] + self.salary_structure_hra, self.annual_hra_exemption, self.monthly_hra_exemption = 0, 0, 0 + if self.get("monthly_house_rent"): + hra_exemption = calculate_annual_eligible_hra_exemption(self) + if hra_exemption: + self.total_exemption_amount += hra_exemption["annual_exemption"] + self.salary_structure_hra = hra_exemption["hra_amount"] + self.annual_hra_exemption = hra_exemption["annual_exemption"] + self.monthly_hra_exemption = hra_exemption["monthly_exemption"] + +@frappe.whitelist() +def make_proof_submission(source_name, target_doc=None): + doclist = get_mapped_doc("Employee Tax Exemption Declaration", source_name, { + "Employee Tax Exemption Declaration": { + "doctype": "Employee Tax Exemption Proof Submission", + "field_no_map": ["monthly_house_rent", "monthly_hra_exemption"] + }, + "Employee Tax Exemption Declaration Category": { + "doctype": "Employee Tax Exemption Proof Submission Detail", + "add_if_empty": True + } + }, target_doc) + + return doclist diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index beaddd98dd0..9c87bbd1f30 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext import unittest from erpnext.hr.doctype.employee.test_employee import make_employee +from erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration import DuplicateDeclarationError class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): def setUp(self): @@ -15,71 +16,71 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): create_exemption_category() frappe.db.sql("""delete from `tabEmployee Tax Exemption Declaration`""") - def test_exemption_amount_greater_than_category_max(self): - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 150000)] - }) - self.assertRaises(frappe.ValidationError, declaration.save) - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "payroll_period": "_Test Payroll Period", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 90000)] - }) - self.assertTrue(declaration.save) - def test_duplicate_category_in_declaration(self): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000), - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 50000), - ] + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 100000), + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 50000) + ] }) self.assertRaises(frappe.ValidationError, declaration.save) - def test_duplicate_submission_for_payroll_period(self): + def test_duplicate_entry_for_payroll_period(self): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000), - dict(exemption_sub_category = "_Test1 Sub Category", - exemption_category = "_Test Category", - amount = 50000), - ] + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 100000), + dict(exemption_sub_category = "_Test1 Sub Category", + exemption_category = "_Test Category", + amount = 50000), + ] }).insert() - declaration.submit() - self.assertEquals(declaration.docstatus, 1) + duplicate_declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000) - ] - }).insert() - self.assertRaises(frappe.DocstatusTransitionError, duplicate_declaration.submit) + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 100000) + ] + }) + self.assertRaises(DuplicateDeclarationError, duplicate_declaration.insert) duplicate_declaration.employee = frappe.get_value("Employee", {"user_id":"employee1@taxexepmtion.com"}, "name") - self.assertTrue(duplicate_declaration.submit) + self.assertTrue(duplicate_declaration.insert) + + def test_exemption_amount(self): + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 80000), + dict(exemption_sub_category = "_Test1 Sub Category", + exemption_category = "_Test Category", + amount = 60000), + ] + }).insert() + + self.assertEqual(declaration.total_exemption_amount, 100000) def create_payroll_period(): if not frappe.db.exists("Payroll Period", "_Test Payroll Period"): diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json index ebde4c9c717..56556c10305 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json @@ -1,140 +1,179 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 16:56:23.333041", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-04-13 16:56:23.333041", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "exemption_sub_category", - "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": 0, - "label": "Exemption Sub Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Sub 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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "exemption_sub_category", + "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": 0, + "label": "Exemption Sub Category", + "length": 0, + "no_copy": 0, + "options": "Employee Tax Exemption Sub 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": 1, + "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, - "fetch_from": "exemption_sub_category.exemption_category", - "fieldname": "exemption_category", - "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": 0, - "label": "Exemption Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption 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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "exemption_sub_category.exemption_category", + "fetch_if_empty": 0, + "fieldname": "exemption_category", + "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": 0, + "label": "Exemption Category", + "length": 0, + "no_copy": 0, + "options": "Employee Tax Exemption Category", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "exemption_sub_category.max_amount", + "fetch_if_empty": 0, + "fieldname": "max_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Maximum Exemption Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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, + "fetch_if_empty": 0, + "fieldname": "amount", + "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": "Declared Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-29 15:58:05.779031", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Declaration Category", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-04-25 15:45:11.279158", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Tax Exemption Declaration Category", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js index 99bec14b180..66118c08111 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js @@ -10,6 +10,7 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { } } }); + frm.set_query('payroll_period', function() { if(frm.doc.employee && frm.doc.company){ return { @@ -21,6 +22,7 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { frappe.msgprint(__("Please select Employee")); } }); + frm.set_query('exemption_sub_category', 'tax_exemption_proofs', function() { return { filters: { @@ -29,11 +31,28 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { } }); }, - employee: function(frm){ - if(frm.doc.employee){ - frm.add_fetch('employee', 'company', 'company'); - }else{ - frm.set_value('company', ''); + + refresh: function(frm) { + if(frm.doc.docstatus === 0) { + let filters = { + docstatus: 1, + company: frm.doc.company + }; + if(frm.doc.employee) filters["employee"] = frm.doc.employee; + if(frm.doc.payroll_period) filters["payroll_period"] = frm.doc.payroll_period; + + frm.add_custom_button(__('Get Details From Declaration'), function() { + erpnext.utils.map_current_doc({ + method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + source_doctype: "Employee Tax Exemption Declaration", + target: frm, + date_field: "creation", + setters: { + employee: frm.doc.employee || undefined + }, + get_query_filters: filters + }); + }); } } }); diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index ebc04353e0a..76c09d6c7b7 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -1,8 +1,9 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "allow_import": 1, + "allow_rename": 1, "autoname": "HR-TAX-PRF-.YYYY.-.#####", "beta": 0, "creation": "2018-04-13 17:24:11.456132", @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "employee", "fieldtype": "Link", "hidden": 0, @@ -53,8 +55,10 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company", - "fieldtype": "Link", + "fetch_from": "employee.employee_name", + "fetch_if_empty": 0, + "fieldname": "employee_name", + "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -62,10 +66,9 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Company", + "label": "Employee Name", "length": 0, "no_copy": 0, - "options": "Company", "permlevel": 0, "precision": "", "print_hide": 0, @@ -79,70 +82,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "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, - "fieldname": "payroll_period", - "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": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -151,6 +90,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "employee.department", + "fetch_if_empty": 0, "fieldname": "department", "fieldtype": "Link", "hidden": 0, @@ -184,8 +124,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "submission_date", - "fieldtype": "Date", + "fetch_if_empty": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -193,7 +134,6 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Submission Date", "length": 0, "no_copy": 0, "permlevel": 0, @@ -216,6 +156,273 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "Today", + "fetch_if_empty": 0, + "fieldname": "submission_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Submission Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "payroll_period", + "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": 0, + "label": "Payroll Period", + "length": 0, + "no_copy": 0, + "options": "Payroll Period", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.company", + "fetch_if_empty": 0, + "fieldname": "company", + "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": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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, + "fetch_if_empty": 0, + "fieldname": "section_break_5", + "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, + "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, + "fetch_if_empty": 0, + "fieldname": "tax_exemption_proofs", + "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": "Tax Exemption Proofs", + "length": 0, + "no_copy": 0, + "options": "Employee Tax Exemption Proof Submission Detail", + "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, + "fetch_if_empty": 0, + "fieldname": "section_break_10", + "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, + "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, + "fetch_if_empty": 0, + "fieldname": "total_actual_amount", + "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": "Total Actual Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "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_12", + "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, + "fetch_if_empty": 0, "fieldname": "exemption_amount", "fieldtype": "Currency", "hidden": 0, @@ -248,70 +455,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "section_break_5", - "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, - "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, - "fieldname": "tax_exemption_proofs", - "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": "Tax Exemption Proofs", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Proof Submission Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "fetch_if_empty": 0, "fieldname": "attachment_section", "fieldtype": "Section Break", "hidden": 0, @@ -344,6 +488,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "attachments", "fieldtype": "Attach", "hidden": 0, @@ -376,6 +521,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -412,7 +558,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:38.096846", + "modified": "2019-04-25 17:06:36.569549", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Proof Submission", diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 54e0b2041bc..97ceb63476b 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -6,20 +6,30 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from erpnext.hr.utils import validate_tax_declaration, calculate_hra_exemption_for_period +from frappe.utils import flt +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_hra_exemption_for_period class EmployeeTaxExemptionProofSubmission(Document): def validate(self): validate_tax_declaration(self.tax_exemption_proofs) - self.exemption_amount = 0 + self.set_total_actual_amount() + self.set_total_exemption_amount() self.calculate_hra_exemption() - for proof in self.tax_exemption_proofs: - self.exemption_amount += proof.amount + + def set_total_actual_amount(self): + self.total_actual_amount = flt(self.get("house_rent_payment_amount")) + for d in self.tax_exemption_proofs: + self.total_actual_amount += flt(d.amount) + + def set_total_exemption_amount(self): + self.exemption_amount = get_total_exemption_amount(self.tax_exemption_proofs) def calculate_hra_exemption(self): - hra_exemption = calculate_hra_exemption_for_period(self) - if hra_exemption: - self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] - self.monthly_hra_exemption = hra_exemption["monthly_exemption"] - self.monthly_house_rent = hra_exemption["monthly_house_rent"] - self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] + self.monthly_hra_exemption, self.monthly_house_rent, self.total_eligible_hra_exemption = 0, 0, 0 + if self.get("house_rent_payment_amount"): + hra_exemption = calculate_hra_exemption_for_period(self) + if hra_exemption: + self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] + self.monthly_hra_exemption = hra_exemption["monthly_exemption"] + self.monthly_house_rent = hra_exemption["monthly_house_rent"] + self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json index c1c5896d7f9..b9254afad07 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, @@ -14,10 +15,12 @@ "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": "exemption_sub_category", "fieldtype": "Link", "hidden": 0, @@ -41,16 +44,18 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "exemption_sub_category.exemption_category", + "fetch_from": "exemption_sub_category.exemption_category", + "fetch_if_empty": 0, "fieldname": "exemption_category", "fieldtype": "Read Only", "hidden": 0, @@ -74,15 +79,51 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_from": "exemption_sub_category.max_amount", + "fetch_if_empty": 0, + "fieldname": "max_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Maximum Exemption Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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, + "fetch_if_empty": 0, "fieldname": "type_of_proof", "fieldtype": "Data", "hidden": 0, @@ -106,15 +147,17 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amount", "fieldtype": "Currency", "hidden": 0, @@ -124,7 +167,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Amount", + "label": "Actual Amount", "length": 0, "no_copy": 0, "permlevel": 0, @@ -134,10 +177,10 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 } ], @@ -151,7 +194,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-05-16 22:42:59.750733", + "modified": "2019-04-25 15:45:03.154904", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Proof Submission Detail", @@ -165,5 +208,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json index dc997850675..b0e492e7ca4 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json +++ b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -15,10 +16,12 @@ "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": "exemption_category", "fieldtype": "Link", "hidden": 0, @@ -26,8 +29,8 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, + "in_list_view": 1, + "in_standard_filter": 1, "label": "Tax Exemption Category", "length": 0, "no_copy": 0, @@ -42,14 +45,18 @@ "reqd": 1, "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, + "fetch_from": "exemption_category.max_amount", + "fetch_if_empty": 1, "fieldname": "max_amount", "fieldtype": "Currency", "hidden": 0, @@ -59,7 +66,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Max Amount", + "label": "Max Exemption Amount", "length": 0, "no_copy": 0, "permlevel": 0, @@ -69,17 +76,21 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "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": "1", + "fetch_if_empty": 0, "fieldname": "is_active", "fieldtype": "Check", "hidden": 0, @@ -102,6 +113,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -115,7 +127,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-09 13:25:01.595240", + "modified": "2019-04-25 13:24:05.164877", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Sub Category", @@ -124,7 +136,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -144,7 +155,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -164,7 +174,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -189,6 +198,7 @@ "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py index cd58136d8db..a8dd7e4d6dd 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py +++ b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py @@ -4,7 +4,13 @@ from __future__ import unicode_literals import frappe +from frappe import _ +from frappe.utils import flt from frappe.model.document import Document class EmployeeTaxExemptionSubCategory(Document): - pass + def validate(self): + category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", self.exemption_category, "max_amount") + if flt(self.max_amount) > flt(category_max_amount): + frappe.throw(_("Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}") + .format(category_max_amount, self.exemption_category)) \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_training/__init__.py b/erpnext/hr/doctype/employee_training/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/employee_training/employee_training.json b/erpnext/hr/doctype/employee_training/employee_training.json new file mode 100644 index 00000000000..0e0dc151950 --- /dev/null +++ b/erpnext/hr/doctype/employee_training/employee_training.json @@ -0,0 +1,108 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2019-04-16 16:15:50.931545", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "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": "training", + "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": 0, + "label": "Training", + "length": 0, + "no_copy": 0, + "options": "Training Event", + "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, + "fetch_from": "training.end_time", + "fetch_if_empty": 0, + "fieldname": "training_date", + "fieldtype": "Date", + "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": "Training Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-04-22 12:48:56.925419", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Training", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_training/employee_training.py b/erpnext/hr/doctype/employee_training/employee_training.py new file mode 100644 index 00000000000..810796d66cf --- /dev/null +++ b/erpnext/hr/doctype/employee_training/employee_training.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 EmployeeTraining(Document): + pass diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 9e13cb6739d..1b487a64234 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -167,7 +167,7 @@ class SalarySlip(TransactionBase): if d.amount_based_on_formula: formula = d.formula.strip() if d.formula else None if formula: - amount = frappe.safe_eval(formula, self.whitelisted_globals, data) + amount = rounded(frappe.safe_eval(formula, self.whitelisted_globals, data)) if amount: data[d.abbr] = amount @@ -602,12 +602,11 @@ class SalarySlip(TransactionBase): annual_earning = taxable_earning["taxable_earning"] * period_factor exemption_amount = 0 if frappe.db.exists("Employee Tax Exemption Declaration", {"employee": self.employee, - "payroll_period": payroll_period.name, "docstatus": 1}): + "payroll_period": payroll_period.name, "docstatus": 1}): exemption_amount = frappe.db.get_value("Employee Tax Exemption Declaration", {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, "total_exemption_amount") annual_taxable_earning = annual_earning - exemption_amount - if self.deduct_tax_for_unclaimed_employee_benefits or self.deduct_tax_for_unsubmitted_tax_exemption_proof: tax_detail = self.get_tax_paid_in_period(payroll_period, tax_component) if tax_detail: @@ -739,22 +738,24 @@ class SalarySlip(TransactionBase): # less paid taxes if args.get("pro_rata_tax_paid"): tax_amount -= args.get("pro_rata_tax_paid") + tax_amount = rounded(tax_amount) struct_row = self.get_salary_slip_row(args.get("tax_component")) return [struct_row, tax_amount, benefit_tax, additional_tax] - def calculate_tax_by_tax_slab(self, payroll_period, annual_earning): + def calculate_tax_by_tax_slab(self, payroll_period, annual_taxable_earning): payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) data = self.get_data_for_eval() + data.update({"annual_taxable_earning": annual_taxable_earning}) taxable_amount = 0 for slab in payroll_period_obj.taxable_salary_slabs: if slab.condition and not self.eval_tax_slab_condition(slab.condition, data): continue - if not slab.to_amount and annual_earning > slab.from_amount: - taxable_amount += (annual_earning - slab.from_amount) * slab.percent_deduction *.01 + if not slab.to_amount and annual_taxable_earning > slab.from_amount: + taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 continue - if annual_earning > slab.from_amount and annual_earning < slab.to_amount: - taxable_amount += (annual_earning - slab.from_amount) * slab.percent_deduction *.01 - elif annual_earning > slab.from_amount and annual_earning > slab.to_amount: + if annual_taxable_earning > slab.from_amount and annual_taxable_earning < slab.to_amount: + taxable_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01 + elif annual_taxable_earning > slab.from_amount and annual_taxable_earning > slab.to_amount: taxable_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01 return taxable_amount @@ -773,13 +774,21 @@ class SalarySlip(TransactionBase): def get_period_factor(self, period_start, period_end, start_date=None, end_date=None): # TODO if both deduct checked update the factor to make tax consistent + joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) + if getdate(joining_date) > getdate(period_start): + period_start = joining_date + if relieving_date and getdate(relieving_date) < getdate(period_end): + period_end = relieving_date + payroll_days = date_diff(period_end, period_start) + 1 if start_date and end_date: salary_days = date_diff(end_date, start_date) + 1 return flt(payroll_days)/flt(salary_days) + # if period configured for a year and monthly frequency return 12 to make tax calc consistent if 360 <= payroll_days <= 370 and self.payroll_frequency == "Monthly": return 12 + salary_days = date_diff(self.end_date, self.start_date) + 1 return flt(payroll_days)/flt(salary_days) diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 079bec51b2e..746bf8cff1d 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -13,7 +13,8 @@ from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_ from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.hr.doctype.payroll_entry.payroll_entry import get_month_details from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period, create_exemption_category +from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \ + import create_payroll_period, create_exemption_category class TestSalarySlip(unittest.TestCase): def setUp(self): @@ -36,8 +37,10 @@ class TestSalarySlip(unittest.TestCase): no_of_days = self.get_no_of_days() frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) make_employee("test_employee@salary.com") - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") self.assertEqual(ss.total_working_days, no_of_days[0]) @@ -53,8 +56,10 @@ class TestSalarySlip(unittest.TestCase): no_of_days = self.get_no_of_days() frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) make_employee("test_employee@salary.com") - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1]) @@ -100,16 +105,21 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1)) # set relieving date in the same month - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60))) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", relieving_date) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left") + frappe.db.set_value("Employee",frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60))) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", relieving_date) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left") ss.save() self.assertEqual(ss.total_working_days, no_of_days[0]) self.assertEqual(ss.payment_days, getdate(relieving_date).day) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") def test_employee_salary_slip_read_permission(self): make_employee("test_employee@salary.com") @@ -168,17 +178,22 @@ class TestSalarySlip(unittest.TestCase): def test_tax_for_payroll_period(self): data = {} - # test the impact of tax exemption declaration, tax exemption proof submission and deduct check boxes in annual tax calculation + # test the impact of tax exemption declaration, tax exemption proof submission + # and deduct check boxes in annual tax calculation # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") payroll_period = create_payroll_period() create_tax_slab(payroll_period) employee = make_employee("test_tax@salary.slip") - delete_docs = ["Salary Slip", "Additional Salary", - "Employee Tax Exemption Declaration", - "Employee Tax Exemption Proof Submission", - "Employee Benefit Claim", "Salary Structure Assignment"] + delete_docs = [ + "Salary Slip", + "Additional Salary", + "Employee Tax Exemption Declaration", + "Employee Tax Exemption Proof Submission", + "Employee Benefit Claim", + "Salary Structure Assignment" + ] for doc in delete_docs: frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) @@ -298,12 +313,11 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): if not salary_slip: salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee) - salary_slip.employee_name = frappe.get_value("Employee", {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name") + salary_slip.employee_name = frappe.get_value("Employee", + {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name") salary_slip.payroll_frequency = payroll_frequency salary_slip.posting_date = nowdate() salary_slip.insert() - # salary_slip.submit() - # salary_slip = salary_slip.name return salary_slip @@ -338,99 +352,99 @@ def create_account(company): salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr')) if not salary_account: frappe.get_doc({ - "doctype": "Account", - "account_name": "Salary", - "parent_account": "Indirect Expenses - " + frappe.get_cached_value('Company', company, 'abbr'), - "company": company + "doctype": "Account", + "account_name": "Salary", + "parent_account": "Indirect Expenses - " + frappe.get_cached_value('Company', company, 'abbr'), + "company": company }).insert() return salary_account def make_earning_salary_component(setup=False, test_tax=False): data = [ + { + "salary_component": 'Basic Salary', + "abbr":'BS', + "condition": 'base > 10000', + "formula": 'base*.5', + "type": "Earning", + "amount_based_on_formula": 1 + }, + { + "salary_component": 'HRA', + "abbr":'H', + "amount": 3000, + "type": "Earning" + }, + { + "salary_component": 'Special Allowance', + "abbr":'SA', + "condition": 'H < 10000', + "formula": 'BS*.5', + "type": "Earning", + "amount_based_on_formula": 1 + }, + { + "salary_component": "Leave Encashment", + "abbr": 'LE', + "is_additional_component": 1, + "type": "Earning" + } + ] + if test_tax: + data.extend([ { - "salary_component": 'Basic Salary', - "abbr":'BS', - "condition": 'base > 10000', - "formula": 'base*.5', + "salary_component": "Leave Travel Allowance", + "abbr": 'B', + "is_flexible_benefit": 1, "type": "Earning", - "amount_based_on_formula": 1 + "pay_against_benefit_claim": 1, + "max_benefit_amount": 100000 }, { - "salary_component": 'HRA', - "abbr":'H', - "amount": 3000, - "type": "Earning" - }, - { - "salary_component": 'Special Allowance', - "abbr":'SA', - "condition": 'H < 10000', - "formula": 'BS*.5', + "salary_component": "Medical Allowance", + "abbr": 'B', + "is_flexible_benefit": 1, + "pay_against_benefit_claim": 0, "type": "Earning", - "amount_based_on_formula": 1 + "max_benefit_amount": 15000 }, { - "salary_component": "Leave Encashment", - "abbr": 'LE', + "salary_component": "Perfomance Bonus", + "abbr": 'B', "is_additional_component": 1, "type": "Earning" } - ] - if test_tax: - data.extend([ - { - "salary_component": "Leave Travel Allowance", - "abbr": 'B', - "is_flexible_benefit": 1, - "type": "Earning", - "pay_against_benefit_claim": 1, - "max_benefit_amount": 100000 - }, - { - "salary_component": "Medical Allowance", - "abbr": 'B', - "is_flexible_benefit": 1, - "pay_against_benefit_claim": 0, - "type": "Earning", - "max_benefit_amount": 15000 - }, - { - "salary_component": "Perfomance Bonus", - "abbr": 'B', - "is_additional_component": 1, - "type": "Earning" - } - ]) + ]) if setup or test_tax: make_salary_component(data, test_tax) data.append({ - "salary_component": 'Basic Salary', - "abbr":'BS', - "condition": 'base < 10000', - "formula": 'base*.2', - "type": "Earning", - "amount_based_on_formula": 1 - }) + "salary_component": 'Basic Salary', + "abbr":'BS', + "condition": 'base < 10000', + "formula": 'base*.2', + "type": "Earning", + "amount_based_on_formula": 1 + }) return data def make_deduction_salary_component(setup=False, test_tax=False): data = [ - { - "salary_component": 'Professional Tax', - "abbr":'PT', - "condition": 'base > 10000', - "formula": 'base*.1', - "type": "Deduction", - "amount_based_on_formula": 1 - }, - { - "salary_component": 'TDS', - "abbr":'T', - "formula": 'base*.1', - "type": "Deduction", - "amount_based_on_formula": 1 - } - ] + { + "salary_component": 'Professional Tax', + "abbr":'PT', + "condition": 'base > 10000', + "formula": 'base*.1', + "type": "Deduction", + "amount_based_on_formula": 1 + }, + { + "salary_component": 'TDS', + "abbr":'T', + "formula": 'base*.1', + "type": "Deduction", + "amount_based_on_formula": 1 + } + ] if not test_tax: data.append({ "salary_component": 'TDS', @@ -453,48 +467,63 @@ def get_tax_paid_in_period(employee): def create_exemption_declaration(employee, payroll_period): create_exemption_category() - declaration = frappe.get_doc({"doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "payroll_period": payroll_period, - "company": erpnext.get_default_company()}) - declaration.append("declarations", {"exemption_sub_category": "_Test Sub Category", - "exemption_category": "_Test Category", - "amount": 100000}) + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "payroll_period": payroll_period, + "company": erpnext.get_default_company() + }) + declaration.append("declarations", { + "exemption_sub_category": "_Test Sub Category", + "exemption_category": "_Test Category", + "amount": 100000 + }) declaration.submit() def create_proof_submission(employee, payroll_period, amount): submission_date = add_months(payroll_period.start_date, random.randint(0, 11)) - proof_submission = frappe.get_doc({"doctype": "Employee Tax Exemption Proof Submission", - "employee": employee, - "payroll_period": payroll_period.name, - "submission_date": submission_date}) - proof_submission.append("tax_exemption_proofs", {"exemption_sub_category": "_Test Sub Category", - "exemption_category": "_Test Category", "type_of_proof": "Test", "amount": amount}) + proof_submission = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "employee": employee, + "payroll_period": payroll_period.name, + "submission_date": submission_date + }) + proof_submission.append("tax_exemption_proofs", { + "exemption_sub_category": "_Test Sub Category", + "exemption_category": "_Test Category", + "type_of_proof": "Test", "amount": amount + }) proof_submission.submit() return submission_date def create_benefit_claim(employee, payroll_period, amount, component): claim_date = add_months(payroll_period.start_date, random.randint(0, 11)) - frappe.get_doc({"doctype": "Employee Benefit Claim", "employee": employee, - "claimed_amount": amount, "claim_date": claim_date, "earning_component": - component}).submit() + frappe.get_doc({ + "doctype": "Employee Benefit Claim", + "employee": employee, + "claimed_amount": amount, + "claim_date": claim_date, + "earning_component": component + }).submit() return claim_date def create_tax_slab(payroll_period): - data = [{ - "from_amount": 250000, - "to_amount": 500000, - "percent_deduction": 5 - }, - { - "from_amount": 500000, - "to_amount": 1000000, - "percent_deduction": 20 - }, - { - "from_amount": 1000000, - "percent_deduction": 30 - }] + data = [ + { + "from_amount": 250000, + "to_amount": 500000, + "percent_deduction": 5 + }, + { + "from_amount": 500000, + "to_amount": 1000000, + "percent_deduction": 20 + }, + { + "from_amount": 1000000, + "percent_deduction": 30 + } + ] payroll_period.taxable_salary_slabs = [] for item in data: payroll_period.append("taxable_salary_slabs", item) @@ -526,9 +555,13 @@ def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_p def create_additional_salary(employee, payroll_period, amount): salary_date = add_months(payroll_period.start_date, random.randint(0, 11)) - frappe.get_doc({"doctype": "Additional Salary", "employee": employee, - "company": erpnext.get_default_company(), - "salary_component": "Perfomance Bonus", - "payroll_date": salary_date, - "amount": amount, "type": "Earning"}).submit() + frappe.get_doc({ + "doctype": "Additional Salary", + "employee": employee, + "company": erpnext.get_default_company(), + "salary_component": "Perfomance Bonus", + "payroll_date": salary_date, + "amount": amount, + "type": "Earning" + }).submit() return salary_date diff --git a/erpnext/hr/doctype/skill/__init__.py b/erpnext/hr/doctype/skill/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/skill/skill.js b/erpnext/hr/doctype/skill/skill.js new file mode 100644 index 00000000000..a939ff0dabf --- /dev/null +++ b/erpnext/hr/doctype/skill/skill.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Skill', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/hr/doctype/skill/skill.json b/erpnext/hr/doctype/skill/skill.json new file mode 100644 index 00000000000..518297395bd --- /dev/null +++ b/erpnext/hr/doctype/skill/skill.json @@ -0,0 +1,113 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:skill_name", + "beta": 0, + "creation": "2019-04-16 09:54:39.486915", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "engine": "InnoDB", + "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": "skill_name", + "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": "Skill Name", + "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": 1 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-04-16 09:55:00.536328", + "modified_by": "Administrator", + "module": "HR", + "name": "Skill", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/skill/skill.py b/erpnext/hr/doctype/skill/skill.py new file mode 100644 index 00000000000..8d242120753 --- /dev/null +++ b/erpnext/hr/doctype/skill/skill.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 Skill(Document): + pass diff --git a/erpnext/hr/report/loan_repayment/__init__.py b/erpnext/hr/report/loan_repayment/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/report/loan_repayment/loan_repayment.js b/erpnext/hr/report/loan_repayment/loan_repayment.js new file mode 100644 index 00000000000..21aa2061603 --- /dev/null +++ b/erpnext/hr/report/loan_repayment/loan_repayment.js @@ -0,0 +1,9 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Loan Repayment"] = { + "filters": [ + + ] +} diff --git a/erpnext/hr/report/loan_repayment/loan_repayment.json b/erpnext/hr/report/loan_repayment/loan_repayment.json new file mode 100644 index 00000000000..b967dfdb381 --- /dev/null +++ b/erpnext/hr/report/loan_repayment/loan_repayment.json @@ -0,0 +1,28 @@ +{ + "add_total_row": 0, + "creation": "2019-03-29 18:58:00.166032", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2019-03-29 18:58:00.166032", + "modified_by": "Administrator", + "module": "HR", + "name": "Loan Repayment", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Loan", + "report_name": "Loan Repayment", + "report_type": "Script Report", + "roles": [ + { + "role": "HR Manager" + }, + { + "role": "Employee" + } + ] +} diff --git a/erpnext/hr/report/loan_repayment/loan_repayment.py b/erpnext/hr/report/loan_repayment/loan_repayment.py new file mode 100644 index 00000000000..9e310de48c9 --- /dev/null +++ b/erpnext/hr/report/loan_repayment/loan_repayment.py @@ -0,0 +1,95 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + + columns = create_columns() + data = get_record() + return columns, data + +def create_columns(): + return [ + { + "label": _("Employee"), + "fieldtype": "Data", + "fieldname": "employee", + "options": "Employee", + "width": 200 + }, + { + "label": _("Loan"), + "fieldtype": "Link", + "fieldname": "loan_name", + "options": "Loan", + "width": 200 + }, + { + "label": _("Loan Amount"), + "fieldtype": "Currency", + "fieldname": "loan_amount", + "options": "currency", + "width": 100 + }, + { + "label": _("Interest"), + "fieldtype": "Data", + "fieldname": "interest", + "width": 100 + }, + { + "label": _("Payable Amount"), + "fieldtype": "Currency", + "fieldname": "payable_amount", + "options": "currency", + "width": 100 + }, + { + "label": _("EMI"), + "fieldtype": "Currency", + "fieldname": "emi", + "options": "currency", + "width": 100 + }, + { + "label": _("Paid Amount"), + "fieldtype": "Currency", + "fieldname": "paid_amount", + "options": "currency", + "width": 100 + }, + { + "label": _("Outstanding Amount"), + "fieldtype": "Currency", + "fieldname": "out_amt", + "options": "currency", + "width": 100 + }, + ] + +def get_record(): + data = [] + loans = frappe.get_all("Loan", + filters=[("status", "=", "Fully Disbursed")], + fields=["applicant", "applicant_name", "name", "loan_amount", "rate_of_interest", + "total_payment", "monthly_repayment_amount", "total_amount_paid"] + ) + + for loan in loans: + row = { + "employee": loan.applicant + ": " + loan.applicant_name, + "loan_name": loan.name, + "loan_amount": loan.loan_amount, + "interest": str(loan.rate_of_interest) + "%", + "payable_amount": loan.total_payment, + "emi": loan.monthly_repayment_amount, + "paid_amount": loan.total_amount_paid, + "out_amt": loan.total_payment - loan.total_amount_paid + } + + data.append(row) + + return data diff --git a/erpnext/hr/report/salary_register/salary_register.py b/erpnext/hr/report/salary_register/salary_register.py index 586ca672731..869ca231397 100644 --- a/erpnext/hr/report/salary_register/salary_register.py +++ b/erpnext/hr/report/salary_register/salary_register.py @@ -99,8 +99,6 @@ def get_employee_doj_map(): employee, date_of_joining FROM `tabEmployee` - WHERE - `status`='Active' """)) def get_ss_earning_map(salary_slips): diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 313cfc453f8..a600e758842 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -221,16 +221,30 @@ def get_employee_leave_policy(employee): def validate_tax_declaration(declarations): subcategories = [] - for declaration in declarations: - if declaration.exemption_sub_category in subcategories: - frappe.throw(_("More than one selection for {0} not \ - allowed").format(declaration.exemption_sub_category), frappe.ValidationError) - subcategories.append(declaration.exemption_sub_category) - max_amount = frappe.db.get_value("Employee Tax Exemption Sub Category", \ - declaration.exemption_sub_category, "max_amount") - if declaration.amount > max_amount: - frappe.throw(_("Max exemption amount for {0} is {1}").format(\ - declaration.exemption_sub_category, max_amount), frappe.ValidationError) + for d in declarations: + if d.exemption_sub_category in subcategories: + frappe.throw(_("More than one selection for {0} not allowed").format(d.exemption_sub_category)) + subcategories.append(d.exemption_sub_category) + +def get_total_exemption_amount(declarations): + exemptions = frappe._dict() + for d in declarations: + exemptions.setdefault(d.exemption_category, frappe._dict()) + category_max_amount = exemptions.get(d.exemption_category).max_amount + if not category_max_amount: + category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", d.exemption_category, "max_amount") + exemptions.get(d.exemption_category).max_amount = category_max_amount + sub_category_exemption_amount = d.max_amount \ + if (d.max_amount and flt(d.amount) > flt(d.max_amount)) else d.amount + + exemptions.get(d.exemption_category).setdefault("total_exemption_amount", 0.0) + exemptions.get(d.exemption_category).total_exemption_amount += flt(sub_category_exemption_amount) + + if category_max_amount and exemptions.get(d.exemption_category).total_exemption_amount > category_max_amount: + exemptions.get(d.exemption_category).total_exemption_amount = category_max_amount + + total_exemption_amount = sum([flt(d.total_exemption_amount) for d in exemptions.values()]) + return total_exemption_amount def get_leave_period(from_date, to_date, company): leave_period = frappe.db.sql(""" diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 59861ceba3a..a7d1d859dd7 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -20,13 +20,12 @@ class BOMUpdateTool(Document): for bom in bom_list: try: bom_obj = frappe.get_doc("BOM", bom) - bom_obj.get_doc_before_save() + bom_obj.load_doc_before_save() updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) bom_obj.calculate_cost() bom_obj.update_parent_cost() bom_obj.db_update() - if (getattr(bom_obj.meta, 'track_changes', False) - and bom_obj._doc_before_save and not bom_obj.flags.ignore_version): + if (getattr(bom_obj.meta, 'track_changes', False) and not bom_obj.flags.ignore_version): bom_obj.save_version() frappe.db.commit() @@ -37,7 +36,7 @@ class BOMUpdateTool(Document): def validate_bom(self): if cstr(self.current_bom) == cstr(self.new_bom): frappe.throw(_("Current BOM and New BOM can not be same")) - + if frappe.db.get_value("BOM", self.current_bom, "item") \ != frappe.db.get_value("BOM", self.new_bom, "item"): frappe.throw(_("The selected BOMs are not for the same item")) diff --git a/erpnext/patches/v11_1/woocommerce_set_creation_user.py b/erpnext/patches/v11_1/woocommerce_set_creation_user.py index e50d5ae711b..5ccdec6d262 100644 --- a/erpnext/patches/v11_1/woocommerce_set_creation_user.py +++ b/erpnext/patches/v11_1/woocommerce_set_creation_user.py @@ -1,10 +1,11 @@ from __future__ import unicode_literals import frappe +from frappe.utils import cint def execute(): - woocommerce_setting_enable_sync = frappe.db.sql("SELECT t.value FROM tabSingles t WHERE doctype = 'Woocommerce Settings' AND field = 'enable_sync'", as_dict=True) - if len(woocommerce_setting_enable_sync) and woocommerce_setting_enable_sync[0].value == '1': - frappe.db.sql("""UPDATE tabSingles - SET value = (SELECT t.value FROM tabSingles t WHERE doctype = 'Woocommerce Settings' AND field = 'modified_by') - WHERE doctype = 'Woocommerce Settings' - AND field = 'creation_user';""") \ No newline at end of file + frappe.reload_doc("erpnext_integrations", "doctype","woocommerce_settings") + doc = frappe.get_doc("Woocommerce Settings") + + if cint(doc.enable_sync): + doc.creation_user = doc.modified_by + doc.save(ignore_permissions=True) \ No newline at end of file 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 575ebebadb3..02203d29d40 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 @@ -13,19 +13,19 @@ def execute(): old_item_taxes.setdefault(d.item_code, []) old_item_taxes[d.item_code].append(d) - frappe.reload_doc("accounts", "doctype", "item_tax_template_detail") - frappe.reload_doc("accounts", "doctype", "item_tax_template") - frappe.reload_doc("stock", "doctype", "item") - frappe.reload_doc("stock", "doctype", "item_tax") - frappe.reload_doc("selling", "doctype", "quotation_item") - frappe.reload_doc("selling", "doctype", "sales_order_item") - frappe.reload_doc("stock", "doctype", "delivery_note_item") - frappe.reload_doc("accounts", "doctype", "sales_invoice_item") - frappe.reload_doc("buying", "doctype", "supplier_quotation_item") - frappe.reload_doc("buying", "doctype", "purchase_order_item") - frappe.reload_doc("stock", "doctype", "purchase_receipt_item") - frappe.reload_doc("accounts", "doctype", "purchase_invoice_item") - frappe.reload_doc("accounts", "doctype", "accounts_settings") + frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1) + frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1) + frappe.reload_doc("stock", "doctype", "item", force=1) + frappe.reload_doc("stock", "doctype", "item_tax", force=1) + frappe.reload_doc("selling", "doctype", "quotation_item", force=1) + frappe.reload_doc("selling", "doctype", "sales_order_item", force=1) + frappe.reload_doc("stock", "doctype", "delivery_note_item", force=1) + frappe.reload_doc("accounts", "doctype", "sales_invoice_item", force=1) + frappe.reload_doc("buying", "doctype", "supplier_quotation_item", force=1) + frappe.reload_doc("buying", "doctype", "purchase_order_item", force=1) + frappe.reload_doc("stock", "doctype", "purchase_receipt_item", force=1) + frappe.reload_doc("accounts", "doctype", "purchase_invoice_item", force=1) + frappe.reload_doc("accounts", "doctype", "accounts_settings", force=1) # for each item that have item tax rates for item_code in old_item_taxes.keys(): @@ -77,6 +77,21 @@ def get_item_tax_template(item_tax_templates, rename_template_to_untitled, item_ item_tax_template = frappe.new_doc("Item Tax Template") item_tax_template.title = "{}--{}".format(parent, item_code) if parent else "Item-{}".format(item_code) for tax_type, tax_rate in iteritems(item_tax_map): + if not frappe.db.exists("Account", tax_type): + parts = tax_type.strip().split(" - ") + 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") + + frappe.get_doc({ + "doctype": "Account", + "account_name": account_name, + "company": company, + "account_type": "Tax", + "parent_account": parent_account + }).insert() + item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) item_tax_templates.setdefault(item_tax_template.title, {}) item_tax_templates[item_tax_template.title][tax_type] = tax_rate diff --git a/erpnext/portal/product_configurator/item_variants_cache.py b/erpnext/portal/product_configurator/item_variants_cache.py index 458c229e2f9..f17639c1e71 100644 --- a/erpnext/portal/product_configurator/item_variants_cache.py +++ b/erpnext/portal/product_configurator/item_variants_cache.py @@ -62,7 +62,7 @@ class ItemVariantsCacheManager: item_variants_data = frappe.db.get_all('Item Variant Attribute', {'variant_of': parent_item_code}, ['parent', 'attribute', 'attribute_value'], - order_by='parent', + order_by='name', as_list=1 ) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index f02212c6a6a..405a6d8c31c 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -167,13 +167,17 @@ def get_attributes_and_values(item_code): if attribute in attribute_list: valid_options.setdefault(attribute, set()).add(attribute_value) + item_attribute_values = frappe.db.get_all('Item Attribute Value', + ['parent', 'attribute_value', 'idx'], order_by='parent asc, idx asc') + ordered_attribute_value_map = frappe._dict() + for iv in item_attribute_values: + ordered_attribute_value_map.setdefault(iv.parent, []).append(iv.attribute_value) + # build attribute values in idx order - ordered_attribute_value_map = item_cache.get_ordered_attribute_values() for attr in attributes: valid_attribute_values = valid_options.get(attr.attribute, []) ordered_values = ordered_attribute_value_map.get(attr.attribute, []) attr['values'] = [v for v in ordered_values if v in valid_attribute_values] - attr['values'] = valid_attribute_values return attributes diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 4012346da13..bba258a49a5 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -1,1505 +1,378 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "TASK-.YYYY.-.#####", - "beta": 0, "creation": "2013-01-29 19:25:50", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "field_order": [ + "subject", + "project", + "issue", + "type", + "is_group", + "column_break0", + "status", + "priority", + "task_weight", + "color", + "parent_task", + "sb_timeline", + "exp_start_date", + "expected_time", + "column_break_11", + "exp_end_date", + "progress", + "is_milestone", + "sb_details", + "description", + "sb_depends_on", + "depends_on", + "depends_on_tasks", + "sb_actual", + "act_start_date", + "actual_time", + "column_break_15", + "act_end_date", + "sb_costing", + "total_costing_amount", + "total_expense_claim", + "column_break_20", + "total_billing_amount", + "sb_more_info", + "review_date", + "closing_date", + "column_break_22", + "department", + "company", + "lft", + "rgt", + "old_parent" + ], "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": "subject", "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": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 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": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "project", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, "in_list_view": 1, "in_standard_filter": 1, "label": "Project", - "length": 0, - "no_copy": 0, "oldfieldname": "project", "oldfieldtype": "Link", "options": "Project", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, "remember_last_selected_value": 1, - "report_hide": 0, - "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, - "fetch_if_empty": 0, "fieldname": "issue", "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": "Issue", - "length": 0, - "no_copy": 0, - "options": "Issue", - "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": "Issue" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "type", "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": "Type", - "length": 0, - "no_copy": 0, - "options": "Task 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": "Task Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, "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, - "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 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": "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, "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50%", - "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": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", - "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": "Status", - "length": 0, "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled", - "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": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "priority", "fieldtype": "Select", - "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": "Priority", - "length": 0, - "no_copy": 0, "oldfieldname": "priority", "oldfieldtype": "Select", "options": "Low\nMedium\nHigh\nUrgent", - "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": 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, - "fetch_if_empty": 0, "fieldname": "color", "fieldtype": "Color", - "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": "Color", - "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": "Color" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "parent_task", "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": "Parent Task", - "length": 0, - "no_copy": 0, "options": "Task", - "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": 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": 1, "collapsible_depends_on": "eval:doc.__islocal", - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "sb_timeline", "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": "Timeline", - "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": "Timeline" }, { - "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": "exp_start_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Expected Start Date", - "length": 0, - "no_copy": 0, "oldfieldname": "exp_start_date", - "oldfieldtype": "Date", - "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": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "depends_on": "", - "description": "", - "fetch_if_empty": 0, "fieldname": "expected_time", "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": "Expected Time (in hours)", - "length": 0, - "no_copy": 0, "oldfieldname": "exp_total_hrs", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "fetch_from": "type.weight", - "fetch_if_empty": 0, "fieldname": "task_weight", "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": "Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Weight" }, { - "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_11", - "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": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "exp_end_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Expected End Date", - "length": 0, - "no_copy": 0, "oldfieldname": "exp_end_date", "oldfieldtype": "Date", - "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": 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": "", - "fetch_if_empty": 0, "fieldname": "progress", "fieldtype": "Percent", - "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": "% Progress", - "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": "% Progress" }, { - "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": "is_milestone", "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 Milestone", - "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 Milestone" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "sb_details", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Details", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "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": "Section Break" }, { - "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": "description", "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Task Description", - "length": 0, - "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Text Editor", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "300px", - "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": "300px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "sb_depends_on", "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": "Depends On", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "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": "Dependencies", + "oldfieldtype": "Section Break" }, { - "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": "depends_on", "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": "depends_on", - "length": 0, - "no_copy": 0, - "options": "Task Depends On", - "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": "Dependent Tasks", + "options": "Task Depends On" }, { - "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": "depends_on_tasks", "fieldtype": "Data", "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": "Depends on Tasks", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", - "description": "", - "fetch_if_empty": 0, "fieldname": "sb_actual", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50%", - "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, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "act_start_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Actual Start Date (via Time Sheet)", - "length": 0, - "no_copy": 0, "oldfieldname": "act_start_date", "oldfieldtype": "Date", - "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, - "default": "", - "depends_on": "", - "description": "", - "fetch_if_empty": 0, "fieldname": "actual_time", "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": "Actual Time (in hours)", - "length": 0, - "no_copy": 0, - "options": "", - "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": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_15", - "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, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "act_end_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Actual End Date (via Time Sheet)", - "length": 0, - "no_copy": 0, "oldfieldname": "act_end_date", "oldfieldtype": "Date", - "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": 1, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "sb_costing", "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": "Costing", - "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": "Costing" }, { - "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": "total_costing_amount", "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": "Total Costing Amount (via Time Sheet)", - "length": 0, - "no_copy": 0, "oldfieldname": "actual_budget", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "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, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "total_expense_claim", "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": "Total Expense Claim (via Expense Claim)", - "length": 0, - "no_copy": 0, "options": "Company:company:default_currency", - "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": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_20", - "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, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "total_billing_amount", "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": "Total Billing Amount (via Time Sheet)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "sb_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 Info", - "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": "More Info" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.status == \"Closed\" || doc.status == \"Pending Review\"", - "fetch_if_empty": 0, "fieldname": "review_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Review Date", - "length": 0, - "no_copy": 0, "oldfieldname": "review_date", - "oldfieldtype": "Date", - "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": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.status == \"Closed\"", - "fetch_if_empty": 0, "fieldname": "closing_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Closing Date", - "length": 0, - "no_copy": 0, "oldfieldname": "closing_date", - "oldfieldtype": "Date", - "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": "Date" }, { - "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_22", - "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, - "fetch_if_empty": 0, "fieldname": "department", "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": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "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": "Department" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "company", "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": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "remember_last_selected_value": 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": "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": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "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": "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": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "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": "old_parent", "fieldtype": "Data", "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": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_toolbar": 0, "icon": "fa fa-check", "idx": 1, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, "max_attachments": 5, - "menu_index": 0, - "modified": "2019-04-20 22:45:20.777600", + "modified": "2019-04-29 14:48:10.204819", "modified_by": "Administrator", "module": "Projects", "name": "Task", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Projects User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, "search_fields": "subject", "show_name_in_global_search": 1, "sort_order": "DESC", "timeline_field": "project", "title_field": "subject", - "track_changes": 0, - "track_seen": 1, - "track_views": 0 + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 60e72dad715..45de6eb294f 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -54,5 +54,8 @@ "stock/dashboard/item_dashboard.html", "stock/dashboard/item_dashboard_list.html", "stock/dashboard/item_dashboard.js" + ], + "js/lms.min.js": [ + "public/js/education/lms/lms.js" ] } diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index cb01c525d59..537ca26a953 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1200,6 +1200,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "qty": d.qty, "stock_qty": d.stock_qty, "uom": d.uom, + "stock_uom": d.stock_uom, "parenttype": d.parenttype, "parent": d.parent, "pricing_rules": d.pricing_rules, diff --git a/erpnext/public/js/education/lms/call.js b/erpnext/public/js/education/lms/call.js new file mode 100644 index 00000000000..e35acbdd752 --- /dev/null +++ b/erpnext/public/js/education/lms/call.js @@ -0,0 +1,15 @@ +frappe.ready(() => { + frappe.provide('lms'); + + lms.call = (method, args) => { + const method_path = 'erpnext.www.lms.' + method; + return new Promise((resolve, reject) => { + return frappe.call({ + method: method_path, + args, + }) + .then(r => resolve(r.message)) + .fail(reject); + }); + }; +}); \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Article.vue b/erpnext/public/js/education/lms/components/Article.vue new file mode 100644 index 00000000000..eab1424455c --- /dev/null +++ b/erpnext/public/js/education/lms/components/Article.vue @@ -0,0 +1,44 @@ + + diff --git a/erpnext/public/js/education/lms/components/Breadcrumb.vue b/erpnext/public/js/education/lms/components/Breadcrumb.vue new file mode 100644 index 00000000000..1b617a3751c --- /dev/null +++ b/erpnext/public/js/education/lms/components/Breadcrumb.vue @@ -0,0 +1,56 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Button.vue b/erpnext/public/js/education/lms/components/Button.vue new file mode 100644 index 00000000000..4d8df4b3140 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Button.vue @@ -0,0 +1,25 @@ + + diff --git a/erpnext/public/js/education/lms/components/CardList.vue b/erpnext/public/js/education/lms/components/CardList.vue new file mode 100644 index 00000000000..298627f757e --- /dev/null +++ b/erpnext/public/js/education/lms/components/CardList.vue @@ -0,0 +1,28 @@ + + + diff --git a/erpnext/public/js/education/lms/components/ContentNavigation.vue b/erpnext/public/js/education/lms/components/ContentNavigation.vue new file mode 100644 index 00000000000..a07c0f85f40 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ContentNavigation.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/ContentTitle.vue b/erpnext/public/js/education/lms/components/ContentTitle.vue new file mode 100644 index 00000000000..a488ab85c37 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ContentTitle.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/CourseCard.vue b/erpnext/public/js/education/lms/components/CourseCard.vue new file mode 100644 index 00000000000..16f8873bd1b --- /dev/null +++ b/erpnext/public/js/education/lms/components/CourseCard.vue @@ -0,0 +1,92 @@ + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Navbar.vue b/erpnext/public/js/education/lms/components/Navbar.vue new file mode 100644 index 00000000000..f3f3ce4cbb0 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Navbar.vue @@ -0,0 +1,85 @@ + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/ProfileInfo.vue b/erpnext/public/js/education/lms/components/ProfileInfo.vue new file mode 100644 index 00000000000..6f3e8f12664 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ProfileInfo.vue @@ -0,0 +1,85 @@ + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/ProgramCard.vue b/erpnext/public/js/education/lms/components/ProgramCard.vue new file mode 100644 index 00000000000..20de085e6f3 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ProgramCard.vue @@ -0,0 +1,82 @@ + + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/ProgressCard.vue b/erpnext/public/js/education/lms/components/ProgressCard.vue new file mode 100644 index 00000000000..66b61f694e4 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ProgressCard.vue @@ -0,0 +1,89 @@ + + + diff --git a/erpnext/public/js/education/lms/components/Quiz.vue b/erpnext/public/js/education/lms/components/Quiz.vue new file mode 100644 index 00000000000..0a6199a7568 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Quiz.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/Quiz/QuizMultipleChoice.vue b/erpnext/public/js/education/lms/components/Quiz/QuizMultipleChoice.vue new file mode 100644 index 00000000000..338b1ac0c51 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Quiz/QuizMultipleChoice.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/Quiz/QuizSingleChoice.vue b/erpnext/public/js/education/lms/components/Quiz/QuizSingleChoice.vue new file mode 100644 index 00000000000..235cbce4ae3 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Quiz/QuizSingleChoice.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/ScoreCard.vue b/erpnext/public/js/education/lms/components/ScoreCard.vue new file mode 100644 index 00000000000..80b12cb6f63 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ScoreCard.vue @@ -0,0 +1,60 @@ + + + diff --git a/erpnext/public/js/education/lms/components/TopSection.vue b/erpnext/public/js/education/lms/components/TopSection.vue new file mode 100644 index 00000000000..c27d0031efb --- /dev/null +++ b/erpnext/public/js/education/lms/components/TopSection.vue @@ -0,0 +1,27 @@ + + + diff --git a/erpnext/public/js/education/lms/components/TopSectionButton.vue b/erpnext/public/js/education/lms/components/TopSectionButton.vue new file mode 100644 index 00000000000..0fa49d4da53 --- /dev/null +++ b/erpnext/public/js/education/lms/components/TopSectionButton.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/TopicCard.vue b/erpnext/public/js/education/lms/components/TopicCard.vue new file mode 100644 index 00000000000..4cb8e85c3bf --- /dev/null +++ b/erpnext/public/js/education/lms/components/TopicCard.vue @@ -0,0 +1,112 @@ + + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Video.vue b/erpnext/public/js/education/lms/components/Video.vue new file mode 100644 index 00000000000..27f922f4874 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Video.vue @@ -0,0 +1,64 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/VideoModal.vue b/erpnext/public/js/education/lms/components/VideoModal.vue new file mode 100644 index 00000000000..71227ade2c3 --- /dev/null +++ b/erpnext/public/js/education/lms/components/VideoModal.vue @@ -0,0 +1,35 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/YoutubePlayer.vue b/erpnext/public/js/education/lms/components/YoutubePlayer.vue new file mode 100644 index 00000000000..9377b57d3b8 --- /dev/null +++ b/erpnext/public/js/education/lms/components/YoutubePlayer.vue @@ -0,0 +1,36 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/lms.js b/erpnext/public/js/education/lms/lms.js new file mode 100644 index 00000000000..4665b144c2e --- /dev/null +++ b/erpnext/public/js/education/lms/lms.js @@ -0,0 +1,81 @@ +import Vue from 'vue/dist/vue.js'; +import VueRouter from 'vue-router/dist/vue-router.js'; +import moment from 'moment/min/moment.min.js'; + +import lmsRoot from "./lmsRoot.vue"; +import routes from './routes'; +import './call'; + +Vue.use(VueRouter); + +var store = { + enrolledPrograms: [], + enrolledCourses: [] +}; + +// let profile_page = ` LMS Profile ` +// document.querySelector('#website-post-login > ul').innerHTML += profile_page + +frappe.ready(() => { + frappe.provide('lms'); + + lms.moment = moment; + + lms.store = new Vue({ + data: store, + methods: { + updateEnrolledPrograms() { + if(this.checkLogin()) { + lms.call("get_program_enrollments").then(data => { + this.enrolledPrograms = data; + }); + } + }, + updateEnrolledCourses() { + if(this.checkLogin()) { + lms.call("get_all_course_enrollments").then(data => { + this.enrolledCourses = data; + }); + } + }, + checkLogin() { + return frappe.is_user_logged_in(); + }, + updateState() { + this.checkLogin(); + this.updateEnrolledPrograms(); + this.updateEnrolledCourses(); + }, + checkProgramEnrollment(programName) { + if(this.checkLogin()){ + if(this.enrolledPrograms) { + if(this.enrolledPrograms.includes(programName)) { + return true; + } + else { + return false; + } + } + else { + return false; + } + } + else { + return false; + } + } + } + }); + lms.view = new Vue({ + el: "#lms-app", + router: new VueRouter({ routes }), + template: "", + components: { lmsRoot }, + mounted() { + lms.store.updateState(); + } + }); + lms.view.$router.afterEach((to, from) => { + window.scrollTo(0,0); + }); +}); \ No newline at end of file diff --git a/erpnext/public/js/education/lms/lmsRoot.vue b/erpnext/public/js/education/lms/lmsRoot.vue new file mode 100644 index 00000000000..d359265c587 --- /dev/null +++ b/erpnext/public/js/education/lms/lmsRoot.vue @@ -0,0 +1,45 @@ + + + diff --git a/erpnext/public/js/education/lms/pages/ContentPage.vue b/erpnext/public/js/education/lms/pages/ContentPage.vue new file mode 100644 index 00000000000..542e9371120 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ContentPage.vue @@ -0,0 +1,87 @@ + + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/CoursePage.vue b/erpnext/public/js/education/lms/pages/CoursePage.vue new file mode 100644 index 00000000000..9aaf8a9858d --- /dev/null +++ b/erpnext/public/js/education/lms/pages/CoursePage.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/Home.vue b/erpnext/public/js/education/lms/pages/Home.vue new file mode 100644 index 00000000000..569008674f9 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/Home.vue @@ -0,0 +1,48 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/ListPage.vue b/erpnext/public/js/education/lms/pages/ListPage.vue new file mode 100644 index 00000000000..07681910e04 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ListPage.vue @@ -0,0 +1,53 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/ProfilePage.vue b/erpnext/public/js/education/lms/pages/ProfilePage.vue new file mode 100644 index 00000000000..c926463fcd2 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ProfilePage.vue @@ -0,0 +1,50 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/ProgramPage.vue b/erpnext/public/js/education/lms/pages/ProgramPage.vue new file mode 100644 index 00000000000..2a136614df4 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ProgramPage.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/routes.js b/erpnext/public/js/education/lms/routes.js new file mode 100644 index 00000000000..483f2220c3f --- /dev/null +++ b/erpnext/public/js/education/lms/routes.js @@ -0,0 +1,92 @@ +import Home from "./pages/Home.vue"; +import ProgramPage from "./pages/ProgramPage.vue"; +import CoursePage from "./pages/CoursePage.vue"; +import ContentPage from "./pages/ContentPage.vue"; +import ListPage from "./pages/ListPage.vue"; +import ProfilePage from "./pages/ProfilePage.vue"; + +const routes = [{ + name: 'home', + path: '', + component: Home +}, +{ + name: 'program', + path: '/Program/:program_name', + component: ProgramPage, + props: true +}, +{ + name: 'course', + path: '/Program/:program_name/:course_name/', + component: CoursePage, + props: true, +}, +{ + name: 'content', + path: '/Program/:program_name/:course_name/:topic/:type/:content', + component: ContentPage, + props: true, + beforeRouteUpdate (to, from, next) { + if (lms.store.checkProgramEnrollment(to.params.program_name)) { + next(); + } else { + next({ + name: 'program', + params: { + program_name: to.params.program_name + } + }); + } + } +}, +{ + name: 'list', + path: '/List/:master', + component: ListPage, + props: true +}, +{ + name: 'signup', + path: '/Signup', + beforeEnter(to, from, next) { + window.location = window.location.origin.toString() + '/login#signup'; + }, + component: Home, + props: true +}, +{ + name: 'login', + path: '/Login', + beforeEnter(to, from, next) { + window.location = window.location.origin.toString() + '/login#login'; + }, + component: Home, + props: true +}, +{ + name: 'logout', + path: '/Logout', + beforeEnter(to, from, next) { + window.location = window.location.origin.toString() + '/?cmd=web_logout'; + }, + component: Home, + props: true +}, +{ + name: 'profile', + path: '/Profile', + component: ProfilePage, + props: true, + beforeEnter: (to, from, next) => { + if (!lms.store.checkLogin()) { + next({ + name: 'home' + }); + } else { + next(); + } + } +}]; + +export default routes; \ No newline at end of file diff --git a/erpnext/public/js/newsletter.js b/erpnext/public/js/newsletter.js new file mode 100644 index 00000000000..3a4dbf89330 --- /dev/null +++ b/erpnext/public/js/newsletter.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.ui.form.on('Newsletter', { + refresh() { + erpnext.toggle_naming_series(); + } +}); diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 6b583af475b..f3a4f7cb12f 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -284,18 +284,18 @@ def make_custom_fields(update=True): 'Employee Tax Exemption Declaration':[ dict(fieldname='hra_section', label='HRA Exemption', fieldtype='Section Break', insert_after='declarations'), - dict(fieldname='salary_structure_hra', label='HRA as per Salary Structure', - fieldtype='Currency', insert_after='hra_section', read_only=1), dict(fieldname='monthly_house_rent', label='Monthly House Rent', - fieldtype='Currency', insert_after='salary_structure_hra'), + fieldtype='Currency', insert_after='hra_section'), dict(fieldname='rented_in_metro_city', label='Rented in Metro City', - fieldtype='Check', insert_after='monthly_house_rent'), + fieldtype='Check', insert_after='monthly_house_rent', depends_on='monthly_house_rent'), + dict(fieldname='salary_structure_hra', label='HRA as per Salary Structure', + fieldtype='Currency', insert_after='rented_in_metro_city', read_only=1, depends_on='monthly_house_rent'), dict(fieldname='hra_column_break', fieldtype='Column Break', - insert_after='rented_in_metro_city'), + insert_after='salary_structure_hra', depends_on='monthly_house_rent'), dict(fieldname='annual_hra_exemption', label='Annual HRA Exemption', - fieldtype='Currency', insert_after='hra_column_break', read_only=1), + fieldtype='Currency', insert_after='hra_column_break', read_only=1, depends_on='monthly_house_rent'), dict(fieldname='monthly_hra_exemption', label='Monthly HRA Exemption', - fieldtype='Currency', insert_after='annual_hra_exemption', read_only=1) + fieldtype='Currency', insert_after='annual_hra_exemption', read_only=1, depends_on='monthly_house_rent') ], 'Employee Tax Exemption Proof Submission': [ dict(fieldname='hra_section', label='HRA Exemption', @@ -303,19 +303,19 @@ def make_custom_fields(update=True): dict(fieldname='house_rent_payment_amount', label='House Rent Payment Amount', fieldtype='Currency', insert_after='hra_section'), dict(fieldname='rented_in_metro_city', label='Rented in Metro City', - fieldtype='Check', insert_after='house_rent_payment_amount'), + fieldtype='Check', insert_after='house_rent_payment_amount', depends_on='house_rent_payment_amount'), dict(fieldname='rented_from_date', label='Rented From Date', - fieldtype='Date', insert_after='rented_in_metro_city'), + fieldtype='Date', insert_after='rented_in_metro_city', depends_on='house_rent_payment_amount'), dict(fieldname='rented_to_date', label='Rented To Date', - fieldtype='Date', insert_after='rented_from_date'), + fieldtype='Date', insert_after='rented_from_date', depends_on='house_rent_payment_amount'), dict(fieldname='hra_column_break', fieldtype='Column Break', - insert_after='rented_to_date'), + insert_after='rented_to_date', depends_on='house_rent_payment_amount'), dict(fieldname='monthly_house_rent', label='Monthly House Rent', - fieldtype='Currency', insert_after='hra_column_break', read_only=1), + fieldtype='Currency', insert_after='hra_column_break', read_only=1, depends_on='house_rent_payment_amount'), dict(fieldname='monthly_hra_exemption', label='Monthly Eligible Amount', - fieldtype='Currency', insert_after='monthly_house_rent', read_only=1), + fieldtype='Currency', insert_after='monthly_house_rent', read_only=1, depends_on='house_rent_payment_amount'), dict(fieldname='total_eligible_hra_exemption', label='Total Eligible HRA Exemption', - fieldtype='Currency', insert_after='monthly_hra_exemption', read_only=1) + fieldtype='Currency', insert_after='monthly_hra_exemption', read_only=1, depends_on='house_rent_payment_amount') ], 'Supplier': [ { diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index e379ed80501..f413a8ed4d6 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe, re from frappe import _ -from frappe.utils import cstr, flt, date_diff, getdate +from frappe.utils import cstr, flt, date_diff, nowdate from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount from erpnext.controllers.accounts_controller import get_taxes_and_charges @@ -144,24 +144,40 @@ def get_regional_address_details(out, doctype, company): def calculate_annual_eligible_hra_exemption(doc): basic_component = frappe.get_cached_value('Company', doc.company, "basic_component") hra_component = frappe.get_cached_value('Company', doc.company, "hra_component") + if not (basic_component and hra_component): + frappe.throw(_("Please mention Basic and HRA component in Company")) + annual_exemption, monthly_exemption, hra_amount = 0, 0, 0 if hra_component and basic_component: - assignment = get_salary_assignment(doc.employee, getdate()) - if assignment and frappe.db.exists("Salary Detail", { - "parent": assignment.salary_structure, - "salary_component": hra_component, "parentfield": "earnings"}): - basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee, - assignment.salary_structure, basic_component, hra_component) - if hra_amount: - if doc.monthly_house_rent: - annual_exemption = calculate_hra_exemption(assignment.salary_structure, - basic_amount, hra_amount, doc.monthly_house_rent, - doc.rented_in_metro_city) - if annual_exemption > 0: - monthly_exemption = annual_exemption / 12 - else: - annual_exemption = 0 - return {"hra_amount": hra_amount, "annual_exemption": annual_exemption, "monthly_exemption": monthly_exemption} + assignment = get_salary_assignment(doc.employee, nowdate()) + + if assignment: + hra_component_exists = frappe.db.exists("Salary Detail", { + "parent": assignment.salary_structure, + "salary_component": hra_component, + "parentfield": "earnings", + "parenttype": "Salary Structure" + }) + if hra_component_exists: + basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee, + assignment.salary_structure, basic_component, hra_component) + if hra_amount: + if doc.monthly_house_rent: + annual_exemption = calculate_hra_exemption(assignment.salary_structure, + basic_amount, hra_amount, doc.monthly_house_rent, + doc.rented_in_metro_city) + if annual_exemption > 0: + monthly_exemption = annual_exemption / 12 + else: + annual_exemption = 0 + elif doc.docstatus == 1: + frappe.throw(_("Salary Structure must be submitted before submission of Tax Ememption Declaration")) + + return frappe._dict({ + "hra_amount": hra_amount, + "annual_exemption": annual_exemption, + "monthly_exemption": monthly_exemption + }) def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component): salary_slip = make_salary_slip(salary_structure, employee=employee) @@ -181,8 +197,10 @@ def calculate_hra_exemption(salary_structure, basic, monthly_hra, monthly_house_ frequency = frappe.get_value("Salary Structure", salary_structure, "payroll_frequency") # case 1: The actual amount allotted by the employer as the HRA. exemptions.append(get_annual_component_pay(frequency, monthly_hra)) + actual_annual_rent = monthly_house_rent * 12 annual_basic = get_annual_component_pay(frequency, basic) + # case 2: Actual rent paid less 10% of the basic salary. exemptions.append(flt(actual_annual_rent) - flt(annual_basic * 0.1)) # case 3: 50% of the basic salary, if the employee is staying in a metro city (40% for a non-metro city). @@ -205,15 +223,25 @@ def get_annual_component_pay(frequency, amount): def validate_house_rent_dates(doc): if not doc.rented_to_date or not doc.rented_from_date: frappe.throw(_("House rented dates required for exemption calculation")) + if date_diff(doc.rented_to_date, doc.rented_from_date) < 14: frappe.throw(_("House rented dates should be atleast 15 days apart")) - proofs = frappe.db.sql("""select name from `tabEmployee Tax Exemption Proof Submission` - where docstatus=1 and employee='{0}' and payroll_period='{1}' and - (rented_from_date between '{2}' and '{3}' or rented_to_date between - '{2}' and '{3}')""".format(doc.employee, doc.payroll_period, - doc.rented_from_date, doc.rented_to_date)) + + proofs = frappe.db.sql(""" + select name + from `tabEmployee Tax Exemption Proof Submission` + where + docstatus=1 and employee=%(employee)s and payroll_period=%(payroll_period)s + and (rented_from_date between %(from_date)s and %(to_date)s or rented_to_date between %(from_date)s and %(to_date)s) + """, { + "employee": doc.employee, + "payroll_period": doc.payroll_period, + "from_date": doc.rented_from_date, + "to_date": doc.rented_to_date + }) + if proofs: - frappe.throw(_("House rent paid days overlap with {0}").format(proofs[0][0])) + frappe.throw(_("House rent paid days overlapping with {0}").format(proofs[0][0])) def calculate_hra_exemption_for_period(doc): monthly_rent, eligible_hra = 0, 0 diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index adb58a1307f..dc22b5b0fee 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -204,6 +204,20 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( erpnext.utils.make_subscription(doc.doctype, doc.name) }, __('Create')) } + + if (doc.docstatus === 1 && !doc.inter_company_order_reference) { + let me = this; + frappe.model.with_doc("Customer", me.frm.doc.customer, () => { + let customer = frappe.model.get_doc("Customer", me.frm.doc.customer); + let internal = customer.is_internal_customer; + let disabled = customer.disabled; + if (internal === 1 && disabled === 0) { + me.frm.add_custom_button("Inter Company Order", function() { + me.make_inter_company_order(); + }, __('Create')); + } + }); + } } // payment request if(flt(doc.per_billed)==0) { @@ -500,6 +514,13 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( }) }, + make_inter_company_order: function() { + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.sales_order.sales_order.make_inter_company_purchase_order", + frm: this.frm + }); + }, + make_maintenance_visit: function() { frappe.model.open_mapped_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 30123db3565..95c803d8f4a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1449,6 +1449,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "hidden": 0, @@ -1481,6 +1482,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "pricing_rules", "fieldtype": "Table", "hidden": 0, @@ -1514,6 +1516,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_31", "fieldtype": "Section Break", "hidden": 0, @@ -3230,6 +3233,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "inter_company_order_reference", + "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": "Inter Company Order Reference", + "length": 0, + "no_copy": 0, + "options": "Purchase Order", + "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, @@ -4292,17 +4329,15 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-file-text", "idx": 105, - "image_view": 0, "in_create": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-02-13 01:02:45.882179", + "modified": "2019-04-18 12:05:23.464968", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", @@ -4425,7 +4460,6 @@ ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 1, "search_fields": "status,transaction_date,customer,customer_name, territory,order_type,company", "show_name_in_global_search": 1, "sort_field": "modified", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d09e2811379..fb7a3353024 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -19,6 +19,8 @@ from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests +from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ + unlink_inter_company_doc form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -42,6 +44,7 @@ class SalesOrder(SellingController): self.validate_warehouse() self.validate_drop_ship() self.validate_serial_no_based_delivery() + validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_order_reference) from erpnext.stock.doctype.packed_item.packed_item import make_packing_list make_packing_list(self) @@ -182,6 +185,8 @@ class SalesOrder(SellingController): self.update_blanket_order() + update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) + def on_cancel(self): super(SalesOrder, self).on_cancel() @@ -198,6 +203,8 @@ class SalesOrder(SellingController): self.update_blanket_order() + unlink_inter_company_doc(self.doctype, self.name, self.inter_company_order_reference) + def update_project(self): if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction": return @@ -544,7 +551,7 @@ def make_project(source_name, target_doc=None): "Sales Order Item": { "doctype": "Project Task", "field_map": { - "description": "title", + "item_code": "title", }, } }, target_doc, postprocess) @@ -764,6 +771,7 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier=None, target target.apply_discount_on = "" target.additional_discount_percentage = 0.0 target.discount_amount = 0.0 + target.inter_company_order_reference = "" default_price_list = frappe.get_value("Supplier", supplier, "default_price_list") if default_price_list: @@ -970,4 +978,9 @@ def make_raw_material_request(items, company, sales_order, project=None): material_request.flags.ignore_permissions = 1 material_request.run_method("set_missing_values") material_request.submit() - return material_request \ No newline at end of file + return material_request + +@frappe.whitelist() +def make_inter_company_purchase_order(source_name, target_doc=None): + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction + return make_inter_company_transaction("Sales Order", source_name, target_doc) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index cd69dd43b0c..b8c46045cdd 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -412,7 +412,6 @@ def get_returned_qty_map(delivery_note): @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None): doc = frappe.get_doc('Delivery Note', source_name) - sales_orders = [d.against_sales_order for d in doc.items] returned_qty_map = get_returned_qty_map(source_name) invoiced_qty_map = get_invoiced_qty_map(source_name) @@ -452,7 +451,7 @@ def make_sales_invoice(source_name, target_doc=None): returned_qty = 0 return pending_qty, returned_qty - doc = get_mapped_doc("Delivery Note", source_name, { + doc = get_mapped_doc("Delivery Note", source_name, { "Delivery Note": { "doctype": "Sales Invoice", "validation": { @@ -470,7 +469,7 @@ def make_sales_invoice(source_name, target_doc=None): "cost_center": "cost_center" }, "postprocess": update_item, - "filter": lambda d: get_pending_qty(d)[0]<=0 + "filter": lambda d: get_pending_qty(d)[0] <= 0 if not doc.get("is_return") else get_pending_qty(d)[0] > 0 }, "Sales Taxes and Charges": { "doctype": "Sales Taxes and Charges", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index aeab9ed978c..ed02cee3a4a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -90,8 +90,6 @@ class Item(WebsiteGenerator): self.set_opening_stock() def validate(self): - self.get_doc_before_save() - super(Item, self).validate() if not self.item_name: diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 71cf1dab791..6953279df7d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -457,7 +457,7 @@ def make_purchase_invoice(source_name, target_doc=None): "asset": "asset", }, "postprocess": update_item, - "filter": lambda d: get_pending_qty(d)[0]<=0 + "filter": lambda d: get_pending_qty(d)[0] <= 0 if not doc.get("is_return") else get_pending_qty(d)[0] > 0 }, "Purchase Taxes and Charges": { "doctype": "Purchase Taxes and Charges", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index da2d09ff959..a6af4bd5544 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -35,7 +35,7 @@ form_grid_templates = { class StockEntry(StockController): def get_feed(self): - return _("From {0} to {1}").format(self.from_warehouse, self.to_warehouse) + return self.stock_entry_type def onload(self): for item in self.get("items"): diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 4d8022c24fd..30a45dd457c 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -492,7 +492,7 @@ def insert_item_price(args): def get_item_price(args, item_code, ignore_party=False): """ Get name, price_list_rate from Item Price based on conditions - Check if the Derised qty is within the increment of the packing list. + Check if the desired qty is within the increment of the packing list. :param args: dict (or frappe._dict) with mandatory fields price_list, uom optional fields min_qty, transaction_date, customer, supplier :param item_code: str, Item Doctype field item_code @@ -530,11 +530,11 @@ def get_price_list_rate_for(args, item_code): for min_qty 9 and min_qty 20. It returns Item Price Rate for qty 9 as the best fit in the range of avaliable min_qtyies - :param customer: link to Customer DocType - :param supplier: link to Supplier DocType + :param customer: link to Customer DocType + :param supplier: link to Supplier DocType :param price_list: str (Standard Buying or Standard Selling) :param item_code: str, Item Doctype field item_code - :param qty: Derised Qty + :param qty: Desired Qty :param transaction_date: Date of the price """ item_price_args = { @@ -559,7 +559,7 @@ def get_price_list_rate_for(args, item_code): general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) if not general_price_list_rate and args.get("uom") != args.get("stock_uom"): - item_price_args["args"] = args.get("stock_uom") + item_price_args["uom"] = args.get("stock_uom") general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) if general_price_list_rate: @@ -575,11 +575,11 @@ def get_price_list_rate_for(args, item_code): def check_packing_list(price_list_rate_name, desired_qty, item_code): """ - Check if the Derised qty is within the increment of the packing list. + Check if the desired qty is within the increment of the packing list. :param price_list_rate_name: Name of Item Price - :param desired_qty: Derised Qt + :param desired_qty: Desired Qt :param item_code: str, Item Doctype field item_code - :param qty: Derised Qt + :param qty: Desired Qt """ flag = True diff --git a/erpnext/stock/report/inactive_items/__init__.py b/erpnext/stock/report/inactive_items/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/report/inactive_items/inactive_items.js b/erpnext/stock/report/inactive_items/inactive_items.js new file mode 100644 index 00000000000..39dfd5c8c36 --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.js @@ -0,0 +1,34 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Inactive Items"] = { + "filters": [ + { + fieldname: "item", + label: __("Item"), + fieldtype: "Link", + options: "Item" + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group" + }, + { + fieldname: "based_on", + label: __("Based On"), + fieldtype: "Select", + options: "Sales Order\nSales Invoice", + default: "Sales Order" + }, + { + fieldname: "days", + label: __("Days Since Last order"), + fieldtype: "Select", + options: [30, 60, 90], + default: 30 + }, + ] +} diff --git a/erpnext/stock/report/inactive_items/inactive_items.json b/erpnext/stock/report/inactive_items/inactive_items.json new file mode 100644 index 00000000000..b9eb05ec050 --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 0, + "creation": "2019-04-16 16:05:00.647308", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Test Letter Head 1", + "modified": "2019-04-16 16:06:33.630043", + "modified_by": "Administrator", + "module": "Stock", + "name": "Inactive Items", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Invoice", + "report_name": "Inactive Items", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py new file mode 100644 index 00000000000..8d879126da6 --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -0,0 +1,148 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, add_days, today, cint +from frappe import _ + +def execute(filters=None): + + columns = get_columns() + data = get_data(filters) + return columns, data + +def get_columns(): + + columns = [ + { + "fieldname": "territory", + "fieldtype": "Link", + "label": _("Territory"), + "options": "Territory", + "width": 100 + }, + { + "fieldname": "item_group", + "fieldtype": "Link", + "label": _("Item Group"), + "options": "Item Group", + "width": 150 + }, + { + "fieldname": "item_name", + "fieldtype": "Link", + "options": "Item", + "label": "Item", + "width": 150 + }, + { + "fieldname": "item_name", + "fieldtype": "Data", + "label": _("Item Name"), + "width": 150 + }, + + { + "fieldname": "customer", + "fieldtype": "Link", + "label": _("Customer"), + "options": "Customer", + "width": 100 + }, + { + "fieldname": "last_order_date", + "fieldtype": "Date", + "label": _("Last Order Date"), + "width": 100 + }, + { + "fieldname": "qty", + "fieldtype": "Float", + "label": _("Quantity"), + "width": 100 + }, + { + "fieldname": "days_since_last_order", + "fieldtype": "Int", + "label": _("Days Since Last Order"), + "width": 100 + }, + ] + + return columns + + +def get_data(filters): + + data = [] + items = get_items(filters) + sales_invoice_data = get_sales_details(filters) + + for item in items: + if sales_invoice_data.get(item.name): + item_obj = sales_invoice_data[item.name] + if item_obj.days_since_last_order > cint(filters['days']): + row = { + "territory": item_obj.territory, + "item_group": item_obj.item_group, + "item": item_obj.name, + "item_name": item_obj.item_name, + "customer": item_obj.customer, + "last_order_date": item_obj.last_order_date, + "qty": item_obj.qty, + "days_since_last_order": item_obj.days_since_last_order + } + data.append(row) + else: + row = { + "item_group": item.item_group, + "item": item.name, + "item_name": item.item_name + } + data.append(row) + + return data + + +def get_sales_details(filters): + + data = [] + item_details_map = {} + + date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date" + + sales_data = frappe.db.sql(""" + select s.territory, s.customer, si.item_group, si.item_name, si.qty, {date_field} as last_order_date, + DATEDIFF(CURDATE(), {date_field}) as days_since_last_order + from `tab{doctype}` s, `tab{doctype} Item` si + where s.name = si.parent and s.docstatus = 1 + group by si.name order by days_since_last_order """ #nosec + .format(date_field = date_field, doctype = filters['based_on']), as_dict=1) + + for d in sales_data: + item_details_map.setdefault(d.item_name, d) + + return item_details_map + +def get_items(filters): + + filters_dict = { + "disabled": 0, + "is_stock_item": 1 + } + + if filters.get("item_group"): + filters_dict.update({ + "item_group": filters["item_group"] + }) + + if filters.get("item"): + filters_dict.update({ + "name": filters["item"] + }) + + items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name") + + return items + diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 20b5e45e19c..70da5b5980a 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -154,10 +154,10 @@ def get_opening_balance(filters, columns): "posting_date": filters.from_date, "posting_time": "00:00:00" }) - row = [""]*len(columns) - row[1] = _("'Opening'") - for i, v in ((9, 'qty_after_transaction'), (11, 'valuation_rate'), (12, 'stock_value')): - row[i] = last_entry.get(v, 0) + row = {} + row["item_code"] = _("'Opening'") + for dummy, v in ((9, 'qty_after_transaction'), (11, 'valuation_rate'), (12, 'stock_value')): + row[v] = last_entry.get(v, 0) return row diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 3a1c5efd246..91b718e86f2 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -1,1309 +1,1309 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-02-01 10:36:25", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "naming_series:", + "beta": 0, + "creation": "2013-02-01 10:36:25", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "engine": "InnoDB", "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": "subject_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": "Subject", - "length": 0, - "no_copy": 0, - "options": "fa fa-flag", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "subject_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": "Subject", + "length": 0, + "no_copy": 0, + "options": "fa fa-flag", + "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 - }, + }, { - "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": "ISS-.YYYY.-", - "permlevel": 0, - "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": 1, - "translatable": 0, + "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": "ISS-.YYYY.-", + "permlevel": 0, + "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": 1, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "subject", - "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": "Subject", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "subject", + "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": "Subject", + "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": 1, + "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, - "fetch_if_empty": 0, - "fieldname": "issue_type", - "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": "Issue Type", - "length": 0, - "no_copy": 0, - "options": "Issue 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, + "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", + "fieldtype": "Link", + "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": "Customer", + "length": 0, + "no_copy": 0, + "oldfieldname": "customer", + "oldfieldtype": "Link", + "options": "Customer", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "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, - "fetch_if_empty": 0, - "fieldname": "customer", - "fieldtype": "Link", - "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": "Customer", - "length": 0, - "no_copy": 0, - "oldfieldname": "customer", - "oldfieldtype": "Link", - "options": "Customer", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.__islocal", + "fetch_if_empty": 0, + "fieldname": "raised_by", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Raised By (Email)", + "length": 0, + "no_copy": 0, + "oldfieldname": "raised_by", + "oldfieldtype": "Data", + "options": "Email", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "cb00", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "cb00", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Open", - "fetch_if_empty": 0, - "fieldname": "status", - "fieldtype": "Select", - "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": "Status", - "length": 0, - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "Open\nReplied\nHold\nClosed", - "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": 1, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Open", + "fetch_if_empty": 0, + "fieldname": "status", + "fieldtype": "Select", + "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": "Status", + "length": 0, + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "Open\nReplied\nHold\nClosed", + "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": 1, + "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": "Medium", - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "priority", - "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": 1, - "label": "Priority", - "length": 0, - "no_copy": 0, - "options": "Low\nMedium\nHigh", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Medium", + "depends_on": "", + "fetch_if_empty": 0, + "fieldname": "priority", + "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": 1, + "label": "Priority", + "length": 0, + "no_copy": 0, + "options": "Low\nMedium\nHigh", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.__islocal", - "fetch_if_empty": 0, - "fieldname": "raised_by", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Raised By (Email)", - "length": 0, - "no_copy": 0, - "oldfieldname": "raised_by", - "oldfieldtype": "Data", - "options": "Email", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "issue_type", + "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": "Issue Type", + "length": 0, + "no_copy": 0, + "options": "Issue 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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "email_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": "Email Account", - "length": 0, - "no_copy": 0, - "options": "Email 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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": "eval:doc.status!=\"Closed\"", + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "sb_details", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:doc.status!=\"Closed\"", - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "sb_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fetch_if_empty": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "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": "Description", + "length": 0, + "no_copy": 0, + "oldfieldname": "problem_description", + "oldfieldtype": "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "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": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "problem_description", - "oldfieldtype": "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "depends_on": "eval: doc.service_level_agreement", + "fetch_if_empty": 0, + "fieldname": "service_level_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": "Service Level", + "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": 1, - "columns": 0, - "depends_on": "eval: doc.service_level_agreement", - "fetch_if_empty": 0, - "fieldname": "service_level_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": "Service Level", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "service_level_agreement", + "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": "Service Level Agreement", + "length": 0, + "no_copy": 0, + "options": "Service Level Agreement", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "service_level_agreement", - "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": "Service Level Agreement", - "length": 0, - "no_copy": 0, - "options": "Service Level Agreement", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "response_by", + "fieldtype": "Datetime", + "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": "Response By", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "response_by", - "fieldtype": "Datetime", - "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": "Response By", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "cb", + "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, + "label": "", + "length": 0, + "no_copy": 0, + "options": "fa fa-pushpin", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "cb", - "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, - "label": "", - "length": 0, - "no_copy": 0, - "options": "fa fa-pushpin", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Ongoing", + "fetch_if_empty": 0, + "fieldname": "agreement_status", + "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": "Agreement Status", + "length": 0, + "no_copy": 0, + "options": "Ongoing\nFulfilled\nFailed", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Ongoing", - "fetch_if_empty": 0, - "fieldname": "agreement_status", - "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": "Agreement Status", - "length": 0, - "no_copy": 0, - "options": "Ongoing\nFulfilled\nFailed", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "resolution_by", + "fieldtype": "Datetime", + "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": "Resolution By", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "resolution_by", - "fieldtype": "Datetime", - "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": "Resolution By", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "response", + "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": "Response", + "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": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "response", - "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": "Response", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "mins_to_first_response", + "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": "Mins to First Response", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "mins_to_first_response", - "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": "Mins to First Response", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "first_responded_on", + "fieldtype": "Datetime", + "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": "First Responded On", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "first_responded_on", - "fieldtype": "Datetime", - "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": "First Responded On", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "additional_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": "Reference", + "length": 0, + "no_copy": 0, + "options": "fa fa-pushpin", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "additional_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": "Reference", - "length": 0, - "no_copy": 0, - "options": "fa fa-pushpin", - "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, + "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", + "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": "Lead", + "length": 0, + "no_copy": 0, + "options": "Lead", + "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, - "fetch_if_empty": 0, - "fieldname": "lead", - "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": "Lead", - "length": 0, - "no_copy": 0, - "options": "Lead", - "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, + "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", + "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": "Contact", + "length": 0, + "no_copy": 0, + "options": "Contact", + "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 - }, + }, { - "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", - "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": "Contact", - "length": 0, - "no_copy": 0, - "options": "Contact", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "email_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": "Email Account", + "length": 0, + "no_copy": 0, + "options": "Email 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 - }, + }, { - "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_16", - "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, + "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_16", + "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": 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": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "customer_name", - "oldfieldtype": "Data", - "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, + "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": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Customer Name", + "length": 0, + "no_copy": 0, + "oldfieldname": "customer_name", + "oldfieldtype": "Data", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "project", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Project", + "length": 0, + "no_copy": 0, + "options": "Project", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "company", - "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": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "company", + "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": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_19", - "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": "Resolution", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_19", + "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": "Resolution", + "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": "eval:!doc.__islocal", - "fetch_if_empty": 0, - "fieldname": "resolution_details", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Resolution Details", - "length": 0, - "no_copy": 1, - "oldfieldname": "resolution_details", - "oldfieldtype": "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, + "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": "resolution_details", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Resolution Details", + "length": 0, + "no_copy": 1, + "oldfieldname": "resolution_details", + "oldfieldtype": "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 - }, + }, { - "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": "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, - "oldfieldtype": "Column Break", - "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, + "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": "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, + "oldfieldtype": "Column Break", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "opening_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Opening Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "opening_date", - "oldfieldtype": "Date", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fetch_if_empty": 0, + "fieldname": "opening_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Opening Date", + "length": 0, + "no_copy": 1, + "oldfieldname": "opening_date", + "oldfieldtype": "Date", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "opening_time", - "fieldtype": "Time", - "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": "Opening Time", - "length": 0, - "no_copy": 1, - "oldfieldname": "opening_time", - "oldfieldtype": "Time", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "opening_time", + "fieldtype": "Time", + "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": "Opening Time", + "length": 0, + "no_copy": 1, + "oldfieldname": "opening_time", + "oldfieldtype": "Time", + "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 - }, + }, { - "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": "resolution_date", - "fieldtype": "Datetime", - "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": "Resolution Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "resolution_date", - "oldfieldtype": "Date", - "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, + "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": "resolution_date", + "fieldtype": "Datetime", + "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": "Resolution Date", + "length": 0, + "no_copy": 1, + "oldfieldname": "resolution_date", + "oldfieldtype": "Date", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "content_type", - "fieldtype": "Data", - "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": "Content Type", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "content_type", + "fieldtype": "Data", + "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": "Content Type", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "attachment", - "fieldtype": "Attach", - "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": "Attachment", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "attachment", + "fieldtype": "Attach", + "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": "Attachment", + "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, - "fetch_if_empty": 0, - "fieldname": "via_customer_portal", - "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": "Via Customer Portal", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "via_customer_portal", + "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": "Via Customer Portal", + "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 } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "icon": "fa fa-ticket", - "idx": 7, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-04 10:55:40.222692", + ], + "has_web_view": 0, + "hide_toolbar": 0, + "icon": "fa fa-ticket", + "idx": 7, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-04-23 18:04:42.039620", "modified_by": "Administrator", - "module": "Support", - "name": "Issue", - "owner": "Administrator", + "module": "Support", + "name": "Issue", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Support Team", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Support Team", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "search_fields": "status,customer,subject,raised_by", - "show_name_in_global_search": 0, - "sort_order": "ASC", - "timeline_field": "customer", - "title_field": "subject", - "track_changes": 0, - "track_seen": 1, + ], + "quick_entry": 1, + "read_only": 0, + "search_fields": "status,customer,subject,raised_by", + "show_name_in_global_search": 0, + "sort_order": "ASC", + "timeline_field": "customer", + "title_field": "subject", + "track_changes": 0, + "track_seen": 1, "track_views": 0 } \ No newline at end of file diff --git a/erpnext/www/lms.html b/erpnext/www/lms.html new file mode 100644 index 00000000000..17961945854 --- /dev/null +++ b/erpnext/www/lms.html @@ -0,0 +1,10 @@ +{% extends "templates/web.html" %} + +{% block title %}{{ heading or "LMS"}}{% endblock %} + +{% block navbar %}{% endblock %} + +{% block content %} +
+ +{% endblock %} \ No newline at end of file diff --git a/erpnext/www/lms.py b/erpnext/www/lms.py new file mode 100644 index 00000000000..7561d73d207 --- /dev/null +++ b/erpnext/www/lms.py @@ -0,0 +1,242 @@ +from __future__ import unicode_literals +import erpnext.education.utils as utils +import frappe +from frappe import _ + +# LMS Utils to Update State for Vue Store +@frappe.whitelist() +def get_program_enrollments(): + student = utils.get_current_student() + if student == None: + return None + return student.get_program_enrollments() + +@frappe.whitelist() +def get_all_course_enrollments(): + student = utils.get_current_student() + if student == None: + return None + return student.get_all_course_enrollments() + +# Vue Client Functions +@frappe.whitelist(allow_guest=True) +def get_portal_details(): + """ + Returns portal details from Education Settings Doctype. This contains the Title and Description for LMS amoung other things. + """ + from erpnext import get_default_company + + settings = frappe.get_doc("Education Settings") + title = settings.portal_title or get_default_company() + description = settings.description + return dict(title=title, description=description) + +@frappe.whitelist(allow_guest=True) +def get_featured_programs(): + featured_program_names = frappe.get_all("Program", filters={"is_published": True, "is_featured": True}) + if featured_program_names: + featured_list = [utils.get_program_and_enrollment_status(program['name']) for program in featured_program_names] + return featured_list + else: + return get_all_programs()[:2] + +@frappe.whitelist(allow_guest=True) +def get_all_programs(): + program_names = frappe.get_all("Program", filters={"is_published": True}) + if program_names: + program_list = [utils.get_program_and_enrollment_status(program['name']) for program in program_names] + return program_list + +@frappe.whitelist(allow_guest=True) +def get_program(program_name): + try: + return frappe.get_doc('Program', program_name) + except frappe.DoesNotExistError: + frappe.throw(_("Program {0} does not exist.".format(program_name))) + +# Functions to get program & course details +@frappe.whitelist(allow_guest=True) +def get_courses(program_name): + program = frappe.get_doc('Program', program_name) + courses = program.get_course_list() + return courses + +@frappe.whitelist() +def get_next_content(current_content, current_content_type, topic): + if frappe.session.user == "Guest": + return None + topic = frappe.get_doc("Topic", topic) + content_list = [{'content_type':item.doctype, 'content':item.name} for item in topic.get_contents()] + current_index = content_list.index({'content': current_content, 'content_type': current_content_type}) + try: + return content_list[current_index + 1] + except IndexError: + return None + +def get_quiz_with_answers(quiz_name): + try: + quiz = frappe.get_doc("Quiz", quiz_name).get_questions() + quiz_output = [{'name':question.name, 'question':question.question, 'options':[{'name': option.name, 'option':option.option, 'is_correct':option.is_correct} for option in question.options]} for question in quiz] + return quiz_output + except: + frappe.throw("Quiz {0} does not exist".format(quiz_name)) + return None + +@frappe.whitelist() +def get_quiz_without_answers(quiz_name, course_name): + try: + quiz = frappe.get_doc("Quiz", quiz_name) + questions = quiz.get_questions() + except: + frappe.throw("Quiz {0} does not exist".format(quiz_name)) + return None + + if utils.check_super_access(): + quiz_output = [{'name':question.name, 'question':question.question, 'type': question.type, 'options':[{'name': option.name, 'option':option.option} for option in question.options]} for question in questions] + return { 'quizData': quiz_output, 'status': None} + + enrollment = utils.get_course_enrollment(course_name).name + quiz_progress = {} + quiz_progress['is_complete'], quiz_progress['score'], quiz_progress['result'] = utils.check_quiz_completion(quiz, enrollment) + quiz_output = [{'name':question.name, 'question':question.question, 'type': question.type, 'options':[{'name': option.name, 'option':option.option} for option in question.options]} for question in questions] + return { 'quizData': quiz_output, 'status': quiz_progress} + +@frappe.whitelist() +def evaluate_quiz(course, quiz_response, quiz_name): + """LMS Function: Evaluates a simple multiple choice quiz. + :param course: name of the course + :param quiz_response: contains user selected choices for a quiz in the form of a string formatted as a dictionary. The function uses `json.loads()` to convert it to a python dictionary. + :param quiz_name: Name of the quiz attempted + """ + import json + quiz_response = json.loads(quiz_response) + quiz = frappe.get_doc("Quiz", quiz_name) + answers, score, status = quiz.evaluate(quiz_response, quiz_name) + print(answers) + + course_enrollment = utils.get_course_enrollment(course) + if course_enrollment: + course_enrollment.add_quiz_activity(quiz_name, quiz_response, answers, score, status) + + return score + +@frappe.whitelist() +def enroll_in_program(program_name): + student = utils.get_current_student() + if not student: + student = utils.create_student_from_current_user() + program_enrollment = student.enroll_in_program(program_name) + return program_name + +# Academdy Activity +@frappe.whitelist() +def add_activity(course, content_type, content): + if not utils.get_current_student(): + return + enrollment = utils.get_course_enrollment(course) + enrollment.add_activity(content_type, content) + +@frappe.whitelist() +def get_student_course_details(course_name, program_name): + """ + Return the porgress of a course in a program as well as the content to continue from. + :param course_name: + :param program_name: + """ + student = utils.get_current_student() + if not student: + return {'flag':'Start Course' } + + course_enrollment = utils.get_course_enrollment(course_name) + program_enrollment = utils.get_program_enrollment(program_name) + + if not program_enrollment: + return None + + if not course_enrollment: + course_enrollment = utils.enroll_in_course(course_name, program_name) + + progress = course_enrollment.get_progress(student) + count = sum([activity['is_complete'] for activity in progress]) + if count == 0: + return {'flag':'Start Course'} + elif count == len(progress): + return {'flag':'Completed'} + elif count < len(progress): + next_item = next(item for item in progress if item['is_complete']==False) + return {'flag':'Continue'} + +@frappe.whitelist() +def get_student_topic_details(topic_name, course_name): + """ + Return the porgress of a course in a program as well as the content to continue from. + :param topic_name: + :param course_name: + """ + topic = frappe.get_doc("Topic", topic_name) + student = utils.get_current_student() + if not student: + topic_content = topic.get_all_children() + if topic_content: + return {'flag':'Start Course', 'content_type': topic_content[0].content_type, 'content': topic_content[0].content} + else: + return None + course_enrollment = utils.get_course_enrollment(course_name) + progress = student.get_topic_progress(course_enrollment.name, topic) + if not progress: + return { 'flag':'Start Topic', 'content_type': None, 'content': None } + count = sum([activity['is_complete'] for activity in progress]) + if count == 0: + return {'flag':'Start Topic', 'content_type': progress[0]['content_type'], 'content': progress[0]['content']} + elif count == len(progress): + return {'flag':'Completed', 'content_type': progress[0]['content_type'], 'content': progress[0]['content']} + elif count < len(progress): + next_item = next(item for item in progress if item['is_complete']==False) + return {'flag':'Continue', 'content_type': next_item['content_type'], 'content': next_item['content']} + +@frappe.whitelist() +def get_program_progress(program_name): + program_enrollment = frappe.get_doc("Program Enrollment", utils.get_program_enrollment(program_name)) + if not program_enrollment: + return None + else: + return program_enrollment.get_program_progress() + +@frappe.whitelist() +def get_joining_date(): + student = utils.get_current_student() + if student: + return student.joining_date + +@frappe.whitelist() +def get_quiz_progress_of_program(program_name): + program_enrollment = frappe.get_doc("Program Enrollment", utils.get_program_enrollment(program_name)) + if not program_enrollment: + return None + else: + return program_enrollment.get_quiz_progress() + + +@frappe.whitelist(allow_guest=True) +def get_course_details(course_name): + try: + course = frappe.get_doc('Course', course_name) + return course + except: + return None + +# Functions to get program & course details +@frappe.whitelist(allow_guest=True) +def get_topics(course_name): + try: + course = frappe.get_doc('Course', course_name) + return course.get_topics() + except frappe.DoesNotExistError: + frappe.throw(_("Course {0} does not exist.".format(course_name))) + +@frappe.whitelist() +def get_content(content_type, content): + try: + return frappe.get_doc(content_type, content) + except frappe.DoesNotExistError: + frappe.throw(_("{0} {1} does not exist.".format(content_type, content))) \ No newline at end of file diff --git a/erpnext/www/test_lms.py b/erpnext/www/test_lms.py new file mode 100644 index 00000000000..e63f4c913e0 --- /dev/null +++ b/erpnext/www/test_lms.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals +from erpnext.education.doctype.program.test_program import make_program_and_linked_courses + +import frappe +import unittest + +class TestLms(unittest.TestCase): + pass \ No newline at end of file