@@ -1116,7 +1116,7 @@ def as_json(obj, indent=1): | |||
return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler) | |||
def are_emails_muted(): | |||
return flags.mute_emails or conf.get("mute_emails") or False | |||
return flags.mute_emails or int(conf.get("mute_emails") or 0) or False | |||
def get_test_records(doctype): | |||
"""Returns list of objects from `test_records.json` in the given doctype's folder.""" | |||
@@ -52,7 +52,7 @@ def build_docs(context, app, docs_version="current", target=None, local=False, w | |||
or "docs.py" in source_path): | |||
_build_docs_once(site, app, docs_version, target, local, only_content_updated=True) | |||
apps_path = frappe.get_app_path("frappe", "..", "..") | |||
apps_path = frappe.get_app_path(app, "..", "..") | |||
start_watch(apps_path, handler=trigger_make) | |||
def _build_docs_once(site, app, docs_version, target, local, only_content_updated=False): | |||
@@ -156,8 +156,8 @@ def get_data(): | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Email Group Member", | |||
"description": _("Email Group Member List"), | |||
"name": "Auto Email Report", | |||
"description": _("Setup Reports to be emailed at regular intervals"), | |||
}, | |||
] | |||
}, | |||
@@ -41,10 +41,10 @@ and much more out of the box.</p> | |||
<p>Frappe also has a plug-in architecture that can be used to build plugins | |||
to ERPNext.</p> | |||
<p>Frappe Framework was designed to build <a href="https://erpnext.com">ERPNext</a>, open source | |||
<p>Frappe Framework was designed to build <a href="https://erpnext.com" rel="nofollow">ERPNext</a>, open source | |||
ERP for managing small and medium sized businesses.</p> | |||
<p><a href="https://frappe.github.io/frappe/user/">Get started with the Tutorial</a></p> | |||
<p><a href="https://frappe.github.io/frappe/user/" rel="nofollow">Get started with the Tutorial</a></p> | |||
</div> | |||
</div> | |||
@@ -49,7 +49,7 @@ frappe.ui.form.on('Auto Email Report', { | |||
&& frappe.query_reports[frm.doc.report].filters) { | |||
// make a table to show filters | |||
var table = $('<table class="table table-bordered" style="cursor:pointer;"><thead>\ | |||
var table = $('<table class="table table-bordered" style="cursor:pointer; margin:0px;"><thead>\ | |||
<tr><th style="width: 50%">'+__('Filter')+'</th><th>'+__('Value')+'</th></tr>\ | |||
</thead><tbody></tbody></table>').appendTo(wrapper); | |||
$('<p class="text-muted small">' + __("Click table to edit") + '</p>').appendTo(wrapper); | |||
@@ -43,17 +43,43 @@ | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "report_type", | |||
"fieldtype": "Read Only", | |||
"fieldname": "user", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Report Type", | |||
"label": "Based on Permissions For User", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "User", | |||
"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, | |||
"columns": 0, | |||
"fieldname": "enabled", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Enabled", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "report.report_type", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
@@ -70,24 +96,22 @@ | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "user", | |||
"fieldtype": "Link", | |||
"fieldname": "column_break_4", | |||
"fieldtype": "Column Break", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "For User", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "User", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"report_hide": 0, | |||
"reqd": 1, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
@@ -97,16 +121,17 @@ | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "enabled", | |||
"fieldtype": "Check", | |||
"fieldname": "report_type", | |||
"fieldtype": "Read Only", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Enabled", | |||
"label": "Report Type", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "report.report_type", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
@@ -228,17 +253,16 @@ | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "frequency", | |||
"fieldtype": "Select", | |||
"fieldname": "email_to", | |||
"fieldtype": "Small Text", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Frequency", | |||
"label": "Email To", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Daily\nWeekly\nMonthly", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
@@ -279,6 +303,58 @@ | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "column_break_13", | |||
"fieldtype": "Column Break", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"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 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "frequency", | |||
"fieldtype": "Select", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 1, | |||
"label": "Frequency", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Daily\nWeekly\nMonthly", | |||
"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, | |||
@@ -309,16 +385,16 @@ | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"collapsible": 1, | |||
"columns": 0, | |||
"fieldname": "email_to", | |||
"fieldtype": "Small Text", | |||
"fieldname": "section_break_15", | |||
"fieldtype": "Section Break", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Email To", | |||
"label": "Message", | |||
"length": 0, | |||
"no_copy": 0, | |||
"permlevel": 0, | |||
@@ -327,7 +403,7 @@ | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"report_hide": 0, | |||
"reqd": 1, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
@@ -344,7 +420,7 @@ | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Description", | |||
"label": "Message", | |||
"length": 0, | |||
"no_copy": 0, | |||
"permlevel": 0, | |||
@@ -369,7 +445,7 @@ | |||
"issingle": 0, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"modified": "2016-09-14 02:00:21.618956", | |||
"modified": "2016-09-15 01:32:57.454880", | |||
"modified_by": "Administrator", | |||
"module": "Email", | |||
"name": "Auto Email Report", | |||
@@ -67,8 +67,13 @@ class AutoEmailReport(Document): | |||
def send(self): | |||
data = self.get_report_content() | |||
message = '<p>{0}</p>'.format(_('{0} generated on {1}').format(self.name, | |||
frappe.utils.format_datetime(frappe.utils.now_datetime()))) | |||
attachments = None | |||
message = '<p>{0}</p>'.format(_('{0} generated on {1}')\ | |||
.format(frappe.bold(self.name), | |||
frappe.utils.format_datetime(frappe.utils.now_datetime()))) | |||
if self.description: | |||
message += '<hr>' + self.description | |||
if self.format=='HTML': | |||
message += '<hr>' + data | |||
@@ -78,6 +83,8 @@ class AutoEmailReport(Document): | |||
'fcontent': data | |||
}] | |||
message += '<hr><p style="font-size: 10px;"> Edit Auto Email Report Settings: {0}</p>'.format(frappe.utils.get_link_to_form('Auto Email Report', self.name)) | |||
frappe.sendmail( | |||
recipients = self.email_to.split(), | |||
subject = self.name, | |||
@@ -11,52 +11,49 @@ from frappe.utils import get_url, call_hook_method | |||
from frappe.integration_broker.integration_controller import IntegrationController | |||
""" | |||
1. Get paypal controller, | |||
# Integrating PayPal | |||
### 1. Validate Currency Support | |||
Example: | |||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller | |||
controller = get_integration_controller("PayPal", setup=False) | |||
controller().validate_transaction_currency(currency) | |||
### 2. Redirect for payment | |||
2. check whether transaction currency supported by payment gateway, | |||
Controller().validate_transaction_currency(currency) | |||
Example: | |||
3. Get checkout url via controller or api, this will redirect you to payment page of particular gateway. | |||
payment_details = { | |||
"amount": 600, | |||
"title": "Payment for bill : 111", | |||
"description": "payment via cart", | |||
"reference_doctype": "Payment Request", | |||
"reference_docname": "PR0001", | |||
"payer_email": "NuranVerkleij@example.com", | |||
"payer_name": "Nuran Verkleij", | |||
"order_id": "111", | |||
"currency": "USD" | |||
} | |||
payment_details = { | |||
"amount": 600, | |||
"title": "Payment for bill : 111", | |||
"description": "payment via cart", | |||
"reference_doctype": "Payment Request", | |||
"reference_docname": "PR0001", | |||
"payer_email": "NuranVerkleij@example.com", | |||
"payer_name": "Nuran Verkleij", | |||
"order_id": "111", | |||
"currency": "USD" | |||
} | |||
# redirect the user to this url | |||
url = controller().get_payment_url(**payment_details) | |||
Via API | |||
--------- | |||
Get payement url via get_checkout_url(**kwargs) | |||
example: | |||
from frappe.integration.paypal import get_checkout_url | |||
get_checkout_url(**payment_details) | |||
### 3. On Completion of Payment | |||
Via Controller | |||
--------------- | |||
Get payment url via get_payment_url(**kwargs) | |||
Write a method for `on_payment_authorized` in the reference doctype | |||
example: | |||
controller().get_payment_url(**payment_details) | |||
Example: | |||
4.To handle a callback of payment, you need to write `on_payment_authorized` | |||
in reference document. | |||
def on_payment_authorized(payment_status): | |||
# your code to handle callback | |||
example: | |||
def on_payment_authorized(payment_satus): | |||
"your code to handle callback" | |||
##### Note: | |||
parameter description | |||
--------------------- | |||
payment_satus - payment gateway will put payment status on callback. | |||
payment_status - payment gateway will put payment status on callback. | |||
For paypal payment status parameter is one from: [Completed, Cancelled, Failed] | |||
""" | |||
@@ -83,27 +80,27 @@ class Controller(IntegrationController): | |||
"reqd": 1 | |||
} | |||
] | |||
js = "assets/frappe/js/integrations/paypal.js" | |||
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"] | |||
def enable(self, parameters, use_test_account=0): | |||
call_hook_method('payment_gateway_enabled', gateway=self.service_name) | |||
self.parameters = parameters | |||
self.validate_paypal_credentails(use_test_account) | |||
def get_settings(self): | |||
return frappe._dict(self.parameters) | |||
def validate_transaction_currency(self, currency): | |||
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)) | |||
def get_paypal_params_and_url(self, use_test_account): | |||
paypal_settings = frappe._dict(self.get_settings()) | |||
params = { | |||
"USER": paypal_settings.api_username, | |||
"PWD": paypal_settings.api_password, | |||
@@ -111,9 +108,9 @@ class Controller(IntegrationController): | |||
"VERSION": "98", | |||
"METHOD": "GetPalDetails" | |||
} | |||
api_url = "https://api-3t.sandbox.paypal.com/nvp" if use_test_account else "https://api-3t.paypal.com/nvp" | |||
return params, api_url | |||
def validate_paypal_credentails(self, use_test_account): | |||
@@ -134,7 +131,7 @@ class Controller(IntegrationController): | |||
self.parameters = json.loads(custom_settings_json) | |||
response = self.execute_set_express_checkout(kwargs["amount"], kwargs["currency"], use_test_account) | |||
if use_test_account: | |||
return_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" | |||
else: | |||
@@ -144,9 +141,9 @@ class Controller(IntegrationController): | |||
"token": response.get("TOKEN")[0], | |||
"correlation_id": response.get("CORRELATIONID")[0] | |||
}) | |||
self.integration_request = self.create_request(kwargs, "Remote", self.service_name, response.get("TOKEN")[0]) | |||
return return_url.format(kwargs["token"]) | |||
def execute_set_express_checkout(self, amount, currency, use_test_account): | |||
@@ -159,7 +156,7 @@ class Controller(IntegrationController): | |||
"returnUrl": get_url("/api/method/frappe.integrations.paypal.get_express_checkout_details"), | |||
"cancelUrl": get_url("/payment-cancel") | |||
}) | |||
params = urlencode(params) | |||
response = self.post_request(url, data=params.encode("utf-8")) | |||
@@ -172,7 +169,7 @@ class Controller(IntegrationController): | |||
def get_express_checkout_details(token): | |||
use_test_account, custom_settings_json = frappe.db.get_value("Integration Service", "PayPal", ["use_test_account", "custom_settings_json"]) | |||
Controller.parameters = json.loads(custom_settings_json) | |||
params, url = Controller().get_paypal_params_and_url(use_test_account) | |||
params.update({ | |||
"METHOD": "GetExpressCheckoutDetails", | |||
@@ -188,12 +185,12 @@ def get_express_checkout_details(token): | |||
http_status_code=frappe.ValidationError.http_status_code) | |||
return | |||
update_integration_request_status(token, { | |||
"payerid": response.get("PAYERID")[0], | |||
"payer_email": response.get("EMAIL")[0] | |||
}, "Authorized") | |||
frappe.local.response["type"] = "redirect" | |||
frappe.local.response["location"] = get_url( \ | |||
"/api/method/frappe.integrations.paypal.confirm_payment?token={0}".format(token)) | |||
@@ -202,7 +199,7 @@ def get_express_checkout_details(token): | |||
def confirm_payment(token): | |||
redirect = True | |||
status_changed_to, redirect_to = None, None | |||
use_test_account = frappe.db.get_value("Integration Service", "PayPal", "use_test_account") | |||
integration_request = frappe.get_doc("Integration Request", token) | |||
@@ -10,52 +10,53 @@ from frappe.utils import get_url, call_hook_method | |||
from frappe.integration_broker.integration_controller import IntegrationController | |||
""" | |||
1. Get razorpay controller, | |||
# Integrating RazorPay | |||
### Validate Currency | |||
Example: | |||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller | |||
controller = get_integration_controller("Razorpay", setup=False) | |||
2. check whether transaction currency supported by payment gateway, | |||
Controller().validate_transaction_currency(currency) | |||
controller = get_integration_controller("Razorpay", setup=False) | |||
controller().validate_transaction_currency(currency) | |||
3. Get checkout url via controller or api, this will redirect you to payment page of particular gateway. | |||
### 2. Redirect for payment | |||
payment_details = { | |||
"amount": 600, | |||
"title": "Payment for bill : 111", | |||
"description": "payment via cart", | |||
"reference_doctype": "Payment Request", | |||
"reference_docname": "PR0001", | |||
"payer_email": "NuranVerkleij@example.com", | |||
"payer_name": "Nuran Verkleij", | |||
"order_id": "111", | |||
"currency": "INR" | |||
} | |||
Example: | |||
Via API | |||
--------- | |||
Get payement url via get_checkout_url(**kwargs) | |||
payment_details = { | |||
"amount": 600, | |||
"title": "Payment for bill : 111", | |||
"description": "payment via cart", | |||
"reference_doctype": "Payment Request", | |||
"reference_docname": "PR0001", | |||
"payer_email": "NuranVerkleij@example.com", | |||
"payer_name": "Nuran Verkleij", | |||
"order_id": "111", | |||
"currency": "INR" | |||
} | |||
example: | |||
from frappe.integration.razorpay import get_checkout_url | |||
get_checkout_url(**payment_details) | |||
Via Controller | |||
--------------- | |||
Get payment url via get_payment_url(**kwargs) | |||
# Redirect the user to this url | |||
url = controller().get_payment_url(**payment_details) | |||
### 3. On Completion of Payment | |||
example: | |||
controller().get_payment_url(**payment_details) | |||
Write a method for `on_payment_authorized` in the reference doctype | |||
4.To handle a callback of payment, you need to write `on_payment_authorized` | |||
in reference document. | |||
Example: | |||
example: | |||
def on_payment_authorized(payment_satus): | |||
"your code to handle callback" | |||
def on_payment_authorized(payment_status): | |||
# this method will be called when payment is complete | |||
parameter description | |||
--------------------- | |||
payment_satus - payment gateway will put payment status on callback. For razorpay payment status is Authorized | |||
##### Notes: | |||
payment_status - payment gateway will put payment status on callback. | |||
For razorpay payment status is Authorized | |||
""" | |||
@@ -75,7 +76,7 @@ class Controller(IntegrationController): | |||
'reqd': 1 | |||
} | |||
] | |||
# do also changes in razorpay.js scheduler job helper | |||
scheduled_jobs = [ | |||
{ | |||
@@ -84,11 +85,11 @@ class Controller(IntegrationController): | |||
] | |||
} | |||
] | |||
js = "assets/frappe/js/integrations/razorpay.js" | |||
supported_currencies = ["INR"] | |||
def enable(self, parameters, use_test_account=0): | |||
call_hook_method('payment_gateway_enabled', gateway='Razorpay') | |||
self.parameters = parameters | |||
@@ -103,23 +104,23 @@ class Controller(IntegrationController): | |||
auth=(razorpay_settings.api_key, razorpay_settings.api_secret)) | |||
except Exception: | |||
frappe.throw(_("Seems API Key or API Secret is wrong !!!")) | |||
def validate_transaction_currency(self, currency): | |||
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)) | |||
def get_payment_url(self, **kwargs): | |||
return get_url("./integrations/razorpay_checkout?{0}".format(urllib.urlencode(kwargs))) | |||
def get_settings(self): | |||
if hasattr(self, "parameters"): | |||
return frappe._dict(self.parameters) | |||
custom_settings_json = frappe.db.get_value("Integration Service", "Razorpay", "custom_settings_json", debug=1) | |||
if custom_settings_json: | |||
return frappe._dict(json.loads(custom_settings_json)) | |||
def create_request(self, data): | |||
self.data = frappe._dict(data) | |||
@@ -133,29 +134,29 @@ class Controller(IntegrationController): | |||
"redirect_to": frappe.redirect_to_message(_('Server Error'), _("Seems issue with server's razorpay config. Don't worry, in case of failure amount will get refunded to your account.")), | |||
"status": 401 | |||
} | |||
def authorize_payment(self): | |||
""" | |||
An authorization is performed when user’s payment details are successfully authenticated by the bank. | |||
The money is deducted from the customer’s account, but will not be transferred to the merchant’s account | |||
until it is explicitly captured by merchant. | |||
""" | |||
settings = self.get_settings() | |||
if self.integration_request.status != "Authorized": | |||
resp = self.get_request("https://api.razorpay.com/v1/payments/{0}" | |||
.format(self.data.razorpay_payment_id), auth=(settings.api_key, | |||
settings.api_secret)) | |||
if resp.get("status") == "authorized": | |||
self.integration_request.db_set('status', 'Authorized', update_modified=False) | |||
self.flags.status_changed_to = "Authorized" | |||
if self.flags.status_changed_to == "Authorized": | |||
if self.data.reference_doctype and self.data.reference_docname: | |||
redirect_to = frappe.get_doc(self.data.reference_doctype, self.data.reference_docname).run_method("on_payment_authorized", self.flags.status_changed_to) | |||
return { | |||
"redirect_to": redirect_to or "payment-success", | |||
"status": 200 | |||
@@ -180,7 +181,7 @@ def capture_payment(is_sandbox=False, sanbox_response=None): | |||
data = json.loads(doc.data) | |||
resp = controller.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")}) | |||
if resp.get("status") == "captured": | |||
frappe.db.set_value("Integration Request", doc.name, "status", "Completed") | |||
@@ -573,3 +573,9 @@ a.edit:visited, | |||
.page-content-wrapper > .row .col-sm-4 { | |||
display: none; | |||
} | |||
.screenshot { | |||
border: 2px solid #d1d8dd; | |||
box-shadow: 1px 1px 7px rgba(0, 0, 0, 0.15); | |||
margin: 15px 0px; | |||
max-width: 100%; | |||
} |
@@ -58,6 +58,10 @@ frappe.views.ListView = Class.extend({ | |||
add_field(this.meta.title_field); | |||
} | |||
// endabled / disabled | |||
if(frappe.meta.has_field(this.doctype, 'enabled')) { add_field('enabled'); }; | |||
if(frappe.meta.has_field(this.doctype, 'disabled')) { add_field('disabled'); }; | |||
// add workflow field (as priority) | |||
this.workflow_state_fieldname = frappe.workflow.get_state_fieldname(this.doctype); | |||
if(this.workflow_state_fieldname) { | |||
@@ -113,8 +117,8 @@ frappe.views.ListView = Class.extend({ | |||
this.columns.push(name_column); | |||
this.total_colspans = this.columns[0].colspan; | |||
if(frappe.model.is_submittable(this.doctype) | |||
|| this.settings.get_indicator || this.workflow_state_fieldname) { | |||
if(frappe.has_indicator(this.doctype)) { | |||
// indicator | |||
this.columns.push({ | |||
colspan: this.settings.colwidths && this.settings.colwidths.indicator || 3, | |||
@@ -1,5 +1,17 @@ | |||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||
frappe.has_indicator = function(doctype) { | |||
// returns true if indicator is present | |||
if(frappe.model.is_submittable(this.doctype)) { | |||
return true; | |||
} else if(this.settings.get_indicator || this.workflow_state_fieldname) { | |||
return true; | |||
} else if(frappe.meta.has_field(doctype, 'enabled') || frappe.meta.has_field(doctype, 'disabled')) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
frappe.get_indicator = function(doc, doctype) { | |||
if(doc.__unsaved) { | |||
return [__("Not Saved"), "orange"]; | |||
@@ -38,10 +50,12 @@ frappe.get_indicator = function(doc, doctype) { | |||
} | |||
} | |||
// draft if document is submittable | |||
if(is_submittable && doc.docstatus==0 && !settings.has_indicator_for_draft) { | |||
return [__("Draft"), "red", "docstatus,=,0"]; | |||
} | |||
// cancelled | |||
if(is_submittable && doc.docstatus==2) { | |||
return [__("Cancelled"), "red", "docstatus,=,2"]; | |||
} | |||
@@ -51,11 +65,31 @@ frappe.get_indicator = function(doc, doctype) { | |||
if(indicator) return indicator; | |||
} | |||
// if submittable | |||
if(is_submittable && doc.docstatus==1) { | |||
return [__("Submitted"), "blue", "docstatus,=,1"]; | |||
} | |||
// based on status | |||
if(doc.status) { | |||
return [__(doc.status), frappe.utils.guess_colour(doc.status)]; | |||
} | |||
// based on enabled | |||
if(frappe.meta.has_field(doctype, 'enabled')) { | |||
if(doc.enabled) { | |||
return [__('Enabled'), 'blue', 'enabled=1']; | |||
} else { | |||
return [__('Disabled'), 'grey', 'enabled=0']; | |||
} | |||
} | |||
// based on disabled | |||
if(frappe.meta.has_field(doctype, 'disabled')) { | |||
if(doc.disabled) { | |||
return [__('Disabled'), 'grey', 'disabled=1']; | |||
} else { | |||
return [__('Enabled'), 'blue', 'disabled=0']; | |||
} | |||
} | |||
} |
@@ -395,3 +395,11 @@ a.edit, a.edit:hover, a.edit:focus, a.edit:visited, .edit-container .icon { | |||
display: none; | |||
} | |||
} | |||
.screenshot { | |||
border: 2px solid @border-color; | |||
box-shadow: 1px 1px 7px rgba(0,0,0,0.15); | |||
margin: 15px 0px; | |||
max-width: 100%; | |||
} | |||
@@ -8,6 +8,8 @@ frappe.utils.autodoc | |||
Inspect elements of a given module and return its objects | |||
""" | |||
from __future__ import unicode_literals | |||
import inspect, importlib, re, frappe | |||
from frappe.model.document import get_controller | |||
@@ -42,7 +44,7 @@ def automodule(name): | |||
return { | |||
"members": filter(None, attributes), | |||
"docs": getattr(obj, "__doc__", "") | |||
"docs": get_obj_doc(obj) | |||
} | |||
installed = None | |||
@@ -89,11 +91,11 @@ def get_class_info(class_obj, module_name): | |||
"type": "class", | |||
"bases": [b.__module__ + "." + b.__name__ for b in class_obj.__bases__], | |||
"members": filter(None, members), | |||
"docs": parse(getattr(class_obj, "__doc__", "")) | |||
"docs": parse(get_obj_doc(class_obj)) | |||
} | |||
def get_function_info(value): | |||
docs = getattr(value, "__doc__") | |||
docs = get_obj_doc(value) | |||
return { | |||
"name": value.__name__, | |||
"type": "function", | |||
@@ -108,8 +110,6 @@ def parse(docs): | |||
if not docs: | |||
return "" | |||
docs = strip_leading_tabs(docs) | |||
if ":param" in docs: | |||
out, title_set = [], False | |||
for line in docs.splitlines(): | |||
@@ -154,3 +154,10 @@ def strip_leading_tabs(docs): | |||
def automodel(doctype): | |||
"""return doctype template""" | |||
pass | |||
def get_obj_doc(obj): | |||
'''Return `__doc__` of the given object as unicode''' | |||
doc = getattr(obj, "__doc__", "") or '' | |||
if not isinstance(doc, unicode): | |||
doc = unicode(doc, 'utf-8') | |||
return doc |
@@ -205,7 +205,7 @@ class setup_docs(object): | |||
context = {"name": self.app + "." + module_name} | |||
context.update(self.app_context) | |||
f.write(frappe.render_template("templates/autodoc/pymodule.html", | |||
context)) | |||
context).encode('utf-8')) | |||
self.update_index_txt(module_folder) | |||
@@ -272,12 +272,18 @@ class setup_docs(object): | |||
frappe.local.flags.home_page = "index" | |||
from frappe.website.router import get_pages, make_toc | |||
pages = get_pages() | |||
pages = get_pages(self.app) | |||
# clear the user, current folder in target | |||
shutil.rmtree(os.path.join(self.target, "user"), ignore_errors=True) | |||
shutil.rmtree(os.path.join(self.target, "current"), ignore_errors=True) | |||
def raw_replacer(matchobj): | |||
if '{% raw %}' in matchobj.group(0): | |||
return matchobj.group(0) | |||
else: | |||
return '{% raw %}' + matchobj.group(0) + '{% endraw %}' | |||
cnt = 0 | |||
for path, context in pages.iteritems(): | |||
print "Writing {0}".format(path) | |||
@@ -336,7 +342,7 @@ class setup_docs(object): | |||
context.base_template_path = "templates/autodoc/base_template.html" | |||
if '<code>' in context.source: | |||
context.source = re.sub('\<code\>(.*)\</code\>', '<code>{% raw %}\g<1>{% endraw %}</code>', context.source) | |||
context.source = re.sub('\<code\>(.*)\</code\>', raw_replacer, context.source) | |||
html = frappe.render_template(context.source, context) | |||
@@ -110,13 +110,17 @@ def get_page_info_from_doctypes(path=None): | |||
return routes | |||
def get_pages(): | |||
def get_pages(app=None): | |||
'''Get all pages. Called for docs / sitemap''' | |||
pages = {} | |||
frappe.local.flags.in_get_all_pages = True | |||
folders = frappe.local.flags.web_pages_folders or ('www', 'templates/pages') | |||
apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps() | |||
if app: | |||
apps = [app] | |||
else: | |||
apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps() | |||
for app in apps: | |||
app_path = frappe.get_app_path(app) | |||