From 2f5cb2b3e8b20f5559370ceeb1599c9b473efce8 Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Fri, 9 Nov 2018 22:11:59 +0100 Subject: [PATCH 01/16] initial commit --- .gitignore | 6 + MANIFEST.in | 18 ++ README.md | 7 + cpfa/__init__.py | 5 + cpfa/config/__init__.py | 0 cpfa/config/desktop.py | 14 + cpfa/config/docs.py | 11 + cpfa/config/setup.py | 17 + cpfa/cpfa/__init__.py | 0 cpfa/cpfa/doctype/__init__.py | 0 .../doctype/workflow_notification/__init__.py | 0 .../test_workflow_notification.js | 23 ++ .../test_workflow_notification.py | 10 + .../workflow_notification.js | 8 + .../workflow_notification.json | 249 ++++++++++++++ .../workflow_notification.py | 304 ++++++++++++++++++ .../workflow_notification_list.js | 19 ++ cpfa/fixtures/custom_field.json | 78 +++++ cpfa/hooks.py | 132 ++++++++ cpfa/modules.txt | 3 + cpfa/patches.txt | 0 cpfa/templates/__init__.py | 0 cpfa/templates/pages/__init__.py | 0 cpfa/utils/__init__.py | 9 + cpfa/utils/workflow.py | 143 ++++++++ license.txt | 1 + requirements.txt | 1 + setup.py | 25 ++ 28 files changed, 1083 insertions(+) create mode 100644 .gitignore create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100644 cpfa/__init__.py create mode 100644 cpfa/config/__init__.py create mode 100644 cpfa/config/desktop.py create mode 100644 cpfa/config/docs.py create mode 100644 cpfa/config/setup.py create mode 100644 cpfa/cpfa/__init__.py create mode 100644 cpfa/cpfa/doctype/__init__.py create mode 100644 cpfa/cpfa/doctype/workflow_notification/__init__.py create mode 100644 cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.js create mode 100644 cpfa/cpfa/doctype/workflow_notification/test_workflow_notification.py create mode 100644 cpfa/cpfa/doctype/workflow_notification/workflow_notification.js create mode 100644 cpfa/cpfa/doctype/workflow_notification/workflow_notification.json create mode 100644 cpfa/cpfa/doctype/workflow_notification/workflow_notification.py create mode 100644 cpfa/cpfa/doctype/workflow_notification/workflow_notification_list.js create mode 100644 cpfa/fixtures/custom_field.json create mode 100644 cpfa/hooks.py create mode 100644 cpfa/modules.txt create mode 100644 cpfa/patches.txt create mode 100644 cpfa/templates/__init__.py create mode 100644 cpfa/templates/pages/__init__.py create mode 100644 cpfa/utils/__init__.py create mode 100644 cpfa/utils/workflow.py create mode 100644 license.txt create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c010f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +*.pyc +*.egg-info +*.swp +tags +cpfa/docs/current \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..692986b --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,18 @@ +include MANIFEST.in +include requirements.txt +include *.json +include *.md +include *.py +include *.txt +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 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c0f385a --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +## Total CPFA ERPNext + +Total E&P NIG CPFA ERPNext customization + +#### License + +MIT \ No newline at end of file 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..c3670e4 --- /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": "Total CPFA ERPNext", + "color": "green", + "icon": "octicon octicon-file-directory", + "type": "module", + "label": _("Total CPFA ERPNext") + } + ] 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/setup.py b/cpfa/config/setup.py new file mode 100644 index 0000000..4d25c1c --- /dev/null +++ b/cpfa/config/setup.py @@ -0,0 +1,17 @@ + +from frappe import _ + +def get_data(): + return [ + { + "label": _("Workflow"), + "icon": "fa fa-random", + "items": [ + { + "type": "doctype", + "name": "Workflow Notifications", + "description": _("Notifications for workflow actions.") + }, + ] + }, + ] 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/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..b7be7e3 --- /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": 0, + "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": 0, + "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": 0, + "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": 0, + "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": 0, + "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-09 21:43:05.305028", + "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": 0 +} \ 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..fa9800e --- /dev/null +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py @@ -0,0 +1,304 @@ +# -*- 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(doc, user): + 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/frappe.workflow.doctype.workflow_action.workflow_action.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/frappe.workflow.doctype.workflow_action.workflow_action.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)) + + common_args = { + 'template': 'workflow_action', + '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..0ab9c84 --- /dev/null +++ b/cpfa/fixtures/custom_field.json @@ -0,0 +1,78 @@ +[ + { + "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 + } +] \ No newline at end of file diff --git a/cpfa/hooks.py b/cpfa/hooks.py new file mode 100644 index 0000000..1dc7c1f --- /dev/null +++ b/cpfa/hooks.py @@ -0,0 +1,132 @@ +# -*- 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" + +# 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 Action": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.get_permission_query_conditions" +} + +has_permission = { + "Workflow Action": "frappe.workflow.doctype.workflow_action.workflow_action.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" +# ] +# } + +# 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..e0fa61e --- /dev/null +++ b/cpfa/modules.txt @@ -0,0 +1,3 @@ +Total CPFA ERPNext +Workflow Action +CPFA \ 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/templates/__init__.py b/cpfa/templates/__init__.py new file mode 100644 index 0000000..e69de29 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/license.txt b/license.txt new file mode 100644 index 0000000..2fdf7c7 --- /dev/null +++ b/license.txt @@ -0,0 +1 @@ +License: MIT \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5ac1c81 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +frappe \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..8742785 --- /dev/null +++ b/setup.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from setuptools import setup, find_packages +import re, ast + +with open('requirements.txt') as f: + install_requires = f.read().strip().split('\n') + +# 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: + version = str(ast.literal_eval(_version_re.search( + f.read().decode('utf-8')).group(1))) + +setup( + name='cpfa', + version=version, + description='Total E&P NIG CPFA ERPNext customization', + author='Manqala', + author_email='dev@manqala.com', + packages=find_packages(), + zip_safe=False, + include_package_data=True, + install_requires=install_requires +) From 47725b2261f8f478834d6a041291e2edbddc51e9 Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Sat, 10 Nov 2018 16:56:38 +0100 Subject: [PATCH 02/16] create workflow notification on_update and send email --- cpfa/config/setup.py | 5 ++--- .../workflow_notification/workflow_notification.json | 4 ++-- .../workflow_notification/workflow_notification.py | 8 ++++---- cpfa/hooks.py | 2 +- cpfa/modules.txt | 2 -- cpfa/notifications.py | 7 +++++++ cpfa/templates/emails/workflow_notification.html | 8 ++++++++ 7 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 cpfa/notifications.py create mode 100644 cpfa/templates/emails/workflow_notification.html diff --git a/cpfa/config/setup.py b/cpfa/config/setup.py index 4d25c1c..a0397a2 100644 --- a/cpfa/config/setup.py +++ b/cpfa/config/setup.py @@ -1,4 +1,4 @@ - +from __future__ import unicode_literals from frappe import _ def get_data(): @@ -9,8 +9,7 @@ def get_data(): "items": [ { "type": "doctype", - "name": "Workflow Notifications", - "description": _("Notifications for workflow actions.") + "name": "Workflow Notification", }, ] }, diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json index b7be7e3..3352878 100644 --- a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json @@ -209,7 +209,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-09 21:43:05.305028", + "modified": "2018-11-10 14:25:11.893429", "modified_by": "Administrator", "module": "CPFA", "name": "Workflow Notification", @@ -245,5 +245,5 @@ "sort_order": "DESC", "title_field": "reference_name", "track_changes": 1, - "track_seen": 0 + "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 index fa9800e..3430e78 100644 --- a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py @@ -123,7 +123,7 @@ def return_action_confirmation_page(doc, action, action_link, alert_doc_change=F frappe.respond_as_web_page(None, None, indicator_color="blue", - template="confirm_workflow_action", + #template="confirm_workflow_action", context=template_params) def return_link_expired_page(doc, doc_workflow_state): @@ -201,7 +201,7 @@ def send_workflow_action_email(users_data, doc): enqueue(method=frappe.sendmail, queue='short', **email_args) def get_workflow_action_url(action, doc, user): - apply_action_method = "/api/method/frappe.workflow.doctype.workflow_action.workflow_action.apply_action" + apply_action_method = "/api/method/cpfa.cpfa.doctype.workflow_notification.workflow_notification.apply_action" params = { "doctype": doc.get('doctype'), @@ -215,7 +215,7 @@ def get_workflow_action_url(action, doc, user): return get_url(apply_action_method + "?" + get_signed_params(params)) def get_confirm_workflow_action_url(doc, action, user): - confirm_action_method = "/api/method/frappe.workflow.doctype.workflow_action.workflow_action.confirm_action" + confirm_action_method = "/api/method/cpfa.cpfa.doctype.workflow_notification.workflow_notification.confirm_action" params = { "action": action, @@ -281,7 +281,7 @@ def get_common_email_args(doc): response = _('{0}: {1}'.format(doctype, docname)) common_args = { - 'template': 'workflow_action', + 'template': 'workflow_notification', 'attachments': [frappe.attach_print(doctype, docname , file_name=docname)], 'subject': subject, 'message': response diff --git a/cpfa/hooks.py b/cpfa/hooks.py index 1dc7c1f..3f75447 100644 --- a/cpfa/hooks.py +++ b/cpfa/hooks.py @@ -61,7 +61,7 @@ # ------------------ # See frappe.core.notifications.get_notification_config -# notification_config = "cpfa.notifications.get_notification_config" +notification_config = "cpfa.notifications.get_notification_config" # Permissions # ----------- diff --git a/cpfa/modules.txt b/cpfa/modules.txt index e0fa61e..429a5eb 100644 --- a/cpfa/modules.txt +++ b/cpfa/modules.txt @@ -1,3 +1 @@ -Total CPFA ERPNext -Workflow Action 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/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 From 182ad335cd57755d0ad5624fb4036b41a97cb859 Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Mon, 12 Nov 2018 11:05:18 +0100 Subject: [PATCH 03/16] add workflow alert template and fix listview permissions --- .../workflow_notification.py | 9 +++-- cpfa/fixtures/custom_field.json | 38 +++++++++++++++++++ cpfa/hooks.py | 4 +- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py index 3430e78..7ae484d 100644 --- a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py @@ -29,7 +29,7 @@ def get_permission_query_conditions(user): return "(`tabWorkflow Notification`.`user`='{user}')".format(user=user) -def has_permission(doc, user): +def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None): if user not in ['Administrator', doc.user]: return False @@ -189,11 +189,12 @@ def create_workflow_actions_for_users(users, doc): 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'), + #'actions': d.get('possible_actions'), 'message': message }, } @@ -278,7 +279,9 @@ def get_common_email_args(doc): response = frappe.render_template(email_template.response, vars(doc)) else: subject = _('Workflow Notification') - response = _('{0}: {1}'.format(doctype, docname)) + #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', diff --git a/cpfa/fixtures/custom_field.json b/cpfa/fixtures/custom_field.json index 0ab9c84..75d5c4a 100644 --- a/cpfa/fixtures/custom_field.json +++ b/cpfa/fixtures/custom_field.json @@ -74,5 +74,43 @@ "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 index 3f75447..426014b 100644 --- a/cpfa/hooks.py +++ b/cpfa/hooks.py @@ -76,11 +76,11 @@ # } permission_query_conditions = { - "Workflow Action": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.get_permission_query_conditions" + "Workflow Notification": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.get_permission_query_conditions" } has_permission = { - "Workflow Action": "frappe.workflow.doctype.workflow_action.workflow_action.has_permission", + "Workflow Notification": "frappe.workflow.doctype.workflow_notification.workflow_notification.has_permission", } # Document Events From c32c4a1aaa2b77d5cfc4fa47822ed1d746980d92 Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Mon, 12 Nov 2018 17:10:40 +0100 Subject: [PATCH 04/16] fix permission error --- .../workflow_notification/workflow_notification.json | 12 ++++++------ .../workflow_notification/workflow_notification.py | 2 +- cpfa/hooks.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json index 3352878..ff2ba68 100644 --- a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.json @@ -26,7 +26,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Status", "length": 0, @@ -57,7 +57,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Reference Name", "length": 0, @@ -88,7 +88,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Reference Doctype", "length": 0, @@ -114,7 +114,7 @@ "columns": 0, "fieldname": "user", "fieldtype": "Link", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -145,7 +145,7 @@ "columns": 0, "fieldname": "workflow_state", "fieldtype": "Data", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -209,7 +209,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-10 14:25:11.893429", + "modified": "2018-11-12 17:08:30.839842", "modified_by": "Administrator", "module": "CPFA", "name": "Workflow Notification", diff --git a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py index 7ae484d..1393a2f 100644 --- a/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py +++ b/cpfa/cpfa/doctype/workflow_notification/workflow_notification.py @@ -29,7 +29,7 @@ def get_permission_query_conditions(user): return "(`tabWorkflow Notification`.`user`='{user}')".format(user=user) -def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None): +def has_permission(doctype=None, ptype="read", doc=None, verbose=False, user=None): if user not in ['Administrator', doc.user]: return False diff --git a/cpfa/hooks.py b/cpfa/hooks.py index 426014b..7986808 100644 --- a/cpfa/hooks.py +++ b/cpfa/hooks.py @@ -80,7 +80,7 @@ } has_permission = { - "Workflow Notification": "frappe.workflow.doctype.workflow_notification.workflow_notification.has_permission", + "Workflow Notification": "cpfa.cpfa.doctype.workflow_notification.workflow_notification.has_permission", } # Document Events From 35e04de42063362875204a4ef435372ae561f222 Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Tue, 13 Nov 2018 03:15:29 +0100 Subject: [PATCH 05/16] added comments --- app1/utils/hr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app1/utils/hr.py b/app1/utils/hr.py index 1fed0a1..773ccad 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) From b100f650c1606db9340fecbc680e0323efe06062 Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Tue, 13 Nov 2018 10:53:05 +0100 Subject: [PATCH 06/16] prevent self approval according to settings --- cpfa/hooks.py | 2 + cpfa/public/build.json | 5 +++ cpfa/public/js/cpfa.js | 89 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 cpfa/public/build.json create mode 100644 cpfa/public/js/cpfa.js diff --git a/cpfa/hooks.py b/cpfa/hooks.py index 7986808..f543d9e 100644 --- a/cpfa/hooks.py +++ b/cpfa/hooks.py @@ -18,6 +18,8 @@ # 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" 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) + } +}) From 23ab76890038d7b2a93da96c5e2b2bb683984c58 Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Tue, 13 Nov 2018 21:48:25 +0100 Subject: [PATCH 07/16] add azure storage integration for backups --- cpfa/config/integrations.py | 17 ++ .../azure_storage_backup_settings/__init__.py | 0 .../azure_storage_backup_settings.js | 28 ++ .../azure_storage_backup_settings.json | 274 ++++++++++++++++++ .../azure_storage_backup_settings.py | 158 ++++++++++ .../test_azure_storage_backup_settings.js | 23 ++ .../test_azure_storage_backup_settings.py | 10 + cpfa/hooks.py | 12 + requirements.txt | 3 +- 9 files changed, 524 insertions(+), 1 deletion(-) create mode 100644 cpfa/config/integrations.py create mode 100644 cpfa/cpfa/doctype/azure_storage_backup_settings/__init__.py create mode 100644 cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.js create mode 100644 cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.json create mode 100644 cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.py create mode 100644 cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.js create mode 100644 cpfa/cpfa/doctype/azure_storage_backup_settings/test_azure_storage_backup_settings.py 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/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..6648d35 --- /dev/null +++ b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.json @@ -0,0 +1,274 @@ +{ + "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 + } + ], + "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-13 15:00:28.746843", + "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..eca98d6 --- /dev/null +++ b/cpfa/cpfa/doctype/azure_storage_backup_settings/azure_storage_backup_settings.py @@ -0,0 +1,158 @@ +# -*- 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) + +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 + block_blob_service.delete_blob(doc.container_name, blob) 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/hooks.py b/cpfa/hooks.py index f543d9e..9f80cff 100644 --- a/cpfa/hooks.py +++ b/cpfa/hooks.py @@ -117,6 +117,18 @@ # ] # } +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 # ------- diff --git a/requirements.txt b/requirements.txt index 5ac1c81..9e85dd0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -frappe \ No newline at end of file +frappe +azure-storage \ No newline at end of file From fb7f97fce6f5d479c2b01c8c88cb81da051a28bc Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Tue, 13 Nov 2018 22:06:00 +0100 Subject: [PATCH 08/16] Improved Vehicle request feature --- app1/app1/doctype/color/__init__.py | 0 app1/app1/doctype/color/color.js | 8 + app1/app1/doctype/color/color.json | 93 +++++++++++ app1/app1/doctype/color/color.py | 10 ++ app1/app1/doctype/color/test_color.js | 23 +++ app1/app1/doctype/color/test_color.py | 10 ++ .../doctype/insurance_company/__init__.py | 0 .../insurance_company/insurance_company.js | 8 + .../insurance_company/insurance_company.json | 154 ++++++++++++++++++ .../insurance_company/insurance_company.py | 10 ++ .../test_insurance_company.js | 23 +++ .../test_insurance_company.py | 10 ++ .../service_plan_template.json | 2 +- .../vehicle_location/vehicle_location.json | 5 +- .../vehicle_request/vehicle_request.json | 41 ++--- app1/hooks.py | 4 + app1/public/js/autoname.js | 12 -- app1/public/js/filter_model.js | 11 ++ app1/utils/hr.py | 20 ++- 19 files changed, 408 insertions(+), 36 deletions(-) create mode 100644 app1/app1/doctype/color/__init__.py create mode 100644 app1/app1/doctype/color/color.js create mode 100644 app1/app1/doctype/color/color.json create mode 100644 app1/app1/doctype/color/color.py create mode 100644 app1/app1/doctype/color/test_color.js create mode 100644 app1/app1/doctype/color/test_color.py create mode 100644 app1/app1/doctype/insurance_company/__init__.py create mode 100644 app1/app1/doctype/insurance_company/insurance_company.js create mode 100644 app1/app1/doctype/insurance_company/insurance_company.json create mode 100644 app1/app1/doctype/insurance_company/insurance_company.py create mode 100644 app1/app1/doctype/insurance_company/test_insurance_company.js create mode 100644 app1/app1/doctype/insurance_company/test_insurance_company.py delete mode 100644 app1/public/js/autoname.js create mode 100644 app1/public/js/filter_model.js 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/insurance_company.js b/app1/app1/doctype/insurance_company/insurance_company.js new file mode 100644 index 0000000..1ffd344 --- /dev/null +++ b/app1/app1/doctype/insurance_company/insurance_company.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Insurance Company', { + refresh: function(frm) { + + } +}); 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_plan_template/service_plan_template.json b/app1/app1/doctype/service_plan_template/service_plan_template.json index 5b8fc16..1e14a9e 100644 --- a/app1/app1/doctype/service_plan_template/service_plan_template.json +++ b/app1/app1/doctype/service_plan_template/service_plan_template.json @@ -237,7 +237,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-11-12 11:16:43.283445", + "modified": "2018-11-13 08:30:25.464111", "modified_by": "Administrator", "module": "App1", "name": "Service Plan Template", 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_request/vehicle_request.json b/app1/app1/doctype/vehicle_request/vehicle_request.json index 5d0e038..26377d1 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, @@ -335,7 +337,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 +356,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 +367,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 +391,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-11 20:27:21.162141", + "modified": "2018-11-13 10:31:40.458060", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Request", diff --git a/app1/hooks.py b/app1/hooks.py index 97b0b9a..81c3110 100644 --- a/app1/hooks.py +++ b/app1/hooks.py @@ -29,6 +29,7 @@ doctype_js = { "Salary Slip" : "public/js/custom_hr.js", +"Vehicle" : "public/js/filter_model.js", } # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} @@ -88,6 +89,9 @@ }, "Vehicle Model": { "before_save":"app1.utils.hr.change_auto_name" + }, + "Vehicle":{ + "before_save":"app1.utils.hr.setVehicleName" } } 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/filter_model.js b/app1/public/js/filter_model.js new file mode 100644 index 0000000..e984faf --- /dev/null +++ b/app1/public/js/filter_model.js @@ -0,0 +1,11 @@ +frappe.ui.form.on('Vehicle',{ + vehicle_make: function(frm){ + cur_frm.fields_dict["vehicle_model"].get_query=function(doc){ + return { + filters:{ + "vehicle_make":frm.doc.vehicle_make + } + } + } + } +}) diff --git a/app1/utils/hr.py b/app1/utils/hr.py index 773ccad..9e9031d 100644 --- a/app1/utils/hr.py +++ b/app1/utils/hr.py @@ -32,8 +32,24 @@ def calculate_base_amount(salary_doc, event): 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.vmn=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 + return +@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) From 47c67662d5b87966bbea3567e3298c922c1e33c4 Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Wed, 14 Nov 2018 21:53:50 +0100 Subject: [PATCH 09/16] Added notification and Email Alerts --- app1/app1/doctype/trip_log/__init__.py | 0 app1/app1/doctype/trip_log/test_trip_log.js | 23 ++ app1/app1/doctype/trip_log/test_trip_log.py | 10 + app1/app1/doctype/trip_log/trip_log.js | 8 + app1/app1/doctype/trip_log/trip_log.json | 366 ++++++++++++++++++ app1/app1/doctype/trip_log/trip_log.py | 10 + .../vehicle_request/vehicle_request.json | 3 +- .../vehicle_return_application/__init__.py | 0 .../test_vehicle_return_application.js | 23 ++ .../test_vehicle_return_application.py | 10 + .../vehicle_return_application.js | 8 + .../vehicle_return_application.json | 186 +++++++++ .../vehicle_return_application.py | 10 + app1/hooks.py | 2 +- app1/notifications.py | 10 + 15 files changed, 667 insertions(+), 2 deletions(-) create mode 100644 app1/app1/doctype/trip_log/__init__.py create mode 100644 app1/app1/doctype/trip_log/test_trip_log.js create mode 100644 app1/app1/doctype/trip_log/test_trip_log.py create mode 100644 app1/app1/doctype/trip_log/trip_log.js create mode 100644 app1/app1/doctype/trip_log/trip_log.json create mode 100644 app1/app1/doctype/trip_log/trip_log.py create mode 100644 app1/app1/doctype/vehicle_return_application/__init__.py create mode 100644 app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.js create mode 100644 app1/app1/doctype/vehicle_return_application/test_vehicle_return_application.py create mode 100644 app1/app1/doctype/vehicle_return_application/vehicle_return_application.js create mode 100644 app1/app1/doctype/vehicle_return_application/vehicle_return_application.json create mode 100644 app1/app1/doctype/vehicle_return_application/vehicle_return_application.py create mode 100644 app1/notifications.py 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_request/vehicle_request.json b/app1/app1/doctype/vehicle_request/vehicle_request.json index 26377d1..adadc5a 100644 --- a/app1/app1/doctype/vehicle_request/vehicle_request.json +++ b/app1/app1/doctype/vehicle_request/vehicle_request.json @@ -324,6 +324,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "Requested", "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -391,7 +392,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-13 10:31:40.458060", + "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/hooks.py b/app1/hooks.py index 81c3110..d716c96 100644 --- a/app1/hooks.py +++ b/app1/hooks.py @@ -65,7 +65,7 @@ # ------------------ # See frappe.core.notifications.get_notification_config -# notification_config = "app1.notifications.get_notification_config" +notification_config = "app1.notifications.get_notification_config" # Permissions # ----------- 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"}, + } + } From 96d66cd83a66a9abd17cbe4eeed55782212da634 Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Thu, 15 Nov 2018 17:22:42 +0100 Subject: [PATCH 10/16] fix deletion of old backups --- .../azure_storage_backup_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index eca98d6..7b95b75 100644 --- 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 @@ -155,4 +155,5 @@ def delete_old_backups(limit, container): print "Deleting Backup: {0}".format(oldest_backup) for blob in all_backups[oldest_backup]: # delete all keys that are inside the oldest_backup - block_blob_service.delete_blob(doc.container_name, blob) + blob_name = '{}/{}'.format(oldest_backup, blob) + block_blob_service.delete_blob(doc.container_name, blob_name) From 4e28d288a222aaa7a031abb5c57f51d0c82bd065 Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Thu, 15 Nov 2018 22:41:51 +0100 Subject: [PATCH 11/16] Included vehicle servicing and trip links --- app1/app1/doctype/service_details/__init__.py | 0 .../service_details/service_details.json | 163 +++++++++++ .../service_details/service_details.py | 10 + .../service_plan_template.json | 9 +- .../doctype/vehicle_servicing_log/__init__.py | 0 .../test_vehicle_servicing_log.js | 23 ++ .../test_vehicle_servicing_log.py | 10 + .../vehicle_servicing_log.js | 8 + .../vehicle_servicing_log.json | 275 ++++++++++++++++++ .../vehicle_servicing_log.py | 10 + .../vehicle_trip_log/vehicle_trip_log.json | 2 +- app1/hooks.py | 1 + app1/public/js/add_button.js | 12 + 13 files changed, 518 insertions(+), 5 deletions(-) create mode 100644 app1/app1/doctype/service_details/__init__.py create mode 100644 app1/app1/doctype/service_details/service_details.json create mode 100644 app1/app1/doctype/service_details/service_details.py create mode 100644 app1/app1/doctype/vehicle_servicing_log/__init__.py create mode 100644 app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.js create mode 100644 app1/app1/doctype/vehicle_servicing_log/test_vehicle_servicing_log.py create mode 100644 app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js create mode 100644 app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json create mode 100644 app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py create mode 100644 app1/public/js/add_button.js 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..6e07ba8 --- /dev/null +++ b/app1/app1/doctype/service_details/service_details.json @@ -0,0 +1,163 @@ +{ + "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": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "service_item", + "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": "Service Item", + "length": 0, + "no_copy": 0, + "options": "Change Engine oil\nReplace oil filter\nReplace fuel filter\nReplace air filter\nReplace cabin filter\nReplace spark plugs\nChange transmissin fluid\nCheck brakepads\nCheck brake fluid\nCheck tire condition", + "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": 0, + "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": 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": "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, + "fieldname": "currency", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Currency", + "length": 0, + "no_copy": 0, + "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": 1, + "max_attachments": 0, + "modified": "2018-11-15 13:25:45.943323", + "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 1e14a9e..93d786f 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, @@ -30,7 +31,7 @@ "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": "Oil 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", "permlevel": 0, "precision": "", "print_hide": 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-13 08:30:25.464111", + "modified": "2018-11-15 11:38:31.711040", "modified_by": "Administrator", "module": "App1", "name": "Service Plan Template", 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..ce930c4 --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Vehicle Servicing Log', { + refresh: function(frm) { + + } +}); 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..e76b613 --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json @@ -0,0 +1,275 @@ +{ + "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": "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-15 13:32:50.985049", + "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..20407e3 --- /dev/null +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_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 VehicleServicingLog(Document): + pass 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..191a76d 100644 --- a/app1/app1/doctype/vehicle_trip_log/vehicle_trip_log.json +++ b/app1/app1/doctype/vehicle_trip_log/vehicle_trip_log.json @@ -326,7 +326,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-11 21:15:05.136042", + "modified": "2018-11-15 11:45:45.120316", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Trip Log", diff --git a/app1/hooks.py b/app1/hooks.py index d716c96..2c7d52c 100644 --- a/app1/hooks.py +++ b/app1/hooks.py @@ -30,6 +30,7 @@ doctype_js = { "Salary Slip" : "public/js/custom_hr.js", "Vehicle" : "public/js/filter_model.js", +"Vehicle" : "public/js/add_button.js" } # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} diff --git a/app1/public/js/add_button.js b/app1/public/js/add_button.js new file mode 100644 index 0000000..ec4b2fa --- /dev/null +++ b/app1/public/js/add_button.js @@ -0,0 +1,12 @@ +frappe.ui.form.on('Vehicle', { + refresh: function(frm){ + var name__=cur_frm.doc.name + console.log(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")) +} +}) From e749145085c0ecdeac8d83ab3f36927a070bec7c Mon Sep 17 00:00:00 2001 From: crossxcell99 Date: Fri, 16 Nov 2018 11:19:49 +0100 Subject: [PATCH 12/16] add option to remove local backup copies --- cpfa/config/desktop.py | 4 +-- .../azure_storage_backup_settings.json | 32 ++++++++++++++++++- .../azure_storage_backup_settings.py | 4 +++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/cpfa/config/desktop.py b/cpfa/config/desktop.py index c3670e4..9f6226a 100644 --- a/cpfa/config/desktop.py +++ b/cpfa/config/desktop.py @@ -5,10 +5,10 @@ def get_data(): return [ { - "module_name": "Total CPFA ERPNext", + "module_name": "CPFA", "color": "green", "icon": "octicon octicon-file-directory", "type": "module", - "label": _("Total CPFA ERPNext") + "label": "CPFA" } ] 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 index 6648d35..8782a17 100644 --- 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 @@ -223,6 +223,36 @@ "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, @@ -235,7 +265,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-11-13 15:00:28.746843", + "modified": "2018-11-15 22:14:52.422481", "modified_by": "Administrator", "module": "CPFA", "name": "Azure Storage Backup Settings", 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 index 7b95b75..28fde87 100644 --- 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 @@ -115,6 +115,10 @@ def backup_to_azure(): 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): From 87e3043ec16334c7243eb346ca611ab660a1e0bf Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Fri, 16 Nov 2018 22:20:13 +0100 Subject: [PATCH 13/16] Added more doctype functionality like cross field saving --- .../service_details/service_details.json | 38 ++++++++++++++-- .../doctype/vehicle_model/vehicle_model.py | 7 +++ .../vehicle_servicing_log.py | 8 ++++ .../vehicle_trip_log/vehicle_trip_log.json | 5 ++- app1/hooks.py | 14 +++--- app1/public/js/add_button.js | 19 ++++---- app1/public/js/calculate_total.js | 25 +++++++++++ app1/public/js/filter_model.js | 15 ++++++- app1/utils/hr.py | 44 +++++++++++-------- 9 files changed, 135 insertions(+), 40 deletions(-) create mode 100644 app1/public/js/calculate_total.js diff --git a/app1/app1/doctype/service_details/service_details.json b/app1/app1/doctype/service_details/service_details.json index 6e07ba8..9c5136e 100644 --- a/app1/app1/doctype/service_details/service_details.json +++ b/app1/app1/doctype/service_details/service_details.json @@ -30,7 +30,7 @@ "label": "Service Item", "length": 0, "no_copy": 0, - "options": "Change Engine oil\nReplace oil filter\nReplace fuel filter\nReplace air filter\nReplace cabin filter\nReplace spark plugs\nChange transmissin fluid\nCheck brakepads\nCheck brake fluid\nCheck tire condition", + "options": "Change Engine oil\nReplace oil filter\nReplace fuel filter\nReplace air filter\nReplace cabin filter\nReplace spark plugs\nChange transmission fluid\nCheck brakepads\nCheck brake fluid\nCheck tire condition", "permlevel": 0, "precision": "", "print_hide": 0, @@ -110,8 +110,9 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "", "fieldname": "currency", - "fieldtype": "Currency", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -122,6 +123,7 @@ "label": "Currency", "length": 0, "no_copy": 0, + "options": "Currency", "permlevel": 0, "precision": "", "print_hide": 0, @@ -133,6 +135,36 @@ "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, @@ -145,7 +177,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-11-15 13:25:45.943323", + "modified": "2018-11-16 10:11:38.865135", "modified_by": "Administrator", "module": "App1", "name": "Service Details", 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_servicing_log/vehicle_servicing_log.py b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py index 20407e3..a191327 100644 --- a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py @@ -8,3 +8,11 @@ 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 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 191a76d..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-15 11:45:45.120316", + "modified": "2018-11-15 22:55:45.976887", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Trip Log", diff --git a/app1/hooks.py b/app1/hooks.py index 2c7d52c..1e2bf00 100644 --- a/app1/hooks.py +++ b/app1/hooks.py @@ -30,7 +30,8 @@ doctype_js = { "Salary Slip" : "public/js/custom_hr.js", "Vehicle" : "public/js/filter_model.js", -"Vehicle" : "public/js/add_button.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"} @@ -88,12 +89,15 @@ "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":{ + # "before_save":"app1.utils.hr.setMileage" + # } } diff --git a/app1/public/js/add_button.js b/app1/public/js/add_button.js index ec4b2fa..d0da38f 100644 --- a/app1/public/js/add_button.js +++ b/app1/public/js/add_button.js @@ -1,12 +1,11 @@ frappe.ui.form.on('Vehicle', { - refresh: function(frm){ - var name__=cur_frm.doc.name - console.log(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")) -} +// 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/calculate_total.js b/app1/public/js/calculate_total.js new file mode 100644 index 0000000..0fb9926 --- /dev/null +++ b/app1/public/js/calculate_total.js @@ -0,0 +1,25 @@ +frappe.ui.form.on("Vehicle Servicing Log",{ +before_save:function(frm){ + 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) + + }, + validate: function(frm) { + console.log("Hello"); + frappe.call({ + "method": "frappe.client.set_value", + "args": { + "doctype": "Vehicle", + "name": frm.doc.vehicle, + "fieldname": "odometer_2", + "value": frm.doc.odometer + } + }); + } +}) diff --git a/app1/public/js/filter_model.js b/app1/public/js/filter_model.js index e984faf..23b1961 100644 --- a/app1/public/js/filter_model.js +++ b/app1/public/js/filter_model.js @@ -1,11 +1,22 @@ 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 + "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")) +} }) diff --git a/app1/utils/hr.py b/app1/utils/hr.py index 9e9031d..a49cf42 100644 --- a/app1/utils/hr.py +++ b/app1/utils/hr.py @@ -28,28 +28,36 @@ 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 + # year_1=self.model_year + # self.vmn=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 + 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_.odometer_2=self.mileage + vehicle_.save() return -@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) From 5f25e9c38d1184afe72250bf7def11fa0b9f27bd Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Sat, 17 Nov 2018 23:44:26 +0100 Subject: [PATCH 14/16] Fleet management and Attendance based salary --- .../service_details/service_details.json | 31 ++++++++++++++- .../vehicle_servicing_log.json | 31 ++++++++++++++- app1/hooks.py | 4 +- app1/public/js/add_button.js | 4 +- app1/public/js/calculate_total.js | 38 ++++++++++++------- app1/public/js/filter_model.js | 10 ++++- app1/utils/hr.py | 12 +++--- 7 files changed, 103 insertions(+), 27 deletions(-) diff --git a/app1/app1/doctype/service_details/service_details.json b/app1/app1/doctype/service_details/service_details.json index 9c5136e..63fd740 100644 --- a/app1/app1/doctype/service_details/service_details.json +++ b/app1/app1/doctype/service_details/service_details.json @@ -104,6 +104,35 @@ "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, @@ -177,7 +206,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-11-16 10:11:38.865135", + "modified": "2018-11-17 17:17:03.513795", "modified_by": "Administrator", "module": "App1", "name": "Service Details", diff --git a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json index e76b613..b48de5a 100644 --- a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json @@ -103,6 +103,35 @@ "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, @@ -236,7 +265,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-15 13:32:50.985049", + "modified": "2018-11-17 17:17:35.981106", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Servicing Log", diff --git a/app1/hooks.py b/app1/hooks.py index 1e2bf00..a8a796d 100644 --- a/app1/hooks.py +++ b/app1/hooks.py @@ -95,9 +95,7 @@ "Vehicle":{ "before_save":"app1.utils.hr.setVehicleName" }, - # "Vehicle Servicing Log":{ - # "before_save":"app1.utils.hr.setMileage" - # } + } diff --git a/app1/public/js/add_button.js b/app1/public/js/add_button.js index d0da38f..059e719 100644 --- a/app1/public/js/add_button.js +++ b/app1/public/js/add_button.js @@ -1,4 +1,4 @@ -frappe.ui.form.on('Vehicle', { +//frappe.ui.form.on('Vehicle', { // refresh: function(frm){ // var name__=cur_frm.doc.name // cur_frm.add_custom_button(("Vehicle Servicing Log"),function(ev){ @@ -8,4 +8,4 @@ frappe.ui.form.on('Vehicle', { // frappe.set_route("List","Vehicle Trip Log",{"vehicle":name__}) // },("Show")) // } -}) +//}) diff --git a/app1/public/js/calculate_total.js b/app1/public/js/calculate_total.js index 0fb9926..99bd9d3 100644 --- a/app1/public/js/calculate_total.js +++ b/app1/public/js/calculate_total.js @@ -1,5 +1,6 @@ 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; @@ -8,18 +9,29 @@ before_save:function(frm){ 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); - }, - validate: function(frm) { - console.log("Hello"); - frappe.call({ - "method": "frappe.client.set_value", - "args": { - "doctype": "Vehicle", - "name": frm.doc.vehicle, - "fieldname": "odometer_2", - "value": frm.doc.odometer } - }); - } -}) + } + }); + 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 index 23b1961..126d9af 100644 --- a/app1/public/js/filter_model.js +++ b/app1/public/js/filter_model.js @@ -1,6 +1,6 @@ frappe.ui.form.on('Vehicle',{ vehicle_make: function(frm){ - console.log("At the beginning") + //console.log("At the beginning") cur_frm.fields_dict["vehicle_model"].get_query=function(doc){ return { filters:{ @@ -18,5 +18,13 @@ cur_frm.add_custom_button(("Vehicle Servicing Log"),function(ev){ 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.set_route("Form","Vehicle Servicing Log",cur_frm.doc.name) +},("Create")) +console.log(name__); +cur_frm.add_custom_button(("Vehicle Trip Log"),function(ev){ + console.log(name__+ "45") +},("Create")) +; } }) diff --git a/app1/utils/hr.py b/app1/utils/hr.py index a49cf42..4e61b0c 100644 --- a/app1/utils/hr.py +++ b/app1/utils/hr.py @@ -55,9 +55,9 @@ def setVehicleName(self,ev): # 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_.odometer_2=self.mileage - vehicle_.save() - return +# 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 From 0ce903536bb09ef13d8b872ae8062f53ad7ea054 Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Tue, 20 Nov 2018 23:41:13 +0100 Subject: [PATCH 15/16] implemented child table autopopulation --- .../service_details/service_details.json | 43 +++--------------- .../vehicle_servicing_log.js | 44 +++++++++++++++++-- .../vehicle_servicing_log.json | 2 +- .../vehicle_servicing_log.py | 12 +++++ app1/hooks.py | 3 ++ app1/tasks/all.py | 11 +++-- app1/utils/hr.py | 18 ++++++++ 7 files changed, 89 insertions(+), 44 deletions(-) diff --git a/app1/app1/doctype/service_details/service_details.json b/app1/app1/doctype/service_details/service_details.json index 63fd740..b5e1ff8 100644 --- a/app1/app1/doctype/service_details/service_details.json +++ b/app1/app1/doctype/service_details/service_details.json @@ -9,7 +9,7 @@ "docstatus": 0, "doctype": "DocType", "document_type": "", - "editable_grid": 1, + "editable_grid": 0, "engine": "InnoDB", "fields": [ { @@ -19,7 +19,7 @@ "collapsible": 0, "columns": 0, "fieldname": "service_item", - "fieldtype": "Select", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -30,7 +30,7 @@ "label": "Service Item", "length": 0, "no_copy": 0, - "options": "Change Engine oil\nReplace oil filter\nReplace fuel filter\nReplace air filter\nReplace cabin filter\nReplace spark plugs\nChange transmission fluid\nCheck brakepads\nCheck brake fluid\nCheck tire condition", + "options": "Service Type", "permlevel": 0, "precision": "", "print_hide": 0, @@ -56,7 +56,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Type", "length": 0, @@ -69,7 +69,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 @@ -104,35 +104,6 @@ "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, @@ -160,7 +131,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 @@ -206,7 +177,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-11-17 17:17:03.513795", + "modified": "2018-11-20 23:02:05.207565", "modified_by": "Administrator", "module": "App1", "name": "Service Details", diff --git a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js index c9f8b2d..f241373 100644 --- a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.js @@ -1,8 +1,44 @@ // Copyright (c) 2018, frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('Vehicle Servicing Log', { - refresh: function(frm) { - console.log("on log"); +// 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 index b48de5a..3ac77dc 100644 --- a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.json @@ -265,7 +265,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-17 17:17:35.981106", + "modified": "2018-11-20 10:59:05.758867", "modified_by": "Administrator", "module": "App1", "name": "Vehicle Servicing Log", diff --git a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py index a191327..ee7accb 100644 --- a/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py +++ b/app1/app1/doctype/vehicle_servicing_log/vehicle_servicing_log.py @@ -16,3 +16,15 @@ def autoname(self): 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/hooks.py b/app1/hooks.py index 57c9b0b..36f2a90 100644 --- a/app1/hooks.py +++ b/app1/hooks.py @@ -94,6 +94,9 @@ "Vehicle":{ "before_save":"app1.utils.hr.setVehicleName" }, + # "Vehicle Servicing Log":{ + # "vehicle":"app1.app1.doctype.vehicle_servicing_log.vehicle_servicing_log.setChildTable" + # } } diff --git a/app1/tasks/all.py b/app1/tasks/all.py index a68289c..49ee7f1 100644 --- a/app1/tasks/all.py +++ b/app1/tasks/all.py @@ -1,6 +1,11 @@ +from __future__ import unicode_literals import frappe +from frappe.utils import date_diff, nowdate + def setter(): - doc=frappe.get_doc("Vehicle","ML 350-2013_HU-432-REH") - doc.odometer_2=10 - return + 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/utils/hr.py b/app1/utils/hr.py index 4057292..0ca5011 100644 --- a/app1/utils/hr.py +++ b/app1/utils/hr.py @@ -62,3 +62,21 @@ def setVehicleName(self,ev): # 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) From 7244f97f872816d1e02601dd0b22a9e254843bd9 Mon Sep 17 00:00:00 2001 From: talleyrand333 Date: Tue, 20 Nov 2018 23:45:18 +0100 Subject: [PATCH 16/16] initial file commit --- initial | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 initial diff --git a/initial b/initial new file mode 100644 index 0000000..e69de29