Merge branch 'develop' into item-discount
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.js b/erpnext/accounts/doctype/bank_account/bank_account.js
index 5ac41e4..a7b5891 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.js
+++ b/erpnext/accounts/doctype/bank_account/bank_account.js
@@ -12,6 +12,11 @@
}
};
});
+ 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 84a8e68..10db0d7 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 92cdb63..3d0fc0a 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 3df4da5..96ec57d 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.js
+++ b/erpnext/accounts/doctype/cost_center/cost_center.js
@@ -22,6 +22,28 @@
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 @@
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 4dc6433..152e17d 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 7b489de..569f008 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 e1fe5a1..8f09bc3 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 e5b6336..c92b58b 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 =
- `<table class="table table-bordered" style="background-color: #f9f9f9;">
- <tr><td>
- <h4>
- <i class="fa fa-hand-right"></i>
- ${__('Notes')}
- </h4>
- <ul>
- <li>
- ${__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")}
- </li>
- <li>
- ${__("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.")}
- </li>
- <li>
- ${__('Discount Percentage can be applied either against a Price List or for all Price List.')}
- </li>
- <li>
- ${__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')}
- </li>
- </ul>
- </td></tr>
- <tr><td>
- <h4><i class="fa fa-question-sign"></i>
- ${__('How Pricing Rule is applied?')}
- </h4>
- <ol>
- <li>
- ${__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")}
- </li>
- <li>
- ${__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Group, Campaign, Sales Partner etc.")}
- </li>
- <li>
- ${__('Pricing Rules are further filtered based on quantity.')}
- </li>
- <li>
- ${__('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.')}
- </li>
- <li>
- ${__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')}
- <ul>
- <li>
- ${__('Item Code > Item Group > Brand')}
- </li>
- <li>
- ${__('Customer > Customer Group > Territory')}
- </li>
- <li>
- ${__('Supplier > Supplier Group')}
- </li>
- </ul>
- </li>
- <li>
- ${__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')}
- </li>
- </ol>
- </td></tr>
- </table>`;
-
- 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 @@
</td></tr>
</table>`;
- 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 @@
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 7ab7f14..d224961 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -285,6 +285,7 @@
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 0af273c..95d49a4 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.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 @@
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 @@
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 @@
@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 44af743..f21fbd9 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -369,6 +369,7 @@
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 2089f15..31a9c66 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -179,7 +179,7 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
@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 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 74c5f0e..47c6083 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.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 @@
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 f358b4a..48663ca 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -359,7 +359,8 @@
"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 e9a0f70..2d78d26 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -296,6 +296,12 @@
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 8011038..72f5c62 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -101,7 +101,7 @@
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 @@
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 @@
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 @@
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 @@
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 985097b..ef85ffa 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -102,9 +102,9 @@
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 @@
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 @@
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 28ceab5..e63ef60 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -138,6 +138,20 @@
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 @@
});
},
+ 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 46f48fb..13a097a 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 6a4c818..e5156a3 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -17,6 +17,8 @@
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 @@
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 @@
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 @@
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 @@
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 d5f9e2d..4efaaa6 100644
--- a/erpnext/config/education.py
+++ b/erpnext/config/education.py
@@ -35,6 +35,10 @@
},
{
"type": "doctype",
+ "name": "Web Academy Applicant"
+ },
+ {
+ "type": "doctype",
"name": "Student Admission"
},
{
@@ -182,12 +186,16 @@
"items": [
{
"type": "doctype",
+ "name": "Program",
+ },
+ {
+ "type": "doctype",
"name": "Course",
"onboard": 1,
},
{
"type": "doctype",
- "name": "Program"
+ "name": "Topic",
},
{
"type": "doctype",
@@ -202,6 +210,40 @@
]
},
{
+ "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 63ea259..155a996 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -89,7 +89,8 @@
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 1885504..6206dfd 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 @@
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 a28d061..bece079 100644
--- a/erpnext/demo/user/manufacturing.py
+++ b/erpnext/demo/user/manufacturing.py
@@ -102,10 +102,18 @@
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 69ba900..d4b55e8 100644
--- a/erpnext/demo/user/sales.py
+++ b/erpnext/demo/user/sales.py
@@ -21,17 +21,26 @@
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 60b1edc..f95a6b8 100644
--- a/erpnext/demo/user/stock.py
+++ b/erpnext/demo/user/stock.py
@@ -73,13 +73,13 @@
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 c640576..55e4eed 100644
--- a/erpnext/domains/education.py
+++ b/erpnext/domains/education.py
@@ -14,7 +14,7 @@
'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 30d5588..1a19716 100644
--- a/erpnext/education/api.py
+++ b/erpnext/education/api.py
@@ -38,7 +38,7 @@
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 @@
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 @@
: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 @@
: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 @@
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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/article/__init__.py
diff --git a/erpnext/education/doctype/article/article.js b/erpnext/education/doctype/article/article.js
new file mode 100644
index 0000000..4c9c6f0
--- /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 0000000..c30cd18
--- /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 0000000..7dc850b
--- /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 0000000..9dbf063
--- /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 0000000..2fce07f
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/content_activity/__init__.py
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 0000000..b4c95da
--- /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 0000000..2ae7a5c
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/content_question/__init__.py
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 0000000..7615f5e
--- /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 0000000..d390e8e
--- /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 0000000..b239d21
--- /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 0000000..cc869a8
--- /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 0000000..268b9be
--- /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 15360f8..072e8b4 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 69d2fca..987823a 100644
--- a/erpnext/education/doctype/course/course.py
+++ b/erpnext/education/doctype/course/course.py
@@ -10,7 +10,7 @@
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 @@
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 b18f4a9..a24ba8a 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 @@
# 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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/course_activity/__init__.py
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 0000000..5115fc4
--- /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 0000000..99ae9ae
--- /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 0000000..054b192
--- /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 0000000..c89c89e
--- /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 0000000..5269a6b
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/course_content/__init__.py
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 0000000..b9faf99
--- /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 0000000..378e560
--- /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 0000000..0d2f85a
--- /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 0000000..786e67e
--- /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 0000000..9be4b1f
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/course_enrollment/__init__.py
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 0000000..b5d3cc5
--- /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 0000000..6286ec1
--- /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 0000000..9508636
--- /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 0000000..216cc30
--- /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 0000000..5ecace2
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/course_topic/__init__.py
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 0000000..7d03ba3
--- /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 0000000..3fcddc1
--- /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 0000000..2364f17
--- /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 0000000..d8d154f
--- /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 0000000..7ce46d2
--- /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 c1eaa11..3be4988 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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/options/__init__.py
diff --git a/erpnext/education/doctype/options/options.json b/erpnext/education/doctype/options/options.json
new file mode 100644
index 0000000..59deab7
--- /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 0000000..a11d77a
--- /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 5146a19..98263b5 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 05e35a2..cb8d778 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 f626880..dbeda40 100644
--- a/erpnext/education/doctype/program/program.py
+++ b/erpnext/education/doctype/program/program.py
@@ -7,4 +7,8 @@
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 0000000..cb8f742
--- /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 a4accda..3bcca3a 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 e5eda70..7901db3 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 5f4621f..1d8a434 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 320a58a..22cca86 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.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 @@
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 @@
})
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 @@
"program_enrollment": self.name,
"components": fee_components
})
-
+
fees.save()
fees.submit()
fee_list.append(fees.name)
@@ -66,6 +68,56 @@
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 @@
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 c26e899..c6cbee1 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 @@
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 db23ac7..9f8f9f4 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 @@
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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/question/__init__.py
diff --git a/erpnext/education/doctype/question/question.js b/erpnext/education/doctype/question/question.js
new file mode 100644
index 0000000..01b3091
--- /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 0000000..14a9f3c
--- /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 0000000..8cd2398
--- /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 0000000..509939c
--- /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 0000000..552872e
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/quiz/__init__.py
diff --git a/erpnext/education/doctype/quiz/quiz.js b/erpnext/education/doctype/quiz/quiz.js
new file mode 100644
index 0000000..122cf37
--- /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 0000000..f91bc0f
--- /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 0000000..6da50a6
--- /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 0000000..147d139
--- /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 0000000..344fd54
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/quiz_activity/__init__.py
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 0000000..f6ba12c
--- /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 0000000..e78db42
--- /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 0000000..24c7175
--- /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 0000000..94b5ab7
--- /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 0000000..fb0425d
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/quiz_question/__init__.py
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 0000000..3857c5c
--- /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 0000000..317e75b
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/quiz_result/__init__.py
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 0000000..a018749
--- /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 0000000..86505ac
--- /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 0000000..a4fd9f0
--- /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 0000000..43f53a1
--- /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 0000000..86ee52d
--- /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 af6c8b1..bee915e 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 53bf6f7..cf8407c 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -7,7 +7,7 @@
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 @@
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 d86f4f2..0cbd17b 100644
--- a/erpnext/education/doctype/student/student_dashboard.py
+++ b/erpnext/education/doctype/student/student_dashboard.py
@@ -9,7 +9,7 @@
'transactions': [
{
'label': _('Admission'),
- 'items': ['Program Enrollment']
+ 'items': ['Program Enrollment', 'Course Enrollment']
},
{
'label': _('Student Activity'),
@@ -20,6 +20,10 @@
'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 6acc4b6..8ad3afa 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 cc6537f..8610edb 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 297821f..71134e0 100644
--- a/erpnext/education/doctype/student_applicant/student_applicant.json
+++ b/erpnext/education/doctype/student_applicant/student_applicant.json
@@ -150,6 +150,39 @@
"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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "paid",
"fieldtype": "Check",
"hidden": 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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/topic/__init__.py
diff --git a/erpnext/education/doctype/topic/test_topic.js b/erpnext/education/doctype/topic/test_topic.js
new file mode 100644
index 0000000..4460b79
--- /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 0000000..d03db1c
--- /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 0000000..695c174
--- /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 0000000..f47b10d
--- /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 0000000..339fc7d
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/topic_content/__init__.py
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 0000000..bf9a62d
--- /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 0000000..cf304f6
--- /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 0000000..9cda0ca
--- /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 0000000..5220788
--- /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 0000000..9b2c90b
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/doctype/video/__init__.py
diff --git a/erpnext/education/doctype/video/test_video.js b/erpnext/education/doctype/video/test_video.js
new file mode 100644
index 0000000..a82a221
--- /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 0000000..ecb09a2
--- /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 0000000..c35c19b
--- /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 0000000..cc8f718
--- /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 0000000..b19f812
--- /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 1b93c9d..bf766ad 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 @@
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 @@
}, 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 @@
.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 0000000..e69de29
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/__init__.py
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 0000000..104ac57
--- /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 0000000..26415ca
--- /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 0000000..12b646d
--- /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 0000000..433c5e2
--- /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 0000000..9f67e55
--- /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 608e8b2..85f2804 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -11,6 +11,7 @@
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 @@
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 1d4a3cf..4c3888b 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 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/designation_skill/__init__.py
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 0000000..30e23d0
--- /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 0000000..c37d21f
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/employee_skill/__init__.py
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 0000000..4b1419e
--- /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 0000000..ac05fba
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/employee_skill_map/__init__.py
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 0000000..b82b18d
--- /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 0000000..624145f
--- /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 0000000..073f02f
--- /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 7b2804b..66fac5b 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 9560df5..a827eca 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 @@
}
}
});
+
frm.set_query('payroll_period', function() {
const fields = {'employee': 'Employee', 'company': 'Company'};
@@ -27,6 +28,7 @@
}
}
});
+
frm.set_query('exemption_sub_category', 'declarations', function() {
return {
filters: {
@@ -34,5 +36,16 @@
}
}
});
+ },
+
+ 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 865e821..8891b97 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 186b2e1..cbdfcf8 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 @@
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 beaddd9..9c87bbd 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 @@
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 @@
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 ebde4c9..56556c1 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 99bec14..66118c0 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 @@
}
}
});
+
frm.set_query('payroll_period', function() {
if(frm.doc.employee && frm.doc.company){
return {
@@ -21,6 +22,7 @@
frappe.msgprint(__("Please select Employee"));
}
});
+
frm.set_query('exemption_sub_category', 'tax_exemption_proofs', function() {
return {
filters: {
@@ -29,11 +31,28 @@
}
});
},
- 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 ebc0435..76c09d6 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,
@@ -86,71 +89,8 @@
"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,
"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 54e0b20..97ceb63 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 @@
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 c1c5896..b9254af 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 dc99785..b0e492e 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 cd58136..a8dd7e4 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 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/employee_training/__init__.py
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 0000000..0e0dc15
--- /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 0000000..810796d
--- /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 9e13cb6..1b487a6 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -167,7 +167,7 @@
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 @@
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 @@
# 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 @@
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 079bec5..746bf8c 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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_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 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/skill/__init__.py
diff --git a/erpnext/hr/doctype/skill/skill.js b/erpnext/hr/doctype/skill/skill.js
new file mode 100644
index 0000000..a939ff0
--- /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 0000000..5182973
--- /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 0000000..8d24212
--- /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 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/report/loan_repayment/__init__.py
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 0000000..21aa206
--- /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 0000000..b967dfd
--- /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 0000000..9e310de
--- /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 586ca67..869ca23 100644
--- a/erpnext/hr/report/salary_register/salary_register.py
+++ b/erpnext/hr/report/salary_register/salary_register.py
@@ -99,8 +99,6 @@
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 313cfc4..a600e75 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -221,16 +221,30 @@
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 59861ce..a7d1d85 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 @@
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 @@
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 e50d5ae..5ccdec6 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 575ebeb..02203d2 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 @@
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 @@
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 458c229..f17639c 100644
--- a/erpnext/portal/product_configurator/item_variants_cache.py
+++ b/erpnext/portal/product_configurator/item_variants_cache.py
@@ -62,7 +62,7 @@
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 f02212c..405a6d8 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -167,13 +167,17 @@
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 4012346..bba258a 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 60e72da..45de6eb 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 cb01c52..537ca26 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1200,6 +1200,7 @@
"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 0000000..e35acbd
--- /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 0000000..eab1424
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Article.vue
@@ -0,0 +1,44 @@
+<template>
+<div>
+ <ContentTitle :title="contentData.title" :author="contentData.author" :publishDate="contentData.publish_date">
+ <slot></slot>
+ </ContentTitle>
+ <section class="article-content-section">
+ <div>
+ <div class="content" v-html="contentData.content"></div>
+ <div class="text-right">
+ </div>
+ <div class="mt-3 text-right">
+ <a class="text-muted" href="/report"><i class="octicon octicon-issue-opened" title="Report"></i> Report a
+ Mistake</a>
+ </div>
+ </div>
+ </section>
+</div>
+</template>
+<script>
+import ContentTitle from './ContentTitle.vue'
+export default {
+ props: ['content', 'type'],
+ name: 'Article',
+ data() {
+ return {
+ contentData: ''
+ }
+ },
+ mounted() {
+ this.getContent().then(data => this.contentData = data);
+ },
+ methods: {
+ getContent() {
+ return lms.call('get_content', {
+ content_type: this.type,
+ content: this.content
+ })
+ }
+ },
+ components: {
+ ContentTitle
+ }
+};
+</script>
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 0000000..1b617a3
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Breadcrumb.vue
@@ -0,0 +1,56 @@
+<template>
+ <div>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li v-for="(route, index) in routeData" class="breadcrumb-item active" aria-current="page">
+ <router-link v-if="index != routeData.length - 1" :to="route.route">
+ {{ route.label }}
+ </router-link>
+ <span v-else>{{ route.label }}</span>
+ </li>
+ </ol>
+ </nav>
+ </div>
+</template>
+<script type="text/javascript">
+ export default {
+ name: "Breadcrumb",
+ data() {
+ return {
+ routeName: this.$route.name,
+ routeParams: this.$route.params,
+ routeData: [{
+ label: "All Programs",
+ route: "/List/Program"
+ }]
+ }
+ },
+ mounted() {
+ this.buildBreadcrumb()
+ },
+ methods: {
+ buildBreadcrumb() {
+ if(this.routeName == 'program') {
+ return
+ }
+ if(this.routeName == 'course') {
+ let routeObject = {
+ label: this.routeParams.program_name,
+ route: `/Program/${this.routeParams.program_name}`
+ }
+ this.routeData.push(routeObject)
+ }
+ if(this.routeName == 'content') {
+ this.routeData.push({
+ label: this.routeParams.program_name,
+ route: `/Program/${this.routeParams.program_name}`
+ })
+ this.routeData.push({
+ label: this.routeParams.course_name,
+ route: `/Program/${this.routeParams.program_name}/${this.routeParams.course_name}`
+ })
+ }
+ }
+ }
+ };
+</script>
\ 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 0000000..4d8df4b
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Button.vue
@@ -0,0 +1,25 @@
+<template>
+ <button :class="classList" v-on="$listeners" v-bind="$attrs" @click="goToRoute">
+ <slot></slot>
+ </button>
+</template>
+<script>
+export default {
+ name: 'AButton',
+ props: ['type', 'size', 'route'],
+ computed: {
+ classList() {
+ return [
+ 'btn',
+ 'btn-' + this.type,
+ 'btn-' + this.size
+ ]
+ }
+ },
+ methods: {
+ goToRoute() {
+ this.$router.push(this.route);
+ }
+ }
+}
+</script>
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 0000000..298627f
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/CardList.vue
@@ -0,0 +1,28 @@
+<template>
+ <section class="featured-products-section" :class='sectionType'>
+ <h5 class='featured-heading' v-html="title"></h5>
+ <div class="featured-products row">
+ <!-- <p class='lead text-center' v-html="description"></p> -->
+ <slot name="card-list-slot"></slot>
+ </div>
+ <div class='mt-4 text-center'>
+ <slot name="list-bottom"></slot>
+ </div>
+</section>
+</template>
+<script>
+export default {
+ props:['title', 'description', 'sectionType'],
+ name: "CardList",
+};
+</script>
+<style scoped>
+
+.featured-heading {
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ font-size: 12px;
+ font-weight: 500;
+}
+
+</style>
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 0000000..a07c0f8
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/ContentNavigation.vue
@@ -0,0 +1,40 @@
+<template>
+ <div class="nav-buttons">
+ <button class='btn btn-outline-secondary' @click="$router.go(-1)">Back</button>
+ <button v-if="nextContent" class='btn btn-primary' @click="goNext()">Next</button>
+ <button v-else class='btn btn-primary' @click="finish()">Finish Topic</button>
+ </div>
+</template>
+
+<script>
+export default {
+ props: ['nextContent', 'nextContentType'],
+ name: 'ContentNavigation',
+ methods: {
+ addActivity() {
+ if(this.$route.params.type != "Quiz"){
+ console.log("Adding Activity")
+ lms.call("add_activity",
+ {
+ course: this.$route.params.course_name,
+ content_type: this.$route.params.type,
+ content: this.$route.params.content,
+ }
+ )
+ }
+ },
+ goNext() {
+ this.addActivity()
+ this.$router.push({ name: 'content', params: { course: this.$route.params.course_name, type:this.nextContentType, content:this.nextContent }})
+ },
+ finish() {
+ this.addActivity()
+ this.$router.push({ name: 'course', params: { program_name: this.$route.params.program_name, course_name: this.$route.params.course_name}})
+ lms.trigger('course-completed', course_name);
+ }
+ }
+};
+</script>
+
+<style lang="css" scoped>
+</style>
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 0000000..a488ab8
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/ContentTitle.vue
@@ -0,0 +1,29 @@
+<template>
+ <section class='article-top-section video-section-bg'>
+ <div>
+ <div class="row">
+ <div class="col-md-8">
+ <h2>{{ title }}</h2>
+ <span v-if="typeof author !== 'undefined' || author !== null" class="text-muted">
+ <span v-if="publishDate">Published on {{ publishDate }}</span>
+ <span v-if="author">— {{ author }}</span>
+ </span>
+ </div>
+ <div class="col-md-4 text-right">
+ <slot></slot>
+ </div>
+ </div>
+ <hr>
+ </div>
+ </section>
+</template>
+
+<script>
+export default {
+ props: ['title', 'publishDate', 'author'],
+ name: 'ContentTitle',
+};
+</script>
+
+<style lang="css" scoped>
+</style>
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 0000000..16f8873
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/CourseCard.vue
@@ -0,0 +1,92 @@
+<template>
+ <div class="py-3 col-md-4 col-sm-12">
+ <div class="card h-100">
+ <div class="card-hero-img" v-if="course.hero_image" v-bind:style="{ 'background-image': 'url(' + image + ')' }"></div>
+ <div v-else class="card-image-wrapper">
+ <div class="image-body">{{ course.course_name }}</div>
+ </div>
+ <div class='card-body'>
+ <h5 class="card-title">{{ course.course_name }}</h5>
+ <span class="course-list text-muted" id="getting-started">
+ Topics
+ <ul class="mb-0 mt-1" style="padding-left: 1.5em;">
+ <li v-for="topic in course.topics" :key="topic.name">
+ <div>{{ topic.topic_name }}</div>
+ </li>
+ </ul>
+ </span>
+ </div>
+ <div class='p-3' style="display: flex; justify-content: space-between;">
+ <div>
+ <span v-if="complete"><i class="mr-2 text-success fa fa-check-circle" aria-hidden="true"></i>Course Complete</span>
+ </div>
+ <div class='text-right'>
+ <a-button
+ :type="'primary'"
+ size="sm"
+ :route="courseRoute"
+ >
+ {{ buttonName }}
+ </a-button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import AButton from './Button.vue';
+
+export default {
+ props: ['course', 'program_name'],
+ name: "CourseCard",
+ components: {
+ AButton
+ },
+ data() {
+ return {
+ courseDetails: {},
+ }
+ },
+ mounted() {
+ if(lms.store.checkLogin()) this.getCourseDetails().then(data => this.courseDetails = data)
+ },
+ computed: {
+ courseRoute() {
+ return `${this.program_name}/${this.course.name}`
+ },
+ complete() {
+ if(lms.store.checkProgramEnrollment(this.program_name)){
+ if (this.courseDetails.flag === "Completed" ) {
+ return true
+ }
+ else {
+ return false
+ }
+ }
+ else {
+ return false
+ }
+ },
+ isLogin() {
+ return lms.store.checkLogin()
+ },
+ buttonName() {
+ if(lms.store.checkProgramEnrollment(this.program_name)){
+ return "Start Course"
+ }
+ else {
+ return "Explore"
+ }
+ }
+ },
+ methods: {
+ getCourseDetails() {
+ return lms.call('get_student_course_details', {
+ course_name: this.course.name,
+ program_name: this.program_name
+ })
+ },
+ }
+};
+</script>
\ 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 0000000..f3f3ce4
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Navbar.vue
@@ -0,0 +1,85 @@
+<template>
+<nav class="navbar navbar-light bg-white navbar-expand-lg sticky-top shadow-sm">
+ <div class="container">
+ <a class="navbar-brand" href="/lms">
+ <span>{{ portal.title }}</span>
+ </a>
+ <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+ <span class="navbar-toggler-icon"></span>
+ </button>
+
+ <div class="collapse navbar-collapse" id="navbarSupportedContent">
+ <ul class="navbar-nav mr-auto">
+
+ <li class="nav-item">
+ <a class="nav-link" href="lms#/List/Program">
+ All Programs
+ </a>
+ </li>
+
+ <li class="nav-item">
+ <a class="nav-link" href="/lms#/Profile">
+ Profile
+ </a>
+ </li>
+ </ul>
+ <ul class="navbar-nav ml-auto">
+ <!-- post login tools -->
+ <li v-if="isLogin" class="nav-item dropdown logged-in" id="website-post-login" data-label="website-post-login">
+ <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
+ <span class="user-image-wrapper">
+ <span class="avatar avatar-small" :title="fullName">
+ <span class="avatar-frame" :style="avatarStyle" :title="fullName"></span>
+ </span>
+ </span>
+ <span class="full-name">{{ fullName }}</span>
+ <b class="caret"></b>
+ </a>
+ <ul class="dropdown-menu dropdown-menu-right" role="menu">
+ <a class="dropdown-item" href="/me" rel="nofollow"> My Account </a>
+ <a class="dropdown-item" href="/?cmd=web_logout" rel="nofollow"> Logout </a>
+ </ul>
+ </li>
+
+ <li v-else class="nav-item">
+ <a class="nav-link btn-login-area" href="/login">Login</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+</nav>
+</template>
+<script>
+export default {
+ name: "Home",
+ data() {
+ return{
+ portal: {},
+ avatar: frappe.user_image,
+ fullName: frappe.full_name,
+ isLogin: frappe.is_user_logged_in()
+ }
+ },
+ mounted() {
+ this.getPortalDetails().then(data => this.portal = data);
+ },
+ methods: {
+ getPortalDetails() {
+ return lms.call("get_portal_details")
+ }
+ },
+ computed: {
+ avatarStyle() {
+ return `background-image: url("${this.avatar}")`
+ },
+ // isLogin() {
+ // return frappe.is_user_logged_in()
+ // },
+ }
+};
+</script>
+<style scoped>
+a {
+ text-decoration: none;
+}
+</style>
\ 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 0000000..6f3e8f1
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/ProfileInfo.vue
@@ -0,0 +1,85 @@
+<template>
+<section>
+ <div class="py-5">
+ <div class="row">
+ <div class="col-sm-12">
+ <div>
+ <h3>{{ fullName }}</h3>
+ <ul>
+ <li class="row">
+ <div class="col-md-3 col-sm-4 pr-0 text-muted">Email:</div>
+ <div class="col-md-9 col-sm-8">{{ email }}</div>
+ </li>
+ <li v-if="joiningDate" class="row">
+ <div class="col-md-3 col-sm-4 pr-0 text-muted">Date of Joining:</div>
+ <div class="col-md-9 col-sm-8">{{ joiningDate }}</div>
+ </li>
+ <li class="row">
+ <div class="col-md-3 col-sm-4 pr-0 text-muted">Programs Enrolled:</div>
+ <div class="col-md-9 col-sm-8">
+ <ul v-if="enrolledPrograms">
+ <li v-for="program in enrolledPrograms" :key="program">{{ program }}</li>
+ </ul>
+ <span v-else>None</span>
+ </div>
+ </li>
+ </ul>
+ </div>
+ <a href="/update-profile" class="edit-button text-muted">Edit Profile</a>
+ </div>
+ </div>
+ <div ></div>
+ </div>
+ </section>
+</template>
+<script>
+
+export default {
+ props: ['enrolledPrograms'],
+ name: "ProfileInfo",
+ data() {
+ return {
+ avatar: frappe.user_image,
+ fullName: frappe.full_name,
+ abbr: frappe.get_abbr(frappe.get_cookie("full_name")),
+ email: frappe.session.user,
+ joiningDate: ''
+ }
+ },
+ mounted(){
+ this.getJoiningDate().then(data => {
+ if(data) {
+ this.joiningDate = lms.moment(String(data)).format('D MMMM YYYY')
+ }
+ })
+ },
+ computed: {
+ avatarStyle() {
+ return `background-image: url("${this.avatar}")`
+ },
+ },
+ methods: {
+ getJoiningDate() {
+ return lms.call("get_joining_date")
+ }
+ }
+};
+</script>
+<style scoped>
+ .edit-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ }
+
+ .standard-image {
+ font-size: 72px;
+ border-radius: 6px;
+ }
+
+ ul {
+ list-style-type: none;
+ padding: 0;
+ margin: 0
+ }
+</style>
\ 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 0000000..20de085
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/ProgramCard.vue
@@ -0,0 +1,82 @@
+<template>
+<div class='py-3 col-md-4 col-sm-12'>
+ <div class="card h-100">
+ <router-link :to="'/Program/' + program.name">
+ <div class="card-hero-img" v-if="program.hero_image" v-bind:style="{ 'background-image': 'url(' + image + ')' }"></div>
+ <div v-else class="card-image-wrapper text-center">
+ <div class="image-body">{{ program.program_name }}</div>
+ </div>
+ <div class='card-body'>
+ <h5 class='card-title'>{{ program.program_name }}</h5>
+ <div class="text-muted">{{ program.description.substring(0,200) }}...</div>
+ </div>
+ </router-link>
+ <div class='text-right p-3'>
+ <button v-if="program.intro_video" class='btn btn-light btn-sm' data-toggle="modal" data-target="#videoModal">Watch Intro</button>
+ <a-button v-if="enrolled" type="dark" size="sm" :route="programPageRoute">
+ {{ buttonName }}
+ </a-button>
+ <button v-else-if="isLogin" class='btn btn-dark btn-sm' @click="enroll()">{{ enrollButton }}</button>
+ <a v-else class='btn btn-secondary btn-sm' href="/login#signup">Sign Up</a>
+ </div>
+ <VideoModal v-if="program.intro_video" :title="program.program_name" :video="program.intro_video"/>
+ </div>
+</div>
+</template>
+<script>
+import AButton from './Button.vue';
+import VideoModal from './VideoModal.vue';
+export default {
+ props: ['program', 'enrolled'],
+ name: "ProgramCard",
+ data() {
+ return {
+ isLogin: frappe.is_user_logged_in(),
+ enrollButton: 'Enroll Now',
+ programRoute: { name: 'program', params: { program_name: this.program.name }},
+ image: "'" + this.program.hero_image + "'"
+ };
+ },
+ methods: {
+ enroll() {
+ this.enrollButton = 'Enrolling...'
+ lms.call('enroll_in_program', {
+ program_name: this.program.name,
+ }).then(data => {
+ lms.store.updateEnrolledPrograms()
+ this.$router.push(this.programRoute)
+ })
+ }
+ },
+ computed: {
+ buttonName() {
+ if(this.enrolled){
+ return "Start Program"
+ }
+ else {
+ return "Enroll"
+ }
+ },
+ programPageRoute() {
+ return this.programRoute
+ },
+ isEnrolled() {
+ return lms.store.enrolledPrograms.includes(this.program.name)
+ }
+ },
+ components: {
+ AButton,
+ VideoModal
+ }
+};
+</script>
+
+<style lang="css" scoped>
+ a {
+ text-decoration: none;
+ color: black;
+ }
+ a.btn-secondary {
+ color: white !important;
+ }
+</style>
\ 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 0000000..66b61f6
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/ProgressCard.vue
@@ -0,0 +1,89 @@
+<template>
+ <div class='py-3 col-md-4 col-sm-12'>
+ <div class="card h-100">
+ <div class='card-body'>
+ <router-link :to="'/Program/' + programData.name">
+ <h5 class='card-title'>{{ programData.program }}</h5>
+ </router-link>
+ <span class="course-list text-muted" id="getting-started">
+ Courses
+ <ul class="mb-0 mt-1 list-unstyled" style="padding-left: 1.5em;">
+ <li v-for="item in programData.progress" :key="item.name">
+ <span v-if="item.is_complete"><i class="text-success fa fa-check-circle" aria-hidden="true"></i></span>
+ <span v-else><i class="text-secondary fa fa-circle-o" aria-hidden="true"></i></span>
+ {{ item.course_name }}
+ </li>
+ </ul>
+ </span>
+ </div>
+ <div class='p-3' style="display: flex; justify-content: space-between;">
+ <div></div>
+ <div class='text-right'>
+ <a-button
+ :type="buttonType"
+ size="sm btn-block"
+ :route="programRoute"
+ >
+ {{ buttonName }}
+ </a-button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+<script>
+import AButton from './Button.vue';
+export default {
+ props: ['program'],
+ name: "ProgressCard",
+ data() {
+ return {
+ programData: {}
+ };
+ },
+ mounted() {
+ this.getProgramProgress().then(data => this.programData = data)
+ },
+ methods: {
+ getProgramProgress() {
+ return lms.call('get_program_progress', {
+ program_name: this.program
+ })
+ },
+ },
+ computed: {
+ programRoute() {
+ return {name: 'program', params: {program_name: this.program}}
+ },
+ buttonType() {
+ if (this.programData.percentage == 100 ){
+ return "success"
+ }
+ else if (this.programData.percentage == "0" ) {
+ return "secondary"
+ }
+ else {
+ return "info"
+ }
+ },
+ buttonName() {
+ if (this.programData.percentage == 100 ){
+ return "Program Complete"
+ }
+ else {
+ return `${this.programData.percentage}% Completed`
+ }
+ }
+ },
+ components: {
+ AButton
+ },
+};
+</script>
+<style scoped>
+
+ a {
+ text-decoration: none;
+ color: black;
+ }
+</style>
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 0000000..0a6199a
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Quiz.vue
@@ -0,0 +1,119 @@
+<template>
+ <section class="quiz-section">
+ <div>
+ <div class="row">
+ <div class="col-md-8">
+ <h2>{{ content }}</h2>
+ </div>
+ </div>
+ <div class="content">
+ <hr>
+ <div id="quiz" :name="content">
+ <div id="quiz-body">
+ <component v-for="question in quizData" :key="question.name" v-bind:is="question.type" :question="question" @updateResponse="updateResponse" :isDisabled="isDisabled"></component>
+ </div>
+ <div class="mt-3">
+ <div>
+ <div v-if="isDisabled || submitted" id="post-quiz-actions" class="row">
+ <div class="col-md-8 text-left">
+ <span v-html="message"></span>
+ </div>
+ <div class="col-md-4 text-right">
+ <slot></slot>
+ </div>
+ </div>
+ <div v-else id="quiz-actions" class="text-right">
+ <button class='btn btn-outline-secondary' type="reset" :disabled="isDisabled">Reset</button>
+ <button class='btn btn-primary' @click="submitQuiz" type="button" :disabled="isDisabled">Submit</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="mt-3 text-right">
+ <a class="text-muted" href="/report"><i class="octicon octicon-issue-opened" title="Report"></i> Report a
+ Mistake</a>
+ </div>
+ </div>
+</section>
+</template>
+
+<script>
+import QuizSingleChoice from "./Quiz/QuizSingleChoice.vue"
+import QuizMultipleChoice from "./Quiz/QuizMultipleChoice.vue"
+
+export default {
+ props: ['content', 'type'],
+ name: 'Quiz',
+ data() {
+ return {
+ quizData: '',
+ quizResponse: {},
+ score: '',
+ submitted: false,
+ isDisabled: false,
+ quizStatus: {},
+ }
+ },
+ mounted() {
+ this.getQuizWithoutAnswers().then(data => {
+ this.quizData = data.quizData
+ this.quizStatus = data.status
+ this.isDisabled = data.status.is_complete
+ });
+ },
+ components: {
+ 'SingleChoice': QuizSingleChoice,
+ 'MultipleChoice': QuizMultipleChoice
+ },
+ methods: {
+ getQuizWithoutAnswers() {
+ return lms.call("get_quiz_without_answers",
+ {
+ quiz_name: this.content,
+ course_name: this.$route.params.course_name
+ }
+ )
+ },
+ updateResponse(res) {
+ this.quizResponse[res.question] = res.option
+ },
+ submitQuiz() {
+ lms.call("evaluate_quiz",
+ {
+ quiz_response: this.quizResponse,
+ quiz_name: this.content,
+ course: this.$route.params.course_name
+ }
+ ).then(data => {
+ this.score = data
+ this.submitted = true
+ this.quizResponse = null
+ });
+ }
+ },
+ computed: {
+ currentComponent: function() {
+ if(this.quizData.type === "MultipleChoice") {
+ return 'QuizMultipleChoice'
+ }
+ else {
+ return 'QuizSingleChoice'
+ }
+ },
+ message: function() {
+ if(this.submitted) {
+ return '<h3>Your Score: <span id="result">'+ this.score +'</span></h3>'
+ }
+ let message = '<h4>You have exhausted all attempts for this quiz.</h4>'
+ if(this.quizStatus.result == 'Pass') {
+ message = "<h4>You have successfully completed this quiz.</h4>Score: " + this.quizStatus.score
+ }
+ return message
+ }
+ },
+};
+</script>
+
+<style lang="css" scoped>
+</style>
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 0000000..338b1ac
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Quiz/QuizMultipleChoice.vue
@@ -0,0 +1,34 @@
+<template>
+ <div class="question mt-4">
+ <h5>{{ question.question }}</h5>
+ <div class="options ml-2">
+ <div v-for="option in question.options" :key="option.name" class="form-check pb-1">
+ <input v-model="checked" class="form-check-input" type="checkbox" :name="question.name" :id="option.name" :value="option.name" @change="emitResponse(question.name, option.name)" :disabled="isDisabled">
+ <label class="form-check-label" :for="option.name">
+ {{ option.option }}
+ </label>
+ </div>
+ </div>
+</div>
+</template>
+
+<script>
+export default {
+ props: ['question', 'isDisabled'],
+ name: 'QuizSingleChoice',
+ data() {
+ return {
+ checked: []
+ }
+ },
+ methods: {
+ emitResponse(q, o) {
+ console.log(this.checked)
+ this.$emit('updateResponse', {'question':q , 'option': this.checked, 'type': this.question.type})
+ }
+ }
+};
+</script>
+
+<style lang="css" scoped>
+</style>
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 0000000..235cbce
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Quiz/QuizSingleChoice.vue
@@ -0,0 +1,28 @@
+<template>
+ <div class="question mt-4">
+ <h5>{{ question.question }}</h5>
+ <div class="options ml-2">
+ <div v-for="option in question.options" :key="option.name" class="form-check pb-1">
+ <input class="form-check-input" type="radio" :name="question.name" :id="option.name" :value="option.name" @change="emitResponse(question.name, option.name)" :disabled="isDisabled">
+ <label class="form-check-label" :for="option.name">
+ {{ option.option }}
+ </label>
+ </div>
+ </div>
+</div>
+</template>
+
+<script>
+export default {
+ props: ['question', 'isDisabled'],
+ name: 'QuizSingleChoice',
+ methods: {
+ emitResponse(q, o) {
+ this.$emit('updateResponse', {'question':q , 'option': o, 'type': this.question.type})
+ }
+ }
+};
+</script>
+
+<style lang="css" scoped>
+</style>
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 0000000..80b12cb
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/ScoreCard.vue
@@ -0,0 +1,60 @@
+<template>
+ <div v-if="quizData" class='py-3 col-md-4 col-sm-12'>
+ <div class="card h-100">
+ <div class='card-body'>
+ <h5 class='card-title'>{{ quizData.program }}</h5>
+ <div v-for="attempt in quizData.quiz_attempt" :key="attempt.content" class="course-list" id="getting-started">
+ <div>
+ {{ attempt.content }}
+ <ul v-if="attempt.is_complete">
+ <li><span class="text-muted">Score: </span>{{ attempt.score }}</li>
+ <li><span class="text-muted">Status: </span>{{attempt.result }}</li>
+ </ul>
+ <span v-else>- Unattempted</span>
+ </div>
+ </div>
+ </div>
+ <div class='p-3' style="display: flex; justify-content: space-between;">
+ <div></div>
+ <div class='text-right'>
+ <a-button
+ :type="'primary'"
+ size="sm btn-block"
+ :route="programRoute"
+ >
+ Go To Program
+ </a-button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+<script>
+import AButton from './Button.vue';
+export default {
+ props: ['program'],
+ name: "ScoreCard",
+ data() {
+ return {
+ quizData: {}
+ };
+ },
+ mounted() {
+ this.getQuizProgress().then(data => this.quizData = data)
+ },
+ methods: {
+ getQuizProgress() {
+ return lms.call('get_quiz_progress_of_program', {
+ program_name: this.program
+ })
+ },
+ programRoute() {
+ return {name: 'program', params: {program_name: this.program}}
+ },
+ },
+ components: {
+ AButton
+ },
+};
+</script>
+
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 0000000..c27d003
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/TopSection.vue
@@ -0,0 +1,27 @@
+<template>
+<div class="hero">
+ <h1 class="text-center" v-html="title"></h1>
+ <p class='text-center' v-html="description"></p>
+ <p class="text-center padding">
+ <slot></slot>
+ </p>
+</div>
+</template>
+<script>
+
+export default {
+ props: ['title', 'description'],
+ name: "TopSection",
+};
+</script>
+<style scoped>
+ .hero {
+ padding-top: 50px;
+ padding-bottom: 100px;
+ }
+
+ .hero h1 {
+ font-size: 40px;
+ font-weight: 200;
+ }
+</style>
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 0000000..0fa49d4
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/TopSectionButton.vue
@@ -0,0 +1,49 @@
+<template>
+ <button v-if="isLoggedIn" class='btn btn-primary btn-md' @click="primaryAction()">{{ buttonName }}</button>
+ <a v-else class='btn btn-primary btn-md' href="/login#signup">{{ buttonName }}</a>
+</template>
+<script>
+export default {
+ name: "TopSectionButton",
+ data() {
+ return {
+ buttonName: '',
+ isLoggedIn: lms.store.checkLogin(),
+ nextContent: '',
+ nextContentType: '',
+ nextCourse: '',
+ link: '',
+ }
+ },
+ mounted() {
+ this.computeButtons()
+ },
+ methods: {
+ computeButtons(){
+ if(this.isLoggedIn){
+ this.buttonName = 'Explore Programs'
+ }
+ else{
+ this.buttonName = 'Sign Up'
+ }
+ },
+ primaryAction() {
+ if(this.$route.name == 'home'){
+ this.$router.push('List/Program');
+ }
+ else if(this.$route.name == 'program' && lms.store.enrolledPrograms.includes(this.$route.params.program_name)){
+ this.$router.push({ name: 'content', params: { program_name: this.$route.params.program_name, course: this.nextCourse, type: this.nextContentType, content: this.nextContent}})
+ }
+ else {
+ lms.call("enroll_in_program",
+ {
+ program_name: this.$route.params.program_name,
+ student_email_id: frappe.session.user
+ }
+ )
+ lms.store.updateEnrolledPrograms()
+ }
+ },
+ }
+};
+</script>
\ 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 0000000..4cb8e85
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/TopicCard.vue
@@ -0,0 +1,112 @@
+
+<template>
+ <div class="py-3 col-md-4 col-sm-12">
+ <div class="card h-100">
+ <div class="card-hero-img" v-if="topic.hero_image" v-bind:style="{ 'background-image': 'url(' + image + ')' }"></div>
+ <div v-else class="card-image-wrapper">
+ <div class="image-body">{{ topic.topic_name }}</div>
+ </div>
+ <div class='card-body'>
+ <h5 class="card-title">{{ topic.topic_name }}</h5>
+ <span class="course-list text-muted" id="getting-started">
+ Content
+ <ul class="mb-0 mt-1" style="padding-left: 1.5em;">
+ <li v-for="content in topic.topic_content" :key="content.name">
+ <router-link v-if="isLogin" tag="a" :class="'text-muted'" :to="{name: 'content', params:{program_name: program_name, topic:topic.name, course_name: course_name, type:content.content_type, content: content.content} }">
+ {{ content.content }}
+ </router-link>
+ <div v-else><span style="padding-right: 0.4em"></span>{{ content.content }}</div>
+ </li>
+ </ul>
+ </span>
+ </div>
+ <div v-if="isLogin" class='p-3' style="display: flex; justify-content: space-between;">
+ <div>
+ <span v-if="complete"><i class="mr-2 text-success fa fa-check-circle" aria-hidden="true"></i>Course Complete</span>
+ </div>
+ <div class='text-right'>
+ <a-button
+ :type="'primary'"
+ size="sm"
+ :route="firstContentRoute"
+ >
+ {{ buttonName }}
+ </a-button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import AButton from './Button.vue';
+
+export default {
+ props: ['topic', 'course_name', 'program_name'],
+ name: "TopicCard",
+ data() {
+ return {
+ topicDetails: {}
+ }
+ },
+ mounted() {
+ if(lms.store.checkLogin()) this.gettopicDetails().then(data => this.topicDetails = data)
+ },
+ components: {
+ AButton
+ },
+ computed: {
+ firstContentRoute() {
+ if(lms.store.checkLogin()){
+ return `/Program/${this.program_name}/${this.course_name}/${this.topic.name}/${this.topicDetails.content_type}/${this.topicDetails.content}`
+ }
+ else {
+ return {}
+ }
+ },
+ complete() {
+ if(lms.store.checkProgramEnrollment(this.program_name)){
+ if (this.topicDetails.flag === "Completed" ) {
+ return true
+ }
+ else {
+ return false
+ }
+ }
+ else {
+ return false
+ }
+ },
+ isLogin() {
+ // return lms.store.checkProgramEnrollment(this.program_name)
+ return lms.store.checkLogin()
+ },
+ buttonName() {
+ if(lms.store.checkProgramEnrollment(this.program_name)){
+ if (this.topicDetails.flag == 'Continue'){
+ return 'Continue'
+ }
+ else {
+ return 'Start Topic'
+ }
+ }
+ else {
+ return "Explore"
+ }
+ }
+ },
+ methods: {
+ iconClass(content_type) {
+ if(content_type == 'Video') return 'fa fa-play'
+ if(content_type == 'Article') return 'fa fa-file-text-o'
+ if(content_type == 'Quiz') return 'fa fa-question-circle-o'
+ },
+ gettopicDetails() {
+ return lms.call('get_student_topic_details', {
+ topic_name: this.topic.name,
+ course_name: this.course_name,
+ })
+ },
+ }
+};
+</script>
\ 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 0000000..27f922f
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/Video.vue
@@ -0,0 +1,64 @@
+<template>
+<div>
+ <section class='mt-2'>
+ <div>
+ <div class="mt-3 row">
+ <div class="col-md-8">
+ <h2>{{ contentData.name }}</h2>
+ <span class="text-muted">
+ <i class="octicon octicon-clock" title="Duration"></i> {{ contentData.duration }} Mins
+ — Published on {{ contentData.publish_date }}.
+ </span>
+ </div>
+ <div class="col-md-4 text-right">
+ <slot></slot>
+ </div>
+ </div>
+ <youtube-player :url="contentData.url" class="mt-3"/>
+ <hr>
+ </div>
+</section>
+<section class="video-description-section">
+ <div>
+ <div class="content" v-html="contentData.description">
+ </div>
+ <div class="text-right hidden">
+ <a class='btn btn-outline-secondary' href="/classrooms/module">Previous</a>
+ <a class='btn btn-primary' href="/classrooms/module">Next</a>
+ </div>
+ <div class="mt-3 text-right">
+ <a class="text-muted" href="/report"><i class="octicon octicon-issue-opened" title="Report"></i> Report a
+ Mistake</a>
+ </div>
+ </div>
+</section>
+</div>
+</template>
+<script>
+import YoutubePlayer from './YoutubePlayer.vue'
+
+export default {
+ props: ['content', 'type'],
+ name: 'Video',
+ data() {
+ return {
+ contentData: '',
+ }
+ },
+ components: {
+ YoutubePlayer
+ },
+ mounted() {
+ this.getContent()
+ .then(data => this.contentData = data)
+ },
+ methods: {
+ getContent() {
+ return lms.call('get_content', {
+ content_type: this.type,
+ content: this.content
+ })
+ }
+ }
+};
+</script>
\ 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 0000000..71227ad
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/VideoModal.vue
@@ -0,0 +1,35 @@
+<template>
+ <div class="modal" id="videoModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title">{{ title }}</h5>
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+ <span id="close_modal" aria-hidden="true" @click="stopVideo()">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <youtube-player :url="video"/>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+<script type="text/javascript">
+import YoutubePlayer from './YoutubePlayer.vue'
+
+export default {
+ name: 'VideoModal',
+ props: ['title', 'video'],
+ components: {
+ YoutubePlayer
+ },
+ methods: {
+ stopVideo() {
+ $('.yvideo').each(function() {
+ this.contentWindow.postMessage('{"event":"command","func":"stopVideo","args":""}', '*')
+ });
+ }
+ }
+};
+</script>
\ 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 0000000..9377b57
--- /dev/null
+++ b/erpnext/public/js/education/lms/components/YoutubePlayer.vue
@@ -0,0 +1,36 @@
+<template>
+ <div class="embed-responsive embed-responsive-16by9">
+ <iframe class="embed-responsive-item yvideo" :src="'https://www.youtube.com/embed/' + videoID + '?version=3&enablejsapi=1'" allowfullscreen></iframe>
+ </div>
+</template>
+<script type="text/javascript">
+ export default {
+ name: 'YoutubePlayer',
+ props: ['url'],
+ data() {
+ return {
+ videoID: ''
+ }
+ },
+ watch: {
+ url() {
+ this.videoID = this.getVideoID(this.url)
+ }
+ },
+ methods: {
+ getVideoID(link) {
+ if (!Array.prototype.last){
+ Array.prototype.last = function(){
+ return this[this.length - 1];
+ };
+ };
+ if (link.includes('v=')){
+ return link.split('v=')[1].split('&')[0]
+ }
+ else if (link.includes('youtu.be')) {
+ return link.split('/').last().split('?')[0]
+ }
+ }
+ }
+ };
+</script>
\ 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 0000000..4665b14
--- /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 = `<a class="dropdown-item" href="/lms#/Profile" rel="nofollow"> LMS Profile </a>`
+// 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: "<lms-root/>",
+ 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 0000000..d359265
--- /dev/null
+++ b/erpnext/public/js/education/lms/lmsRoot.vue
@@ -0,0 +1,45 @@
+<template>
+ <div id="lms-root">
+ <navbar></navbar>
+ <main class="container my-5">
+ <div class="page_content">
+ <router-view :key="$route.fullPath"></router-view>
+ </div>
+ </main>
+ </div>
+</template>
+<script>
+import Navbar from "./components/Navbar.vue"
+export default {
+ name: "lmsRoot",
+ components: {
+ Navbar
+ }
+};
+</script>
+<style>
+ div.card-hero-img {
+ height: 220px;
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-color: rgb(250, 251, 252);
+ }
+
+ .card-image-wrapper {
+ display: flex;
+ overflow: hidden;
+ height: 220px;
+ background-color: rgb(250, 251, 252);
+ justify-content: center;
+ }
+
+ .image-body {
+ align-self: center;
+ color: #d1d8dd;
+ font-size: 24px;
+ font-weight: 600;
+ line-height: 1;
+ padding: 20px;
+ }
+</style>
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 0000000..542e937
--- /dev/null
+++ b/erpnext/public/js/education/lms/pages/ContentPage.vue
@@ -0,0 +1,87 @@
+<template>
+ <div>
+ <breadcrumb/>
+ <component v-bind:is="currentComponent" :content="content" :type="type">
+ <ContentNavigation :nextContent="nextContent" :nextContentType="nextContentType"/>
+ </component>
+ </div>
+</template>
+<script>
+import Article from "../components/Article.vue"
+import Quiz from "../components/Quiz.vue"
+import Video from "../components/Video.vue"
+import ContentNavigation from "../components/ContentNavigation.vue"
+import Breadcrumb from "../components/Breadcrumb.vue"
+
+export default {
+ props:['program_name', 'course_name', 'topic', 'type', 'content'],
+ name: "ContentPage",
+ data() {
+ return{
+ nextContent: '',
+ nextContentType: '',
+ }
+ },
+ computed: {
+ currentComponent: function() {
+ if(this.type === "Article") {
+ return 'Article'
+ }
+ else if(this.type === "Quiz") {
+ return 'Quiz'
+ }
+ else if(this.type === "Video") {
+ return 'Video'
+ }
+ },
+ },
+ mounted() {
+ this.getNextContent().then(data => {
+ this.nextContent = data.content,
+ this.nextContentType = data.content_type
+ });
+ },
+ methods: {
+ getNextContent(){
+ return lms.call("get_next_content",
+ {
+ current_content: this.content,
+ current_content_type: this.type,
+ topic: this.topic,
+ }
+ );
+ }
+ },
+ components: {
+ Article,
+ Video,
+ Quiz,
+ ContentNavigation,
+ Breadcrumb
+ }
+};
+</script>
+
+<style>
+.footer-message {
+ display: none;
+}
+
+.video-description-section {
+ padding-top: 0em !important;
+}
+
+.article-top-section {
+ padding-top: 0.5em !important;
+ padding-bottom: 0rem !important;
+}
+
+.article-content-section {
+ padding-top: 0em !important;
+}
+
+.quiz-section {
+ padding-top: 0.5em !important;
+ padding-bottom: 0rem !important;
+}
+</style>
\ 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 0000000..9aaf8a9
--- /dev/null
+++ b/erpnext/public/js/education/lms/pages/CoursePage.vue
@@ -0,0 +1,49 @@
+<template>
+<div>
+ <breadcrumb></breadcrumb>
+ <TopSection v-bind:title="course.course_name" v-bind:description="course.description">
+ </TopSection>
+ <CardList :title="'Topics'" :description="''" :sectionType="'section-padding section-bg'">
+ <TopicCard slot="card-list-slot" v-for="topic in topicData" :topic="topic" :course_name="course_name" :program_name="program_name" :key="topic.name"/>
+ </CardList>
+</div>
+</template>
+<script>
+import TopSection from "../components/TopSection.vue"
+import CardList from "../components/CardList.vue"
+import TopicCard from "../components/TopicCard.vue"
+import Breadcrumb from "../components/Breadcrumb.vue"
+
+export default {
+ props: ['program_name','course_name'],
+ name: "CoursePage",
+ components: {
+ TopSection,
+ CardList,
+ TopicCard,
+ Breadcrumb
+ },
+ data() {
+ return {
+ course: {},
+ topicData: [],
+ }
+ },
+ mounted() {
+ this.getCourseDetails().then(data => this.course = data);
+ this.getTopics().then(data => this.topicData = data);
+ },
+ methods: {
+ getCourseDetails() {
+ return lms.call('get_course_details', {
+ course_name: this.course_name
+ });
+ },
+ getTopics() {
+ return lms.call('get_topics', {
+ course_name: this.course_name
+ })
+ }
+ }
+};
+</script>
\ 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 0000000..5690086
--- /dev/null
+++ b/erpnext/public/js/education/lms/pages/Home.vue
@@ -0,0 +1,48 @@
+<template>
+<div>
+ <TopSection :title="portal.title" :description="portal.description">
+ <TopSectionButton/>
+ </TopSection>
+ <CardList :title="'Featured Programs'" :description="'Master ERPNext'" :sectionType="'section-padding section-bg'">
+ <ProgramCard slot="card-list-slot" v-for="item in featuredPrograms" :key="item.program.name" :program="item.program" :enrolled="item.is_enrolled"/>
+ <AButton slot="list-bottom" :type="'primary'" :size="'md'" :route="'List/Program'">View All</AButton>
+ </CardList>
+</div>
+</template>
+<script>
+import Button from '../components/Button.vue';
+import TopSection from "../components/TopSection.vue"
+import CardList from "../components/CardList.vue"
+import ProgramCard from "../components/ProgramCard.vue"
+import TopSectionButton from "../components/TopSectionButton.vue"
+
+export default {
+ name: "Home",
+ data() {
+ return{
+ portal: {},
+ featuredPrograms: {},
+ // enrolledPrograms: new Set()
+ }
+ },
+ components: {
+ AButton: Button,
+ TopSection,
+ CardList,
+ ProgramCard,
+ TopSectionButton
+ },
+ mounted() {
+ this.getPortalDetails().then(data => this.portal = data);
+ this.getFeaturedPrograms().then(data => this.featuredPrograms = data);
+ },
+ methods: {
+ getPortalDetails() {
+ return lms.call("get_portal_details")
+ },
+ getFeaturedPrograms() {
+ return lms.call("get_featured_programs")
+ }
+ }
+};
+</script>
\ 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 0000000..0768191
--- /dev/null
+++ b/erpnext/public/js/education/lms/pages/ListPage.vue
@@ -0,0 +1,53 @@
+<template>
+ <div>
+ <TopSection :title="'Programs at ' + portal.title" :description="portal.description">
+ <AButton v-if="isLogin" :type="'primary'" :size="'lg'" :route="{ name: 'signup'}">Sign Up</AButton>
+ </TopSection>
+ <CardList :title="'All Programs'" :description="''" :sectionType="'section-padding section-bg'">
+ <ProgramCard slot="card-list-slot" v-for="item in masterData" :key="item.program.name" :program="item.program" :enrolled="item.is_enrolled"/>
+ </CardList>
+ </div>
+</template>
+<script>
+import ProgramCard from '../components/ProgramCard.vue';
+import CourseCard from "../components/CourseCard.vue"
+import Button from '../components/Button.vue';
+import TopSection from "../components/TopSection.vue"
+import CardList from "../components/CardList.vue"
+
+
+export default {
+ props: ['master'],
+ name: "ListPage",
+ components: {
+ AButton: Button,
+ CourseCard,
+ ProgramCard,
+ CardList,
+ TopSection
+ },
+ data() {
+ return {
+ portal: {},
+ masterData: {}
+ }
+ },
+ mounted() {
+ this.getPortalDetails().then(data => this.portal = data);
+ this.getMaster().then(data => this.masterData = data);
+ },
+ methods: {
+ getPortalDetails() {
+ return lms.call("get_portal_details")
+ },
+ getMaster() {
+ return lms.call("get_all_programs")
+ }
+ },
+ computed: {
+ isLogin() {
+ return !lms.store.checkLogin()
+ }
+ }
+};
+</script>
\ 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 0000000..c926463
--- /dev/null
+++ b/erpnext/public/js/education/lms/pages/ProfilePage.vue
@@ -0,0 +1,50 @@
+<template>
+<div>
+ <ProfileInfo :enrolledPrograms="enrolledPrograms"></ProfileInfo>
+ <div v-if="enrolledPrograms">
+ <CardList :title="'Your Progress'" :description="''" :sectionType="'section-padding section-bg'">
+ <ProgressCard slot="card-list-slot" v-for="program in enrolledPrograms" :program="program" :key="program"/>
+ </CardList>
+ <CardList :title="'Quiz Attempts'" :description="''" :sectionType="'section-padding section'">
+ <ScoreCard slot="card-list-slot" v-for="program in enrolledPrograms" :program="program" :key="program"/>
+ </CardList>
+ </div>
+ <div v-else>
+ You haven't enrolled in any programs yet.
+ </div>
+
+</div>
+</template>
+<script>
+import Button from '../components/Button.vue';
+import TopSection from "../components/TopSection.vue"
+import CardList from "../components/CardList.vue"
+import ProgressCard from "../components/ProgressCard.vue"
+import ProfileInfo from "../components/ProfileInfo.vue"
+import ScoreCard from "../components/ScoreCard.vue"
+
+export default {
+ name: "ProfilePage",
+ components: {
+ AButton: Button,
+ TopSection,
+ CardList,
+ ProfileInfo,
+ ProgressCard,
+ ScoreCard
+ },
+ data() {
+ return {
+ enrolledPrograms: {},
+ }
+ },
+ mounted() {
+ this.getEnrolledPrograms().then(data => this.enrolledPrograms = data);
+ },
+ methods: {
+ getEnrolledPrograms() {
+ return lms.call("get_program_enrollments")
+ }
+ }
+};
+</script>
\ 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 0000000..2a13661
--- /dev/null
+++ b/erpnext/public/js/education/lms/pages/ProgramPage.vue
@@ -0,0 +1,49 @@
+<template>
+<div>
+ <breadcrumb></breadcrumb>
+ <TopSection v-bind:title="program.program_name" v-bind:description="program.description">
+ </TopSection>
+ <CardList :title="'Courses'" :description="''" :sectionType="'section-padding'">
+ <CourseCard slot="card-list-slot" v-for="course in courseData" :course="course" :program_name="program_name" :key="course.name"/>
+ </CardList>
+</div>
+</template>
+<script>
+import TopSection from "../components/TopSection.vue"
+import CardList from "../components/CardList.vue"
+import CourseCard from "../components/CourseCard.vue"
+import Breadcrumb from "../components/Breadcrumb.vue"
+
+export default {
+ props: ['program_name'],
+ name: "ProgramPage",
+ components: {
+ TopSection,
+ CardList,
+ CourseCard,
+ Breadcrumb
+ },
+ data() {
+ return {
+ program: {},
+ courseData: [],
+ }
+ },
+ mounted() {
+ this.getProgramDetails().then(data => this.program = data);
+ this.getCourses().then(data => this.courseData = data);
+ },
+ methods: {
+ getProgramDetails() {
+ return lms.call('get_program', {
+ program_name: this.program_name
+ });
+ },
+ getCourses() {
+ return lms.call('get_courses', {
+ program_name: this.program_name
+ })
+ }
+ }
+};
+</script>
\ 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 0000000..483f222
--- /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 0000000..3a4dbf8
--- /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 6b583af..f3a4f7c 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -284,18 +284,18 @@
'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 @@
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 e379ed8..f413a8e 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 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 @@
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 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 adb58a1..dc22b5b 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -204,6 +204,20 @@
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 @@
})
},
+ 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 30123db..95c803d 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,
@@ -3237,6 +3240,40 @@
"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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"description": "Track this Sales Order against any Project",
"fetch_if_empty": 0,
"fieldname": "project",
@@ -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 d09e281..fb7a335 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.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 @@
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 @@
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 @@
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 @@
"Sales Order Item": {
"doctype": "Project Task",
"field_map": {
- "description": "title",
+ "item_code": "title",
},
}
}, target_doc, postprocess)
@@ -764,6 +771,7 @@
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 @@
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 cd69dd4..b8c4604 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -412,7 +412,6 @@
@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 @@
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 @@
"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 aeab9ed..ed02cee 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -90,8 +90,6 @@
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 71cf1da..6953279 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -457,7 +457,7 @@
"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 da2d09f..a6af4bd 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -35,7 +35,7 @@
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 4d8022c..30a45dd 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -492,7 +492,7 @@
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 @@
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 @@
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 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 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/inactive_items/__init__.py
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 0000000..39dfd5c
--- /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 0000000..b9eb05e
--- /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 0000000..8d87912
--- /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 20b5e45..70da5b5 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -154,10 +154,10 @@
"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 3a1c5ef..91b718e 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 0000000..1796194
--- /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 %}
+<div id="lms-app"></div>
+<script type="text/javascript" src="/assets/js/lms.min.js"></script>
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/www/lms.py b/erpnext/www/lms.py
new file mode 100644
index 0000000..7561d73
--- /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 0000000..e63f4c9
--- /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