From 99f9955afb9f98f7d46ca9dadce4fb81c02c9b98 Mon Sep 17 00:00:00 2001 From: Avunu LLC Date: Mon, 2 Oct 2023 16:54:39 -0400 Subject: [PATCH 01/12] feat: dynamic payer name & email from form fields --- payments/hooks.py | 8 +- payments/overrides/payment_webform.py | 16 ++- payments/payments/custom/web_form.json | 154 +++++++++++++++++++++++++ payments/public/js/web_form.js | 76 ++++++++++++ payments/utils/__init__.py | 6 +- payments/utils/utils.py | 109 ----------------- 6 files changed, 250 insertions(+), 119 deletions(-) create mode 100644 payments/payments/custom/web_form.json create mode 100644 payments/public/js/web_form.js diff --git a/payments/hooks.py b/payments/hooks.py index 4ad29caf..8193cb6d 100644 --- a/payments/hooks.py +++ b/payments/hooks.py @@ -29,7 +29,7 @@ # page_js = {"page" : "public/js/file.js"} # include js in doctype views -# doctype_js = {"doctype" : "public/js/doctype.js"} +doctype_js = {"Web Form" : "public/js/web_form.js"} # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} @@ -64,12 +64,12 @@ # ------------ before_install = "payments.utils.before_install" -after_install = "payments.utils.make_custom_fields" +# after_install = "payments.utils.make_custom_fields" # Uninstallation # ------------ -before_uninstall = "payments.utils.delete_custom_fields" +# before_uninstall = "payments.utils.delete_custom_fields" # after_uninstall = "pay.uninstall.after_uninstall" # Desk Notifications @@ -94,7 +94,7 @@ # --------------- # Override standard doctype classes -override_doctype_class = {"Web Form": "payments.overrides.payment_webform.PaymentWebForm"} +override_doctype_class = {"Web Form": "payments.overrides.payment_webform.PaymentWebForm"} # Document Events # --------------- diff --git a/payments/overrides/payment_webform.py b/payments/overrides/payment_webform.py index 3b2d7afa..42789d8d 100644 --- a/payments/overrides/payment_webform.py +++ b/payments/overrides/payment_webform.py @@ -27,6 +27,8 @@ def get_payment_gateway_url(self, doc): controller = get_payment_gateway_controller(self.payment_gateway) title = f"Payment for {doc.doctype} {doc.name}" + + # Set Amount amount = self.amount if self.amount_based_on_field: amount = doc.get(self.amount_field) @@ -35,6 +37,16 @@ def get_payment_gateway_url(self, doc): if amount is None or Decimal(amount) <= 0: return frappe.utils.get_url(self.success_url or self.route) + + # Set Payer Name + payer_name = frappe.utils.get_fullname(frappe.session.user) + if self.payer_name_based_on_field: + payer_name = doc.get(self.payer_name_field) + + # Set Payer Email + payer_email = frappe.session.user + if self.payer_email_based_on_field: + payer_email = doc.get(self.payer_email_field) payment_details = { "amount": amount, @@ -42,8 +54,8 @@ def get_payment_gateway_url(self, doc): "description": title, "reference_doctype": doc.doctype, "reference_docname": doc.name, - "payer_email": frappe.session.user, - "payer_name": frappe.utils.get_fullname(frappe.session.user), + "payer_email": payer_email, + "payer_name": payer_name, "order_id": doc.name, "currency": self.currency, "redirect_to": frappe.utils.get_url(self.success_url or self.route), diff --git a/payments/payments/custom/web_form.json b/payments/payments/custom/web_form.json new file mode 100644 index 00000000..50172401 --- /dev/null +++ b/payments/payments/custom/web_form.json @@ -0,0 +1,154 @@ +{ + "custom_fields": [ + { + "dt": "Web Form", + "fieldname": "payments_tab", + "fieldtype": "Tab Break", + "label": "Payments", + "insert_after": "custom_css", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "payments_sb_0", + "fieldtype": "Section Break", + "insert_after": "payments_tab", + "is_system_generated": 1 + }, + { + "default": "0", + "dt": "Web Form", + "fieldname": "accept_payment", + "fieldtype": "Check", + "label": "Accept Payments", + "insert_after": "payments_sb_0", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "payments_cb", + "fieldtype": "Column Break", + "insert_after": "accept_payment", + "is_system_generated": 1 + }, + { + "default": "0", + "depends_on": "accept_payment", + "dt": "Web Form", + "fieldname": "amount_based_on_field", + "fieldtype": "Check", + "label": "Amount Based On Field", + "insert_after": "payments_cb", + "is_system_generated": 1 + }, + { + "depends_on": "eval:doc.accept_payment && doc.amount_based_on_field", + "dt": "Web Form", + "fieldname": "amount_field", + "fieldtype": "Select", + "label": "Amount Field", + "insert_after": "amount_based_on_field", + "is_system_generated": 1 + }, + { + "default": "0", + "depends_on": "accept_payment", + "dt": "Web Form", + "fieldname": "payer_name_based_on_field", + "fieldtype": "Check", + "label": "Payer Name Based On Field", + "insert_after": "amount_field", + "is_system_generated": 1 + }, + { + "depends_on": "eval:doc.accept_payment && doc.payer_name_based_on_field", + "dt": "Web Form", + "fieldname": "payer_name_field", + "fieldtype": "Select", + "label": "Payer Name Field", + "insert_after": "payer_name_based_on_field", + "is_system_generated": 1 + }, + { + "default": "0", + "depends_on": "accept_payment", + "dt": "Web Form", + "fieldname": "payer_email_based_on_field", + "fieldtype": "Check", + "label": "Payer Email Based On Field", + "insert_after": "payer_name_field", + "is_system_generated": 1 + }, + { + "depends_on": "eval:doc.accept_payment && doc.payer_email_based_on_field", + "dt": "Web Form", + "fieldname": "payer_email_field", + "fieldtype": "Select", + "label": "Payer Email Field", + "insert_after": "payer_email_based_on_field", + "is_system_generated": 1 + }, + { + "depends_on": "accept_payment", + "dt": "Web Form", + "fieldname": "payments_sb_1", + "fieldtype": "Section Break", + "insert_after": "payer_email_field", + "is_system_generated": 1 + }, + { + "default": "Buy Now", + "dt": "Web Form", + "fieldname": "payment_button_label", + "fieldtype": "Data", + "label": "Button Label", + "insert_after": "payments_sb_1", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "payment_button_help", + "fieldtype": "Small Text", + "label": "Button Help", + "insert_after": "payment_button_label", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "payments_cb_1", + "fieldtype": "Column Break", + "insert_after": "payment_button_help", + "is_system_generated": 1 + }, + { + "depends_on": "eval:!doc.amount_based_on_field", + "dt": "Web Form", + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "insert_after": "payments_cb_1", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "insert_after": "amount", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "payment_gateway", + "fieldtype": "Link", + "label": "Payment Gateway", + "options": "Payment Gateway", + "insert_after": "currency", + "is_system_generated": 1 + } + ], + "property_setters": [], + "sync_on_migrate": 1, + "doctype": "Web Form" +} \ No newline at end of file diff --git a/payments/public/js/web_form.js b/payments/public/js/web_form.js new file mode 100644 index 00000000..33bbd61e --- /dev/null +++ b/payments/public/js/web_form.js @@ -0,0 +1,76 @@ +frappe.ui.form.on("Web Form", { + set_fields(frm) { + let doc = frm.doc; + + let update_options = (options) => { + [frm.fields_dict.web_form_fields.grid, frm.fields_dict.list_columns.grid].forEach( + (obj) => { + obj.update_docfield_property("fieldname", "options", options); + } + ); + }; + + if (!doc.doc_type) { + update_options([]); + frm.set_df_property("amount_field", "options", []); + frm.set_df_property("payer_name_field", "options", []); + frm.set_df_property("payer_email_field", "options", []); + return; + } + + update_options([`Fetching fields from ${doc.doc_type}...`]); + + get_fields_for_doctype(doc.doc_type).then((fields) => { + let as_select_option = (df) => ({ + label: df.label, + value: df.fieldname, + }); + update_options(fields.map(as_select_option)); + + // Amount Field Options + let currency_fields = fields + .filter((df) => ["Currency", "Float"].includes(df.fieldtype)) + .map(as_select_option); + if (!currency_fields.length) { + currency_fields = [ + { + label: `No currency fields in ${doc.doc_type}`, + value: "", + disabled: true, + }, + ]; + } + frm.set_df_property("amount_field", "options", currency_fields); + + // Payer Name Field Options + let payer_name_fields = fields + .filter((df) => ["Data", "Link"].includes(df.fieldtype)) + .map(as_select_option); + if (!payer_name_fields.length) { + payer_name_fields = [ + { + label: `No data or link fields in ${doc.doc_type}`, + value: "", + disabled: true, + }, + ]; + } + frm.set_df_property("payer_name_field", "options", payer_name_fields); + + // Payer Email Field Options + let payer_email_fields = fields + .filter((df) => ["Email"].includes(df.options)) + .map(as_select_option); + if (!payer_email_fields.length) { + payer_email_fields = [ + { + label: `No email fields in ${doc.doc_type}`, + value: "", + disabled: true, + }, + ]; + } + frm.set_df_property("payer_email_field", "options", payer_email_fields); + }); + } +}); diff --git a/payments/utils/__init__.py b/payments/utils/__init__.py index 54a5c2e8..a42d725d 100644 --- a/payments/utils/__init__.py +++ b/payments/utils/__init__.py @@ -1,7 +1,5 @@ from payments.utils.utils import ( before_install, create_payment_gateway, - delete_custom_fields, - get_payment_gateway_controller, - make_custom_fields, -) + get_payment_gateway_controller + ) diff --git a/payments/utils/utils.py b/payments/utils/utils.py index 20c6bd1e..a43033b0 100644 --- a/payments/utils/utils.py +++ b/payments/utils/utils.py @@ -1,7 +1,6 @@ import click import frappe from frappe import _ -from frappe.custom.doctype.custom_field.custom_field import create_custom_fields def get_payment_gateway_controller(payment_gateway): @@ -52,114 +51,6 @@ def create_payment_gateway(gateway, settings=None, controller=None): payment_gateway.insert(ignore_permissions=True) -def make_custom_fields(): - if not frappe.get_meta("Web Form").has_field("payments_tab"): - click.secho("* Installing Payment Custom Fields in Web Form") - - create_custom_fields( - { - "Web Form": [ - { - "fieldname": "payments_tab", - "fieldtype": "Tab Break", - "label": "Payments", - "insert_after": "custom_css", - }, - { - "default": "0", - "fieldname": "accept_payment", - "fieldtype": "Check", - "label": "Accept Payment", - "insert_after": "payments", - }, - { - "depends_on": "accept_payment", - "fieldname": "payment_gateway", - "fieldtype": "Link", - "label": "Payment Gateway", - "options": "Payment Gateway", - "insert_after": "accept_payment", - }, - { - "default": "Buy Now", - "depends_on": "accept_payment", - "fieldname": "payment_button_label", - "fieldtype": "Data", - "label": "Button Label", - "insert_after": "payment_gateway", - }, - { - "depends_on": "accept_payment", - "fieldname": "payment_button_help", - "fieldtype": "Text", - "label": "Button Help", - "insert_after": "payment_button_label", - }, - { - "fieldname": "payments_cb", - "fieldtype": "Column Break", - "insert_after": "payment_button_help", - }, - { - "default": "0", - "depends_on": "accept_payment", - "fieldname": "amount_based_on_field", - "fieldtype": "Check", - "label": "Amount Based On Field", - "insert_after": "payments_cb", - }, - { - "depends_on": "eval:doc.accept_payment && doc.amount_based_on_field", - "fieldname": "amount_field", - "fieldtype": "Select", - "label": "Amount Field", - "insert_after": "amount_based_on_field", - }, - { - "depends_on": "eval:doc.accept_payment && !doc.amount_based_on_field", - "fieldname": "amount", - "fieldtype": "Currency", - "label": "Amount", - "insert_after": "amount_field", - }, - { - "depends_on": "accept_payment", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "insert_after": "amount", - }, - ] - } - ) - - frappe.clear_cache(doctype="Web Form") - - -def delete_custom_fields(): - if frappe.get_meta("Web Form").has_field("payments_tab"): - click.secho("* Uninstalling Payment Custom Fields from Web Form") - - fieldnames = ( - "payments_tab", - "accept_payment", - "payment_gateway", - "payment_button_label", - "payment_button_help", - "payments_cb", - "amount_field", - "amount_based_on_field", - "amount", - "currency", - ) - - for fieldname in fieldnames: - frappe.db.delete("Custom Field", {"name": "Web Form-" + fieldname}) - - frappe.clear_cache(doctype="Web Form") - - def before_install(): # TODO: remove this # This is done for erpnext CI patch test From e1d37a3998a9c2cd6b5df7930cd394bdf9dfe80f Mon Sep 17 00:00:00 2001 From: Avunu LLC Date: Mon, 2 Oct 2023 16:58:37 -0400 Subject: [PATCH 02/12] fix: remove whitespace --- payments/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payments/hooks.py b/payments/hooks.py index 8193cb6d..65bb729f 100644 --- a/payments/hooks.py +++ b/payments/hooks.py @@ -94,7 +94,7 @@ # --------------- # Override standard doctype classes -override_doctype_class = {"Web Form": "payments.overrides.payment_webform.PaymentWebForm"} +override_doctype_class = {"Web Form": "payments.overrides.payment_webform.PaymentWebForm"} # Document Events # --------------- From 7878942d8b5f4dfd4ba1787554a8bfc97fc17528 Mon Sep 17 00:00:00 2001 From: Avunu LLC Date: Mon, 2 Oct 2023 17:16:33 -0400 Subject: [PATCH 03/12] fix: remove "Button Label" & "Button Help" fields --- payments/payments/custom/web_form.json | 112 +++++++++---------------- 1 file changed, 39 insertions(+), 73 deletions(-) diff --git a/payments/payments/custom/web_form.json b/payments/payments/custom/web_form.json index 50172401..bf0aeaa1 100644 --- a/payments/payments/custom/web_form.json +++ b/payments/payments/custom/web_form.json @@ -9,31 +9,58 @@ "is_system_generated": 1 }, { + "default": "0", "dt": "Web Form", - "fieldname": "payments_sb_0", - "fieldtype": "Section Break", + "fieldname": "accept_payment", + "fieldtype": "Check", + "label": "Accept Payments", "insert_after": "payments_tab", "is_system_generated": 1 }, { - "default": "0", + "depends_on": "accept_payment", "dt": "Web Form", - "fieldname": "accept_payment", - "fieldtype": "Check", - "label": "Accept Payments", - "insert_after": "payments_sb_0", + "fieldname": "payments_sb", + "fieldtype": "Section Break", + "insert_after": "accept_payment", + "is_system_generated": 1 + }, + { + "depends_on": "eval:!doc.amount_based_on_field", + "dt": "Web Form", + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "insert_after": "payments_sb", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "insert_after": "amount", + "is_system_generated": 1 + }, + { + "dt": "Web Form", + "fieldname": "payment_gateway", + "fieldtype": "Link", + "label": "Payment Gateway", + "options": "Payment Gateway", + "insert_after": "currency", "is_system_generated": 1 }, { "dt": "Web Form", "fieldname": "payments_cb", "fieldtype": "Column Break", - "insert_after": "accept_payment", + "insert_after": "payment_gateway", "is_system_generated": 1 }, { "default": "0", - "depends_on": "accept_payment", "dt": "Web Form", "fieldname": "amount_based_on_field", "fieldtype": "Check", @@ -42,7 +69,7 @@ "is_system_generated": 1 }, { - "depends_on": "eval:doc.accept_payment && doc.amount_based_on_field", + "depends_on": "eval:doc.amount_based_on_field", "dt": "Web Form", "fieldname": "amount_field", "fieldtype": "Select", @@ -52,7 +79,6 @@ }, { "default": "0", - "depends_on": "accept_payment", "dt": "Web Form", "fieldname": "payer_name_based_on_field", "fieldtype": "Check", @@ -61,7 +87,7 @@ "is_system_generated": 1 }, { - "depends_on": "eval:doc.accept_payment && doc.payer_name_based_on_field", + "depends_on": "eval:doc.payer_name_based_on_field", "dt": "Web Form", "fieldname": "payer_name_field", "fieldtype": "Select", @@ -71,7 +97,6 @@ }, { "default": "0", - "depends_on": "accept_payment", "dt": "Web Form", "fieldname": "payer_email_based_on_field", "fieldtype": "Check", @@ -80,72 +105,13 @@ "is_system_generated": 1 }, { - "depends_on": "eval:doc.accept_payment && doc.payer_email_based_on_field", + "depends_on": "eval:doc.payer_email_based_on_field", "dt": "Web Form", "fieldname": "payer_email_field", "fieldtype": "Select", "label": "Payer Email Field", "insert_after": "payer_email_based_on_field", "is_system_generated": 1 - }, - { - "depends_on": "accept_payment", - "dt": "Web Form", - "fieldname": "payments_sb_1", - "fieldtype": "Section Break", - "insert_after": "payer_email_field", - "is_system_generated": 1 - }, - { - "default": "Buy Now", - "dt": "Web Form", - "fieldname": "payment_button_label", - "fieldtype": "Data", - "label": "Button Label", - "insert_after": "payments_sb_1", - "is_system_generated": 1 - }, - { - "dt": "Web Form", - "fieldname": "payment_button_help", - "fieldtype": "Small Text", - "label": "Button Help", - "insert_after": "payment_button_label", - "is_system_generated": 1 - }, - { - "dt": "Web Form", - "fieldname": "payments_cb_1", - "fieldtype": "Column Break", - "insert_after": "payment_button_help", - "is_system_generated": 1 - }, - { - "depends_on": "eval:!doc.amount_based_on_field", - "dt": "Web Form", - "fieldname": "amount", - "fieldtype": "Currency", - "label": "Amount", - "insert_after": "payments_cb_1", - "is_system_generated": 1 - }, - { - "dt": "Web Form", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "insert_after": "amount", - "is_system_generated": 1 - }, - { - "dt": "Web Form", - "fieldname": "payment_gateway", - "fieldtype": "Link", - "label": "Payment Gateway", - "options": "Payment Gateway", - "insert_after": "currency", - "is_system_generated": 1 } ], "property_setters": [], From f6a790eb698ee8fe7c116248b2bdb92f0bd5ee22 Mon Sep 17 00:00:00 2001 From: Avunu LLC Date: Tue, 5 Dec 2023 21:04:01 -0500 Subject: [PATCH 04/12] Add line in override to set payment gateway. Modify pyproject.toml and .gitignore to support newer versions of stripe. --- .gitignore | 1 + payments/overrides/payment_webform.py | 2 ++ pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 175055b1..e7dc1714 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +*~ *.pyc *.egg-info *.swp diff --git a/payments/overrides/payment_webform.py b/payments/overrides/payment_webform.py index 42789d8d..6b258a9e 100644 --- a/payments/overrides/payment_webform.py +++ b/payments/overrides/payment_webform.py @@ -113,6 +113,8 @@ def accept(web_form, data, docname=None, for_payment=False): if for_payment: web_form.validate_mandatory(doc) doc.run_method("validate_payment") + # set payment gateway + doc.set("payment_gateway", web_form.payment_gateway) if doc.name: if web_form.has_web_form_permission(doc.doctype, doc.name, "write"): diff --git a/pyproject.toml b/pyproject.toml index 46c94ee9..505a93a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dynamic = ["version"] dependencies = [ "paytmchecksum~=1.7.0", "razorpay~=1.2.0", - "stripe~=2.56.0", + "stripe>=2.56.0,<=5.0.0", "braintree~=4.20.0", "pycryptodome~=3.18.0", ] From af62692cc5e980875f8b125fa49f4e97c8642da2 Mon Sep 17 00:00:00 2001 From: Avunu LLC Date: Thu, 7 Dec 2023 16:17:11 -0500 Subject: [PATCH 05/12] Undo unneccessary changes to .gitignore and pyproject.toml made during debugging. Add custom_redirect_to_override option in stripe_settings.py finalize_request() so that the successful payment redirect address can be fully customized. --- .gitignore | 1 - .../doctype/stripe_settings/stripe_settings.py | 5 ++++- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index e7dc1714..175055b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .DS_Store -*~ *.pyc *.egg-info *.swp diff --git a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py index 0547a528..ea371159 100644 --- a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py +++ b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py @@ -241,12 +241,13 @@ def finalize_request(self): redirect_to = self.data.get("redirect_to") or None redirect_message = self.data.get("redirect_message") or None status = self.integration_request.status + custom_redirect_to_override = None if self.flags.status_changed_to == "Completed": if self.data.reference_doctype and self.data.reference_docname: custom_redirect_to = None try: - custom_redirect_to = frappe.get_doc( + custom_redirect_to, custom_redirect_to_override = frappe.get_doc( self.data.reference_doctype, self.data.reference_docname ).run_method("on_payment_authorized", self.flags.status_changed_to) except Exception: @@ -267,6 +268,8 @@ def finalize_request(self): redirect_url += "?" + urlencode({"redirect_to": redirect_to}) if redirect_message: redirect_url += "&" + urlencode({"redirect_message": redirect_message}) + if custom_redirect_to_override: + redirect_url = redirect_to return {"redirect_to": redirect_url, "status": status} diff --git a/pyproject.toml b/pyproject.toml index 505a93a3..46c94ee9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dynamic = ["version"] dependencies = [ "paytmchecksum~=1.7.0", "razorpay~=1.2.0", - "stripe>=2.56.0,<=5.0.0", + "stripe~=2.56.0", "braintree~=4.20.0", "pycryptodome~=3.18.0", ] From 4b54933623297ca0c713de35e9b66dd6f01f54c7 Mon Sep 17 00:00:00 2001 From: Kevin Shenk Date: Tue, 19 Mar 2024 10:07:42 -0400 Subject: [PATCH 06/12] refactor: re-instate make_custom_fields (for ERPNext) --- payments/utils/utils.py | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/payments/utils/utils.py b/payments/utils/utils.py index a43033b0..0c802c55 100644 --- a/payments/utils/utils.py +++ b/payments/utils/utils.py @@ -1,6 +1,8 @@ import click import frappe from frappe import _ +from contextlib import contextmanager +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields def get_payment_gateway_controller(payment_gateway): @@ -51,6 +53,51 @@ def create_payment_gateway(gateway, settings=None, controller=None): payment_gateway.insert(ignore_permissions=True) +def make_custom_fields(): + if "erpnext" in frappe.get_installed_apps(): + custom_fields = { + "GoCardless Mandate": [ + { + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer", + "reqd": 1, + "insert_after": "disabled", + } + ] + } + + create_custom_fields(custom_fields) + + +def delete_custom_fields(): + if frappe.get_meta("Web Form").has_field("payments_tab"): + click.secho("* Uninstalling Payment Custom Fields from Web Form") + + fieldnames = ( + "accept_payment", + "amount_based_on_field", + "amount_field", + "amount", + "currency", + "payer_email_based_on_field", + "payer_email_field", + "payer_name_based_on_field", + "payer_name_field", + "payment_gateway", + "payments_cb", + "payments_sb", + "payments_tab" + ) + + for fieldname in fieldnames: + frappe.db.delete("Custom Field", {"name": "Web Form-" + fieldname}) + + frappe.clear_cache(doctype="Web Form") + + def before_install(): # TODO: remove this # This is done for erpnext CI patch test @@ -62,3 +109,16 @@ def before_install(): # a lot of apis don;t exist in v10 and this is a (at the moment) required app for erpnext. if not frappe.get_meta("Module Def").has_field("custom"): return False + + +@contextmanager +def erpnext_app_import_guard(): + marketplace_link = 'Marketplace' + github_link = 'GitHub' + msg = _("erpnext app is not installed. Please install it from {} or {}").format( + marketplace_link, github_link + ) + try: + yield + except ImportError: + frappe.throw(msg, title=_("Missing ERPNext App")) From 0f5a90aac31d57d90d48c6cb9281cba5c2c85adf Mon Sep 17 00:00:00 2001 From: Kevin Shenk Date: Tue, 19 Mar 2024 10:08:17 -0400 Subject: [PATCH 07/12] refactor: re-enable make_custom_fields hook --- payments/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payments/hooks.py b/payments/hooks.py index 65bb729f..5c485dfd 100644 --- a/payments/hooks.py +++ b/payments/hooks.py @@ -64,7 +64,7 @@ # ------------ before_install = "payments.utils.before_install" -# after_install = "payments.utils.make_custom_fields" +after_install = "payments.utils.make_custom_fields" # Uninstallation # ------------ From 7461c40e44eaf2cf49607a209dad48598a50bbf5 Mon Sep 17 00:00:00 2001 From: Kevin Shenk Date: Tue, 19 Mar 2024 10:11:26 -0400 Subject: [PATCH 08/12] refactor: restore utils __init__.py --- payments/utils/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/payments/utils/__init__.py b/payments/utils/__init__.py index a42d725d..fb540bd5 100644 --- a/payments/utils/__init__.py +++ b/payments/utils/__init__.py @@ -1,5 +1,8 @@ from payments.utils.utils import ( before_install, create_payment_gateway, - get_payment_gateway_controller - ) + delete_custom_fields, + get_payment_gateway_controller, + make_custom_fields, + erpnext_app_import_guard, +) From 36bf21fa10f3600fbd810681ceb55d3f32cdb241 Mon Sep 17 00:00:00 2001 From: Kevin Shenk Date: Tue, 19 Mar 2024 10:13:13 -0400 Subject: [PATCH 09/12] refactor: restore delete_custom_fields --- payments/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payments/hooks.py b/payments/hooks.py index 5c485dfd..bbd1ada6 100644 --- a/payments/hooks.py +++ b/payments/hooks.py @@ -69,7 +69,7 @@ # Uninstallation # ------------ -# before_uninstall = "payments.utils.delete_custom_fields" +before_uninstall = "payments.utils.delete_custom_fields" # after_uninstall = "pay.uninstall.after_uninstall" # Desk Notifications From 6510e8f6f7251ff1e81c6a14182d85005874564f Mon Sep 17 00:00:00 2001 From: Avunu LLC Date: Wed, 29 May 2024 15:42:23 -0400 Subject: [PATCH 10/12] refactor: relative stripe URL --- .../payment_gateways/doctype/stripe_settings/stripe_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py index dc2dc1cc..0bf5e9e2 100644 --- a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py +++ b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py @@ -188,7 +188,7 @@ def validate_minimum_transaction_amount(self, currency, amount): ) def get_payment_url(self, **kwargs): - return get_url(f"./stripe_checkout?{urlencode(kwargs)}") + return f"/stripe_checkout?{urlencode(kwargs)}" def create_request(self, data): import stripe From b3ada19934f4aeeab46d356c3cb3d5bf90c9d134 Mon Sep 17 00:00:00 2001 From: Kevin Shenk Date: Tue, 10 Dec 2024 19:29:37 -0500 Subject: [PATCH 11/12] fmt: black & prettier --- .pre-commit-config.yaml | 2 +- payments/hooks.py | 2 +- payments/overrides/payment_webform.py | 2 +- payments/public/js/web_form.js | 148 ++++++++++++++------------ payments/utils/utils.py | 2 +- 5 files changed, 85 insertions(+), 71 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9f5b27e..2fb83911 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ exclude: 'node_modules|.git' -default_stages: [commit] +default_stages: [pre-commit] fail_fast: false diff --git a/payments/hooks.py b/payments/hooks.py index 3a77d1f3..a7a25d5c 100644 --- a/payments/hooks.py +++ b/payments/hooks.py @@ -29,7 +29,7 @@ # page_js = {"page" : "public/js/file.js"} # include js in doctype views -doctype_js = {"Web Form" : "public/js/web_form.js"} +doctype_js = {"Web Form": "public/js/web_form.js"} # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} diff --git a/payments/overrides/payment_webform.py b/payments/overrides/payment_webform.py index 6b258a9e..163433c9 100644 --- a/payments/overrides/payment_webform.py +++ b/payments/overrides/payment_webform.py @@ -37,7 +37,7 @@ def get_payment_gateway_url(self, doc): if amount is None or Decimal(amount) <= 0: return frappe.utils.get_url(self.success_url or self.route) - + # Set Payer Name payer_name = frappe.utils.get_fullname(frappe.session.user) if self.payer_name_based_on_field: diff --git a/payments/public/js/web_form.js b/payments/public/js/web_form.js index 33bbd61e..eb74fc60 100644 --- a/payments/public/js/web_form.js +++ b/payments/public/js/web_form.js @@ -1,76 +1,90 @@ frappe.ui.form.on("Web Form", { - set_fields(frm) { - let doc = frm.doc; + set_fields(frm) { + let doc = frm.doc; - let update_options = (options) => { - [frm.fields_dict.web_form_fields.grid, frm.fields_dict.list_columns.grid].forEach( - (obj) => { - obj.update_docfield_property("fieldname", "options", options); - } - ); - }; + let update_options = (options) => { + [ + frm.fields_dict.web_form_fields.grid, + frm.fields_dict.list_columns.grid, + ].forEach((obj) => { + obj.update_docfield_property("fieldname", "options", options); + }); + }; - if (!doc.doc_type) { - update_options([]); - frm.set_df_property("amount_field", "options", []); - frm.set_df_property("payer_name_field", "options", []); - frm.set_df_property("payer_email_field", "options", []); - return; - } + if (!doc.doc_type) { + update_options([]); + frm.set_df_property("amount_field", "options", []); + frm.set_df_property("payer_name_field", "options", []); + frm.set_df_property("payer_email_field", "options", []); + return; + } - update_options([`Fetching fields from ${doc.doc_type}...`]); + update_options([`Fetching fields from ${doc.doc_type}...`]); - get_fields_for_doctype(doc.doc_type).then((fields) => { - let as_select_option = (df) => ({ - label: df.label, - value: df.fieldname, - }); - update_options(fields.map(as_select_option)); + new Promise((resolve) => + frappe.model.with_doctype(doc.doc_type, resolve) + ).then(() => { + const fields = frappe.meta.get_docfields(doc.doc_type).filter((df) => { + return ( + (frappe.model.is_value_type(df.fieldtype) && + !["lft", "rgt"].includes(df.fieldname)) || + ["Table", "Table Multiselect"].includes(df.fieldtype) || + frappe.model.layout_fields.includes(df.fieldtype) + ); + }); - // Amount Field Options - let currency_fields = fields - .filter((df) => ["Currency", "Float"].includes(df.fieldtype)) - .map(as_select_option); - if (!currency_fields.length) { - currency_fields = [ - { - label: `No currency fields in ${doc.doc_type}`, - value: "", - disabled: true, - }, - ]; - } - frm.set_df_property("amount_field", "options", currency_fields); + let as_select_option = (df) => ({ + label: df.label, + value: df.fieldname, + }); - // Payer Name Field Options - let payer_name_fields = fields - .filter((df) => ["Data", "Link"].includes(df.fieldtype)) - .map(as_select_option); - if (!payer_name_fields.length) { - payer_name_fields = [ - { - label: `No data or link fields in ${doc.doc_type}`, - value: "", - disabled: true, - }, - ]; - } - frm.set_df_property("payer_name_field", "options", payer_name_fields); + // Update main field options + update_options(fields.map(as_select_option)); - // Payer Email Field Options - let payer_email_fields = fields - .filter((df) => ["Email"].includes(df.options)) - .map(as_select_option); - if (!payer_email_fields.length) { - payer_email_fields = [ - { - label: `No email fields in ${doc.doc_type}`, - value: "", - disabled: true, - }, - ]; - } - frm.set_df_property("payer_email_field", "options", payer_email_fields); - }); - } + // Amount Field Options + let currency_fields = fields + .filter((df) => ["Currency", "Float"].includes(df.fieldtype)) + .map(as_select_option); + if (!currency_fields.length) { + currency_fields = [ + { + label: `No currency fields in ${doc.doc_type}`, + value: "", + disabled: true, + }, + ]; + } + frm.set_df_property("amount_field", "options", currency_fields); + + // Payer Name Field Options + let payer_name_fields = fields + .filter((df) => ["Data", "Link"].includes(df.fieldtype)) + .map(as_select_option); + if (!payer_name_fields.length) { + payer_name_fields = [ + { + label: `No data or link fields in ${doc.doc_type}`, + value: "", + disabled: true, + }, + ]; + } + frm.set_df_property("payer_name_field", "options", payer_name_fields); + + // Payer Email Field Options + let payer_email_fields = fields + .filter((df) => ["Email"].includes(df.options)) + .map(as_select_option); + if (!payer_email_fields.length) { + payer_email_fields = [ + { + label: `No email fields in ${doc.doc_type}`, + value: "", + disabled: true, + }, + ]; + } + frm.set_df_property("payer_email_field", "options", payer_email_fields); + }); + }, }); diff --git a/payments/utils/utils.py b/payments/utils/utils.py index e24f78c3..40665ee8 100644 --- a/payments/utils/utils.py +++ b/payments/utils/utils.py @@ -112,7 +112,7 @@ def delete_custom_fields(): "payment_gateway", "payments_cb", "payments_sb", - "payments_tab" + "payments_tab", ) for fieldname in fieldnames: From 134048df805bdee6cbdf0d0c4568ce88edfdc5f2 Mon Sep 17 00:00:00 2001 From: Avunu LLC Date: Mon, 30 Dec 2024 12:07:14 -0500 Subject: [PATCH 12/12] fix: add payment_gateway to payment_details --- payments/overrides/payment_webform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/payments/overrides/payment_webform.py b/payments/overrides/payment_webform.py index 163433c9..c1a4b56d 100644 --- a/payments/overrides/payment_webform.py +++ b/payments/overrides/payment_webform.py @@ -54,6 +54,7 @@ def get_payment_gateway_url(self, doc): "description": title, "reference_doctype": doc.doctype, "reference_docname": doc.name, + "payment_gateway": self.payment_gateway, "payer_email": payer_email, "payer_name": payer_name, "order_id": doc.name,