* [fix] remove razorpay from integration service * [fix] clean-up js side integration service dependancies * [fix] remove integration service, .py cleanup, move integration request to integration * [fix] move oauth to integrations and deprecate integration broker * [fix] mark services enabled and update integrations listing * [fix] V7.1 and V7.2 integration service related pathch fixes and move payment gateway dotype to core module * [fix] create payment gateway records if not exists * [fix] module page for integrations * [minor][fix] minor checks * [fix] remove integration broker module only if not allocated to any doctypeversion-14
@@ -133,7 +133,7 @@ def validate_oauth(): | |||||
form_dict = frappe.local.form_dict | form_dict = frappe.local.form_dict | ||||
authorization_header = frappe.get_request_header("Authorization").split(" ") if frappe.get_request_header("Authorization") else None | authorization_header = frappe.get_request_header("Authorization").split(" ") if frappe.get_request_header("Authorization") else None | ||||
if authorization_header and authorization_header[0].lower() == "bearer": | if authorization_header and authorization_header[0].lower() == "bearer": | ||||
from frappe.integration_broker.oauth2 import get_oauth_server | |||||
from frappe.integrations.oauth2 import get_oauth_server | |||||
token = authorization_header[1] | token = authorization_header[1] | ||||
r = frappe.request | r = frappe.request | ||||
parsed_url = urlparse(r.url) | parsed_url = urlparse(r.url) | ||||
@@ -36,6 +36,13 @@ def get_data(): | |||||
"type": "module", | "type": "module", | ||||
"hidden": 1 | "hidden": 1 | ||||
}, | }, | ||||
{ | |||||
"module_name": "Integrations", | |||||
"color": "#16a085", | |||||
"icon": "octicon octicon-globe", | |||||
"type": "module", | |||||
"hidden": 1 | |||||
}, | |||||
{ | { | ||||
"module_name": "Setup", | "module_name": "Setup", | ||||
"color": "#bdc3c7", | "color": "#bdc3c7", | ||||
@@ -0,0 +1,57 @@ | |||||
from __future__ import unicode_literals | |||||
from frappe import _ | |||||
def get_data(): | |||||
return [ | |||||
{ | |||||
"label": _("Payments"), | |||||
"icon": "fa fa-star", | |||||
"items": [ | |||||
{ | |||||
"type": "doctype", | |||||
"name": "PayPal Settings", | |||||
"description": _("PayPal payment gateway settings"), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Razorpay Settings", | |||||
"description": _("Razorpay Payment gateway settings"), | |||||
}, | |||||
] | |||||
}, | |||||
{ | |||||
"label": _("Backup"), | |||||
"items": [ | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Dropbox Settings", | |||||
"description": _("Dropbox backup settings"), | |||||
}, | |||||
] | |||||
}, | |||||
{ | |||||
"label": _("Authentication"), | |||||
"items": [ | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Social Login Keys", | |||||
"description": _("Enter keys to enable login via Facebook, Google, GitHub."), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "LDAP Settings", | |||||
"description": _("Ldap settings"), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "OAuth Client", | |||||
"description": _("Register OAuth Client App"), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "OAuth Provider Settings", | |||||
"description": _("Settings for OAuth Provider"), | |||||
}, | |||||
] | |||||
} | |||||
] |
@@ -204,39 +204,6 @@ def get_data(): | |||||
}, | }, | ||||
] | ] | ||||
}, | }, | ||||
{ | |||||
"label": _("Integrations"), | |||||
"icon": "fa fa-star", | |||||
"items": [ | |||||
{ | |||||
"type": "page", | |||||
"name": "applications", | |||||
"label": _("Application Installer"), | |||||
"description": _("Install Applications."), | |||||
"icon": "fa fa-download" | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Social Login Keys", | |||||
"description": _("Enter keys to enable login via Facebook, Google, GitHub."), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "Integration Service", | |||||
"description": _("Centralize access to Integrations"), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "OAuth Client", | |||||
"description": _("Register OAuth Client App"), | |||||
}, | |||||
{ | |||||
"type": "doctype", | |||||
"name": "OAuth Provider Settings", | |||||
"description": _("Settings for OAuth Provider"), | |||||
}, | |||||
] | |||||
}, | |||||
{ | { | ||||
"label": _("Customize"), | "label": _("Customize"), | ||||
"icon": "fa fa-glass", | "icon": "fa fa-glass", | ||||
@@ -277,6 +244,18 @@ def get_data(): | |||||
] | ] | ||||
}, | }, | ||||
{ | |||||
"label": _("Applications"), | |||||
"items":[ | |||||
{ | |||||
"type": "page", | |||||
"name": "applications", | |||||
"label": _("Application Installer"), | |||||
"description": _("Install Applications."), | |||||
"icon": "fa fa-download" | |||||
}, | |||||
] | |||||
} | |||||
] | ] | ||||
add_setup_section(data, "frappe", "website", _("Website"), "fa fa-globe") | add_setup_section(data, "frappe", "website", _("Website"), "fa fa-globe") | ||||
return data | return data |
@@ -0,0 +1,8 @@ | |||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors | |||||
// For license information, please see license.txt | |||||
frappe.ui.form.on('Payment Gateway', { | |||||
refresh: function(frm) { | |||||
} | |||||
}); |
@@ -0,0 +1,91 @@ | |||||
{ | |||||
"allow_copy": 0, | |||||
"allow_guest_to_view": 0, | |||||
"allow_import": 0, | |||||
"allow_rename": 0, | |||||
"autoname": "field:gateway", | |||||
"beta": 0, | |||||
"creation": "2015-12-15 22:26:45.221162", | |||||
"custom": 0, | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "", | |||||
"editable_grid": 1, | |||||
"fields": [ | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "gateway", | |||||
"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": "Gateway", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"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": 1, | |||||
"is_submittable": 0, | |||||
"issingle": 0, | |||||
"istable": 0, | |||||
"max_attachments": 0, | |||||
"modified": "2017-03-09 12:40:56.176464", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "Payment Gateway", | |||||
"name_case": "", | |||||
"owner": "Administrator", | |||||
"permissions": [ | |||||
{ | |||||
"amend": 0, | |||||
"apply_user_permissions": 0, | |||||
"cancel": 0, | |||||
"create": 0, | |||||
"delete": 0, | |||||
"email": 0, | |||||
"export": 0, | |||||
"if_owner": 0, | |||||
"import": 0, | |||||
"permlevel": 0, | |||||
"print": 0, | |||||
"read": 1, | |||||
"report": 0, | |||||
"role": "System Manager", | |||||
"set_user_permissions": 0, | |||||
"share": 0, | |||||
"submit": 0, | |||||
"write": 0 | |||||
} | |||||
], | |||||
"quick_entry": 1, | |||||
"read_only": 0, | |||||
"read_only_onload": 0, | |||||
"show_name_in_global_search": 0, | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_changes": 0, | |||||
"track_seen": 0 | |||||
} |
@@ -1,10 +1,10 @@ | |||||
# -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
# Copyright (c) 2015, Frappe Technologies and contributors | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors | |||||
# For license information, please see license.txt | # For license information, please see license.txt | ||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
class IntegrationServiceParameter(Document): | |||||
pass | |||||
class PaymentGateway(Document): | |||||
pass |
@@ -0,0 +1,12 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
import unittest | |||||
# test_records = frappe.get_test_records('Payment Gateway') | |||||
class TestPaymentGateway(unittest.TestCase): | |||||
pass |
@@ -125,6 +125,7 @@ scheduler_events = { | |||||
"frappe.email.doctype.email_account.email_account.pull", | "frappe.email.doctype.email_account.email_account.pull", | ||||
"frappe.email.doctype.email_account.email_account.notify_unreplied", | "frappe.email.doctype.email_account.email_account.notify_unreplied", | ||||
"frappe.oauth.delete_oauth2_data", | "frappe.oauth.delete_oauth2_data", | ||||
"frappe.integrations.doctype.razorpay_settings.razorpay_settings.capture_payment" | |||||
], | ], | ||||
"hourly": [ | "hourly": [ | ||||
"frappe.model.utils.link_count.update_link_count", | "frappe.model.utils.link_count.update_link_count", | ||||
@@ -145,7 +146,11 @@ scheduler_events = { | |||||
"frappe.email.doctype.auto_email_report.auto_email_report.send_daily", | "frappe.email.doctype.auto_email_report.auto_email_report.send_daily", | ||||
"frappe.desk.page.backups.backups.delete_downloadable_backups", | "frappe.desk.page.backups.backups.delete_downloadable_backups", | ||||
"frappe.core.doctype.feedback_request.feedback_request.delete_feedback_request", | "frappe.core.doctype.feedback_request.feedback_request.delete_feedback_request", | ||||
"frappe.core.doctype.authentication_log.authentication_log.clear_authentication_logs" | |||||
"frappe.core.doctype.authentication_log.authentication_log.clear_authentication_logs", | |||||
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily" | |||||
], | |||||
"weekly": [ | |||||
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_weekly" | |||||
], | ], | ||||
"monthly": [ | "monthly": [ | ||||
"frappe.email.doctype.auto_email_report.auto_email_report.send_monthly" | "frappe.email.doctype.auto_email_report.auto_email_report.send_monthly" | ||||
@@ -179,5 +184,3 @@ bot_parsers = [ | |||||
setup_wizard_exception = "frappe.desk.page.setup_wizard.setup_wizard.email_setup_wizard_exception" | setup_wizard_exception = "frappe.desk.page.setup_wizard.setup_wizard.email_setup_wizard_exception" | ||||
before_write_file = "frappe.limits.validate_space_limit" | before_write_file = "frappe.limits.validate_space_limit" | ||||
integration_services = ["PayPal", "Razorpay", "Dropbox", "LDAP"] |
@@ -1,78 +0,0 @@ | |||||
// Copyright (c) 2016, Frappe Technologies and contributors | |||||
// For license information, please see license.txt | |||||
frappe.provide("frappe.integration_service"); | |||||
{% include 'frappe/integrations/doctype/razorpay_settings/razorpay_settings.js' %} | |||||
{% include 'frappe/integrations/doctype/paypal_settings/paypal_settings.js' %} | |||||
{% include 'frappe/integrations/doctype/dropbox_settings/dropbox_settings.js' %} | |||||
{% include 'frappe/integrations/doctype/ldap_settings/ldap_settings.js' %} | |||||
frappe.ui.form.on('Integration Service', { | |||||
onload: function(frm) { | |||||
frappe.call({ | |||||
method: "frappe.integration_broker.doctype.integration_service.integration_service.get_integration_services", | |||||
callback: function(r){ | |||||
set_field_options("service", r.message) | |||||
} | |||||
}) | |||||
}, | |||||
refresh: function(frm){ | |||||
if (frm.doc.service){ | |||||
frm.events.setup_custom_buttons(frm); | |||||
frm.events.setup_service_details(frm); | |||||
} | |||||
}, | |||||
service: function(frm){ | |||||
frm.events.setup_custom_buttons(frm); | |||||
frm.events.setup_service_details(frm); | |||||
}, | |||||
setup_custom_buttons: function(frm) { | |||||
frm.clear_custom_buttons(); | |||||
frm.add_custom_button(__("{0} Settings", [frm.doc.service]), function(){ | |||||
frappe.set_route("List", frm.doc.service + " Settings"); | |||||
}); | |||||
frm.add_custom_button(__("Show Log"), function(){ | |||||
frappe.route_options = {"integration_request_service": frm.doc.service}; | |||||
frappe.set_route("List", "Integration Request"); | |||||
}); | |||||
}, | |||||
setup_service_details: function(frm) { | |||||
var service_name = frm.doc.service.toLowerCase().replace(/ /g, "_"); | |||||
var service_handelr = service_name + "_settings"; | |||||
service_handelr = new frappe.integration_service[service_handelr](); | |||||
service_handelr.get_service_info(frm); | |||||
var scheduler_job_info = service_handelr.get_scheduler_job_info(); | |||||
frm.events.render_scheduler_jobs(frm, scheduler_job_info); | |||||
}, | |||||
render_scheduler_jobs: function(frm, scheduler_job_info) { | |||||
var schduler_job_view = frm.fields_dict.scheduler_events_html.wrapper; | |||||
$(schduler_job_view).empty(); | |||||
$(schduler_job_view).append('\ | |||||
<table class="table table-bordered">\ | |||||
<thead>\ | |||||
<tr>\ | |||||
<th class="text-muted"> Event </th>\ | |||||
<th class="text-muted"> Action </th>\ | |||||
</tr>\ | |||||
</thead> \ | |||||
<tbody></tbody> \ | |||||
</table>') | |||||
$.each(scheduler_job_info, function(evnt, help){ | |||||
$row = $("<tr>").appendTo($(schduler_job_view).find("tbody")); | |||||
$("<td>").appendTo($row).html(evnt).css("font-size", "12px"); | |||||
$("<td>").appendTo($row).html(help).css("font-size", "12px"); | |||||
}) | |||||
} | |||||
}); |
@@ -1,230 +0,0 @@ | |||||
{ | |||||
"allow_copy": 0, | |||||
"allow_import": 0, | |||||
"allow_rename": 0, | |||||
"autoname": "field:service", | |||||
"beta": 0, | |||||
"creation": "2016-08-01 12:59:46.408707", | |||||
"custom": 0, | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "System", | |||||
"editable_grid": 1, | |||||
"fields": [ | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "service", | |||||
"fieldtype": "Select", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Service", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "", | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 0, | |||||
"remember_last_selected_value": 0, | |||||
"report_hide": 0, | |||||
"reqd": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"default": "0", | |||||
"fieldname": "enabled", | |||||
"fieldtype": "Check", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Enabled", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_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_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "section_break_3", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "integration_service_help", | |||||
"fieldtype": "HTML", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "", | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 0, | |||||
"remember_last_selected_value": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 1, | |||||
"columns": 0, | |||||
"fieldname": "background_events", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Background Events", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_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_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "scheduler_events_html", | |||||
"fieldtype": "HTML", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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 | |||||
} | |||||
], | |||||
"hide_heading": 0, | |||||
"hide_toolbar": 0, | |||||
"idx": 0, | |||||
"image_view": 0, | |||||
"in_create": 0, | |||||
"in_dialog": 0, | |||||
"is_submittable": 0, | |||||
"issingle": 0, | |||||
"istable": 0, | |||||
"max_attachments": 0, | |||||
"modified": "2016-12-29 14:40:30.774208", | |||||
"modified_by": "Administrator", | |||||
"module": "Integration Broker", | |||||
"name": "Integration Service", | |||||
"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, | |||||
"is_custom": 0, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "System Manager", | |||||
"set_user_permissions": 0, | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 1 | |||||
} | |||||
], | |||||
"quick_entry": 0, | |||||
"read_only": 0, | |||||
"read_only_onload": 0, | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_changes": 1, | |||||
"track_seen": 0 | |||||
} |
@@ -1,113 +0,0 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
from frappe import _ | |||||
from frappe.model.document import Document | |||||
import json, urlparse | |||||
from frappe.utils import get_request_session | |||||
class IntegrationService(Document): | |||||
def on_update(self): | |||||
if self.enabled: | |||||
if not self.flags.ignore_mandatory: | |||||
self.enable_service() | |||||
self.install_fixtures() | |||||
frappe.cache().delete_value('scheduler_events') | |||||
def install_fixtures(self): | |||||
pass | |||||
def enable_service(self): | |||||
service_doc = "{0} Settings".format(self.service) | |||||
frappe.get_doc(service_doc).enable() | |||||
#rest request handler | |||||
def get_request(self, url, auth=None, data=None): | |||||
if not auth: | |||||
auth = '' | |||||
if not data: | |||||
data = {} | |||||
try: | |||||
s = get_request_session() | |||||
frappe.flags.integration_request = s.get(url, data={}, auth=auth) | |||||
frappe.flags.integration_request.raise_for_status() | |||||
return frappe.flags.integration_request.json() | |||||
except Exception, exc: | |||||
frappe.log_error(frappe.get_traceback()) | |||||
raise exc | |||||
def post_request(self, url, auth=None, data=None): | |||||
if not auth: | |||||
auth = '' | |||||
if not data: | |||||
data = {} | |||||
try: | |||||
s = get_request_session() | |||||
res = s.post(url, data=data, auth=auth) | |||||
res.raise_for_status() | |||||
if res.headers.get("content-type") == "text/plain; charset=utf-8": | |||||
return urlparse.parse_qs(res.text) | |||||
return res.json() | |||||
except Exception, exc: | |||||
frappe.log_error() | |||||
raise exc | |||||
def put_request(url, auth=None, data=None): | |||||
pass | |||||
def create_request(self, data, integration_type, service_name, name=None): | |||||
if isinstance(data, basestring): | |||||
data = json.loads(data) | |||||
integration_request = frappe.get_doc({ | |||||
"doctype": "Integration Request", | |||||
"integration_type": integration_type, | |||||
"integration_request_service": service_name, | |||||
"reference_doctype": data.get("reference_doctype"), | |||||
"reference_docname": data.get("reference_docname"), | |||||
"data": json.dumps(data) | |||||
}) | |||||
if name: | |||||
integration_request.flags._name = name | |||||
integration_request.insert(ignore_permissions=True) | |||||
frappe.db.commit() | |||||
return integration_request | |||||
def get_integration_controller(service_name): | |||||
'''Returns integration controller module from app_name.integrations.{service}''' | |||||
try: | |||||
return frappe.get_doc("{0} Settings".format(service_name)) | |||||
except Exception: | |||||
frappe.throw(_("Module {service} not found".format(service=service_name))) | |||||
@frappe.whitelist() | |||||
def get_integration_services(): | |||||
services = [""] | |||||
for app in frappe.get_installed_apps(): | |||||
services.extend(frappe.get_hooks("integration_services", app_name = app)) | |||||
return services | |||||
def get_integration_service_events(): | |||||
'''Get scheduler events for enabled integrations''' | |||||
events = {} | |||||
for service in frappe.get_all("Integration Service", filters={"enabled": 1}, | |||||
fields=["name"]): | |||||
controller = get_integration_controller(service.name) | |||||
if hasattr(controller, "scheduler_events"): | |||||
for key, handlers in controller.scheduler_events.items(): | |||||
events.setdefault(key, []).extend(handlers) | |||||
return events |
@@ -1,26 +0,0 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies and Contributors | |||||
# See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
import unittest | |||||
from frappe.utils.scheduler import get_scheduler_events | |||||
# test_records = frappe.get_test_records('Integration Service') | |||||
class TestIntegrationService(unittest.TestCase): | |||||
def test_scheudler_events(self): | |||||
if not frappe.db.exists("Integration Service", "Dropbox"): | |||||
frappe.get_doc({ | |||||
"doctype": "Integration Service", | |||||
"service": "Dropbox" | |||||
}).insert(ignore_permissions=True) | |||||
frappe.db.set_value("Integration Service", "Dropbox", "enabled", 1) | |||||
frappe.cache().delete_key('scheduler_events') | |||||
events = get_scheduler_events('daily_long') | |||||
self.assertTrue('frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily' in events) | |||||
frappe.db.set_value("Integration Service", "Dropbox", "enabled", 0) |
@@ -1,113 +0,0 @@ | |||||
{ | |||||
"allow_copy": 0, | |||||
"allow_import": 0, | |||||
"allow_rename": 0, | |||||
"beta": 0, | |||||
"creation": "2016-08-01 12:51:31.838970", | |||||
"custom": 0, | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "System", | |||||
"editable_grid": 1, | |||||
"fields": [ | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"fieldname": "label", | |||||
"fieldtype": "Small Text", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 1, | |||||
"label": "Parameter", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "", | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 1, | |||||
"report_hide": 0, | |||||
"reqd": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"fieldname": "value", | |||||
"fieldtype": "Small Text", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 1, | |||||
"label": "Value", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"fieldname": "fieldname", | |||||
"fieldtype": "Data", | |||||
"hidden": 1, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Fieldname", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
} | |||||
], | |||||
"hide_heading": 0, | |||||
"hide_toolbar": 0, | |||||
"idx": 0, | |||||
"image_view": 0, | |||||
"in_create": 0, | |||||
"in_dialog": 0, | |||||
"is_submittable": 0, | |||||
"issingle": 0, | |||||
"istable": 1, | |||||
"max_attachments": 0, | |||||
"modified": "2016-08-03 05:36:47.261367", | |||||
"modified_by": "Administrator", | |||||
"module": "Integration Broker", | |||||
"name": "Integration Service Parameter", | |||||
"name_case": "", | |||||
"owner": "Administrator", | |||||
"permissions": [], | |||||
"quick_entry": 1, | |||||
"read_only": 0, | |||||
"read_only_onload": 0, | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_seen": 0 | |||||
} |
@@ -1,12 +0,0 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# License: GNU General Public License v3. See license.txt | |||||
from __future__ import unicode_literals | |||||
from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService | |||||
class IntegrationController(IntegrationService): | |||||
def __init__(self, setup=True): | |||||
'''Load the current controller data if setup is true''' | |||||
if setup: | |||||
super(IntegrationController, self).__init__('Integration Service', self.service_name) |
@@ -1,8 +1,6 @@ | |||||
// Copyright (c) 2016, Frappe Technologies and contributors | // Copyright (c) 2016, Frappe Technologies and contributors | ||||
// For license information, please see license.txt | // For license information, please see license.txt | ||||
frappe.provide("frappe.integration_service") | |||||
frappe.ui.form.on('Dropbox Settings', { | frappe.ui.form.on('Dropbox Settings', { | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
frm.clear_custom_buttons(); | frm.clear_custom_buttons(); | ||||
@@ -39,26 +37,3 @@ frappe.ui.form.on('Dropbox Settings', { | |||||
} | } | ||||
}); | }); | ||||
frappe.integration_service.dropbox_settings = Class.extend({ | |||||
init: function(frm) { | |||||
}, | |||||
get_scheduler_job_info: function() { | |||||
return { | |||||
"Daily": "Take backup of database and files to dropbox on daily basis", | |||||
"Weekly": "Take backup of database and files to dropbox on weekly basis" | |||||
} | |||||
}, | |||||
get_service_info: function(frm) { | |||||
frappe.call({ | |||||
method: "frappe.integrations.doctype.dropbox_settings.dropbox_settings.get_service_details", | |||||
callback: function(r){ | |||||
var integration_service_help = frm.fields_dict.integration_service_help.wrapper; | |||||
$(integration_service_help).empty(); | |||||
$(integration_service_help).append(r.message); | |||||
} | |||||
}) | |||||
} | |||||
}) |
@@ -1,5 +1,6 @@ | |||||
{ | { | ||||
"allow_copy": 0, | "allow_copy": 0, | ||||
"allow_guest_to_view": 0, | |||||
"allow_import": 0, | "allow_import": 0, | ||||
"allow_rename": 0, | "allow_rename": 0, | ||||
"beta": 0, | "beta": 0, | ||||
@@ -10,6 +11,35 @@ | |||||
"document_type": "System", | "document_type": "System", | ||||
"editable_grid": 1, | "editable_grid": 1, | ||||
"fields": [ | "fields": [ | ||||
{ | |||||
"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": "Enabled", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_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_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
@@ -21,6 +51,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Send Notifications To", | "label": "Send Notifications To", | ||||
@@ -49,6 +80,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Backup Frequency", | "label": "Backup Frequency", | ||||
@@ -79,6 +111,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "App Access Key", | "label": "App Access Key", | ||||
@@ -108,6 +141,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "App Secret Key", | "label": "App Secret Key", | ||||
@@ -136,6 +170,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Allow Dropbox Access", | "label": "Allow Dropbox Access", | ||||
@@ -164,6 +199,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Dropbox Access Key", | "label": "Dropbox Access Key", | ||||
@@ -192,6 +228,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Dropbox Access Secret", | "label": "Dropbox Access Secret", | ||||
@@ -220,6 +257,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Dropbox Setup via Site Config", | "label": "Dropbox Setup via Site Config", | ||||
@@ -238,17 +276,17 @@ | |||||
"unique": 0 | "unique": 0 | ||||
} | } | ||||
], | ], | ||||
"has_web_view": 0, | |||||
"hide_heading": 0, | "hide_heading": 0, | ||||
"hide_toolbar": 0, | "hide_toolbar": 0, | ||||
"idx": 0, | "idx": 0, | ||||
"image_view": 0, | "image_view": 0, | ||||
"in_create": 1, | "in_create": 1, | ||||
"in_dialog": 0, | |||||
"is_submittable": 0, | "is_submittable": 0, | ||||
"issingle": 1, | "issingle": 1, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-12-29 14:39:48.832630", | |||||
"modified": "2017-03-08 17:25:45.564492", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Integrations", | "module": "Integrations", | ||||
"name": "Dropbox Settings", | "name": "Dropbox Settings", | ||||
@@ -265,7 +303,6 @@ | |||||
"export": 1, | "export": 1, | ||||
"if_owner": 0, | "if_owner": 0, | ||||
"import": 0, | "import": 0, | ||||
"is_custom": 0, | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print": 1, | "print": 1, | ||||
"read": 1, | "read": 1, | ||||
@@ -280,6 +317,7 @@ | |||||
"quick_entry": 0, | "quick_entry": 0, | ||||
"read_only": 1, | "read_only": 1, | ||||
"read_only_onload": 0, | "read_only_onload": 0, | ||||
"show_name_in_global_search": 0, | |||||
"sort_field": "modified", | "sort_field": "modified", | ||||
"sort_order": "DESC", | "sort_order": "DESC", | ||||
"track_changes": 1, | "track_changes": 1, | ||||
@@ -10,20 +10,11 @@ from frappe.utils.backups import new_backup | |||||
from frappe.utils.background_jobs import enqueue | from frappe.utils.background_jobs import enqueue | ||||
from frappe.utils import (cint, split_emails, get_request_site_address, cstr, | from frappe.utils import (cint, split_emails, get_request_site_address, cstr, | ||||
get_files_path, get_backups_path, encode) | get_files_path, get_backups_path, encode) | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService | |||||
from frappe.model.document import Document | |||||
ignore_list = [".DS_Store"] | ignore_list = [".DS_Store"] | ||||
class DropboxSettings(IntegrationService): | |||||
scheduler_events = { | |||||
"daily_long": [ | |||||
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily" | |||||
], | |||||
"weekly_long": [ | |||||
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_weekly" | |||||
] | |||||
} | |||||
class DropboxSettings(Document): | |||||
def onload(self): | def onload(self): | ||||
if not self.app_access_key and frappe.conf.dropbox_access_key: | if not self.app_access_key and frappe.conf.dropbox_access_key: | ||||
self.dropbox_setup_via_site_config = 1 | self.dropbox_setup_via_site_config = 1 | ||||
@@ -32,14 +23,6 @@ class DropboxSettings(IntegrationService): | |||||
if not self.flags.ignore_mandatory: | if not self.flags.ignore_mandatory: | ||||
self.validate_dropbox_credentails() | self.validate_dropbox_credentails() | ||||
def on_update(self): | |||||
pass | |||||
def enable(self): | |||||
""" enable service """ | |||||
if not self.flags.ignore_mandatory: | |||||
self.validate_dropbox_credentails() | |||||
def validate_dropbox_credentails(self): | def validate_dropbox_credentails(self): | ||||
try: | try: | ||||
self.get_dropbox_session() | self.get_dropbox_session() | ||||
@@ -63,46 +46,6 @@ class DropboxSettings(IntegrationService): | |||||
return sess | return sess | ||||
@frappe.whitelist() | |||||
def get_service_details(): | |||||
return """ | |||||
<div> | |||||
Steps to enable dropbox backup service: | |||||
<ol> | |||||
<li> Create a dropbox app then get App Key and App Secret, | |||||
<a href="https://www.dropbox.com/developers/apps" target="_blank"> | |||||
https://www.dropbox.com/developers/apps | |||||
</a> | |||||
</li> | |||||
<br> | |||||
<li> Setup credentials on Dropbox Settings doctype. | |||||
Click on | |||||
<button class="btn btn-default btn-xs disabled"> Dropbox Settings </button> | |||||
top right corner | |||||
</li> | |||||
<br> | |||||
<li> After settings up App key and App Secret, generate access token | |||||
<button class="btn btn-default btn-xs disabled"> Allow Dropbox Access </button> | |||||
</li> | |||||
<br> | |||||
<li> | |||||
After saving settings, | |||||
<label> | |||||
<span class="input-area"> | |||||
<input type="checkbox" class="input-with-feedback" checked disabled> | |||||
</span> | |||||
<span class="label-area small">Enable</span> | |||||
</label> | |||||
Dropbox Integration Service and Save a document. | |||||
</li> | |||||
</ol> | |||||
<p> | |||||
After enabling service, system will take backup of files and database on daily or weekly basis | |||||
as per set on Dropbox Settings page and upload it to your dropbox. | |||||
</p> | |||||
</div> | |||||
""" | |||||
#get auth token | #get auth token | ||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def get_dropbox_authorize_url(): | def get_dropbox_authorize_url(): | ||||
@@ -179,7 +122,7 @@ def take_backups_if(freq): | |||||
def take_backup_to_dropbox(): | def take_backup_to_dropbox(): | ||||
did_not_upload, error_log = [], [] | did_not_upload, error_log = [], [] | ||||
try: | try: | ||||
if cint(frappe.db.get_value("Integration Service", "Dropbox", "enabled")): | |||||
if cint(frappe.db.get_value("Dropbox Settings", None, "enabled")): | |||||
did_not_upload, error_log = backup_to_dropbox() | did_not_upload, error_log = backup_to_dropbox() | ||||
if did_not_upload: raise Exception | if did_not_upload: raise Exception | ||||
@@ -252,9 +252,9 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-12-29 14:40:00.783063", | |||||
"modified": "2017-03-08 14:40:00.783063", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Integration Broker", | |||||
"module": "Integrations", | |||||
"name": "Integration Request", | "name": "Integration Request", | ||||
"name_case": "", | "name_case": "", | ||||
"owner": "Administrator", | "owner": "Administrator", |
@@ -1,31 +1,8 @@ | |||||
// Copyright (c) 2016, Frappe Technologies and contributors | // Copyright (c) 2016, Frappe Technologies and contributors | ||||
// For license information, please see license.txt | // For license information, please see license.txt | ||||
frappe.provide("frappe.integration_service") | |||||
frappe.ui.form.on('LDAP Settings', { | frappe.ui.form.on('LDAP Settings', { | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
} | } | ||||
}); | }); | ||||
frappe.integration_service.ldap_settings = Class.extend({ | |||||
init: function(frm) { | |||||
}, | |||||
get_scheduler_job_info: function() { | |||||
return {} | |||||
}, | |||||
get_service_info: function(frm) { | |||||
frappe.call({ | |||||
method: "frappe.integrations.doctype.ldap_settings.ldap_settings.get_service_details", | |||||
callback: function(r){ | |||||
var integration_service_help = frm.fields_dict.integration_service_help.wrapper; | |||||
$(integration_service_help).empty(); | |||||
$(integration_service_help).append(r.message); | |||||
} | |||||
}) | |||||
} | |||||
}) |
@@ -1,5 +1,6 @@ | |||||
{ | { | ||||
"allow_copy": 0, | "allow_copy": 0, | ||||
"allow_guest_to_view": 0, | |||||
"allow_import": 0, | "allow_import": 0, | ||||
"allow_rename": 0, | "allow_rename": 0, | ||||
"beta": 0, | "beta": 0, | ||||
@@ -10,6 +11,35 @@ | |||||
"document_type": "System", | "document_type": "System", | ||||
"editable_grid": 1, | "editable_grid": 1, | ||||
"fields": [ | "fields": [ | ||||
{ | |||||
"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": "Enabled", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_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_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
@@ -20,6 +50,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "LDAP Server Url", | "label": "LDAP Server Url", | ||||
@@ -47,6 +79,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Organizational Unit", | "label": "Organizational Unit", | ||||
@@ -74,6 +108,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Base Distinguished Name (DN)", | "label": "Base Distinguished Name (DN)", | ||||
@@ -101,6 +137,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Password for Base DN", | "label": "Password for Base DN", | ||||
@@ -128,6 +166,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"length": 0, | "length": 0, | ||||
@@ -154,6 +194,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "LDAP Search String", | "label": "LDAP Search String", | ||||
@@ -181,6 +223,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "LDAP First Name Field", | "label": "LDAP First Name Field", | ||||
@@ -208,6 +252,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "LDAP Email Field", | "label": "LDAP Email Field", | ||||
@@ -235,6 +281,8 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "LDAP Username Field", | "label": "LDAP Username Field", | ||||
@@ -253,17 +301,17 @@ | |||||
"unique": 0 | "unique": 0 | ||||
} | } | ||||
], | ], | ||||
"has_web_view": 0, | |||||
"hide_heading": 0, | "hide_heading": 0, | ||||
"hide_toolbar": 0, | "hide_toolbar": 0, | ||||
"idx": 0, | "idx": 0, | ||||
"image_view": 0, | "image_view": 0, | ||||
"in_create": 1, | "in_create": 1, | ||||
"in_dialog": 0, | |||||
"is_submittable": 0, | "is_submittable": 0, | ||||
"issingle": 1, | "issingle": 1, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2017-02-06 15:37:20.481090", | |||||
"modified": "2017-03-08 17:16:01.087365", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Integrations", | "module": "Integrations", | ||||
"name": "LDAP Settings", | "name": "LDAP Settings", | ||||
@@ -294,6 +342,7 @@ | |||||
"quick_entry": 0, | "quick_entry": 0, | ||||
"read_only": 1, | "read_only": 1, | ||||
"read_only_onload": 0, | "read_only_onload": 0, | ||||
"show_name_in_global_search": 0, | |||||
"sort_field": "modified", | "sort_field": "modified", | ||||
"sort_order": "DESC", | "sort_order": "DESC", | ||||
"track_changes": 1, | "track_changes": 1, | ||||
@@ -6,19 +6,12 @@ from __future__ import unicode_literals | |||||
import frappe | import frappe | ||||
from frappe import _ | from frappe import _ | ||||
from frappe.utils import cstr, cint | from frappe.utils import cstr, cint | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService | |||||
from frappe.model.document import Document | |||||
class LDAPSettings(IntegrationService): | |||||
def on_update(self): | |||||
pass | |||||
class LDAPSettings(Document): | |||||
def validate(self): | def validate(self): | ||||
if not self.flags.ignore_mandatory: | if not self.flags.ignore_mandatory: | ||||
self.validate_ldap_credentails() | self.validate_ldap_credentails() | ||||
def enable(self): | |||||
if not self.flags.ignore_mandatory: | |||||
self.validate_ldap_credentails() | |||||
def validate_ldap_credentails(self): | def validate_ldap_credentails(self): | ||||
try: | try: | ||||
@@ -40,50 +33,11 @@ class LDAPSettings(IntegrationService): | |||||
conn.unbind_s() | conn.unbind_s() | ||||
frappe.throw("Incorrect UserId or Password") | frappe.throw("Incorrect UserId or Password") | ||||
@frappe.whitelist() | |||||
def get_service_details(): | |||||
return """ | |||||
<div> | |||||
<p> Steps to configure Service | |||||
<ol> | |||||
<li> Setup credentials on LDAP settings doctype | |||||
Click on | |||||
<button class="btn btn-default btn-xs disabled"> LDAP Settings </button> | |||||
top right corner | |||||
<br> | |||||
To setup LDAP you need, | |||||
<ul> | |||||
<li> Server URL & Port : ldap://ldap.forumsys.com:389</li> | |||||
<li> Base Distinguished Name : cn=read-only-admin,dc=example,dc=com</li> | |||||
<li> Organisational Unit : ou=mathematicians,dc=example,dc=com</li> | |||||
<li> Password : Base DN password</li> | |||||
<li> LDAP Search Criteria : uid=*{0}</li> | |||||
<li> LDAP First Name Fields : cn</li> | |||||
<li> LDAP Username Field : ui</li> | |||||
<li> LDAP Email Field : mail</li> | |||||
</ul> | |||||
</li> | |||||
<br> | |||||
<li> | |||||
After saving settings, | |||||
<label> | |||||
<span class="input-area"> | |||||
<input type="checkbox" class="input-with-feedback" checked disabled> | |||||
</span> | |||||
<span class="label-area small">Enable</span> | |||||
</label> | |||||
LDAP Integration Service and Save a document. | |||||
</li> | |||||
</ol> | |||||
</div> | |||||
""" | |||||
def get_ldap_settings(): | def get_ldap_settings(): | ||||
try: | try: | ||||
settings = frappe.get_doc("LDAP Settings") | settings = frappe.get_doc("LDAP Settings") | ||||
settings.update({ | settings.update({ | ||||
"enabled": cint(frappe.db.get_value("Integration Service", "LDAP", "enabled")), | |||||
"method": "frappe.integrations.doctype.ldap_settings.ldap_settings.login" | "method": "frappe.integrations.doctype.ldap_settings.ldap_settings.login" | ||||
}) | }) | ||||
return settings | return settings | ||||
@@ -217,9 +217,9 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-12-29 14:40:04.113884", | |||||
"modified": "2017-03-08 14:40:04.113884", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Integration Broker", | |||||
"module": "Integrations", | |||||
"name": "OAuth Authorization Code", | "name": "OAuth Authorization Code", | ||||
"name_case": "", | "name_case": "", | ||||
"owner": "Administrator", | "owner": "Administrator", |
@@ -244,9 +244,9 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-12-29 14:40:04.209039", | |||||
"modified": "2017-03-08 14:40:04.209039", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Integration Broker", | |||||
"module": "Integrations", | |||||
"name": "OAuth Bearer Token", | "name": "OAuth Bearer Token", | ||||
"name_case": "", | "name_case": "", | ||||
"owner": "Administrator", | "owner": "Administrator", |
@@ -446,9 +446,9 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-12-29 14:40:03.031779", | |||||
"modified": "2017-03-08 14:40:03.031779", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Integration Broker", | |||||
"module": "Integrations", | |||||
"name": "OAuth Client", | "name": "OAuth Client", | ||||
"name_case": "", | "name_case": "", | ||||
"owner": "Administrator", | "owner": "Administrator", |
@@ -1,31 +1,8 @@ | |||||
// Copyright (c) 2016, Frappe Technologies and contributors | // Copyright (c) 2016, Frappe Technologies and contributors | ||||
// For license information, please see license.txt | // For license information, please see license.txt | ||||
frappe.provide("frappe.integration_service") | |||||
frappe.ui.form.on('PayPal Settings', { | frappe.ui.form.on('PayPal Settings', { | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
} | } | ||||
}); | }); | ||||
frappe.integration_service.paypal_settings = Class.extend({ | |||||
init: function(frm) { | |||||
}, | |||||
get_scheduler_job_info: function() { | |||||
return {} | |||||
}, | |||||
get_service_info: function(frm) { | |||||
frappe.call({ | |||||
method: "frappe.integrations.doctype.paypal_settings.paypal_settings.get_service_details", | |||||
callback: function(r){ | |||||
var integration_service_help = frm.fields_dict.integration_service_help.wrapper; | |||||
$(integration_service_help).empty(); | |||||
$(integration_service_help).append(r.message); | |||||
} | |||||
}) | |||||
} | |||||
}) |
@@ -9,9 +9,9 @@ | |||||
Example: | Example: | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller | |||||
from frappe.integrations.utils import get_payment_gateway_controller | |||||
controller = get_integration_controller("PayPal") | |||||
controller = get_payment_gateway_controller("PayPal") | |||||
controller().validate_transaction_currency(currency) | controller().validate_transaction_currency(currency) | ||||
### 2. Redirect for payment | ### 2. Redirect for payment | ||||
@@ -27,7 +27,8 @@ Example: | |||||
"payer_email": "NuranVerkleij@example.com", | "payer_email": "NuranVerkleij@example.com", | ||||
"payer_name": "Nuran Verkleij", | "payer_name": "Nuran Verkleij", | ||||
"order_id": "111", | "order_id": "111", | ||||
"currency": "USD" | |||||
"currency": "USD", | |||||
"payment_gateway": "Razorpay" | |||||
} | } | ||||
# redirect the user to this url | # redirect the user to this url | ||||
@@ -60,12 +61,11 @@ import json | |||||
from frappe import _ | from frappe import _ | ||||
from frappe.utils import get_url, call_hook_method, cint | from frappe.utils import get_url, call_hook_method, cint | ||||
from urllib import urlencode | from urllib import urlencode | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService | |||||
from frappe.model.document import Document | |||||
import urllib | import urllib | ||||
from frappe.integrations.utils import create_request_log, make_post_request, create_payment_gateway | |||||
class PayPalSettings(IntegrationService): | |||||
service_name = "PayPal" | |||||
class PayPalSettings(Document): | |||||
supported_currencies = ["AUD", "BRL", "CAD", "CZK", "DKK", "EUR", "HKD", "HUF", "ILS", "JPY", "MYR", "MXN", | supported_currencies = ["AUD", "BRL", "CAD", "CZK", "DKK", "EUR", "HKD", "HUF", "ILS", "JPY", "MYR", "MXN", | ||||
"TWD", "NZD", "NOK", "PHP", "PLN", "GBP", "RUB", "SGD", "SEK", "CHF", "THB", "TRY", "USD"] | "TWD", "NZD", "NOK", "PHP", "PLN", "GBP", "RUB", "SGD", "SEK", "CHF", "THB", "TRY", "USD"] | ||||
@@ -77,20 +77,17 @@ class PayPalSettings(IntegrationService): | |||||
setattr(self, "use_sandbox", cint(frappe._dict(data).use_sandbox) or 0) | setattr(self, "use_sandbox", cint(frappe._dict(data).use_sandbox) or 0) | ||||
def validate(self): | def validate(self): | ||||
create_payment_gateway("PayPal") | |||||
call_hook_method('payment_gateway_enabled', gateway="PayPal") | |||||
if not self.flags.ignore_mandatory: | if not self.flags.ignore_mandatory: | ||||
self.validate_paypal_credentails() | self.validate_paypal_credentails() | ||||
def on_update(self): | def on_update(self): | ||||
pass | pass | ||||
def enable(self): | |||||
call_hook_method('payment_gateway_enabled', gateway=self.service_name) | |||||
if not self.flags.ignore_mandatory: | |||||
self.validate_paypal_credentails() | |||||
def validate_transaction_currency(self, currency): | def validate_transaction_currency(self, currency): | ||||
if currency not in self.supported_currencies: | if currency not in self.supported_currencies: | ||||
frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency)) | |||||
frappe.throw(_("Please select another payment method. PayPal does not support transactions in currency '{0}'").format(currency)) | |||||
def get_paypal_params_and_url(self): | def get_paypal_params_and_url(self): | ||||
params = { | params = { | ||||
@@ -117,7 +114,7 @@ class PayPalSettings(IntegrationService): | |||||
params = urlencode(params) | params = urlencode(params) | ||||
try: | try: | ||||
res = self.post_request(url=url, data=params.encode("utf-8")) | |||||
res = make_post_request(url=url, data=params.encode("utf-8")) | |||||
if res["ACK"][0] == "Failure": | if res["ACK"][0] == "Failure": | ||||
raise Exception | raise Exception | ||||
@@ -140,7 +137,7 @@ class PayPalSettings(IntegrationService): | |||||
"correlation_id": response.get("CORRELATIONID")[0] | "correlation_id": response.get("CORRELATIONID")[0] | ||||
}) | }) | ||||
self.integration_request = self.create_request(kwargs, "Remote", self.service_name, response.get("TOKEN")[0]) | |||||
self.integration_request = create_request_log(kwargs, "Remote", "PayPal", response.get("TOKEN")[0]) | |||||
return return_url.format(kwargs["token"]) | return return_url.format(kwargs["token"]) | ||||
@@ -157,49 +154,12 @@ class PayPalSettings(IntegrationService): | |||||
params = urlencode(params) | params = urlencode(params) | ||||
response = self.post_request(url, data=params.encode("utf-8")) | |||||
response = make_post_request(url, data=params.encode("utf-8")) | |||||
if response.get("ACK")[0] != "Success": | if response.get("ACK")[0] != "Success": | ||||
frappe.throw("Looks like something is wrong with this site's Paypal configuration.") | frappe.throw("Looks like something is wrong with this site's Paypal configuration.") | ||||
return response | return response | ||||
@frappe.whitelist() | |||||
def get_service_details(): | |||||
return """ | |||||
<div> | |||||
<p> Steps to configure Service | |||||
<ol> | |||||
<li> Get PayPal api credentials from link: | |||||
<a href="https://developer.paypal.com/docs/classic/api/apiCredentials/" target="_blank"> | |||||
https://developer.paypal.com/docs/classic/api/apiCredentials/ | |||||
</a> | |||||
</li> | |||||
<br> | |||||
<li> Setup credentials on PayPal settings doctype. | |||||
Click on | |||||
<button class="btn btn-default btn-xs disabled"> PayPal Settings </button> | |||||
top right corner | |||||
</li> | |||||
<br> | |||||
<li> | |||||
After saving settings, | |||||
<label> | |||||
<span class="input-area"> | |||||
<input type="checkbox" class="input-with-feedback" checked disabled> | |||||
</span> | |||||
<span class="label-area small">Enabled</span> | |||||
</label> | |||||
PayPal Integration Service and Save a document. | |||||
</li> | |||||
<br> | |||||
<li> | |||||
To view PayPal payment logs, | |||||
<button class="btn btn-default btn-xs disabled"> Show Log </button> | |||||
</li> | |||||
</ol> | |||||
</div> | |||||
""" | |||||
@frappe.whitelist(allow_guest=True, xss_safe=True) | @frappe.whitelist(allow_guest=True, xss_safe=True) | ||||
def get_express_checkout_details(token): | def get_express_checkout_details(token): | ||||
try: | try: | ||||
@@ -212,7 +172,7 @@ def get_express_checkout_details(token): | |||||
"TOKEN": token | "TOKEN": token | ||||
}) | }) | ||||
response = doc.post_request(url, data=params) | |||||
response = make_post_request(url, data=params) | |||||
if response.get("ACK")[0] != "Success": | if response.get("ACK")[0] != "Success": | ||||
frappe.respond_as_web_page(_("Something went wrong"), | frappe.respond_as_web_page(_("Something went wrong"), | ||||
@@ -259,7 +219,7 @@ def confirm_payment(token): | |||||
"PAYMENTREQUEST_0_CURRENCYCODE": data.get("currency").upper() | "PAYMENTREQUEST_0_CURRENCYCODE": data.get("currency").upper() | ||||
}) | }) | ||||
response = doc.post_request(url, data=params) | |||||
response = make_post_request(url, data=params) | |||||
if response.get("ACK")[0] == "Success": | if response.get("ACK")[0] == "Success": | ||||
update_integration_request_status(token, { | update_integration_request_status(token, { | ||||
@@ -291,14 +251,3 @@ def confirm_payment(token): | |||||
def update_integration_request_status(token, data, status, error=False): | def update_integration_request_status(token, data, status, error=False): | ||||
frappe.get_doc("Integration Request", token).update_status(data, status) | frappe.get_doc("Integration Request", token).update_status(data, status) | ||||
@frappe.whitelist(allow_guest=True, xss_safe=True) | |||||
def get_checkout_url(**kwargs): | |||||
try: | |||||
doc = frappe.get_doc("PayPal Settings") | |||||
return doc.get_payment_url(**kwargs) | |||||
except Exception: | |||||
frappe.respond_as_web_page(_("Something went wrong"), | |||||
_("Looks like something is wrong with this site's Paypal configuration. Don't worry! No payment has been made from your Paypal account."), | |||||
indicator_color='red', | |||||
http_status_code=frappe.ValidationError.http_status_code) |
@@ -1,33 +1,8 @@ | |||||
// Copyright (c) 2016, Frappe Technologies and contributors | // Copyright (c) 2016, Frappe Technologies and contributors | ||||
// For license information, please see license.txt | // For license information, please see license.txt | ||||
frappe.provide("frappe.integration_service") | |||||
frappe.ui.form.on('Razorpay Settings', { | frappe.ui.form.on('Razorpay Settings', { | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
} | } | ||||
}); | |||||
frappe.integration_service.razorpay_settings = Class.extend({ | |||||
init: function(frm) { | |||||
}, | |||||
get_scheduler_job_info: function() { | |||||
return { | |||||
"Execute on every few minits of interval": " Captures all authorised payments" | |||||
} | |||||
}, | |||||
get_service_info: function(frm) { | |||||
frappe.call({ | |||||
method: "frappe.integrations.doctype.razorpay_settings.razorpay_settings.get_service_details", | |||||
callback: function(r){ | |||||
var integration_service_help = frm.fields_dict.integration_service_help.wrapper; | |||||
$(integration_service_help).empty(); | |||||
$(integration_service_help).append(r.message); | |||||
} | |||||
}) | |||||
} | |||||
}) | |||||
}); |
@@ -9,9 +9,9 @@ | |||||
Example: | Example: | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller | |||||
from frappe.integrations.utils import get_payment_gateway_controller | |||||
controller = get_integration_controller("Razorpay") | |||||
controller = get_payment_gateway_controller("Razorpay") | |||||
controller().validate_transaction_currency(currency) | controller().validate_transaction_currency(currency) | ||||
### 2. Redirect for payment | ### 2. Redirect for payment | ||||
@@ -27,7 +27,8 @@ Example: | |||||
"payer_email": "NuranVerkleij@example.com", | "payer_email": "NuranVerkleij@example.com", | ||||
"payer_name": "Nuran Verkleij", | "payer_name": "Nuran Verkleij", | ||||
"order_id": "111", | "order_id": "111", | ||||
"currency": "INR" | |||||
"currency": "INR", | |||||
"payment_gateway": "Razorpay" | |||||
} | } | ||||
# Redirect the user to this url | # Redirect the user to this url | ||||
@@ -53,45 +54,32 @@ For razorpay payment status is Authorized | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
from frappe.utils import get_url, call_hook_method, cint | |||||
from frappe import _ | from frappe import _ | ||||
import urllib, json | import urllib, json | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService | |||||
from frappe.model.document import Document | |||||
from frappe.utils import get_url, call_hook_method, cint | |||||
from frappe.integrations.utils import make_get_request, make_post_request, create_request_log, create_payment_gateway | |||||
class RazorpaySettings(IntegrationService): | |||||
service_name = "Razorpay" | |||||
class RazorpaySettings(Document): | |||||
supported_currencies = ["INR"] | supported_currencies = ["INR"] | ||||
scheduler_events = { | |||||
"all": [ | |||||
"frappe.integrations.doctype.razorpay_settings.razorpay_settings.capture_payment" | |||||
] | |||||
} | |||||
def validate(self): | def validate(self): | ||||
if not self.flags.ignore_mandatory: | |||||
self.validate_razorpay_credentails() | |||||
def on_update(self): | |||||
pass | |||||
def enable(self): | |||||
create_payment_gateway('Razorpay') | |||||
call_hook_method('payment_gateway_enabled', gateway='Razorpay') | call_hook_method('payment_gateway_enabled', gateway='Razorpay') | ||||
if not self.flags.ignore_mandatory: | if not self.flags.ignore_mandatory: | ||||
self.validate_razorpay_credentails() | self.validate_razorpay_credentails() | ||||
def validate_razorpay_credentails(self): | def validate_razorpay_credentails(self): | ||||
if self.api_key and self.api_secret: | if self.api_key and self.api_secret: | ||||
try: | try: | ||||
self.get_request(url="https://api.razorpay.com/v1/payments", | |||||
make_get_request(url="https://api.razorpay.com/v1/payments", | |||||
auth=(self.api_key, self.get_password(fieldname="api_secret", raise_exception=False))) | auth=(self.api_key, self.get_password(fieldname="api_secret", raise_exception=False))) | ||||
except Exception: | except Exception: | ||||
frappe.throw(_("Seems API Key or API Secret is wrong !!!")) | frappe.throw(_("Seems API Key or API Secret is wrong !!!")) | ||||
def validate_transaction_currency(self, currency): | def validate_transaction_currency(self, currency): | ||||
if currency not in self.supported_currencies: | if currency not in self.supported_currencies: | ||||
frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency)) | |||||
frappe.throw(_("Please select another payment method. Razorpay does not support transactions in currency '{0}'").format(currency)) | |||||
def get_payment_url(self, **kwargs): | def get_payment_url(self, **kwargs): | ||||
return get_url("./integrations/razorpay_checkout?{0}".format(urllib.urlencode(kwargs))) | return get_url("./integrations/razorpay_checkout?{0}".format(urllib.urlencode(kwargs))) | ||||
@@ -100,8 +88,7 @@ class RazorpaySettings(IntegrationService): | |||||
self.data = frappe._dict(data) | self.data = frappe._dict(data) | ||||
try: | try: | ||||
self.integration_request = super(RazorpaySettings, self).create_request(self.data, "Host", \ | |||||
"Razorpay") | |||||
self.integration_request = create_request_log(self.data, "Host", "Razorpay") | |||||
return self.authorize_payment() | return self.authorize_payment() | ||||
except Exception: | except Exception: | ||||
@@ -124,7 +111,7 @@ class RazorpaySettings(IntegrationService): | |||||
redirect_message = data.get('notes', {}).get('redirect_message') or None | redirect_message = data.get('notes', {}).get('redirect_message') or None | ||||
try: | try: | ||||
resp = self.get_request("https://api.razorpay.com/v1/payments/{0}" | |||||
resp = make_get_request("https://api.razorpay.com/v1/payments/{0}" | |||||
.format(self.data.razorpay_payment_id), auth=(settings.api_key, | .format(self.data.razorpay_payment_id), auth=(settings.api_key, | ||||
settings.api_secret)) | settings.api_secret)) | ||||
@@ -201,7 +188,7 @@ def capture_payment(is_sandbox=False, sanbox_response=None): | |||||
data = json.loads(doc.data) | data = json.loads(doc.data) | ||||
settings = controller.get_settings(data) | settings = controller.get_settings(data) | ||||
resp = controller.post_request("https://api.razorpay.com/v1/payments/{0}/capture".format(data.get("razorpay_payment_id")), | |||||
resp = make_post_request("https://api.razorpay.com/v1/payments/{0}/capture".format(data.get("razorpay_payment_id")), | |||||
auth=(settings.api_key, settings.api_secret), data={"amount": data.get("amount")}) | auth=(settings.api_key, settings.api_secret), data={"amount": data.get("amount")}) | ||||
if resp.get("status") == "captured": | if resp.get("status") == "captured": | ||||
@@ -211,51 +198,4 @@ def capture_payment(is_sandbox=False, sanbox_response=None): | |||||
doc = frappe.get_doc("Integration Request", doc.name) | doc = frappe.get_doc("Integration Request", doc.name) | ||||
doc.status = "Failed" | doc.status = "Failed" | ||||
doc.error = frappe.get_traceback() | doc.error = frappe.get_traceback() | ||||
frappe.log_error(doc.error, '{0} Failed'.format(doc.name)) | |||||
@frappe.whitelist(allow_guest=True, xss_safe=True) | |||||
def get_checkout_url(**kwargs): | |||||
try: | |||||
return frappe.get_doc("Razorpay Settings").get_payment_url(**kwargs) | |||||
except Exception: | |||||
frappe.respond_as_web_page(_("Something went wrong"), | |||||
_("Looks like something is wrong with this site's Razorpay configuration. No payment has been made."), | |||||
indicator_color='red', | |||||
http_status_code=frappe.ValidationError.http_status_code) | |||||
@frappe.whitelist() | |||||
def get_service_details(): | |||||
return """ | |||||
<div> | |||||
<p> Steps to configure Service | |||||
<ol> | |||||
<li> Get Razorpay api credentials by login to: | |||||
<a href="https://razorpay.com/" target="_blank"> | |||||
https://razorpay.com/ | |||||
</a> | |||||
</li> | |||||
<br> | |||||
<li> Setup credentials on Razorpay Settings doctype. | |||||
Click on | |||||
<button class="btn btn-default btn-xs disabled"> Razorpay Settings </button> | |||||
top right corner | |||||
</li> | |||||
<br> | |||||
<li> | |||||
After saving settings, | |||||
<label> | |||||
<span class="input-area"> | |||||
<input type="checkbox" class="input-with-feedback" checked disabled> | |||||
</span> | |||||
<span class="label-area small">Enable</span> | |||||
</label> | |||||
Razorpay Integration Service and Save a document. | |||||
</li> | |||||
<br> | |||||
<li> | |||||
To view Razorpays payment logs, | |||||
<button class="btn btn-default btn-xs disabled"> Show Log </button> | |||||
</li> | |||||
</ol> | |||||
</div> | |||||
""" | |||||
frappe.log_error(doc.error, '{0} Failed'.format(doc.name)) |
@@ -53,13 +53,13 @@ def authorize(*args, **kwargs): | |||||
oauth_settings = get_oauth_settings() | oauth_settings = get_oauth_settings() | ||||
params = get_urlparams_from_kwargs(kwargs) | params = get_urlparams_from_kwargs(kwargs) | ||||
request_url = urlparse(frappe.request.url) | request_url = urlparse(frappe.request.url) | ||||
success_url = request_url.scheme + "://" + request_url.netloc + "/api/method/frappe.integration_broker.oauth2.approve?" + params | |||||
success_url = request_url.scheme + "://" + request_url.netloc + "/api/method/frappe.integrations.oauth2.approve?" + params | |||||
failure_url = frappe.form_dict["redirect_uri"] + "?error=access_denied" | failure_url = frappe.form_dict["redirect_uri"] + "?error=access_denied" | ||||
if frappe.session['user']=='Guest': | if frappe.session['user']=='Guest': | ||||
#Force login, redirect to preauth again. | #Force login, redirect to preauth again. | ||||
frappe.local.response["type"] = "redirect" | frappe.local.response["type"] = "redirect" | ||||
frappe.local.response["location"] = "/login?redirect-to=/api/method/frappe.integration_broker.oauth2.authorize?" + quote(params.replace("+"," ")) | |||||
frappe.local.response["location"] = "/login?redirect-to=/api/method/frappe.integrations.oauth2.authorize?" + quote(params.replace("+"," ")) | |||||
elif frappe.session['user']!='Guest': | elif frappe.session['user']!='Guest': | ||||
try: | try: |
@@ -0,0 +1,94 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
import json, urlparse | |||||
from frappe.utils import get_request_session | |||||
from frappe import _ | |||||
def make_get_request(url, auth=None, data=None): | |||||
if not auth: | |||||
auth = '' | |||||
if not data: | |||||
data = {} | |||||
try: | |||||
s = get_request_session() | |||||
frappe.flags.integration_request = s.get(url, data={}, auth=auth) | |||||
frappe.flags.integration_request.raise_for_status() | |||||
return frappe.flags.integration_request.json() | |||||
except Exception, exc: | |||||
frappe.log_error(frappe.get_traceback()) | |||||
raise exc | |||||
def make_post_request(url, auth=None, data=None): | |||||
if not auth: | |||||
auth = '' | |||||
if not data: | |||||
data = {} | |||||
try: | |||||
s = get_request_session() | |||||
res = s.post(url, data=data, auth=auth) | |||||
res.raise_for_status() | |||||
if res.headers.get("content-type") == "text/plain; charset=utf-8": | |||||
return urlparse.parse_qs(res.text) | |||||
return res.json() | |||||
except Exception, exc: | |||||
frappe.log_error() | |||||
raise exc | |||||
def create_request_log(data, integration_type, service_name, name=None): | |||||
if isinstance(data, basestring): | |||||
data = json.loads(data) | |||||
integration_request = frappe.get_doc({ | |||||
"doctype": "Integration Request", | |||||
"integration_type": integration_type, | |||||
"integration_request_service": service_name, | |||||
"reference_doctype": data.get("reference_doctype"), | |||||
"reference_docname": data.get("reference_docname"), | |||||
"data": json.dumps(data) | |||||
}) | |||||
if name: | |||||
integration_request.flags._name = name | |||||
integration_request.insert(ignore_permissions=True) | |||||
frappe.db.commit() | |||||
return integration_request | |||||
def get_payment_gateway_controller(payment_gateway): | |||||
'''Return payment gateway controller''' | |||||
try: | |||||
return frappe.get_doc("{0} Settings".format(payment_gateway)) | |||||
except Exception: | |||||
frappe.throw(_("{0} Settings not found".format(payment_gateway))) | |||||
@frappe.whitelist(allow_guest=True, xss_safe=True) | |||||
def get_checkout_url(**kwargs): | |||||
try: | |||||
if kwargs.get('payment_gateway'): | |||||
doc = frappe.get_doc("{0} Settings".format(kwargs.get('payment_gateway'))) | |||||
return doc.get_payment_url(**kwargs) | |||||
else: | |||||
raise Exception | |||||
except Exception: | |||||
frappe.respond_as_web_page(_("Something went wrong"), | |||||
_("Looks like something is wrong with this site's payment gateway configuration. No payment has been made."), | |||||
indicator_color='red', | |||||
http_status_code=frappe.ValidationError.http_status_code) | |||||
def create_payment_gateway(gateway): | |||||
# NOTE: we don't translate Payment Gateway name because it is an internal doctype | |||||
if not frappe.db.exists("Payment Gateway", gateway): | |||||
payment_gateway = frappe.get_doc({ | |||||
"doctype": "Payment Gateway", | |||||
"gateway": gateway | |||||
}) | |||||
payment_gateway.insert(ignore_permissions=True) |
@@ -6,5 +6,4 @@ Custom | |||||
Geo | Geo | ||||
Desk | Desk | ||||
Integrations | |||||
Integration Broker | |||||
Integrations |
@@ -170,3 +170,4 @@ execute:frappe.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_ | |||||
frappe.patches.v8_0.rename_listsettings_to_usersettings | frappe.patches.v8_0.rename_listsettings_to_usersettings | ||||
frappe.patches.v7_2.update_communications | frappe.patches.v7_2.update_communications | ||||
frappe.patches.v8_0.update_published_in_global_search | frappe.patches.v8_0.update_published_in_global_search | ||||
frappe.patches.v8_0.deprecate_integration_broker |
@@ -3,6 +3,7 @@ import frappe | |||||
from frappe.exceptions import DataError | from frappe.exceptions import DataError | ||||
from frappe.utils.password import get_decrypted_password | from frappe.utils.password import get_decrypted_password | ||||
from frappe.utils import cstr | from frappe.utils import cstr | ||||
import os | |||||
app_list = [ | app_list = [ | ||||
{"app_name": "razorpay_integration", "service_name": "Razorpay", "doctype": "Razorpay Settings", "remove": True}, | {"app_name": "razorpay_integration", "service_name": "Razorpay", "doctype": "Razorpay Settings", "remove": True}, | ||||
@@ -11,8 +12,6 @@ app_list = [ | |||||
] | ] | ||||
def execute(): | def execute(): | ||||
frappe.reload_doc("integration_broker", "doctype", "integration_service") | |||||
installed_apps = frappe.get_installed_apps() | installed_apps = frappe.get_installed_apps() | ||||
for app_details in app_list: | for app_details in app_list: | ||||
@@ -34,6 +33,14 @@ def setup_integration_service(app_details, settings=None): | |||||
setup_service_settings(app_details["service_name"], settings) | setup_service_settings(app_details["service_name"], settings) | ||||
doc_path = frappe.get_app_path("frappe", "integration_broker", "doctype", | |||||
"integration_service", "integration_service.json") | |||||
if not os.path.exists(doc_path): | |||||
return | |||||
frappe.reload_doc("integration_broker", "doctype", "integration_service") | |||||
if frappe.db.exists("Integration Service", app_details["service_name"]): | if frappe.db.exists("Integration Service", app_details["service_name"]): | ||||
integration_service = frappe.get_doc("Integration Service", app_details["service_name"]) | integration_service = frappe.get_doc("Integration Service", app_details["service_name"]) | ||||
else: | else: | ||||
@@ -49,9 +56,9 @@ def get_app_settings(app_details): | |||||
doctype = docname = app_details["doctype"] | doctype = docname = app_details["doctype"] | ||||
app_settings = get_parameters(app_details) | app_settings = get_parameters(app_details) | ||||
if app_settings: | |||||
if app_settings: | |||||
settings = app_settings["settings"] | settings = app_settings["settings"] | ||||
frappe.reload_doc("integrations", "doctype", "{0}_settings".format(app_details["service_name"].lower())) | |||||
frappe.reload_doc("integrations", "doctype", "{0}_settings".format(app_details["service_name"].lower())) | |||||
controller = frappe.get_meta("{0} Settings".format(app_details["service_name"])) | controller = frappe.get_meta("{0} Settings".format(app_details["service_name"])) | ||||
for d in controller.fields: | for d in controller.fields: | ||||
@@ -86,7 +93,7 @@ def get_parameters(app_details): | |||||
elif app_details["service_name"] == "Dropbox": | elif app_details["service_name"] == "Dropbox": | ||||
doc = frappe.db.get_value(app_details["doctype"], None, | doc = frappe.db.get_value(app_details["doctype"], None, | ||||
["dropbox_access_key", "dropbox_access_secret", "upload_backups_to_dropbox"], as_dict=1) | ["dropbox_access_key", "dropbox_access_secret", "upload_backups_to_dropbox"], as_dict=1) | ||||
if not doc: | if not doc: | ||||
return | return | ||||
@@ -99,7 +106,8 @@ def get_parameters(app_details): | |||||
"app_secret_key": frappe.conf.dropbox_secret_key, | "app_secret_key": frappe.conf.dropbox_secret_key, | ||||
"dropbox_access_key": doc.dropbox_access_key, | "dropbox_access_key": doc.dropbox_access_key, | ||||
"dropbox_access_secret": doc.dropbox_access_secret, | "dropbox_access_secret": doc.dropbox_access_secret, | ||||
"backup_frequency": doc.upload_backups_to_dropbox | |||||
"backup_frequency": doc.upload_backups_to_dropbox, | |||||
"enabled": doc.send_backups_to_dropbox | |||||
} | } | ||||
} | } | ||||
@@ -4,10 +4,13 @@ from frappe.utils import cint | |||||
def execute(): | def execute(): | ||||
frappe.reload_doc("integrations", "doctype", "ldap_settings") | frappe.reload_doc("integrations", "doctype", "ldap_settings") | ||||
if not frappe.db.exists("Integration Server", "LDAP"): | |||||
if not frappe.db.exists("DocType", "Integration Service"): | |||||
return | return | ||||
if not cint(frappe.db.get_value("Integration Server", "LDAP", 'enabled')): | |||||
if not frappe.db.exists("Integration Service", "LDAP"): | |||||
return | |||||
if not cint(frappe.db.get_value("Integration Service", "LDAP", 'enabled')): | |||||
return | return | ||||
import ldap | import ldap | ||||
@@ -0,0 +1,50 @@ | |||||
import frappe | |||||
from frappe.integrations.utils import create_payment_gateway | |||||
def execute(): | |||||
setup_enabled_integrations() | |||||
for doctype in ["integration_request", "oauth_authorization_code", "oauth_bearer_token", "oauth_client"]: | |||||
frappe.reload_doc('integrations', 'doctype', doctype) | |||||
frappe.reload_doc("core", "doctype", "payment_gateway") | |||||
update_doctype_module() | |||||
create_payment_gateway_master_records() | |||||
for doctype in ["Integration Service", "Integration Service Parameter"]: | |||||
frappe.delete_doc("DocType", doctype) | |||||
if not frappe.db.get_value("DocType", {"module": "Integration Broker"}, "name"): | |||||
frappe.delete_doc("Module Def", "Integration Broker") | |||||
def setup_enabled_integrations(): | |||||
if not frappe.db.exists("DocType", "Integration Service"): | |||||
return | |||||
for service in frappe.get_all("Integration Service", | |||||
filters={"enabled": 1, "service": ('in', ("Dropbox", "LDAP"))}, fields=["name"]): | |||||
doctype = "{0} Settings".format(service.name) | |||||
frappe.db.set_value(doctype, doctype, 'enabled', 1) | |||||
def update_doctype_module(): | |||||
frappe.db.sql("""update tabDocType set module='Integrations' | |||||
where name in ('Integration Request', 'Oauth Authorization Code', | |||||
'Oauth Bearer Token', 'Oauth Client') """) | |||||
frappe.db.sql(""" update tabDocType set module='Core' where name = 'Payment Gateway'""") | |||||
def create_payment_gateway_master_records(): | |||||
for payment_gateway in ["Razorpay", "PayPal"]: | |||||
doctype = "{0} Settings".format(payment_gateway) | |||||
doc = frappe.get_doc(doctype) | |||||
doc_meta = frappe.get_meta(doctype) | |||||
all_mandatory_fields_has_value = True | |||||
for d in doc_meta.fields: | |||||
if d.reqd and not doc.get(d.fieldname): | |||||
all_mandatory_fields_has_value = False | |||||
break | |||||
if all_mandatory_fields_has_value: | |||||
create_payment_gateway(payment_gateway) |
@@ -73,8 +73,8 @@ def get_oauth2_providers(): | |||||
out['frappe'] = { | out['frappe'] = { | ||||
"flow_params": { | "flow_params": { | ||||
"name": "frappe", | "name": "frappe", | ||||
"authorize_url": frappe_server_url + "/api/method/frappe.integration_broker.oauth2.authorize", | |||||
"access_token_url": frappe_server_url + "/api/method/frappe.integration_broker.oauth2.get_token", | |||||
"authorize_url": frappe_server_url + "/api/method/frappe.integrations.oauth2.authorize", | |||||
"access_token_url": frappe_server_url + "/api/method/frappe.integrations.oauth2.get_token", | |||||
"base_url": frappe_server_url | "base_url": frappe_server_url | ||||
}, | }, | ||||
@@ -86,7 +86,7 @@ def get_oauth2_providers(): | |||||
}, | }, | ||||
# relative to base_url | # relative to base_url | ||||
"api_endpoint": "/api/method/frappe.integration_broker.oauth2.openid_profile" | |||||
"api_endpoint": "/api/method/frappe.integrations.oauth2.openid_profile" | |||||
} | } | ||||
return out | return out | ||||
@@ -23,7 +23,6 @@ from frappe.limits import has_expired | |||||
from frappe.utils.data import get_datetime, now_datetime | from frappe.utils.data import get_datetime, now_datetime | ||||
from frappe.core.doctype.user.user import STANDARD_USERS | from frappe.core.doctype.user.user import STANDARD_USERS | ||||
from frappe.installer import update_site_config | from frappe.installer import update_site_config | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_service_events | |||||
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' | DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' | ||||
@@ -158,21 +157,15 @@ def trigger(site, event, queued_jobs=(), now=False): | |||||
else: | else: | ||||
scheduler_task(site=site, event=event, handler=handler, now=True) | scheduler_task(site=site, event=event, handler=handler, now=True) | ||||
def get_scheduler_events(event): | def get_scheduler_events(event): | ||||
'''Get scheduler events from hooks and integrations''' | '''Get scheduler events from hooks and integrations''' | ||||
scheduler_events = frappe.cache().get_value('scheduler_events') | scheduler_events = frappe.cache().get_value('scheduler_events') | ||||
if not scheduler_events: | if not scheduler_events: | ||||
scheduler_events = frappe.get_hooks("scheduler_events") | scheduler_events = frappe.get_hooks("scheduler_events") | ||||
integration_events = get_integration_service_events() | |||||
for key, handlers in integration_events.items(): | |||||
scheduler_events.setdefault(key, []).extend(handlers) | |||||
frappe.cache().set_value('scheduler_events', scheduler_events) | frappe.cache().set_value('scheduler_events', scheduler_events) | ||||
return scheduler_events.get(event) or [] | return scheduler_events.get(event) or [] | ||||
def log(method, message=None): | def log(method, message=None): | ||||
"""log error in patch_log""" | """log error in patch_log""" | ||||
message = frappe.utils.cstr(message) + "\n" if message else "" | message = frappe.utils.cstr(message) + "\n" if message else "" | ||||
@@ -814,7 +814,7 @@ | |||||
"label": "Payment Gateway", | "label": "Payment Gateway", | ||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"options": "Integration Service", | |||||
"options": "Payment Gateway", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"precision": "", | "precision": "", | ||||
"print_hide": 0, | "print_hide": 0, | ||||
@@ -1139,7 +1139,7 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2017-03-06 16:29:35.691485", | |||||
"modified": "2017-03-09 12:52:43.388176", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Website", | "module": "Website", | ||||
"name": "Web Form", | "name": "Web Form", | ||||
@@ -9,10 +9,10 @@ from frappe.utils import cstr | |||||
from frappe.utils.file_manager import save_file, remove_file_by_url | from frappe.utils.file_manager import save_file, remove_file_by_url | ||||
from frappe.website.utils import get_comment_list | from frappe.website.utils import get_comment_list | ||||
from frappe.custom.doctype.customize_form.customize_form import docfield_properties | from frappe.custom.doctype.customize_form.customize_form import docfield_properties | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller | |||||
from frappe.utils.file_manager import get_max_file_size | from frappe.utils.file_manager import get_max_file_size | ||||
from frappe.modules.utils import export_module_json, get_doc_module | from frappe.modules.utils import export_module_json, get_doc_module | ||||
from urllib import urlencode | from urllib import urlencode | ||||
from frappe.integrations.utils import get_payment_gateway_controller | |||||
class WebForm(WebsiteGenerator): | class WebForm(WebsiteGenerator): | ||||
website = frappe._dict( | website = frappe._dict( | ||||
@@ -221,7 +221,7 @@ def get_context(context): | |||||
def get_payment_gateway_url(self, doc): | def get_payment_gateway_url(self, doc): | ||||
if self.accept_payment: | if self.accept_payment: | ||||
controller = get_integration_controller(self.payment_gateway) | |||||
controller = get_payment_gateway_controller(self.payment_gateway) | |||||
title = "Payment for {0} {1}".format(doc.doctype, doc.name) | title = "Payment for {0} {1}".format(doc.doctype, doc.name) | ||||
amount = self.amount | amount = self.amount | ||||