diff --git a/bloomstack_core/bloomstack_core/doctype/authorization_request/__init__.py b/bloomstack_core/bloomstack_core/doctype/authorization_request/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.js b/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.js
new file mode 100644
index 000000000..657146f2b
--- /dev/null
+++ b/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.js
@@ -0,0 +1,6 @@
+// Copyright (c) 2019, Bloom Stack, Inc and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Authorization Request", {
+
+});
diff --git a/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.json b/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.json
new file mode 100644
index 000000000..c1df7f9b7
--- /dev/null
+++ b/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.json
@@ -0,0 +1,495 @@
+{
+ "allow_copy": 0,
+ "allow_events_in_timeline": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "AR.######",
+ "beta": 0,
+ "creation": "2019-05-10 04:18:42.975410",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 0,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "linked_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Linked 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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "linked_docname",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Linked DocName",
+ "length": 0,
+ "no_copy": 0,
+ "options": "linked_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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "",
+ "fetch_if_empty": 0,
+ "fieldname": "authorizer_email",
+ "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": "Authorizer Email",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "cb",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "signature",
+ "fieldtype": "Signature",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Signature",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "signee_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": "Signee Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "token",
+ "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": "Token",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "token_generated_on",
+ "fieldtype": "Datetime",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Token Generated On",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "request_link",
+ "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": "Request Link",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "section_break_13",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Request Sent\nApproved\nRejected",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amended From",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Authorization Request",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2019-06-06 03:26:19.653725",
+ "modified_by": "Administrator",
+ "module": "Bloomstack Core",
+ "name": "Authorization Request",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "ASC",
+ "track_changes": 1,
+ "track_seen": 0,
+ "track_views": 0
+}
\ No newline at end of file
diff --git a/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.py b/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.py
new file mode 100644
index 000000000..eef97cb8d
--- /dev/null
+++ b/bloomstack_core/bloomstack_core/doctype/authorization_request/authorization_request.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Bloom Stack, Inc 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 import now_datetime
+from frappe.utils.data import get_url
+from erpnext import get_default_company
+
+class AuthorizationRequest(Document):
+ def after_insert(self):
+ self.generate_token()
+ self.send_authorization_request()
+ self.status = "Request Sent"
+
+ def generate_token(self):
+ self.token = frappe.generate_hash(self.name, 32)
+ self.token_generated_on = now_datetime()
+ self.request_link = "{0}/authorize_document?token={1}&name={2}".format(get_url(), self.token, self.name)
+ self.save()
+
+ def send_authorization_request(self):
+ """
+ Email the link to user for them to authorize the document
+ """
+
+ doc = frappe.get_doc(self.linked_doctype, self.linked_docname)
+ company = doc.company if hasattr(doc, 'company') else get_default_company()
+
+ subject = "{0} requests your authorization on {1}".format(company, self.linked_doctype)
+ message = frappe.render_template("templates/emails/authorization_request.html", {
+ "authorization_request": self,
+ })
+
+ frappe.sendmail(recipients=[self.authorizer_email], subject=subject, message=message)
\ No newline at end of file
diff --git a/bloomstack_core/bloomstack_core/doctype/authorization_request/test_authorization_request.js b/bloomstack_core/bloomstack_core/doctype/authorization_request/test_authorization_request.js
new file mode 100644
index 000000000..ec8cade49
--- /dev/null
+++ b/bloomstack_core/bloomstack_core/doctype/authorization_request/test_authorization_request.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: Authorization Request", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Authorization Request
+ () => frappe.tests.make('Authorization Request', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/bloomstack_core/bloomstack_core/doctype/authorization_request/test_authorization_request.py b/bloomstack_core/bloomstack_core/doctype/authorization_request/test_authorization_request.py
new file mode 100644
index 000000000..04f34047c
--- /dev/null
+++ b/bloomstack_core/bloomstack_core/doctype/authorization_request/test_authorization_request.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Bloom Stack, Inc and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestAuthorizationRequest(unittest.TestCase):
+ pass
diff --git a/bloomstack_core/hooks.py b/bloomstack_core/hooks.py
index 4e523ebe0..1759cf385 100755
--- a/bloomstack_core/hooks.py
+++ b/bloomstack_core/hooks.py
@@ -53,7 +53,9 @@
"Company": "public/js/company.js",
"Supplier": "public/js/supplier.js",
"Item": "public/js/item.js",
- "Batch": "public/js/batch.js"
+ "Batch": "public/js/batch.js",
+ "Quotation": "public/js/quotation.js",
+ "Contract": "public/js/contract.js"
}
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
diff --git a/bloomstack_core/public/js/contract.js b/bloomstack_core/public/js/contract.js
new file mode 100644
index 000000000..fc124a25b
--- /dev/null
+++ b/bloomstack_core/public/js/contract.js
@@ -0,0 +1,44 @@
+frappe.ui.form.on("Contract", {
+ refresh: (frm) => {
+ if (!frm.is_new() && frm.doc.docstatus === 0) {
+ if (!frm.doc.customer_signature) {
+ frm.add_custom_button(__("Authorize"), () => {
+ frappe.prompt([
+ {
+ "label": "Contact Email",
+ "fieldtype": "Data",
+ "options": "Email",
+ "fieldname": "contact_email",
+ "default": frm.doc.party_user,
+ "reqd": 1
+ },
+ {
+ "label": "Contact Person",
+ "fieldtype": "Data",
+ "fieldname": "contact_person",
+ "default": frm.doc.party_name
+ }
+ ],
+ function (data) {
+ frappe.call({
+ method: "bloomstack_core.utils.create_authorization_request",
+ args: {
+ dt: frm.doc.doctype,
+ dn: frm.doc.name,
+ contact_email: data.contact_email,
+ contact_name: data.contact_person
+ },
+ callback: (r) => {
+ if (!r.exc) {
+ frappe.msgprint(__(`${frm.doc.name} has been successfully sent to ${data.contact_email}`))
+ }
+ }
+ })
+ },
+ __("Send Authorization Request"))
+ }).addClass("btn-primary");
+
+ }
+ }
+ },
+});
\ No newline at end of file
diff --git a/bloomstack_core/public/js/quotation.js b/bloomstack_core/public/js/quotation.js
new file mode 100644
index 000000000..bcae8f346
--- /dev/null
+++ b/bloomstack_core/public/js/quotation.js
@@ -0,0 +1,45 @@
+frappe.ui.form.on('Quotation', {
+ refresh: (frm) => {
+ if (!frm.is_new() && frm.doc.docstatus === 0) {
+ if (!frm.doc.customer_signature) {
+ frm.add_custom_button(__("Authorize"), () => {
+ frappe.prompt([
+ {
+ "label": "Contact Email",
+ "fieldtype": "Data",
+ "options": "Email",
+ "fieldname": "contact_email",
+ "default": frm.doc.contact_email,
+ "reqd": 1
+ },
+ {
+ "label": "Contact Person",
+ "fieldtype": "Data",
+ "fieldname": "contact_person",
+ "default": frm.doc.contact_person
+ }
+ ],
+ function (data) {
+ frappe.call({
+ method: "bloomstack_core.utils.create_authorization_request",
+ args: {
+ dt: frm.doc.doctype,
+ dn: frm.doc.name,
+ contact_email: data.contact_email,
+ contact_name: data.contact_person
+ },
+ callback: (r) => {
+ if (!r.exc) {
+ frappe.msgprint(__(`The document ${frm.doc.name} has been successfully emailed to ${data.contact_person}`))
+ }
+ }
+ })
+ },
+ __("Send Authorization Request"))
+ }).addClass("btn-primary");
+
+ }
+ }
+ },
+
+});
diff --git a/bloomstack_core/templates/emails/authorization_request.html b/bloomstack_core/templates/emails/authorization_request.html
new file mode 100644
index 000000000..cc97a5d61
--- /dev/null
+++ b/bloomstack_core/templates/emails/authorization_request.html
@@ -0,0 +1,971 @@
+
+
+
+
+
+
+
+
+ *|MC:SUBJECT|*
+
+
+
+
+ *|MC_PREVIEW_TEXT|*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Authorization Request
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+You are requested to authorize a document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Happy Blooming!
+Bloom Bot
+
+PS: This is an auto-generated email. If you'd like any technical support, please send an email to my humans - support@bloomstack.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bloomstack_core/templates/includes/sign_document.js b/bloomstack_core/templates/includes/sign_document.js
new file mode 100644
index 000000000..425a9d207
--- /dev/null
+++ b/bloomstack_core/templates/includes/sign_document.js
@@ -0,0 +1,43 @@
+$(document).ready(function () {
+ var $sigdiv = $("#signature");
+ $sigdiv.jSignature(); // inits the jSignature widget.
+ $sigdiv.jSignature("reset"); // clears the canvas and rerenders the decor on it.
+
+ $("#approveDocument").on("click", function () {
+ var sign = $sigdiv.jSignature("getData");
+ var signee = $("#signee").val();
+ if ($sigdiv.jSignature('getData', 'native').length != 0 && signee) { // proceed only if user has put signature and signee name.
+ $(".user-signature").hide();
+ frappe.call({
+ method: "bloomstack_core.utils.authorize_document",
+ args: {
+ sign: sign,
+ signee: signee,
+ docname: "{{ auth_req_docname }}",
+ },
+ freeze: true,
+ callback: (r) => {
+ frappe.msgprint(__("The document has been approved by you!"));
+ }
+ });
+ }
+ else {
+
+ frappe.throw(__("Please put your name and signature!"));
+ }
+ });
+
+ $("#rejectDocument").on("click", function () {
+ $(".user-signature").hide();
+ frappe.call({
+ method: "bloomstack_core.utils.reject_document",
+ args: {
+ docname: "{{ auth_req_docname }}",
+ },
+ freeze: true,
+ callback: (r) => {
+ frappe.msgprint(__("The document has been rejected by you!"));
+ }
+ })
+ });
+});
\ No newline at end of file
diff --git a/bloomstack_core/utils.py b/bloomstack_core/utils.py
index d17ac3083..8f971af7b 100755
--- a/bloomstack_core/utils.py
+++ b/bloomstack_core/utils.py
@@ -78,3 +78,40 @@ def log_request(endpoint, request_data, response, ref_dt=None, ref_dn=None):
})
request.insert()
frappe.db.commit()
+
+
+@frappe.whitelist(allow_guest=True)
+def authorize_document(sign=None, signee=None, docname=None):
+ if frappe.db.exists("Authorization Request", docname):
+ authorization_request = frappe.get_doc("Authorization Request", docname)
+ authorization_request.signature = sign
+ authorization_request.signee_name = signee
+ authorization_request.status = "Approved"
+ authorization_request.flags.ignore_permissions = True
+ authorization_request.save()
+
+ authorized_doc = frappe.get_doc(authorization_request.linked_doctype, authorization_request.linked_docname)
+ if hasattr(authorized_doc, "is_signed") and hasattr(authorized_doc, "authorizer_signature") and hasattr(authorized_doc, "signee"):
+ if authorized_doc.is_signed == 0:
+ authorized_doc.is_signed = 1
+ authorized_doc.authorizer_signature = sign
+ authorized_doc.signee = signee
+
+ authorized_doc.submit()
+
+
+@frappe.whitelist(allow_guest=True)
+def reject_document(docname):
+ if frappe.db.exists("Authorization Request", docname):
+ authorization_request = frappe.get_doc("Authorization Request", docname)
+ authorization_request.status = "Rejected"
+ authorization_request.save()
+
+
+@frappe.whitelist()
+def create_authorization_request(dt, dn, contact_email, contact_name=None):
+ new_authorization_request = frappe.new_doc("Authorization Request")
+ new_authorization_request.linked_doctype = dt
+ new_authorization_request.linked_docname = dn
+ new_authorization_request.authorizer_email = contact_email
+ new_authorization_request.save()
\ No newline at end of file
diff --git a/bloomstack_core/www/__init__.py b/bloomstack_core/www/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/bloomstack_core/www/authorize_document.html b/bloomstack_core/www/authorize_document.html
new file mode 100644
index 000000000..fb85bea75
--- /dev/null
+++ b/bloomstack_core/www/authorize_document.html
@@ -0,0 +1,260 @@
+{% extends "templates/web.html" %}
+{% block title %} Authorize Document {% endblock %}
+{% block style %}
+
+{% endblock %}
+{% block page_content %}
+{% if error %}
+{{ error }}
+{% else %}
+
+
+
{{company}} has requested you to authorize a {{doc.doctype}}
+
+
+
+
+
+ {{ print_doc }}
+
+
+
+
+
+ Your Name:
+
+ Signature:
+
+
+
+
+
+{% endif %}
+{% endblock %}
+
+{% block script %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/bloomstack_core/www/authorize_document.py b/bloomstack_core/www/authorize_document.py
new file mode 100644
index 000000000..0a0c091af
--- /dev/null
+++ b/bloomstack_core/www/authorize_document.py
@@ -0,0 +1,27 @@
+from __future__ import unicode_literals
+from erpnext import get_default_company
+import frappe
+import re
+no_cache = 1
+
+
+def get_context(context):
+ token = frappe.local.request.args.get("token")
+ docname = frappe.local.request.args.get("name")
+ auth_req = frappe.get_doc("Authorization Request", docname)
+
+ if not token or token != auth_req.token:
+ context.error = "Token is not valid. Click here to re-send email"
+
+ elif auth_req.status == "Approved":
+ context.error = "This document has already been authorized by you!"
+
+ elif auth_req.status == "Rejected":
+ context.error = "This document has already been rejected by you!"
+
+ frappe.local.flags.ignore_print_permissions = True
+ print_doc = frappe.get_print(auth_req.linked_doctype, auth_req.linked_docname)
+ context.print_doc = print_doc[print_doc.find('')+len(''):len(print_doc)-len('')]
+ context.doc = frappe.get_doc(auth_req.linked_doctype, auth_req.linked_docname)
+ context.company = context.doc.company if hasattr(context.doc, 'company') else get_default_company()
+ context.auth_req_docname = docname