diff --git a/.gitignore b/.gitignore index a0c6757..bf08615 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,8 @@ *.egg-info *.swp tags -app1/docs/current \ No newline at end of file +<<<<<<< HEAD +app1/docs/current +======= +cpfa/docs/current +>>>>>>> e749145085c0ecdeac8d83ab3f36927a070bec7c diff --git a/MANIFEST.in b/MANIFEST.in index d449e48..dee8e43 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,6 +4,7 @@ include *.json include *.md include *.py include *.txt +<<<<<<< HEAD recursive-include app1 *.css recursive-include app1 *.csv recursive-include app1 *.html @@ -15,4 +16,18 @@ recursive-include app1 *.png recursive-include app1 *.py recursive-include app1 *.svg recursive-include app1 *.txt -recursive-exclude app1 *.pyc \ No newline at end of file +recursive-exclude app1 *.pyc +======= +recursive-include cpfa *.css +recursive-include cpfa *.csv +recursive-include cpfa *.html +recursive-include cpfa *.ico +recursive-include cpfa *.js +recursive-include cpfa *.json +recursive-include cpfa *.md +recursive-include cpfa *.png +recursive-include cpfa *.py +recursive-include cpfa *.svg +recursive-include cpfa *.txt +recursive-exclude cpfa *.pyc +>>>>>>> e749145085c0ecdeac8d83ab3f36927a070bec7c diff --git a/README.md b/README.md index 8975acd..a24b8e6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ +<<<<<<< HEAD ## App1 Test App +======= +## Total CPFA ERPNext + +Total E&P NIG CPFA ERPNext customization +>>>>>>> e749145085c0ecdeac8d83ab3f36927a070bec7c #### License diff --git a/app1/app1/doctype/color/__init__.py b/app1/app1/doctype/color/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app1/app1/doctype/color/color.js b/app1/app1/doctype/color/color.js new file mode 100644 index 0000000..e415a6e --- /dev/null +++ b/app1/app1/doctype/color/color.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Color', { + refresh: function(frm) { + + } +}); diff --git a/app1/app1/doctype/color/color.json b/app1/app1/doctype/color/color.json new file mode 100644 index 0000000..ceb2876 --- /dev/null +++ b/app1/app1/doctype/color/color.json @@ -0,0 +1,93 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:color", + "beta": 0, + "creation": "2018-11-13 10:51:58.790680", + "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": "color", + "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": "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": 1, + "search_index": 0, + "set_only_once": 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-13 10:54:45.332066", + "modified_by": "Administrator", + "module": "App1", + "name": "Color", + "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": "System Manager", + "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 +} \ No newline at end of file diff --git a/app1/app1/doctype/color/color.py b/app1/app1/doctype/color/color.py new file mode 100644 index 0000000..c17a1c6 --- /dev/null +++ b/app1/app1/doctype/color/color.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class Color(Document): + pass diff --git a/app1/app1/doctype/color/test_color.js b/app1/app1/doctype/color/test_color.js new file mode 100644 index 0000000..1ed9661 --- /dev/null +++ b/app1/app1/doctype/color/test_color.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: Color", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Color + () => frappe.tests.make('Color', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/app1/app1/doctype/color/test_color.py b/app1/app1/doctype/color/test_color.py new file mode 100644 index 0000000..98c4de7 --- /dev/null +++ b/app1/app1/doctype/color/test_color.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestColor(unittest.TestCase): + pass diff --git a/app1/app1/doctype/insurance_company/__init__.py b/app1/app1/doctype/insurance_company/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app1/app1/doctype/insurance_company/address__.html b/app1/app1/doctype/insurance_company/address__.html new file mode 100644 index 0000000..88c2262 --- /dev/null +++ b/app1/app1/doctype/insurance_company/address__.html @@ -0,0 +1,12 @@ + + + + + + +< +

Your address and contacts

+

{{addy.address_line1}}>

+

{{addy.address_line2}}

+

City:{{addy.city}}

+ diff --git a/app1/app1/doctype/insurance_company/insurance_company.js b/app1/app1/doctype/insurance_company/insurance_company.js new file mode 100644 index 0000000..bbabd07 --- /dev/null +++ b/app1/app1/doctype/insurance_company/insurance_company.js @@ -0,0 +1,19 @@ +// Copyright (c) 2018, frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Insurance Company', { + refresh: function(frm) { + // console.log("Hello there"); + var addy=frappe.get_doc("Address","Address 2-Office") + var temp = frappe.render_template("address__.html",data={addy:addy}) + }, + new_contact:function(frm){ + frappe.new_doc("Contact") + }, + add_address:function(frm){ + frappe.new_doc("Address") + + } + + +}); diff --git a/app1/app1/doctype/insurance_company/insurance_company.json b/app1/app1/doctype/insurance_company/insurance_company.json new file mode 100644 index 0000000..c62968b --- /dev/null +++ b/app1/app1/doctype/insurance_company/insurance_company.json @@ -0,0 +1,154 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-11-13 08:43:13.896939", + "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": "company_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": "Company 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "insurance_type", + "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": "Insurance Type", + "length": 0, + "no_copy": 0, + "options": "Agric\nHealth\nDeposit\nLife\nMortgage\nProperty\nTravel\nVehicle\nGroup\nOthers", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "address", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Address", + "length": 0, + "no_copy": 0, + "options": "Address", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "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-13 08:43:13.896939", + "modified_by": "Administrator", + "module": "App1", + "name": "Insurance Company", + "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": "System Manager", + "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 +} \ No newline at end of file diff --git a/app1/app1/doctype/insurance_company/insurance_company.py b/app1/app1/doctype/insurance_company/insurance_company.py new file mode 100644 index 0000000..e2c08e1 --- /dev/null +++ b/app1/app1/doctype/insurance_company/insurance_company.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class InsuranceCompany(Document): + pass diff --git a/app1/app1/doctype/insurance_company/test_insurance_company.js b/app1/app1/doctype/insurance_company/test_insurance_company.js new file mode 100644 index 0000000..f6983b7 --- /dev/null +++ b/app1/app1/doctype/insurance_company/test_insurance_company.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: Insurance Company", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Insurance Company + () => frappe.tests.make('Insurance Company', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/app1/app1/doctype/insurance_company/test_insurance_company.py b/app1/app1/doctype/insurance_company/test_insurance_company.py new file mode 100644 index 0000000..cb1542d --- /dev/null +++ b/app1/app1/doctype/insurance_company/test_insurance_company.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestInsuranceCompany(unittest.TestCase): + pass diff --git a/app1/app1/doctype/service_details/__init__.py b/app1/app1/doctype/service_details/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app1/app1/doctype/service_details/service_details.json b/app1/app1/doctype/service_details/service_details.json new file mode 100644 index 0000000..b5e1ff8 --- /dev/null +++ b/app1/app1/doctype/service_details/service_details.json @@ -0,0 +1,195 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-11-15 13:25:45.943323", + "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": "service_item", + "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": "Service Item", + "length": 0, + "no_copy": 0, + "options": "Service 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "Type", + "length": 0, + "no_copy": 0, + "options": "Inspection\nService\nChange", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "expense", + "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": "Expense", + "length": 0, + "no_copy": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "currency", + "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": "Currency", + "length": 0, + "no_copy": 0, + "options": "Currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "other", + "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": "Other", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 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-11-20 23:02:05.207565", + "modified_by": "Administrator", + "module": "App1", + "name": "Service Details", + "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 +} \ No newline at end of file diff --git a/app1/app1/doctype/service_details/service_details.py b/app1/app1/doctype/service_details/service_details.py new file mode 100644 index 0000000..ccd100b --- /dev/null +++ b/app1/app1/doctype/service_details/service_details.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class ServiceDetails(Document): + pass diff --git a/app1/app1/doctype/service_plan_template/service_plan_template.json b/app1/app1/doctype/service_plan_template/service_plan_template.json index 5b8fc16..23073eb 100644 --- a/app1/app1/doctype/service_plan_template/service_plan_template.json +++ b/app1/app1/doctype/service_plan_template/service_plan_template.json @@ -3,6 +3,7 @@ "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, + "autoname": "", "beta": 0, "creation": "2018-11-11 22:21:49.140257", "custom": 0, @@ -19,18 +20,18 @@ "collapsible": 0, "columns": 0, "fieldname": "service_item", - "fieldtype": "Select", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 1, + "in_list_view": 0, "in_standard_filter": 0, "label": "Service Item", "length": 0, "no_copy": 0, - "options": "Select Item\nOil Change\nOil Filer Change\nTransmission Fluid Change\nEngine Oil Change\nChange Spark Plugs\nCheck Fuel filter\nCheck air filter\nCheck Brake Pads/Discs\nCheck Power steering Fluid\nCheck Transmission fluid level\nCheck Tyre Condition", + "options": "Service Type", "permlevel": 0, "precision": "", "print_hide": 0, @@ -38,7 +39,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -61,7 +62,7 @@ "label": "Type", "length": 0, "no_copy": 0, - "options": "Select Type\nInspection\nService\nChange/Repairs", + "options": "Inspection\nService\nChange/Repairs", "permlevel": 0, "precision": "", "print_hide": 0, @@ -191,7 +192,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -237,7 +238,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-11-12 11:16:43.283445", + "modified": "2018-11-19 17:29:31.948177", "modified_by": "Administrator", "module": "App1", "name": "Service Plan Template", diff --git a/app1/app1/doctype/service_type/service_type.py b/app1/app1/doctype/service_type/service_type.py index 6eee157..440d178 100644 --- a/app1/app1/doctype/service_type/service_type.py +++ b/app1/app1/doctype/service_type/service_type.py @@ -8,3 +8,7 @@ class ServiceType(Document): pass + + def autoname(self): + self.name=self.service_item + return diff --git a/app1/app1/doctype/trip_log/__init__.py b/app1/app1/doctype/trip_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app1/app1/doctype/trip_log/test_trip_log.js b/app1/app1/doctype/trip_log/test_trip_log.js new file mode 100644 index 0000000..074dbd6 --- /dev/null +++ b/app1/app1/doctype/trip_log/test_trip_log.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: Trip Log", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Trip Log + () => frappe.tests.make('Trip Log', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/app1/app1/doctype/trip_log/test_trip_log.py b/app1/app1/doctype/trip_log/test_trip_log.py new file mode 100644 index 0000000..c0e43fc --- /dev/null +++ b/app1/app1/doctype/trip_log/test_trip_log.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestTripLog(unittest.TestCase): + pass diff --git a/app1/app1/doctype/trip_log/trip_log.js b/app1/app1/doctype/trip_log/trip_log.js new file mode 100644 index 0000000..d09384d --- /dev/null +++ b/app1/app1/doctype/trip_log/trip_log.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Trip Log', { + refresh: function(frm) { + + } +}); diff --git a/app1/app1/doctype/trip_log/trip_log.json b/app1/app1/doctype/trip_log/trip_log.json new file mode 100644 index 0000000..4782210 --- /dev/null +++ b/app1/app1/doctype/trip_log/trip_log.json @@ -0,0 +1,366 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-11-14 11:04:48.402400", + "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": "vehicle", + "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": "Vehicle", + "length": 0, + "no_copy": 0, + "options": "Vehicle", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 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": 1, + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "driver", + "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": "Driver", + "length": 0, + "no_copy": 0, + "options": "Driver", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "trip_started", + "fieldtype": "Datetime", + "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": "Trip Started", + "length": 0, + "no_copy": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "trip_ended", + "fieldtype": "Datetime", + "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": "Trip Ended", + "length": 0, + "no_copy": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "incident", + "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": "Incident", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "incident_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": "Incident 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "vehicle_request", + "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": "Vehicle Request", + "length": 0, + "no_copy": 0, + "options": "Vehicle Request", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mileage", + "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": "Mileage", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mileage_uom", + "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": "Mileage UOM", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 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-14 11:05:05.680293", + "modified_by": "Administrator", + "module": "App1", + "name": "Trip Log", + "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": "System Manager", + "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 +} \ No newline at end of file diff --git a/app1/app1/doctype/trip_log/trip_log.py b/app1/app1/doctype/trip_log/trip_log.py new file mode 100644 index 0000000..3d31b94 --- /dev/null +++ b/app1/app1/doctype/trip_log/trip_log.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class TripLog(Document): + pass diff --git a/app1/app1/doctype/vehicle_location/vehicle_location.json b/app1/app1/doctype/vehicle_location/vehicle_location.json index 5133b94..db397c3 100644 --- a/app1/app1/doctype/vehicle_location/vehicle_location.json +++ b/app1/app1/doctype/vehicle_location/vehicle_location.json @@ -139,7 +139,7 @@ "collapsible": 0, "columns": 0, "fieldname": "address", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -150,6 +150,7 @@ "label": "Address", "length": 0, "no_copy": 0, + "options": "Address", "permlevel": 0, "precision": "", "print_hide": 0, @@ -173,7 +174,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-11 15:39:58.484228", + "modified": "2018-11-13 08:16:51.564649", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Location", diff --git a/app1/app1/doctype/vehicle_model/vehicle_model.py b/app1/app1/doctype/vehicle_model/vehicle_model.py index 31a9457..7a84f98 100644 --- a/app1/app1/doctype/vehicle_model/vehicle_model.py +++ b/app1/app1/doctype/vehicle_model/vehicle_model.py @@ -8,3 +8,10 @@ class VehicleModel(Document): pass + def autoname(self): + """This function changes the default naming series of the vehicle model when it is created""" + name_1=self.name1 + year_1=self.model_year + self.vmn=name_1+"-"+str(year_1) + self.name=self.vmn + return self.name diff --git a/app1/app1/doctype/vehicle_request/vehicle_request.json b/app1/app1/doctype/vehicle_request/vehicle_request.json index 5d0e038..adadc5a 100644 --- a/app1/app1/doctype/vehicle_request/vehicle_request.json +++ b/app1/app1/doctype/vehicle_request/vehicle_request.json @@ -50,7 +50,7 @@ "collapsible": 0, "columns": 0, "fieldname": "request_type", - "fieldtype": "Data", + "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -61,7 +61,7 @@ "label": "Request Type", "length": 0, "no_copy": 0, - "options": "Vehicle Request Reason", + "options": "Official\nPrivate\nOther", "permlevel": 0, "precision": "", "print_hide": 0, @@ -99,7 +99,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -129,7 +129,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -159,7 +159,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -189,7 +189,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -232,8 +232,8 @@ "collapsible": 0, "columns": 0, "fieldname": "vehicle_assigned", - "fieldtype": "Data", - "hidden": 0, + "fieldtype": "Link", + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -243,8 +243,8 @@ "label": "Vehicle Assigned", "length": 0, "no_copy": 0, - "options": " ", - "permlevel": 0, + "options": "Vehicle", + "permlevel": 1, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, @@ -263,7 +263,7 @@ "collapsible": 0, "columns": 0, "fieldname": "driver_required", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -274,6 +274,7 @@ "label": "Driver Required", "length": 0, "no_copy": 0, + "options": "Driver", "permlevel": 0, "precision": "", "print_hide": 0, @@ -293,8 +294,8 @@ "collapsible": 0, "columns": 0, "fieldname": "driver_assigned", - "fieldtype": "Data", - "hidden": 0, + "fieldtype": "Link", + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -304,7 +305,8 @@ "label": "Driver Assigned", "length": 0, "no_copy": 0, - "permlevel": 0, + "options": "Driver", + "permlevel": 1, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, @@ -322,6 +324,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "Requested", "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -335,7 +338,7 @@ "length": 0, "no_copy": 0, "options": "Requested\nRejected\nApproved\nIn Progress\nReturned", - "permlevel": 0, + "permlevel": 1, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, @@ -354,8 +357,8 @@ "collapsible": 0, "columns": 0, "fieldname": "event", - "fieldtype": "Data", - "hidden": 0, + "fieldtype": "Link", + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -365,7 +368,8 @@ "label": "Event", "length": 0, "no_copy": 0, - "permlevel": 0, + "options": "Calendar View", + "permlevel": 1, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, @@ -388,7 +392,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-11 20:27:21.162141", + "modified": "2018-11-14 09:59:09.804019", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Request", diff --git a/app1/app1/doctype/vehicle_return_application/__init__.py b/app1/app1/doctype/vehicle_return_application/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.js b/app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.js new file mode 100644 index 0000000..9670837 --- /dev/null +++ b/app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.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: Vehicle Return Application", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Vehicle Return Application + () => frappe.tests.make('Vehicle Return Application', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.py b/app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.py new file mode 100644 index 0000000..f570a7a --- /dev/null +++ b/app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestVehicleReturnApplication(unittest.TestCase): + pass diff --git a/app1/app1/doctype/vehicle_return_application/vehicle_return_application.js b/app1/app1/doctype/vehicle_return_application/vehicle_return_application.js new file mode 100644 index 0000000..6cd7207 --- /dev/null +++ b/app1/app1/doctype/vehicle_return_application/vehicle_return_application.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Vehicle Return Application', { + refresh: function(frm) { + + } +}); diff --git a/app1/app1/doctype/vehicle_return_application/vehicle_return_application.json b/app1/app1/doctype/vehicle_return_application/vehicle_return_application.json new file mode 100644 index 0000000..bb29a5d --- /dev/null +++ b/app1/app1/doctype/vehicle_return_application/vehicle_return_application.json @@ -0,0 +1,186 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-11-14 14:05:41.648045", + "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": "employee", + "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": "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "driver", + "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": "Driver", + "length": 0, + "no_copy": 0, + "options": "Driver", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "vehicle", + "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": "Vehicle", + "length": 0, + "no_copy": 0, + "options": "Vehicle", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "trip_log", + "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": "Trip Log", + "length": 0, + "no_copy": 0, + "options": "Trip Log", + "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, + "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-14 14:05:47.821486", + "modified_by": "Administrator", + "module": "App1", + "name": "Vehicle Return Application", + "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": "System Manager", + "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 +} \ No newline at end of file diff --git a/app1/app1/doctype/vehicle_return_application/vehicle_return_application.py b/app1/app1/doctype/vehicle_return_application/vehicle_return_application.py new file mode 100644 index 0000000..b8ee939 --- /dev/null +++ b/app1/app1/doctype/vehicle_return_application/vehicle_return_application.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class VehicleReturnApplication(Document): + pass diff --git a/app1/app1/doctype/vehicle_servicing_log/__init__.py b/app1/app1/doctype/vehicle_servicing_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.js b/app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.js new file mode 100644 index 0000000..74d8ab5 --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.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: Vehicle Servicing Log", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Vehicle Servicing Log + () => frappe.tests.make('Vehicle Servicing Log', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.py b/app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.py new file mode 100644 index 0000000..c33c164 --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestVehicleServicingLog(unittest.TestCase): + pass diff --git a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js new file mode 100644 index 0000000..f241373 --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js @@ -0,0 +1,44 @@ +// Copyright (c) 2018, frappe and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Vehicle Servicing Log","vehicle", function(frm){ +// frappe.model.with_doc("Service Plan Template",frm.doc.vehicle,function(){ +// var tabletransfer=frappe.model.get_doc("Service Plan Template", frm.doc.vehicle) + // $.each(tabletransfer.service_plan_,function(index,row) + // { + // d=frm.add_child("Service Details") + // d.service_item=row.service_item + // d.type=row.doctype + // cur_frm.refresh_field("service_details") + // }) + +frappe.ui.form.on("Vehicle Servicing Log",{ + vehicle:function (frm) { + var vehicle_name=cur_frm.doc.vehicle + if(vehicle_name==" "){ + vehicle_name="blank" + } + frappe.call({ + args:{vehicle_name:vehicle_name}, + method:'app1.utils.hr.getServicePlan', + callback:function(response){ + for(var o=0;o<=response.message[0].length-1;o++){ + frm.add_child("service_details") + cur_frm.doc.service_details[o].service_item=response.message[0][o] + cur_frm.doc.service_details[o].type=response.message[1][o] + cur_frm.refresh_field("service_details") + console.log(response.message[1]); + } + } + }) + }, +}) + +// frappe.ui.form.on("Vehicle Service Template", "refresh", function(frm) { +// frappe.ui.form.on("Service Details", { +// "service_item": function(frm) { +// frm.add_fetch("[CHILD_TABLE_LINK FIELD]", "[SOURCE_CUSTOM_FIELD2]", "[TARGET_CUSTOM_FIELD2]"); +// } +// }); +// +// }); diff --git a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json new file mode 100644 index 0000000..3ac77dc --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json @@ -0,0 +1,304 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-11-15 13:32:50.985049", + "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": "vehicle", + "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": "Vehicle", + "length": 0, + "no_copy": 0, + "options": "Vehicle", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "service_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": "Service 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "return_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": "Return 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, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mileage", + "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": "Mileage", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mileage_uom", + "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": "Mileage UOM", + "length": 0, + "no_copy": 0, + "options": "UOM", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "service_details", + "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": "Service Details", + "length": 0, + "no_copy": 0, + "options": "Service Details", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 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-20 10:59:05.758867", + "modified_by": "Administrator", + "module": "App1", + "name": "Vehicle Servicing Log", + "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": "System Manager", + "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 +} \ No newline at end of file diff --git a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py new file mode 100644 index 0000000..ee7accb --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, frappe and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class VehicleServicingLog(Document): + pass + + def autoname(self): + vehicle_=frappe.get_doc("Vehicle",self.vehicle) + license_plate=vehicle_.license_plate + start_date=self.service_date + name2=start_date+"_"+license_plate + self.name=name2 + return + + # @frappe.whitelist() + # def getServicePlan(vn): + # current_vehicle=frappe.get_doc("Vehicle",vn) + # vehicle_model=current_vehicle.vehicle_model + # service_plan=vehicle_model.service_plan_ + # return service_plan + +# @frappe.whitelist() +# def setChildTable(name): +# print("Ok") +# return("linked") diff --git a/app1/app1/doctype/vehicle_trip_log/vehicle_trip_log.json b/app1/app1/doctype/vehicle_trip_log/vehicle_trip_log.json index 1e658ac..34de2f4 100644 --- a/app1/app1/doctype/vehicle_trip_log/vehicle_trip_log.json +++ b/app1/app1/doctype/vehicle_trip_log/vehicle_trip_log.json @@ -81,7 +81,7 @@ "collapsible": 0, "columns": 0, "fieldname": "driver", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -92,6 +92,7 @@ "label": "Driver", "length": 0, "no_copy": 0, + "options": "Driver", "permlevel": 0, "precision": "", "print_hide": 0, @@ -326,7 +327,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-11 21:15:05.136042", + "modified": "2018-11-15 22:55:45.976887", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Trip Log", diff --git a/app1/assets/app1/js/app1.js b/app1/assets/app1/js/app1.js new file mode 100644 index 0000000..e69de29 diff --git a/app1/config/hr.py b/app1/config/hr.py index 4035e90..954450c 100644 --- a/app1/config/hr.py +++ b/app1/config/hr.py @@ -10,6 +10,27 @@ def get_data(): "type": "doctype", "name": "Vehicle Request" }, + { + "type": "doctype", + "name": "Vehicle Make" + }, + { + "type": "doctype", + "name": "Vehicle Model" + }, + { + "type": "doctype", + "name": "Vehicle Servicing Log" + }, + { + "type": "doctype", + "name": "Vehicle Trip Log" + }, + { + "type": "doctype", + "name": "Vehicle Type" + }, + ] }, diff --git a/app1/hooks.py b/app1/hooks.py index 97b0b9a..36f2a90 100644 --- a/app1/hooks.py +++ b/app1/hooks.py @@ -10,13 +10,12 @@ app_color = "grey" app_email = "ebukaakeru@gmail.com" app_license = "MIT" - # Includes in # ------------------ - # include js, css files in header of desk.html # app_include_css = "/assets/app1/css/app1.css" -# app_include_js = "/assets/app1/js/app1.js" + + #app_include_js = "/assets/app1/js/app1.js" # include js, css files in header of web template # web_include_css = "/assets/app1/css/app1.css" @@ -29,6 +28,9 @@ doctype_js = { "Salary Slip" : "public/js/custom_hr.js", +"Vehicle" : "public/js/filter_model.js", +#"Vehicle" : "public/js/add_button.js", + "Vehicle Servicing Log": "public/js/calculate_total.js", } # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} @@ -64,7 +66,7 @@ # ------------------ # See frappe.core.notifications.get_notification_config -# notification_config = "app1.notifications.get_notification_config" +notification_config = "app1.notifications.get_notification_config" # Permissions # ----------- @@ -86,9 +88,16 @@ "Salary Slip": { "validate": "app1.utils.hr.calculate_base_amount" }, - "Vehicle Model": { - "before_save":"app1.utils.hr.change_auto_name" - } + # "Vehicle Model": { + # "before_save":"app1.utils.hr.change_auto_name" + # }, + "Vehicle":{ + "before_save":"app1.utils.hr.setVehicleName" + }, + # "Vehicle Servicing Log":{ + # "vehicle":"app1.app1.doctype.vehicle_servicing_log.vehicle_servicing_log.setChildTable" + # } + } @@ -103,11 +112,10 @@ # Scheduled Tasks # --------------- - -# scheduler_events = { -# "all": [ -# "app1.tasks.all" -# ], +scheduler_events = { + "all": [ + "app1.tasks.all.setter" + ] # "daily": [ # "app1.tasks.daily" # ], @@ -120,7 +128,7 @@ # "monthly": [ # "app1.tasks.monthly" # ] -# } + } # Testing # ------- diff --git a/app1/notifications.py b/app1/notifications.py new file mode 100644 index 0000000..01d6b60 --- /dev/null +++ b/app1/notifications.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals +import frappe + + +def get_notification_config(): + return { "for_doctype": + { + "Vehicle Request": {"status": "Requested"}, + } + } diff --git a/app1/public/build.json b/app1/public/build.json new file mode 100644 index 0000000..93a3cda --- /dev/null +++ b/app1/public/build.json @@ -0,0 +1,5 @@ +{ + "assets/app1/js/app1.js": [ + "public/js/templates/Includes/address_.html" + ] +} diff --git a/app1/public/js/add_button.js b/app1/public/js/add_button.js new file mode 100644 index 0000000..059e719 --- /dev/null +++ b/app1/public/js/add_button.js @@ -0,0 +1,11 @@ +//frappe.ui.form.on('Vehicle', { +// refresh: function(frm){ +// var name__=cur_frm.doc.name +// cur_frm.add_custom_button(("Vehicle Servicing Log"),function(ev){ +// frappe.set_route("List","Vehicle Servicing Log",{"vehicle":name__}) +// },("Show")) +// cur_frm.add_custom_button(("Vehicle Trip Log"),function(ev){ +// frappe.set_route("List","Vehicle Trip Log",{"vehicle":name__}) +// },("Show")) +// } +//}) diff --git a/app1/public/js/autoname.js b/app1/public/js/autoname.js deleted file mode 100644 index 133eb78..0000000 --- a/app1/public/js/autoname.js +++ /dev/null @@ -1,12 +0,0 @@ -frappe.ui.form.on('Vehicle Model',{ - refresh:function(frm){ - console.log("Working") - }, -cur_frm.on_save:function(frm){ - var make_=frm.doc.vehicle_make - var name_=frm.doc.name1 - var year_=frm.doc.model_year.toString() - var uniqueID=make_.substring(0,3)+name_.substring(0,3)+year_.substring(0,2) - cur_frm.set_value("vmn",uniqueID) - } -}) diff --git a/app1/public/js/calculate_total.js b/app1/public/js/calculate_total.js new file mode 100644 index 0000000..99bd9d3 --- /dev/null +++ b/app1/public/js/calculate_total.js @@ -0,0 +1,37 @@ +frappe.ui.form.on("Vehicle Servicing Log",{ +before_save:function(frm){ + console.log("Calculating"); + var doc_ser_det=cur_frm.doc.service_details + var service_details_arr=Object.values(doc_ser_det) + var sum=0; + for(var i in service_details_arr){ + var tempsum=service_details_arr[i].expense + sum=sum+tempsum + } + cur_frm.set_value("total_expenses",sum) + console.log("Finished Calculation"); + frappe.call({ + "method": "frappe.client.set_value", + "args": { + "doctype": "Vehicle", + "name": cur_frm.doc.vehicle, + "fieldname": { + // "last_odometer":cur_frm.doc.odometer, + "date_of_last_service":cur_frm.doc.service_date + //console.log(cur_frm.doc.service_date); + + } + } + }); + console.log("ran to the end"); + // frappe.call({ + // "method":"frappe.client.set_value", + // "args":{ + // "doctype":"Vehicle", + // "name":cur_frm.doc.vehicle, + // "fieldname":"date_of_last_service", + // "value":cur_frm.doc.service_date + // } + // }) + } + }) diff --git a/app1/public/js/filter_model.js b/app1/public/js/filter_model.js new file mode 100644 index 0000000..65f7705 --- /dev/null +++ b/app1/public/js/filter_model.js @@ -0,0 +1,29 @@ +frappe.ui.form.on('Vehicle',{ + vehicle_make: function(frm){ + //console.log("At the beginning") + cur_frm.fields_dict["vehicle_model"].get_query=function(doc){ + return { + filters:{ + "vehicle_make":frm.doc.vehicle_make, + //console.log("Finished") + } + } + } + }, + refresh: function(frm){ + var name__=cur_frm.doc.name +cur_frm.add_custom_button(("Vehicle Servicing Log"),function(ev){ + frappe.set_route("List","Vehicle Servicing Log",{"vehicle":name__}) +},("Show")) +cur_frm.add_custom_button(("Vehicle Trip Log"),function(ev){ + frappe.set_route("List","Vehicle Trip Log",{"vehicle":name__}) +},("Show")) +cur_frm.add_custom_button(("Vehicle Servicing Log"),function(ev){ + frappe.new_doc("Vehicle Servicing Log") +},("Create")) +cur_frm.add_custom_button(("Vehicle Trip Log"),function(ev){ +doc=frappe.new_doc("Vehicle Trip Log") +},("Create")) + +} +}) diff --git a/app1/tasks/__init__.py b/app1/tasks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app1/tasks/all.py b/app1/tasks/all.py new file mode 100644 index 0000000..49ee7f1 --- /dev/null +++ b/app1/tasks/all.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe +from frappe.utils import date_diff, nowdate + + +def setter(): + print("Starting Operation") + doc=frappe.get_doc("Vehicle","Pathfinder-2016_YI-896-JJN") + doc.odometer_2=1414 + doc.save() + print("Done") diff --git a/app1/templates/includes/address_.html b/app1/templates/includes/address_.html new file mode 100644 index 0000000..bfc035a --- /dev/null +++ b/app1/templates/includes/address_.html @@ -0,0 +1,14 @@ +
+ +
+
+ {{ doc.address_title }} +
+
{{ _(doc.address_type) }}
+
{{ doc.city }}
+
+ {{ frappe.get_doc(doc).get_display() }} +
+
+
+
diff --git a/app1/utils/hr.py b/app1/utils/hr.py index 1fed0a1..0ca5011 100644 --- a/app1/utils/hr.py +++ b/app1/utils/hr.py @@ -14,6 +14,7 @@ def get_days_present(employee,cal_end,cal_start): return package def calculate_base_amount(salary_doc, event): + """This calculates the amount an employee earns according to his/her attendance in a month""" query = "Select base from `tabSalary Structure Employee` where employee='%s'" %salary_doc.employee query2="SELECT COUNT(*)from tabAttendance where employee_name='%s' and status='Absent' and attendance_date>='%s' and attendance_date<='%s'" %(salary_doc.employee_name,salary_doc.start_date,salary_doc.end_date) sal2=frappe.db.sql(query2) @@ -27,12 +28,55 @@ def calculate_base_amount(salary_doc, event): net_deduc=daily_pay*sal2[0][0] salary_doc.salary_for_month=monthly_sal-net_deduc return +# +# def change_auto_name(self,ev): +# # """This function changes the default naming series of the vehicle model when it is created""" + # name_1=self.name1 + # year_1=self.model_year + # self.vmn=name_1+"-"+str(year_1) + # self.name=self.vmn + # return -def change_auto_name(self,ev): - """This function changes the default naming series of the vehicle model when it is created""" - name_1=self.name1 - model_=self.vehicle_make - year_1=self.model_year - self.vmn=model_+name_1+str(year_1) - self.name=self.vmn - return +def setVehicleName(self,ev): + """This function concantenates the licence plate and model of every vehicle created""" + licence_plate=self.license_plate + model=self.vehicle_model + self.name=model+"_"+licence_plate + #frappe.msgprint("Name set") + return self.name + +# @frappe.whitelist() +# def getVehicleModel(make_): +# """This returns the model/models of a vehicle when the make is selected""" +# query="SELECT * from `tabVehicle Model` where vehicle_make='%s'" %make_ +# result1=frappe.db.sql(query,as_dict=1) +# result2=[] +# for i in result1: +# result2.append(i.name1)#name1 is the column name of the model in the database +# result3=list(filter(lambda x:x!=None and x!=make_,result2)) +# return(result3) + +# def setMileage(self,ev): +# #This method sets the mileage value from a car after being serviced. +# vehicle_=frappe.get_doc("Vehicle",self.vehicle) +# vehicle_.last_odometer=self.odometer +# vehicle_.save() +# return + +@frappe.whitelist() +def getServicePlan(vehicle_name): + current_vehicle=frappe.get_doc("Vehicle",vehicle_name) + vehicle_model=current_vehicle.vehicle_model + query1="select service_item,type,name from `tabService Plan Template` where parent='%s' " %vehicle_model + result_set=frappe.db.sql(query1,as_dict=1) + service_item_list=[] + service_type_list=[] + name_list=[] + for i in result_set: + service_item_list.append(i.service_item) + for i in result_set: + service_type_list.append(i.type) + container=[] + container.append(service_item_list) + container.append(service_type_list) + return(container) diff --git a/cpfa/__init__.py b/cpfa/__init__.py new file mode 100644 index 0000000..95d1338 --- /dev/null +++ b/cpfa/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +__version__ = '0.0.1' + diff --git a/cpfa/config/__init__.py b/cpfa/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/config/desktop.py b/cpfa/config/desktop.py new file mode 100644 index 0000000..9f6226a --- /dev/null +++ b/cpfa/config/desktop.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return [ + { + "module_name": "CPFA", + "color": "green", + "icon": "octicon octicon-file-directory", + "type": "module", + "label": "CPFA" + } + ] diff --git a/cpfa/config/docs.py b/cpfa/config/docs.py new file mode 100644 index 0000000..c5d20ff --- /dev/null +++ b/cpfa/config/docs.py @@ -0,0 +1,11 @@ +""" +Configuration for docs +""" + +# source_link = "https://github.com/[org_name]/cpfa" +# docs_base_url = "https://[org_name].github.io/cpfa" +# headline = "App that does everything" +# sub_heading = "Yes, you got that right the first time, everything" + +def get_context(context): + context.brand_html = "Total CPFA ERPNext" diff --git a/cpfa/config/integrations.py b/cpfa/config/integrations.py new file mode 100644 index 0000000..c25b208 --- /dev/null +++ b/cpfa/config/integrations.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return [ + { + "label": _("Backup"), + "icon": "fa fa-star", + "items": [ + { + "type": "doctype", + "name": "Azure Storage Backup Settings", + "description": _("Azure Storage Backup Settings"), + }, + ] + } + ] diff --git a/cpfa/config/setup.py b/cpfa/config/setup.py new file mode 100644 index 0000000..a0397a2 --- /dev/null +++ b/cpfa/config/setup.py @@ -0,0 +1,16 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return [ + { + "label": _("Workflow"), + "icon": "fa fa-random", + "items": [ + { + "type": "doctype", + "name": "Workflow Notification", + }, + ] + }, + ] diff --git a/cpfa/cpfa/__init__.py b/cpfa/cpfa/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/cpfa/doctype/__init__.py b/cpfa/cpfa/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/cpfa/doctype/azure_storage_backup_settings/__init__.py b/cpfa/cpfa/doctype/azure_storage_backup_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.js b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.js new file mode 100644 index 0000000..4c9423c --- /dev/null +++ b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.js @@ -0,0 +1,28 @@ +// Copyright (c) 2018, Manqala and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Azure Storage Backup Settings', { + refresh: function(frm) { + frm.clear_custom_buttons(); + frm.events.take_backup(frm); + }, + + take_backup: function(frm) { + if (frm.doc.account_key && frm.doc.account_name && frm.doc.container_name) { + frm.add_custom_button(__("Take Backup Now"), function(){ + frm.dashboard.set_headline_alert("Azure Storage Backup Started!"); + frappe.call({ + method: "cpfa.cpfa.doctype.azure_storage_backup_settings.azure_storage_backup_settings.take_backups_azure", + callback: function(r) { + if(!r.exc) { + frappe.msgprint(__("Azure Storage Backup complete!")); + frm.dashboard.clear_headline(); + } else { + frm.dashboard.clear_headline(); + } + } + }); + }).addClass("btn-primary"); + } + } +}); diff --git a/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.json b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.json new file mode 100644 index 0000000..8782a17 --- /dev/null +++ b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.json @@ -0,0 +1,304 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-11-13 13:31:22.734999", + "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": "enabled", + "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": "Enable Automatic Backup", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_email", + "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": "Send Notifications To", + "length": 0, + "no_copy": 0, + "options": "Email", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "frequency", + "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": "Backup Frequency", + "length": 0, + "no_copy": 0, + "options": "Daily\nWeekly\nMonthly\nNone", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "account_key", + "fieldtype": "Password", + "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 Key", + "length": 0, + "no_copy": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "container_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": "Container 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "backup_limit", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Backup Limit", + "length": 0, + "no_copy": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "remove_local_backup", + "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": "Remove Local Backup", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 1, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-15 22:14:52.422481", + "modified_by": "Administrator", + "module": "CPFA", + "name": "Azure Storage Backup 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, + "write": 1 + } + ], + "quick_entry": 0, + "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 +} \ No newline at end of file diff --git a/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.py b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.py new file mode 100644 index 0000000..28fde87 --- /dev/null +++ b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Manqala and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import os +import os.path +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import cint, split_emails +from frappe.utils.background_jobs import enqueue +from azure.storage.blob import BlockBlobService +from azure.storage._error import AzureHttpError, AzureException + + +class AzureStorageBackupSettings(Document): + + def validate(self): + block_blob_service = BlockBlobService( + account_name=self.account_name, + account_key=self.get_password('account_key'), + ) + + container_name = str(self.container_name) + + try: + containers_generator = block_blob_service.list_containers() + except (AzureHttpError,AzureException): + frappe.throw(_("Invalid Access Key ID or Secret Access Key.")) + + try: + block_blob_service.create_container(container_name=container_name) + except (AzureHttpError, AzureException): + frappe.throw(_("Unable to create bucket: {0}. Change it to a more unique name.").format(container_name)) + + +@frappe.whitelist() +def take_backup(): + "Enqueue longjob for taking backup to Azure Storage" + enqueue("cpfa.cpfa.doctype.azure_storage_backup_settings.azure_storage_backup_settings.take_backups_azure", queue='long', timeout=1500) + frappe.msgprint(_("Queued for backup. It may take a few minutes to an hour.")) + + +def take_backups_daily(): + take_backups_if("Daily") + + +def take_backups_weekly(): + take_backups_if("Weekly") + + +def take_backups_monthly(): + take_backups_if("Monthly") + + +def take_backups_if(freq): + if cint(frappe.db.get_value("Azure Storage Backup Settings", None, "enabled")): + if frappe.db.get_value("Azure Storage Backup Settings", None, "frequency") == freq: + take_backups_azure() + + +@frappe.whitelist() +def take_backups_azure(): + try: + backup_to_azure() + send_email(True, "Azure Storage Backup Settings") + except Exception: + error_message = frappe.get_traceback() + frappe.errprint(error_message) + send_email(False, "Azure Storage Backup Settings", error_message) + + +def send_email(success, service_name, error_status=None): + if success: + subject = "Backup Upload Successful" + message = """

Backup Uploaded Successfully!

Hi there, this is just to inform you + that your backup was successfully uploaded to your Azure Storage container. So relax!

""" + + else: + subject = "[Warning] Backup Upload Failed" + message = """

Backup Upload Failed!

Oops, your automated backup to Azure Storage failed. +

Error message: %s

Please contact your system manager + for more information.

""" % error_status + + if not frappe.db: + frappe.connect() + + if frappe.db.get_value("Azure Storage Backup Settings", None, "notification_email"): + recipients = split_emails(frappe.db.get_value("Azure Storage Backup Settings", None, "notification_email")) + frappe.sendmail(recipients=recipients, subject=subject, message=message) + + +def backup_to_azure(): + from frappe.utils.backups import new_backup + from frappe.utils import get_backups_path + + doc = frappe.get_single("Azure Storage Backup Settings") + container = doc.container_name + + block_blob_service = BlockBlobService( + account_name=doc.account_name, + account_key=doc.get_password('account_key'), + ) + + backup = new_backup(ignore_files=False, backup_path_db=None, + backup_path_files=None, backup_path_private_files=None, force=True) + db_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) + files_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_files)) + private_files = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_private_files)) + folder = os.path.basename(db_filename)[:15] + '/' + # for adding datetime to folder name + + upload_file_to_azure(db_filename, folder, block_blob_service, container) + upload_file_to_azure(private_files, folder, block_blob_service, container) + upload_file_to_azure(files_filename, folder, block_blob_service, container) + delete_old_backups(doc.backup_limit, container) + if cint(doc.remove_local_backup): + os.remove(db_filename) + os.remove(private_files) + os.remove(files_filename) + +def upload_file_to_azure(filename, folder, block_blob_service, container): + + destpath = os.path.join(folder, os.path.basename(filename)) + try: + print "Uploading file:", filename + block_blob_service.create_blob_from_path(container, destpath, filename) + + except Exception as e: + print "Error uploading: %s" % (e) + + +def delete_old_backups(limit, container): + from collections import defaultdict + + root = 'root_dir' + all_backups = defaultdict(set) + doc = frappe.get_single("Azure Storage Backup Settings") + backup_limit = int(limit) + + block_blob_service = BlockBlobService( + account_name=doc.account_name, + account_key=doc.get_password('account_key'), + ) + #bucket = s3.Bucket(bucket) + #objects = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Delimiter='/') + for blob in block_blob_service.list_blobs(doc.container_name): + blob_name = blob.name.split('/',1) + if len(blob_name) > 1: + all_backups[blob_name[0]].add(blob_name[1]) + continue + all_backups[root].add(blob_name[0]) + + backup_dirs = [i for i in all_backups.keys() if i != root] + oldest_backup = sorted(backup_dirs)[0] + + if len(backup_dirs) > backup_limit: + print "Deleting Backup: {0}".format(oldest_backup) + for blob in all_backups[oldest_backup]: + # delete all keys that are inside the oldest_backup + blob_name = '{}/{}'.format(oldest_backup, blob) + block_blob_service.delete_blob(doc.container_name, blob_name) diff --git a/cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.js b/cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.js new file mode 100644 index 0000000..9275f5b --- /dev/null +++ b/cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.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: Azure Storage Backup Settings", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Azure Storage Backup Settings + () => frappe.tests.make('Azure Storage Backup Settings', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.py b/cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.py new file mode 100644 index 0000000..101b921 --- /dev/null +++ b/cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Manqala and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestAzureStorageBackupSettings(unittest.TestCase): + pass diff --git a/cpfa/cpfa/doctype/workflow_notification/__init__.py b/cpfa/cpfa/doctype/workflow_notification/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.js b/cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.js new file mode 100644 index 0000000..6d1abf9 --- /dev/null +++ b/cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.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: Workflow Notification", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Workflow Notification + () => frappe.tests.make('Workflow Notification', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.py b/cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.py new file mode 100644 index 0000000..3a4e90f --- /dev/null +++ b/cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Manqala and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestWorkflowNotification(unittest.TestCase): + pass diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.js b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.js new file mode 100644 index 0000000..dc9b58b --- /dev/null +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Manqala and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Workflow Notification', { + refresh: function(frm) { + + } +}); diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json new file mode 100644 index 0000000..ff2ba68 --- /dev/null +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json @@ -0,0 +1,249 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "WFN.####", + "beta": 0, + "creation": "2018-11-09 15:55:56.210122", + "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": "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": 0, + "label": "Status", + "length": 0, + "no_copy": 0, + "options": "Open\nCompleted", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference_name", + "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": "Reference Name", + "length": 0, + "no_copy": 0, + "options": "reference_doctype", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Reference Doctype", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "user", + "fieldtype": "Link", + "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": "User", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "workflow_state", + "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": "Workflow State", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "completed_by", + "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": "Completed By", + "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, + "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-12 17:08:30.839842", + "modified_by": "Administrator", + "module": "CPFA", + "name": "Workflow Notification", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "reference_name", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py new file mode 100644 index 0000000..1393a2f --- /dev/null +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py @@ -0,0 +1,307 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Manqala and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from frappe.utils.background_jobs import enqueue +from frappe.utils import get_url, get_datetime +#from frappe.desk.form.utils import get_pdf_link +from cpfa.utils import get_pdf_link +from frappe.utils.verified_command import get_signed_params, verify_request +from frappe import _ +from frappe.model.workflow import get_workflow_name + +from cpfa.utils.workflow import apply_workflow, \ + has_approval_access, get_workflow_state_field, send_email_alert + +from frappe.desk.notifications import clear_doctype_notifications + + +class WorkflowNotification(Document): + pass + +def get_permission_query_conditions(user): + if not user: user = frappe.session.user + + if user == "Administrator": return "" + + return "(`tabWorkflow Notification`.`user`='{user}')".format(user=user) + +def has_permission(doctype=None, ptype="read", doc=None, verbose=False, user=None): + if user not in ['Administrator', doc.user]: + return False + +def process_workflow_actions(doc, state): + workflow = get_workflow_name(doc.get('doctype')) + if not workflow: + return + + if state == "on_trash": + clear_workflow_actions(doc.get('doctype'), doc.get('name')) + return + + if is_workflow_action_already_created(doc): + return + + clear_old_workflow_actions(doc) + update_completed_workflow_actions(doc) + clear_doctype_notifications(frappe.new_doc('Workflow Notification')) + + next_possible_transitions = get_next_possible_transitions(workflow, get_doc_workflow_state(doc)) + + if not next_possible_transitions: + return + + user_data_map = get_users_next_action_data(next_possible_transitions, doc) + + if not user_data_map: + return + + create_workflow_actions_for_users(user_data_map.keys(), doc) + + if send_email_alert(workflow): + enqueue(send_workflow_action_email, queue='short', users_data=list(user_data_map.values()), doc=doc) + +@frappe.whitelist(allow_guest=True) +def apply_action(action, doctype, docname, current_state, user=None, last_modified=None): + if not verify_request(): + return + + doc = frappe.get_doc(doctype, docname) + doc_workflow_state = get_doc_workflow_state(doc) + + if doc_workflow_state == current_state: + action_link = get_confirm_workflow_action_url(doc, action, user) + + if not last_modified or get_datetime(doc.modified) == get_datetime(last_modified): + return_action_confirmation_page(doc, action, action_link) + else: + return_action_confirmation_page(doc, action, action_link, alert_doc_change=True) + + else: + return_link_expired_page(doc, doc_workflow_state) + +@frappe.whitelist(allow_guest=True) +def confirm_action(doctype, docname, user, action): + if not verify_request(): + return + + logged_in_user = frappe.session.user + if logged_in_user == 'Guest' and user: + # to allow user to apply action without login + frappe.set_user(user) + + doc = frappe.get_doc(doctype, docname) + newdoc = apply_workflow(doc, action) + frappe.db.commit() + return_success_page(newdoc) + + # reset session user + frappe.set_user(logged_in_user) + +def return_success_page(doc): + frappe.respond_as_web_page(_("Success"), + _("{0}: {1} is set to state {2}".format( + doc.get('doctype'), + frappe.bold(doc.get('name')), + frappe.bold(get_doc_workflow_state(doc)) + )), indicator_color='green') + +def return_action_confirmation_page(doc, action, action_link, alert_doc_change=False): + template_params = { + 'title': doc.get('name'), + 'doctype': doc.get('doctype'), + 'docname': doc.get('name'), + 'action': action, + 'action_link': action_link, + 'alert_doc_change': alert_doc_change + } + + template_params['pdf_link'] = get_pdf_link(doc.get('doctype'), doc.get('name')) + + frappe.respond_as_web_page(None, None, + indicator_color="blue", + #template="confirm_workflow_action", + context=template_params) + +def return_link_expired_page(doc, doc_workflow_state): + frappe.respond_as_web_page(_("Link Expired"), + _("Document {0} has been set to state {1} by {2}" + .format( + frappe.bold(doc.get('name')), + frappe.bold(doc_workflow_state), + frappe.bold(frappe.get_value('User', doc.get("modified_by"), 'full_name')) + )), indicator_color='blue') + +def clear_old_workflow_actions(doc, user=None): + user = user if user else frappe.session.user + frappe.db.sql("""DELETE FROM `tabWorkflow Notification` + WHERE `reference_doctype`=%s AND `reference_name`=%s AND `user`!=%s AND `status`='Open'""", + (doc.get('doctype'), doc.get('name'), user)) + +def update_completed_workflow_actions(doc, user=None): + user = user if user else frappe.session.user + frappe.db.sql("""UPDATE `tabWorkflow Notification` SET `status`='Completed', `completed_by`=%s + WHERE `reference_doctype`=%s AND `reference_name`=%s AND `user`=%s AND `status`='Open'""", + (user, doc.get('doctype'), doc.get('name'), user)) + +def get_next_possible_transitions(workflow_name, state): + return frappe.get_all('Workflow Transition', + fields=['allowed', 'action', 'state', 'allow_self_approval'], + filters=[['parent', '=', workflow_name], + ['state', '=', state]]) + +def get_users_next_action_data(transitions, doc): + user_data_map = {} + for transition in transitions: + users = get_users_with_role(transition.allowed) + filtered_users = filter_allowed_users(users, doc, transition) + for user in filtered_users: + if not user_data_map.get(user): + user_data_map[user] = { + 'possible_actions': [], + 'email': frappe.db.get_value('User', user, 'email'), + } + + user_data_map[user].get('possible_actions').append({ + 'action_name': transition.action, + 'action_link': get_workflow_action_url(transition.action, doc, user) + }) + return user_data_map + + +def create_workflow_actions_for_users(users, doc): + for user in users: + frappe.get_doc({ + 'doctype': 'Workflow Notification', + 'reference_doctype': doc.get('doctype'), + 'reference_name': doc.get('name'), + 'workflow_state': get_doc_workflow_state(doc), + 'status': 'Open', + 'user': user + }).insert(ignore_permissions=True) + + frappe.db.commit() + + +def send_workflow_action_email(users_data, doc): + common_args = get_common_email_args(doc) + message = common_args.pop('message', None) + + for d in users_data: + email_args = { + 'recipients': [d.get('email')], + 'args': { + #'actions': d.get('possible_actions'), + 'message': message + }, + } + email_args.update(common_args) + enqueue(method=frappe.sendmail, queue='short', **email_args) + +def get_workflow_action_url(action, doc, user): + apply_action_method = "/api/method/cpfa.cpfa.doctype.workflow_notification.workflow_notification.apply_action" + + params = { + "doctype": doc.get('doctype'), + "docname": doc.get('name'), + "action": action, + "current_state": get_doc_workflow_state(doc), + "user": user, + "last_modified": doc.get('modified') + } + + return get_url(apply_action_method + "?" + get_signed_params(params)) + +def get_confirm_workflow_action_url(doc, action, user): + confirm_action_method = "/api/method/cpfa.cpfa.doctype.workflow_notification.workflow_notification.confirm_action" + + params = { + "action": action, + "doctype": doc.get('doctype'), + "docname": doc.get('name'), + "user": user + } + + return get_url(confirm_action_method + "?" + get_signed_params(params)) + + +def get_users_with_role(role): + return [p[0] for p in frappe.db.sql("""SELECT DISTINCT `tabUser`.`name` + FROM `tabHas Role`, `tabUser` + WHERE `tabHas Role`.`role`=%s + AND `tabUser`.`name`!='Administrator' + AND `tabHas Role`.`parent`=`tabUser`.`name` + AND `tabUser`.`enabled`=1""", role)] + +def is_workflow_action_already_created(doc): + return frappe.db.exists({ + 'doctype': 'Workflow Notification', + 'reference_doctype': doc.get('doctype'), + 'reference_name': doc.get('name'), + 'workflow_state': get_doc_workflow_state(doc) + }) + +def clear_workflow_actions(doctype, name): + if not (doctype and name): + return + + frappe.db.sql('''delete from `tabWorkflow Notification` + where reference_doctype=%s and reference_name=%s''', + (doctype, name)) + +def get_doc_workflow_state(doc): + workflow_name = get_workflow_name(doc.get('doctype')) + workflow_state_field = get_workflow_state_field(workflow_name) + return doc.get(workflow_state_field) + +def filter_allowed_users(users, doc, transition): + """Filters list of users by checking if user has access to doc and + if the user satisfies 'workflow transision self approval' condition + """ + from frappe.permissions import has_permission + filtered_users = [] + for user in users: + if (has_approval_access(user, doc, transition) + and has_permission(doctype=doc.doctype, user=user)): + filtered_users.append(user) + return filtered_users + +def get_common_email_args(doc): + doctype = doc.get('doctype') + docname = doc.get('name') + + email_template = get_email_template(doc) + if email_template: + subject = frappe.render_template(email_template.subject, vars(doc)) + response = frappe.render_template(email_template.response, vars(doc)) + else: + subject = _('Workflow Notification') + #response = _('{0}: {1}'.format(doctype, docname)) + email_template = frappe.db.get_value('Workflow',get_workflow_name(doc.doctype), 'email_alert_template') + response = frappe.render_template(email_template, {'doc':vars(doc)}) + + common_args = { + 'template': 'workflow_notification', + 'attachments': [frappe.attach_print(doctype, docname , file_name=docname)], + 'subject': subject, + 'message': response + } + return common_args + +def get_email_template(doc): + """Returns next_action_email_template + for workflow state (if available) based on doc current workflow state + """ + return + workflow_name = get_workflow_name(doc.get('doctype')) + doc_state = get_doc_workflow_state(doc) + template_name = frappe.db.get_value('Workflow Document State', { + 'parent': workflow_name, + 'state': doc_state + }, 'next_action_email_template') + + if not template_name: return + return frappe.get_doc('Email Template', template_name) diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification_list.js b/cpfa/cpfa/doctype/workflow_notification/workflow_notification_list.js new file mode 100644 index 0000000..3d27874 --- /dev/null +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification_list.js @@ -0,0 +1,19 @@ +frappe.listview_settings['Workflow Notification'] = { + get_form_link: (doc) => { + let doctype = ''; + let docname = ''; + if(doc.status === 'Open') { + doctype = doc.reference_doctype; + docname = doc.reference_name; + } else { + doctype = 'Workflow Notification'; + docname = doc.name; + } + docname = docname.match(/[%'"]/) + ? encodeURIComponent(docname) + : docname; + + const link = '#Form/' + doctype + '/' + docname; + return link; + } +}; \ No newline at end of file diff --git a/cpfa/fixtures/custom_field.json b/cpfa/fixtures/custom_field.json new file mode 100644 index 0000000..75d5c4a --- /dev/null +++ b/cpfa/fixtures/custom_field.json @@ -0,0 +1,116 @@ +[ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Workflow Transition", + "fieldname": "allow_self_approval", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "insert_after": null, + "label": "Allow Self Approval", + "modified": "2018-11-09 17:31:47.232119", + "name": "Workflow Transition-allow_self_approval", + "no_copy": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "unique": 0, + "width": null + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": "Emails will be sent with next possible workflow actions", + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Workflow", + "fieldname": "send_email_alert", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "insert_after": "override_status", + "label": "Send Email Alert", + "modified": "2018-11-09 21:41:18.525016", + "name": "Workflow-send_email_alert", + "no_copy": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "unique": 0, + "width": null + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": "

{{_(\"Pending Workflow Action\")}}

\n\n\n
{{ doc.doctype }}
\n{{_(\"Document ID\")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n
{{_(\"Workflow State\")}}: {{ doc.workflow_state }}", + "depends_on": null, + "description": "Properties from workflow notification are available", + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Workflow", + "fieldname": "email_alert_template", + "fieldtype": "Code", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "insert_after": "workflow_state_field", + "label": "Email Alert Template", + "modified": "2018-11-12 10:11:39.682586", + "name": "Workflow-email_alert_template", + "no_copy": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "unique": 0, + "width": null + } +] \ No newline at end of file diff --git a/cpfa/hooks.py b/cpfa/hooks.py new file mode 100644 index 0000000..9f80cff --- /dev/null +++ b/cpfa/hooks.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from . import __version__ as app_version + +app_name = "cpfa" +app_title = "Total CPFA ERPNext" +app_publisher = "Manqala" +app_description = "Total E&P NIG CPFA ERPNext customization" +app_icon = "octicon octicon-file-directory" +app_color = "green" +app_email = "dev@manqala.com" +app_license = "MIT" + +# Includes in +# ------------------ + +# include js, css files in header of desk.html +# app_include_css = "/assets/cpfa/css/cpfa.css" +# app_include_js = "/assets/cpfa/js/cpfa.js" + +app_include_js = "/assets/cpfa/js/cpfa.js" + +# include js, css files in header of web template +# web_include_css = "/assets/cpfa/css/cpfa.css" +# web_include_js = "/assets/cpfa/js/cpfa.js" + +# include js in page +# page_js = {"page" : "public/js/file.js"} + +# include js in doctype views +# doctype_js = {"doctype" : "public/js/doctype.js"} +# doctype_list_js = {"doctype" : "public/js/doctype_list.js"} +# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} +# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} + +# Home Pages +# ---------- + +# application home page (will override Website Settings) +# home_page = "login" + +# website user home page (by Role) +# role_home_page = { +# "Role": "home_page" +# } + +# Website user home page (by function) +# get_website_user_home_page = "cpfa.utils.get_home_page" + +# Generators +# ---------- + +# automatically create page for each record of this doctype +# website_generators = ["Web Page"] + +# Installation +# ------------ + +# before_install = "cpfa.install.before_install" +# after_install = "cpfa.install.after_install" + +# Desk Notifications +# ------------------ +# See frappe.core.notifications.get_notification_config + +notification_config = "cpfa.notifications.get_notification_config" + +# Permissions +# ----------- +# Permissions evaluated in scripted ways + +# permission_query_conditions = { +# "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", +# } +# +# has_permission = { +# "Event": "frappe.desk.doctype.event.event.has_permission", +# } + +permission_query_conditions = { + "Workflow Notification": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.get_permission_query_conditions" +} + +has_permission = { + "Workflow Notification": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.has_permission", +} + +# Document Events +# --------------- +# Hook on document methods and events + +doc_events = { + "*": { + "on_update": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.process_workflow_actions", + "on_cancel": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.process_workflow_actions", + } +} + +# Scheduled Tasks +# --------------- + +# scheduler_events = { +# "all": [ +# "cpfa.tasks.all" +# ], +# "daily": [ +# "cpfa.tasks.daily" +# ], +# "hourly": [ +# "cpfa.tasks.hourly" +# ], +# "weekly": [ +# "cpfa.tasks.weekly" +# ] +# "monthly": [ +# "cpfa.tasks.monthly" +# ] +# } + +scheduler_events = { + "daily_long": [ + "cpfa.cpfa.doctype.azure_storage_backup_settings.azure_storage_backup_settings.take_backups_daily" + ], + "weekly_long": [ + "cpfa.cpfa.doctype.azure_storage_backup_settings.azure_storage_backup_settings.take_backups_weekly", + ], + "monthly_long": [ + "cpfa.cpfa.doctype.azure_storage_backup_settings.azure_storage_backup_settings.take_backups_monthly" + ] +} + +# Testing +# ------- + +# before_tests = "cpfa.install.before_tests" + +# Overriding Whitelisted Methods +# ------------------------------ +# +# override_whitelisted_methods = { +# "frappe.desk.doctype.event.event.get_events": "cpfa.event.get_events" +# } + +fixtures = [ + {"dt":"Custom Field", "filters": [["dt", "in", ["Workflow","Workflow Transition"]]]} +] \ No newline at end of file diff --git a/cpfa/modules.txt b/cpfa/modules.txt new file mode 100644 index 0000000..429a5eb --- /dev/null +++ b/cpfa/modules.txt @@ -0,0 +1 @@ +CPFA \ No newline at end of file diff --git a/cpfa/notifications.py b/cpfa/notifications.py new file mode 100644 index 0000000..5bd012b --- /dev/null +++ b/cpfa/notifications.py @@ -0,0 +1,7 @@ + +def get_notification_config(): + return { + "for_doctype": { + "Workflow Notification": {"status": "Open"}, + }, + } \ No newline at end of file diff --git a/cpfa/patches.txt b/cpfa/patches.txt new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/public/build.json b/cpfa/public/build.json new file mode 100644 index 0000000..64451c9 --- /dev/null +++ b/cpfa/public/build.json @@ -0,0 +1,5 @@ +{ + "js/cpfa.js": [ + "public/js/cpfa.js" + ] +} \ No newline at end of file diff --git a/cpfa/public/js/cpfa.js b/cpfa/public/js/cpfa.js new file mode 100644 index 0000000..79724eb --- /dev/null +++ b/cpfa/public/js/cpfa.js @@ -0,0 +1,89 @@ + +$(function(){ + try { + frappe.ui.form.States.prototype.show_actions = function(state) { + var added = false, + me = this; + + this.frm.page.clear_actions_menu(); + + // if the loaded doc is dirty, don't show workflow buttons + if (this.frm.doc.__unsaved===1) { + return; + } + + function has_approval_access(transition) { + let approval_access = false; + const user = frappe.session.user; + if (user === 'Administrator' + || transition.allow_self_approval + || user !== me.frm.doc.owner) { + approval_access = true; + } + return approval_access; + } + + $.each(frappe.workflow.get_transitions(this.frm.doctype, state), function(i, d) { + if(frappe.user_roles.includes(d.allowed) && has_approval_access(d)) { + added = true; + me.frm.page.add_action_item(__(d.action), function() { + var action = d.action; + // capture current state + var doc_before_action = copy_dict(me.frm.doc); + + // set new state + var next_state = frappe.workflow.get_next_state(me.frm.doctype, + me.frm.doc[me.state_fieldname], action); + me.frm.doc[me.state_fieldname] = next_state; + var new_state = frappe.workflow.get_document_state(me.frm.doctype, next_state); + var new_docstatus = cint(new_state.doc_status); + + + if(new_state.update_field) { + me.frm.set_value(new_state.update_field, new_state.update_value); + } + + // revert state on error + var on_error = function() { + // reset in locals + frappe.model.add_to_locals(doc_before_action); + me.frm.refresh(); + } + + // success - add a comment + var success = function() { + me.frm.timeline.insert_comment("Workflow", next_state); + } + if(new_docstatus==1 && me.frm.doc.docstatus==0) { + me.frm.savesubmit(null, success, on_error); + } else if(new_docstatus==0 && me.frm.doc.docstatus==0) { + me.frm.save("Save", success, null, on_error); + } else if(new_docstatus==1 && me.frm.doc.docstatus==1) { + me.frm.save("Update", success, null, on_error); + } else if(new_docstatus==2 && me.frm.doc.docstatus==1) { + me.frm.savecancel(null, success, on_error); + } else { + frappe.msgprint(__("Document Status transition from ") + me.frm.doc.docstatus + " " + + __("to") + + new_docstatus + " " + __("is not allowed.")); + frappe.msgprint(__("Document Status transition from {0} to {1} is not allowed", [me.frm.doc.docstatus, new_docstatus])); + return false; + } + + return false; + + }); + } + }); + + if(added) { + this.frm.page.btn_primary.addClass("hide"); + this.frm.toolbar.current_status = ""; + this.setup_help(); + } + }; + } catch(e){ + console.trace('cpfa.js error') + console.trace(e) + } +}) diff --git a/cpfa/templates/__init__.py b/cpfa/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/templates/emails/workflow_notification.html b/cpfa/templates/emails/workflow_notification.html new file mode 100644 index 0000000..3cc0116 --- /dev/null +++ b/cpfa/templates/emails/workflow_notification.html @@ -0,0 +1,8 @@ +

{{ message }}

+
+

+ {% for action in actions %} + {{_(action.action_name)}} + {% endfor %} +

+
\ No newline at end of file diff --git a/cpfa/templates/pages/__init__.py b/cpfa/templates/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpfa/utils/__init__.py b/cpfa/utils/__init__.py new file mode 100644 index 0000000..edb1549 --- /dev/null +++ b/cpfa/utils/__init__.py @@ -0,0 +1,9 @@ + + +def get_pdf_link(doctype, docname, print_format='Standard', no_letterhead=0): + return '/api/method/frappe.utils.print_format.download_pdf?doctype={doctype}&name={docname}&format={print_format}&no_letterhead={no_letterhead}'.format( + doctype = doctype, + docname = docname, + print_format = print_format, + no_letterhead = no_letterhead + ) \ No newline at end of file diff --git a/cpfa/utils/workflow.py b/cpfa/utils/workflow.py new file mode 100644 index 0000000..eec753c --- /dev/null +++ b/cpfa/utils/workflow.py @@ -0,0 +1,143 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import cint +from frappe import _ +from frappe.model.workflow import get_workflow_name + +class WorkflowStateError(frappe.ValidationError): pass +class WorkflowTransitionError(frappe.ValidationError): pass +class WorkflowPermissionError(frappe.ValidationError): pass + +@frappe.whitelist() +def get_transitions(doc, workflow = None): + '''Return list of possible transitions for the given doc''' + doc = frappe.get_doc(frappe.parse_json(doc)) + + if doc.is_new(): + return [] + + frappe.has_permission(doc, 'read', throw=True) + roles = frappe.get_roles() + + if not workflow: + workflow = get_workflow(doc.doctype) + current_state = doc.get(workflow.workflow_state_field) + + if not current_state: + frappe.throw(_('Workflow State not set'), WorkflowStateError) + + transitions = [] + for transition in workflow.transitions: + if transition.state == current_state and transition.allowed in roles: + if transition.condition: + # if condition, evaluate + # access to frappe.db.get_value and frappe.db.get_list + success = frappe.safe_eval(transition.condition, + dict(frappe = frappe._dict( + db = frappe._dict(get_value = frappe.db.get_value, get_list=frappe.db.get_list), + session = frappe.session + )), + dict(doc = doc)) + if not success: + continue + transitions.append(transition.as_dict()) + + return transitions + +@frappe.whitelist() +def apply_workflow(doc, action): + '''Allow workflow action on the current doc''' + doc = frappe.get_doc(frappe.parse_json(doc)) + workflow = get_workflow(doc.doctype) + transitions = get_transitions(doc, workflow) + user = frappe.session.user + + # find the transition + transition = None + for t in transitions: + if t.action == action: + transition = t + + if not transition: + frappe.throw(_("Not a valid Workflow Action"), WorkflowTransitionError) + + if not has_approval_access(user, doc, transition): + frappe.throw(_("Self approval is not allowed")) + + # update workflow state field + doc.set(workflow.workflow_state_field, transition.next_state) + + # find settings for the next state + next_state = [d for d in workflow.states if d.state == transition.next_state][0] + + # update any additional field + if next_state.update_field: + doc.set(next_state.update_field, next_state.update_value) + + new_docstatus = cint(next_state.doc_status) + if doc.docstatus == 0 and new_docstatus == 0: + doc.save() + elif doc.docstatus == 0 and new_docstatus == 1: + doc.submit() + elif doc.docstatus == 1 and new_docstatus == 1: + doc.save() + elif doc.docstatus == 1 and new_docstatus == 2: + doc.cancel() + else: + frappe.throw(_('Illegal Document Status for {0}').format(next_state.state)) + + doc.add_comment('Workflow', _(next_state.state)) + + return doc + +def validate_workflow(doc): + '''Validate Workflow State and Transition for the current user. + + - Check if user is allowed to edit in current state + - Check if user is allowed to transition to the next state (if changed) + ''' + workflow = get_workflow(doc.doctype) + + current_state = None + if getattr(doc, '_doc_before_save', None): + current_state = doc._doc_before_save.get(workflow.workflow_state_field) + next_state = doc.get(workflow.workflow_state_field) + + if not next_state: + next_state = workflow.states[0].state + doc.set(workflow.workflow_state_field, next_state) + + if not current_state: + current_state = workflow.states[0].state + + state_row = [d for d in workflow.states if d.state == current_state] + if not state_row: + frappe.throw(_('{0} is not a valid Workflow State. Please update your Workflow and try again.'.format(frappe.bold(current_state)))) + state_row = state_row[0] + + # if transitioning, check if user is allowed to transition + if current_state != next_state: + transitions = get_transitions(doc._doc_before_save) + transition = [d for d in transitions if d.next_state == next_state] + if not transition: + frappe.throw(_('Workflow State {0} is not allowed').format(frappe.bold(next_state)), WorkflowPermissionError) + +def get_workflow(doctype): + return frappe.get_doc('Workflow', get_workflow_name(doctype)) + +def has_approval_access(user, doc, transition): + return (user == 'Administrator' + or transition.get('allow_self_approval') + or user != doc.owner) + +def get_workflow_state_field(workflow_name): + return get_workflow_field_value(workflow_name, 'workflow_state_field') + +def send_email_alert(workflow_name): + return get_workflow_field_value(workflow_name, 'send_email_alert') + +def get_workflow_field_value(workflow_name, field): + return frappe.db.get_value("Workflow", workflow_name, field) \ No newline at end of file diff --git a/initial b/initial new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index 5ac1c81..c42503f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,6 @@ -frappe \ No newline at end of file +<<<<<<< HEAD +frappe +======= +frappe +azure-storage +>>>>>>> e749145085c0ecdeac8d83ab3f36927a070bec7c diff --git a/setup.py b/setup.py index 07c28c8..5cabca8 100644 --- a/setup.py +++ b/setup.py @@ -5,19 +5,34 @@ with open('requirements.txt') as f: install_requires = f.read().strip().split('\n') +<<<<<<< HEAD # get version from __version__ variable in app1/__init__.py _version_re = re.compile(r'__version__\s+=\s+(.*)') with open('app1/__init__.py', 'rb') as f: +======= +# get version from __version__ variable in cpfa/__init__.py +_version_re = re.compile(r'__version__\s+=\s+(.*)') + +with open('cpfa/__init__.py', 'rb') as f: +>>>>>>> e749145085c0ecdeac8d83ab3f36927a070bec7c version = str(ast.literal_eval(_version_re.search( f.read().decode('utf-8')).group(1))) setup( +<<<<<<< HEAD name='app1', version=version, description='Test App', author='frappe', author_email='ebukaakeru@gmail.com', +======= + name='cpfa', + version=version, + description='Total E&P NIG CPFA ERPNext customization', + author='Manqala', + author_email='dev@manqala.com', +>>>>>>> e749145085c0ecdeac8d83ab3f36927a070bec7c packages=find_packages(), zip_safe=False, include_package_data=True,