diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..1f225527 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,130 @@ +{ + "env": { + "browser": true, + "node": true, + "es2022": true + }, + "parserOptions": { + "sourceType": "module" + }, + "extends": "eslint:recommended", + "rules": { + "indent": "off", + "brace-style": "off", + "no-mixed-spaces-and-tabs": "off", + "no-useless-escape": "off", + "space-unary-ops": ["error", { "words": true }], + "linebreak-style": "off", + "quotes": ["off"], + "semi": "off", + "camelcase": "off", + "no-unused-vars": "off", + "no-console": ["warn"], + "no-extra-boolean-cast": ["off"], + "no-control-regex": ["off"] + }, + "root": true, + "globals": { + "frappe": true, + "Vue": true, + "SetVueGlobals": true, + "erpnext": true, + "hub": true, + "$": true, + "jQuery": true, + "moment": true, + "hljs": true, + "Awesomplete": true, + "CalHeatMap": true, + "Sortable": true, + "Showdown": true, + "Taggle": true, + "Gantt": true, + "Slick": true, + "PhotoSwipe": true, + "PhotoSwipeUI_Default": true, + "fluxify": true, + "io": true, + "c3": true, + "__": true, + "_p": true, + "_f": true, + "repl": true, + "Class": true, + "locals": true, + "cint": true, + "cstr": true, + "cur_frm": true, + "cur_dialog": true, + "cur_page": true, + "cur_list": true, + "cur_tree": true, + "cur_pos": true, + "msg_dialog": true, + "is_null": true, + "in_list": true, + "has_common": true, + "posthog": true, + "has_words": true, + "validate_email": true, + "open_web_template_values_editor": true, + "get_number_format": true, + "format_number": true, + "format_currency": true, + "round_based_on_smallest_currency_fraction": true, + "roundNumber": true, + "comment_when": true, + "replace_newlines": true, + "open_url_post": true, + "toTitle": true, + "lstrip": true, + "strip": true, + "strip_html": true, + "replace_all": true, + "flt": true, + "precision": true, + "md5": true, + "CREATE": true, + "AMEND": true, + "CANCEL": true, + "copy_dict": true, + "get_number_format_info": true, + "print_table": true, + "Layout": true, + "web_form_settings": true, + "$c": true, + "$a": true, + "$i": true, + "$bg": true, + "$y": true, + "$c_obj": true, + "$c_obj_csv": true, + "refresh_many": true, + "refresh_field": true, + "toggle_field": true, + "get_field_obj": true, + "get_query_params": true, + "unhide_field": true, + "hide_field": true, + "set_field_options": true, + "getCookie": true, + "getCookies": true, + "get_url_arg": true, + "get_server_fields": true, + "set_multiple": true, + "QUnit": true, + "Chart": true, + "Cypress": true, + "cy": true, + "describe": true, + "expect": true, + "it": true, + "context": true, + "before": true, + "beforeEach": true, + "onScan": true, + "extend_cscript": true, + "localforage": true, + "Plaid": true + } +} diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..56274db0 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,15 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# pre-commit formatting ruff, eslint, prettier (automated) +63a4acf6c9fe9657fa6d7ad659465b0d5ef3d73f + +# pre-commit formatting ruff, eslint, prettier (manual fixup) +cecf0bec9de2dcd176fc632e8a5348ab2f491cbe \ No newline at end of file diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..65bc68f2 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,4 @@ +# Any python files modifed but no test files modified +needs-tests: +- any: ['payments/**/*.py'] + all: ['!payments/**/test*.py'] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f552a81d..e9f5b27e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,24 +20,47 @@ repos: - id: check-yaml - id: debug-statements - - repo: https://github.com/asottile/pyupgrade - rev: v2.34.0 + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.7.1 hooks: - - id: pyupgrade - args: ['--py310-plus'] + - id: prettier + types_or: [javascript, vue, scss] + # Ignore any files that might contain jinja / bundles + exclude: | + (?x)^( + payments/public/dist/.*| + cypress/.*| + .*node_modules.*| + payments/templates/includes/.* + )$ - - repo: https://github.com/adityahase/black - rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119 + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.44.0 hooks: - - id: black - additional_dependencies: ['click==8.0.4'] + - id: eslint + types_or: [javascript] + args: ['--quiet'] + # Ignore any files that might contain jinja / bundles + exclude: | + (?x)^( + payments/public/dist/.*| + cypress/.*| + .*node_modules.*| + payments/templates/includes/.* + )$ - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.0 hooks: - - id: flake8 - additional_dependencies: ['flake8-bugbear',] - args: ['--config', '.github/helper/flake8.conf'] + - id: ruff + name: "Run ruff import sorter" + args: ["--select=I", "--fix"] + + - id: ruff + name: "Run ruff linter" + + - id: ruff-format + name: "Run ruff formatter" ci: autoupdate_schedule: weekly diff --git a/commitlint.config.js b/commitlint.config.js index 8847564e..bd5cebce 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,25 +1,25 @@ module.exports = { - parserPreset: 'conventional-changelog-conventionalcommits', - rules: { - 'subject-empty': [2, 'never'], - 'type-case': [2, 'always', 'lower-case'], - 'type-empty': [2, 'never'], - 'type-enum': [ - 2, - 'always', - [ - 'build', - 'chore', - 'ci', - 'docs', - 'feat', - 'fix', - 'perf', - 'refactor', - 'revert', - 'style', - 'test', - ], - ], - }, + parserPreset: "conventional-changelog-conventionalcommits", + rules: { + "subject-empty": [2, "never"], + "type-case": [2, "always", "lower-case"], + "type-empty": [2, "never"], + "type-enum": [ + 2, + "always", + [ + "build", + "chore", + "ci", + "docs", + "feat", + "fix", + "perf", + "refactor", + "revert", + "style", + "test", + ], + ], + }, }; diff --git a/payments/payment_gateways/doctype/braintree_settings/braintree_settings.js b/payments/payment_gateways/doctype/braintree_settings/braintree_settings.js index c844022c..f9d77bf7 100644 --- a/payments/payment_gateways/doctype/braintree_settings/braintree_settings.js +++ b/payments/payment_gateways/doctype/braintree_settings/braintree_settings.js @@ -1,6 +1,4 @@ // Copyright (c) 2018, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Braintree Settings', { - -}); +frappe.ui.form.on("Braintree Settings", {}); diff --git a/payments/payment_gateways/doctype/braintree_settings/braintree_settings.py b/payments/payment_gateways/doctype/braintree_settings/braintree_settings.py index 717443e8..5c0fe801 100644 --- a/payments/payment_gateways/doctype/braintree_settings/braintree_settings.py +++ b/payments/payment_gateways/doctype/braintree_settings/braintree_settings.py @@ -14,7 +14,7 @@ class BraintreeSettings(Document): - supported_currencies = [ + supported_currencies = ( "AED", "AMD", "AOA", @@ -150,7 +150,7 @@ class BraintreeSettings(Document): "ZAR", "ZMK", "ZWD", - ] + ) def validate(self): if not self.flags.ignore_mandatory: diff --git a/payments/payment_gateways/doctype/gocardless_mandate/gocardless_mandate.js b/payments/payment_gateways/doctype/gocardless_mandate/gocardless_mandate.js index 37f9f7b9..9f8a22bb 100644 --- a/payments/payment_gateways/doctype/gocardless_mandate/gocardless_mandate.js +++ b/payments/payment_gateways/doctype/gocardless_mandate/gocardless_mandate.js @@ -1,5 +1,4 @@ // Copyright (c) 2018, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('GoCardless Mandate', { -}); +frappe.ui.form.on("GoCardless Mandate", {}); diff --git a/payments/payment_gateways/doctype/gocardless_settings/__init__.py b/payments/payment_gateways/doctype/gocardless_settings/__init__.py index 65be5993..d8f8a3d3 100644 --- a/payments/payment_gateways/doctype/gocardless_settings/__init__.py +++ b/payments/payment_gateways/doctype/gocardless_settings/__init__.py @@ -34,7 +34,7 @@ def set_status(event): def set_mandate_status(event): mandates = [] - if isinstance(event["links"], (list,)): + if isinstance(event["links"], list): for link in event["links"]: mandates.append(link["mandate"]) else: diff --git a/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.js b/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.js index ef1b97f6..a4db118d 100644 --- a/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.js +++ b/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.js @@ -5,4 +5,4 @@ // refresh(frm) { // }, -// }); \ No newline at end of file +// }); diff --git a/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.py b/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.py index ac6b83a9..4abb7e97 100644 --- a/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.py +++ b/payments/payment_gateways/doctype/gocardless_settings/gocardless_settings.py @@ -13,7 +13,7 @@ class GoCardlessSettings(Document): - supported_currencies = ["EUR", "DKK", "GBP", "SEK", "AUD", "NZD", "CAD", "USD"] + supported_currencies = ("EUR", "DKK", "GBP", "SEK", "AUD", "NZD", "CAD", "USD") def validate(self): self.initialize_client() @@ -21,9 +21,7 @@ def validate(self): def initialize_client(self): self.environment = self.get_environment() try: - self.client = gocardless_pro.Client( - access_token=self.access_token, environment=self.environment - ) + self.client = gocardless_pro.Client(access_token=self.access_token, environment=self.environment) return self.client except Exception as e: frappe.throw(e) @@ -64,7 +62,6 @@ def on_payment_request_submission(self, data): return True def check_mandate_validity(self, data): - if frappe.db.exists("GoCardless Mandate", dict(customer=data.get("payer_name"), disabled=0)): registered_mandate = frappe.db.get_value( "GoCardless Mandate", dict(customer=data.get("payer_name"), disabled=0), "mandate" @@ -124,9 +121,7 @@ def create_charge_on_gocardless(self): redirect_to = self.data.get("redirect_to") or None redirect_message = self.data.get("redirect_message") or None - reference_doc = frappe.get_doc( - self.data.get("reference_doctype"), self.data.get("reference_docname") - ) + reference_doc = frappe.get_doc(self.data.get("reference_doctype"), self.data.get("reference_docname")) self.initialize_client() try: @@ -172,7 +167,7 @@ def create_charge_on_gocardless(self): frappe.log_error("Gocardless payment failed") self.integration_request.db_set("error", payment.status, update_modified=False) - except Exception as e: + except Exception: frappe.log_error("GoCardless Payment Error") if self.flags.status_changed_to == "Completed": diff --git a/payments/payment_gateways/doctype/mpesa_settings/mpesa_connector.py b/payments/payment_gateways/doctype/mpesa_settings/mpesa_connector.py index 7eb8b9c0..ce6df833 100644 --- a/payments/payment_gateways/doctype/mpesa_settings/mpesa_connector.py +++ b/payments/payment_gateways/doctype/mpesa_settings/mpesa_connector.py @@ -119,10 +119,8 @@ def stk_push( errorMessage(str): This is a predefined code that indicates the reason for request failure. """ - time = ( - str(datetime.datetime.now()).split(".")[0].replace("-", "").replace(" ", "").replace(":", "") - ) - password = f"{str(business_shortcode)}{str(passcode)}{time}" + time = str(datetime.datetime.now()).split(".")[0].replace("-", "").replace(" ", "").replace(":", "") + password = f"{business_shortcode!s}{passcode!s}{time}" encoded = base64.b64encode(bytes(password, encoding="utf8")) payload = { "BusinessShortCode": business_shortcode, @@ -135,9 +133,7 @@ def stk_push( "CallBackURL": callback_url, "AccountReference": reference_code, "TransactionDesc": description, - "TransactionType": "CustomerPayBillOnline" - if self.env == "sandbox" - else "CustomerBuyGoodsOnline", + "TransactionType": "CustomerPayBillOnline" if self.env == "sandbox" else "CustomerBuyGoodsOnline", } headers = { "Authorization": f"Bearer {self.authentication_token}", diff --git a/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.js b/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.js index 9d625736..491223da 100644 --- a/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.js +++ b/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.js @@ -1,36 +1,38 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Mpesa Settings', { - onload_post_render: function(frm) { - frm.events.setup_account_balance_html(frm); - }, +frappe.ui.form.on("Mpesa Settings", { + onload_post_render: function (frm) { + frm.events.setup_account_balance_html(frm); + }, - refresh: function(frm) { - frappe.realtime.on("refresh_mpesa_dashboard", function(){ - frm.reload_doc(); - frm.events.setup_account_balance_html(frm); - }); - }, + refresh: function (frm) { + frappe.realtime.on("refresh_mpesa_dashboard", function () { + frm.reload_doc(); + frm.events.setup_account_balance_html(frm); + }); + }, - get_account_balance: function(frm) { - if (!frm.doc.initiator_name && !frm.doc.security_credential) { - frappe.throw(__("Please set the initiator name and the security credential")); - } - frappe.call({ - method: "get_account_balance_info", - doc: frm.doc - }); - }, + get_account_balance: function (frm) { + if (!frm.doc.initiator_name && !frm.doc.security_credential) { + frappe.throw( + __("Please set the initiator name and the security credential") + ); + } + frappe.call({ + method: "get_account_balance_info", + doc: frm.doc, + }); + }, - setup_account_balance_html: function(frm) { - if (!frm.doc.account_balance) return; - $("div").remove(".form-dashboard-section.custom"); - frm.dashboard.add_section( - frappe.render_template('account_balance', { - data: JSON.parse(frm.doc.account_balance) - }) - ); - frm.dashboard.show(); - } + setup_account_balance_html: function (frm) { + if (!frm.doc.account_balance) return; + $("div").remove(".form-dashboard-section.custom"); + frm.dashboard.add_section( + frappe.render_template("account_balance", { + data: JSON.parse(frm.doc.account_balance), + }) + ); + frm.dashboard.show(); + }, }); diff --git a/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.py b/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.py index 43d5348b..97e9aacc 100644 --- a/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.py +++ b/payments/payment_gateways/doctype/mpesa_settings/mpesa_settings.py @@ -18,7 +18,7 @@ class MpesaSettings(Document): - supported_currencies = ["KES"] + supported_currencies = ("KES",) def validate_transaction_currency(self, currency): if currency not in self.supported_currencies: @@ -51,7 +51,7 @@ def request_for_payment(self, **kwargs): args = frappe._dict(kwargs) request_amounts = self.split_request_amount_according_to_transaction_limit(args) - for i, amount in enumerate(request_amounts): + for _i, amount in enumerate(request_amounts): args.request_amount = amount if frappe.flags.in_test: from payments.payment_gateways.doctype.mpesa_settings.test_mpesa_settings import ( @@ -104,8 +104,8 @@ def get_account_balance_info(self): def handle_api_response(self, global_id, request_dict, response): """Response received from API calls returns a global identifier for each transaction, this code is returned during the callback.""" # check error response - if getattr(response, "requestId"): - req_name = getattr(response, "requestId") + if response.requestId: + req_name = response.requestId error = response else: # global checkout id used as request name @@ -116,7 +116,7 @@ def handle_api_response(self, global_id, request_dict, response): create_request_log(request_dict, "Host", "Mpesa", req_name, error) if error: - frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error")) + frappe.throw(_(response.errorMessage), title=_("Transaction Error")) def generate_stk_push(**kwargs): @@ -197,7 +197,7 @@ def verify_transaction(**kwargs): ) total_paid = amount + sum(completed_payments) - mpesa_receipts = ", ".join(mpesa_receipts + [mpesa_receipt]) + mpesa_receipts = ", ".join([*mpesa_receipts, mpesa_receipt]) if total_paid >= pr.grand_total: pr.run_method("on_payment_authorized", "Completed") @@ -318,9 +318,7 @@ def process_balance_info(**kwargs): ) except Exception: request.handle_failure(account_balance_response) - frappe.log_error( - title="Mpesa Account Balance Processing Error", message=account_balance_response - ) + frappe.log_error(title="Mpesa Account Balance Processing Error", message=account_balance_response) else: request.handle_failure(account_balance_response) diff --git a/payments/payment_gateways/doctype/mpesa_settings/test_mpesa_settings.py b/payments/payment_gateways/doctype/mpesa_settings/test_mpesa_settings.py index aab37d93..c0eb227e 100644 --- a/payments/payment_gateways/doctype/mpesa_settings/test_mpesa_settings.py +++ b/payments/payment_gateways/doctype/mpesa_settings/test_mpesa_settings.py @@ -5,17 +5,16 @@ from json import dumps import frappe - from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_customer from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice -from erpnext.stock.doctype.item.test_item import make_item from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile +from erpnext.stock.doctype.item.test_item import make_item from payments.payment_gateways.doctype.mpesa_settings.mpesa_settings import ( + create_mode_of_payment, process_balance_info, verify_transaction, ) -from payments.payment_gateways.doctype.mpesa_settings.mpesa_settings import create_mode_of_payment class TestMpesaSettings(unittest.TestCase): @@ -120,9 +119,7 @@ def test_processing_of_callback_payload(self): pluck="name", ) - callback_response = get_payment_callback_payload( - Amount=500, CheckoutRequestID=integration_req_ids[0] - ) + callback_response = get_payment_callback_payload(Amount=500, CheckoutRequestID=integration_req_ids[0]) verify_transaction(**callback_response) # test creation of integration request integration_request = frappe.get_doc("Integration Request", integration_req_ids[0]) diff --git a/payments/payment_gateways/doctype/paypal_settings/paypal_settings.js b/payments/payment_gateways/doctype/paypal_settings/paypal_settings.js index 63480bc9..9f8ad92a 100644 --- a/payments/payment_gateways/doctype/paypal_settings/paypal_settings.js +++ b/payments/payment_gateways/doctype/paypal_settings/paypal_settings.js @@ -1,8 +1,6 @@ // Copyright (c) 2016, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('PayPal Settings', { - refresh: function(frm) { - - } +frappe.ui.form.on("PayPal Settings", { + refresh: function (frm) {}, }); diff --git a/payments/payment_gateways/doctype/paypal_settings/paypal_settings.py b/payments/payment_gateways/doctype/paypal_settings/paypal_settings.py index 04943a5c..5bf4a2fb 100644 --- a/payments/payment_gateways/doctype/paypal_settings/paypal_settings.py +++ b/payments/payment_gateways/doctype/paypal_settings/paypal_settings.py @@ -79,7 +79,7 @@ def on_payment_authorized(payment_status): class PayPalSettings(Document): - supported_currencies = [ + supported_currencies = ( "AUD", "BRL", "CAD", @@ -105,14 +105,14 @@ class PayPalSettings(Document): "THB", "TRY", "USD", - ] + ) def __setup__(self): - setattr(self, "use_sandbox", 0) + self.use_sandbox = 0 def setup_sandbox_env(self, token): data = json.loads(frappe.db.get_value("Integration Request", token, "data")) - setattr(self, "use_sandbox", cint(frappe._dict(data).use_sandbox) or 0) + self.use_sandbox = cint(frappe._dict(data).use_sandbox) or 0 def validate(self): create_payment_gateway("PayPal") @@ -171,7 +171,7 @@ def validate_paypal_credentails(self): frappe.throw(_("Invalid payment gateway credentials")) def get_payment_url(self, **kwargs): - setattr(self, "use_sandbox", cint(kwargs.get("use_sandbox", 0))) + self.use_sandbox = cint(kwargs.get("use_sandbox", 0)) response = self.execute_set_express_checkout(**kwargs) diff --git a/payments/payment_gateways/doctype/paytm_settings/paytm_settings.js b/payments/payment_gateways/doctype/paytm_settings/paytm_settings.js index fe2ee7c9..e561698e 100644 --- a/payments/payment_gateways/doctype/paytm_settings/paytm_settings.js +++ b/payments/payment_gateways/doctype/paytm_settings/paytm_settings.js @@ -1,8 +1,14 @@ // Copyright (c) 2020, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Paytm Settings', { - refresh: function(frm) { - frm.dashboard.set_headline(__("For more information, {0}.", [`${__('Click here')}`])); - } +frappe.ui.form.on("Paytm Settings", { + refresh: function (frm) { + frm.dashboard.set_headline( + __("For more information, {0}.", [ + `${__( + "Click here" + )}`, + ]) + ); + }, }); diff --git a/payments/payment_gateways/doctype/paytm_settings/paytm_settings.py b/payments/payment_gateways/doctype/paytm_settings/paytm_settings.py index 99e9f909..db879255 100644 --- a/payments/payment_gateways/doctype/paytm_settings/paytm_settings.py +++ b/payments/payment_gateways/doctype/paytm_settings/paytm_settings.py @@ -24,7 +24,7 @@ class PaytmSettings(Document): - supported_currencies = ["INR"] + supported_currencies = ("INR",) def validate(self): create_payment_gateway("Paytm") @@ -75,7 +75,6 @@ def get_paytm_config(): def get_paytm_params(payment_details, order_id, paytm_config): - # initialize a dictionary paytm_params = dict() @@ -127,9 +126,7 @@ def verify_transaction(**paytm_params): http_status_code=401, indicator_color="red", ) - frappe.log_error( - "Order unsuccessful. Failed Response:" + cstr(paytm_params), "Paytm Payment Failed" - ) + frappe.log_error("Order unsuccessful. Failed Response:" + cstr(paytm_params), "Paytm Payment Failed") def verify_transaction_status(paytm_config, order_id): diff --git a/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.js b/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.js index 6915c5c5..00a5ec9c 100644 --- a/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.js +++ b/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.js @@ -1,8 +1,6 @@ // Copyright (c) 2016, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Razorpay Settings', { - refresh: function(frm) { - - } -}); \ No newline at end of file +frappe.ui.form.on("Razorpay Settings", { + refresh: function (frm) {}, +}); diff --git a/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.py b/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.py index ebf16056..7394456f 100644 --- a/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.py +++ b/payments/payment_gateways/doctype/razorpay_settings/razorpay_settings.py @@ -79,7 +79,7 @@ def on_payment_authorized(payment_status): class RazorpaySettings(Document): - supported_currencies = ["INR"] + supported_currencies = ("INR",) def init_client(self): if self.api_key: @@ -251,8 +251,8 @@ def create_request(self, data): 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 + 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. """ data = json.loads(self.integration_request.data) @@ -306,8 +306,8 @@ def authorize_payment(self): if custom_redirect_to: redirect_to = custom_redirect_to - redirect_url = "payment-success?doctype={}&docname={}".format( - self.data.reference_doctype, self.data.reference_docname + redirect_url = ( + f"payment-success?doctype={self.data.reference_doctype}&docname={self.data.reference_docname}" ) else: redirect_url = "payment-failed" @@ -341,7 +341,7 @@ def cancel_subscription(self, subscription_id): settings = self.get_settings({}) try: - resp = make_post_request( + make_post_request( f"https://api.razorpay.com/v1/subscriptions/{subscription_id}/cancel", auth=(settings.api_key, settings.api_secret), ) @@ -393,7 +393,9 @@ def capture_payment(is_sandbox=False, sanbox_response=None): if resp.get("status") == "authorized": resp = make_post_request( - "https://api.razorpay.com/v1/payments/{}/capture".format(data.get("razorpay_payment_id")), + "https://api.razorpay.com/v1/payments/{}/capture".format( + data.get("razorpay_payment_id") + ), auth=(settings.api_key, settings.api_secret), data={"amount": data.get("amount")}, ) diff --git a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.js b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.js index 578ae949..31636949 100644 --- a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.js +++ b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Stripe Settings', { - refresh: function(frm) { - - } +frappe.ui.form.on("Stripe Settings", { + refresh: function (frm) {}, }); diff --git a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py index bb1bc677..ce3a1a02 100644 --- a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py +++ b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py @@ -1,6 +1,7 @@ # Copyright (c) 2017, Frappe Technologies and contributors # License: MIT. See LICENSE +from types import MappingProxyType from urllib.parse import urlencode import frappe @@ -11,9 +12,27 @@ from payments.utils import create_payment_gateway +currency_wise_minimum_charge_amount = { + "JPY": 50, + "MXN": 10, + "DKK": 2.50, + "HKD": 4.00, + "NOK": 3.00, + "SEK": 3.00, + "USD": 0.50, + "AUD": 0.50, + "BRL": 0.50, + "CAD": 0.50, + "CHF": 0.50, + "EUR": 0.50, + "GBP": 0.30, + "NZD": 0.50, + "SGD": 0.50, +} + class StripeSettings(Document): - supported_currencies = [ + supported_currencies = ( "AED", "ALL", "ANG", @@ -128,25 +147,9 @@ class StripeSettings(Document): "XPF", "YER", "ZAR", - ] - - currency_wise_minimum_charge_amount = { - "JPY": 50, - "MXN": 10, - "DKK": 2.50, - "HKD": 4.00, - "NOK": 3.00, - "SEK": 3.00, - "USD": 0.50, - "AUD": 0.50, - "BRL": 0.50, - "CAD": 0.50, - "CHF": 0.50, - "EUR": 0.50, - "GBP": 0.30, - "NZD": 0.50, - "SGD": 0.50, - } + ) + + currency_wise_minimum_charge_amount = MappingProxyType(currency_wise_minimum_charge_amount) def on_update(self): create_payment_gateway( @@ -225,7 +228,7 @@ def create_charge_on_stripe(self): receipt_email=self.data.payer_email, ) - if charge.captured == True: + if charge.captured is True: self.integration_request.db_set("status", "Completed", update_modified=False) self.flags.status_changed_to = "Completed" @@ -255,9 +258,7 @@ def finalize_request(self): if custom_redirect_to: redirect_to = custom_redirect_to - redirect_url = "payment-success?doctype={}&docname={}".format( - self.data.reference_doctype, self.data.reference_docname - ) + redirect_url = f"payment-success?doctype={self.data.reference_doctype}&docname={self.data.reference_docname}" if self.redirect_url: redirect_url = self.redirect_url diff --git a/payments/payment_gateways/stripe_integration.py b/payments/payment_gateways/stripe_integration.py index 35c63c55..2d7e8a5d 100644 --- a/payments/payment_gateways/stripe_integration.py +++ b/payments/payment_gateways/stripe_integration.py @@ -1,8 +1,8 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -import stripe import frappe +import stripe from frappe import _ from frappe.integrations.utils import create_request_log diff --git a/payments/payments/doctype/payment_gateway/payment_gateway.js b/payments/payments/doctype/payment_gateway/payment_gateway.js index 0eff5a56..3e74b9cb 100644 --- a/payments/payments/doctype/payment_gateway/payment_gateway.js +++ b/payments/payments/doctype/payment_gateway/payment_gateway.js @@ -1,8 +1,6 @@ // 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) { - - } +frappe.ui.form.on("Payment Gateway", { + refresh: function (frm) {}, }); diff --git a/payments/public/js/razorpay.js b/payments/public/js/razorpay.js index c74d1be7..cf55b991 100644 --- a/payments/public/js/razorpay.js +++ b/payments/public/js/razorpay.js @@ -54,95 +54,110 @@ Razorpay Payment frappe.provide("frappe.checkout"); -frappe.require('https://checkout.razorpay.com/v1/checkout.js').then(() => { - frappe.checkout.razorpay = class RazorpayCheckout { - constructor(opts) { - Object.assign(this, opts); - } - - init() { - frappe.run_serially([ - () => this.get_key(), - () => this.make_order(), - () => this.prepare_options(), - () => this.setup_handler(), - () => this.show() - ]); - } - - show() { - this.razorpay = new Razorpay(this.options); - this.razorpay.once('ready', (response) => { - this.on_open && this.on_open(response); - }) - this.razorpay.open(); - } - - get_key() { - return new Promise(resolve => { - frappe.call("payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.get_api_key").then(res => { - this.key = res.message; - resolve(true); - }) - }); - } - - make_order() { - return new Promise(resolve => { - frappe.call("payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.get_order", { - doctype: this.doctype, - docname: this.docname - }).then(res => { - this.order = res.message; - resolve(true); - }) - }); - } - - order_success(response) { - frappe.call("payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.order_payment_success", { - integration_request: this.order.integration_request, - params: { - razorpay_payment_id: response.razorpay_payment_id, - razorpay_order_id: response.razorpay_order_id, - razorpay_signature: response.razorpay_signature - } - }) - } - - order_fail(response) { - frappe.call( "payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.order_payment_failure", { - integration_request: this.order.integration_request, - params: response - }) - } - - prepare_options() { - this.options = { - "key": this.key, - "amount": this.order.amount_due, - "currency": this.order.currency, - "name": this.name, - "description": this.description, - "image": this.image, - "order_id": this.order.id, - "prefill": this.prefill, - "theme": this.theme, - "modal": this.modal - }; - } - - setup_handler() { - this.options.handler = (response) => { - if (response.error) { - this.order_fail(response); - this.on_fail && this.on_fail(response); - } - else if (response.razorpay_payment_id) { - this.order_success(response); - this.on_success && this.on_success(response); - } - } - } - } +frappe.require("https://checkout.razorpay.com/v1/checkout.js").then(() => { + frappe.checkout.razorpay = class RazorpayCheckout { + constructor(opts) { + Object.assign(this, opts); + } + + init() { + frappe.run_serially([ + () => this.get_key(), + () => this.make_order(), + () => this.prepare_options(), + () => this.setup_handler(), + () => this.show(), + ]); + } + + show() { + // eslint-disable-next-line no-undef + this.razorpay = new Razorpay(this.options); + this.razorpay.once("ready", (response) => { + this.on_open && this.on_open(response); + }); + this.razorpay.open(); + } + + get_key() { + return new Promise((resolve) => { + frappe + .call( + "payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.get_api_key" + ) + .then((res) => { + this.key = res.message; + resolve(true); + }); + }); + } + + make_order() { + return new Promise((resolve) => { + frappe + .call( + "payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.get_order", + { + doctype: this.doctype, + docname: this.docname, + } + ) + .then((res) => { + this.order = res.message; + resolve(true); + }); + }); + } + + order_success(response) { + frappe.call( + "payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.order_payment_success", + { + integration_request: this.order.integration_request, + params: { + razorpay_payment_id: response.razorpay_payment_id, + razorpay_order_id: response.razorpay_order_id, + razorpay_signature: response.razorpay_signature, + }, + } + ); + } + + order_fail(response) { + frappe.call( + "payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.order_payment_failure", + { + integration_request: this.order.integration_request, + params: response, + } + ); + } + + prepare_options() { + this.options = { + key: this.key, + amount: this.order.amount_due, + currency: this.order.currency, + name: this.name, + description: this.description, + image: this.image, + order_id: this.order.id, + prefill: this.prefill, + theme: this.theme, + modal: this.modal, + }; + } + + setup_handler() { + this.options.handler = (response) => { + if (response.error) { + this.order_fail(response); + this.on_fail && this.on_fail(response); + } else if (response.razorpay_payment_id) { + this.order_success(response); + this.on_success && this.on_success(response); + } + }; + } + }; }); diff --git a/payments/templates/pages/braintree_checkout.py b/payments/templates/pages/braintree_checkout.py index 12b01369..a0dd968f 100644 --- a/payments/templates/pages/braintree_checkout.py +++ b/payments/templates/pages/braintree_checkout.py @@ -40,9 +40,7 @@ def get_context(context): context["amount"] = flt(context["amount"]) gateway_controller = get_gateway_controller(context.reference_docname) - context["header_img"] = frappe.db.get_value( - "Braintree Settings", gateway_controller, "header_img" - ) + context["header_img"] = frappe.db.get_value("Braintree Settings", gateway_controller, "header_img") else: frappe.redirect_to_message( diff --git a/payments/templates/pages/gocardless_checkout.py b/payments/templates/pages/gocardless_checkout.py index fa780d23..89665995 100644 --- a/payments/templates/pages/gocardless_checkout.py +++ b/payments/templates/pages/gocardless_checkout.py @@ -38,9 +38,7 @@ def get_context(context): context["amount"] = flt(context["amount"]) gateway_controller = get_gateway_controller(context.reference_docname) - context["header_img"] = frappe.db.get_value( - "GoCardless Settings", gateway_controller, "header_img" - ) + context["header_img"] = frappe.db.get_value("GoCardless Settings", gateway_controller, "header_img") else: frappe.redirect_to_message( @@ -95,6 +93,6 @@ def check_mandate(data, reference_doctype, reference_docname): return {"redirect_to": redirect_flow.redirect_url} - except Exception as e: + except Exception: frappe.log_error("GoCardless Payment Error") return {"redirect_to": "payment-failed"} diff --git a/payments/templates/pages/gocardless_confirmation.py b/payments/templates/pages/gocardless_confirmation.py index 3fe7d99b..02420641 100644 --- a/payments/templates/pages/gocardless_confirmation.py +++ b/payments/templates/pages/gocardless_confirmation.py @@ -33,7 +33,6 @@ def get_context(context): @frappe.whitelist(allow_guest=True) def confirm_payment(redirect_flow_id, reference_doctype, reference_docname): - client = gocardless_initialization(reference_docname) try: @@ -59,7 +58,7 @@ def confirm_payment(redirect_flow_id, reference_doctype, reference_docname): try: create_mandate(data) - except Exception as e: + except Exception: frappe.log_error("GoCardless Mandate Registration Error") gateway_controller = get_gateway_controller(reference_docname) @@ -67,7 +66,7 @@ def confirm_payment(redirect_flow_id, reference_doctype, reference_docname): return {"redirect_to": confirmation_url} - except Exception as e: + except Exception: frappe.log_error("GoCardless Payment Error") return {"redirect_to": "payment-failed"} diff --git a/payments/templates/pages/payment_success.py b/payments/templates/pages/payment_success.py index 8985850a..e2d1115b 100644 --- a/payments/templates/pages/payment_success.py +++ b/payments/templates/pages/payment_success.py @@ -7,7 +7,6 @@ def get_context(context): - token = frappe.local.form_dict.token doc = frappe.get_doc(frappe.local.form_dict.doctype, frappe.local.form_dict.docname) context.payment_message = "" diff --git a/payments/templates/pages/razorpay_checkout.py b/payments/templates/pages/razorpay_checkout.py index d0e77f6d..dab9a7ab 100644 --- a/payments/templates/pages/razorpay_checkout.py +++ b/payments/templates/pages/razorpay_checkout.py @@ -38,7 +38,7 @@ def get_context(context): payment_details["subscription_id"] if payment_details.get("subscription_id") else "" ) - except Exception as e: + except Exception: frappe.redirect_to_message( _("Invalid Token"), _("Seems token you are using is invalid!"), diff --git a/payments/templates/pages/stripe_checkout.py b/payments/templates/pages/stripe_checkout.py index 7572002e..8d8c1430 100644 --- a/payments/templates/pages/stripe_checkout.py +++ b/payments/templates/pages/stripe_checkout.py @@ -72,9 +72,7 @@ def get_header_image(doc, gateway_controller): @frappe.whitelist(allow_guest=True) -def make_payment( - stripe_token_id, data, reference_doctype=None, reference_docname=None, payment_gateway=None -): +def make_payment(stripe_token_id, data, reference_doctype=None, reference_docname=None, payment_gateway=None): data = json.loads(data) data.update({"stripe_token_id": stripe_token_id}) diff --git a/payments/utils/__init__.py b/payments/utils/__init__.py index fb540bd5..1a494cb7 100644 --- a/payments/utils/__init__.py +++ b/payments/utils/__init__.py @@ -2,7 +2,7 @@ before_install, create_payment_gateway, delete_custom_fields, + erpnext_app_import_guard, get_payment_gateway_controller, make_custom_fields, - erpnext_app_import_guard, ) diff --git a/payments/utils/utils.py b/payments/utils/utils.py index e56a89c2..fed9ac04 100644 --- a/payments/utils/utils.py +++ b/payments/utils/utils.py @@ -1,7 +1,8 @@ +from contextlib import contextmanager + import click import frappe from frappe import _ -from contextlib import contextmanager from frappe.custom.doctype.custom_field.custom_field import create_custom_fields diff --git a/pyproject.toml b/pyproject.toml index a102875d..1eb59a56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,14 +20,43 @@ dependencies = [ requires = ["flit_core >=3.4,<4"] build-backend = "flit_core.buildapi" -[tool.black] -line-length = 99 +[tool.ruff] +line-length = 110 +target-version = "py310" -[tool.isort] -line_length = 99 -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -use_parentheses = true -ensure_newline_before_comments = true -indent = "\t" +[tool.ruff.lint] +select = [ + "F", + "E", + "W", + "I", + "UP", + "B", + "RUF", +] +ignore = [ + "B017", # assertRaises(Exception) - should be more specific + "B018", # useless expression, not assigned to anything + "B023", # function doesn't bind loop variable - will have last iteration's value + "B904", # raise inside except without from + "E101", # indentation contains mixed spaces and tabs + "E402", # module level import not at top of file + "E501", # line too long + "E741", # ambiguous variable name + "F401", # "unused" imports + "F403", # can't detect undefined names from * import + "F405", # can't detect undefined names from * import + "F722", # syntax error in forward type annotation + "W191", # indentation contains tabs + "RUF001", # string contains ambiguous unicode character +] +typing-modules = ["frappe.types.DF"] + +[tool.ruff.format] +quote-style = "double" +indent-style = "tab" +docstring-code-format = true + +[project.urls] +Repository = "https://github.com/frappe/payments.git" +"Bug Reports" = "https://github.com/frappe/payments/issues"