diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index afc3804ff74..8820161a368 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -31,7 +31,7 @@ frappe.ui.form.on("Payment Request", "refresh", function(frm) { }); }); } - + if(!frm.doc.payment_gateway_account && frm.doc.status == "Initiated") { frm.add_custom_button(__('Make Payment Entry'), function(){ frappe.call({ @@ -49,3 +49,25 @@ frappe.ui.form.on("Payment Request", "refresh", function(frm) { } }); +frappe.ui.form.on("Payment Request", "is_a_subscription", function(frm) { + frm.toggle_reqd("payment_gateway_account", frm.doc.is_a_subscription); + frm.toggle_reqd("subscription_plans", frm.doc.is_a_subscription); + + if (frm.doc.is_a_subscription) { + frappe.call({ + method: "erpnext.accounts.doctype.payment_request.payment_request.get_subscription_details", + args: {"reference_doctype": frm.doc.reference_doctype, "reference_name": frm.doc.reference_name}, + freeze: true, + callback: function(data){ + if(!data.exc) { + $.each(data.message || [], function(i, v){ + var d = frappe.model.add_child(frm.doc, "Subscription Plan Detail", "subscription_plans"); + d.qty = v.qty; + d.plan = v.plan; + }); + frm.refresh_field("subscription_plans"); + } + } + }); + } +}); diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index e21afa47895..4148dce3b4d 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -15,6 +15,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -42,11 +43,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 1, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -73,11 +75,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -106,11 +109,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -137,11 +141,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -168,11 +173,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -198,11 +204,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -230,11 +237,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -262,11 +270,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -295,11 +304,44 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_a_subscription", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Is a Subscription", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -328,11 +370,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -360,11 +403,79 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": "", + "columns": 0, + "depends_on": "eval:doc.is_a_subscription", + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Subscription Section", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription_plans", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Subscription Plans", + "length": 0, + "no_copy": 0, + "options": "Subscription Plan Detail", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -390,11 +501,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -421,11 +533,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -453,11 +566,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -484,11 +598,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -515,11 +630,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -547,16 +663,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "payment_gateway_account.payment_gateway", + "fetch_from": "payment_gateway_account.payment_gateway", "fieldname": "payment_gateway", "fieldtype": "Read Only", "hidden": 0, @@ -580,16 +697,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "payment_gateway_account.payment_account", + "fetch_from": "payment_gateway_account.payment_account", "fieldname": "payment_account", "fieldtype": "Read Only", "hidden": 0, @@ -613,11 +731,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -644,11 +763,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -676,11 +796,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -708,11 +829,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -739,7 +861,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 } ], @@ -753,7 +875,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-16 22:43:28.136835", + "modified": "2018-06-20 17:06:43.850174", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index a633cc31a61..c58b185a089 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -12,12 +12,15 @@ from erpnext.accounts.utils import get_account_currency from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, get_company_defaults from frappe.integrations.utils import get_payment_gateway_controller from frappe.utils.background_jobs import enqueue +from erpnext.erpnext_integrations.stripe_integration import create_stripe_subscription +from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate class PaymentRequest(Document): def validate(self): self.validate_reference_document() self.validate_payment_request() self.validate_currency() + self.validate_subscription_details() def validate_reference_document(self): if not self.reference_doctype or not self.reference_name: @@ -33,6 +36,21 @@ class PaymentRequest(Document): if self.payment_account and ref_doc.currency != frappe.db.get_value("Account", self.payment_account, "account_currency"): frappe.throw(_("Transaction currency must be same as Payment Gateway currency")) + def validate_subscription_details(self): + if self.is_a_subscription: + amount = 0 + for subscription_plan in self.subscription_plans: + payment_gateway = frappe.db.get_value("Subscription Plan", subscription_plan.plan, "payment_gateway") + if payment_gateway != self.payment_gateway_account: + frappe.throw(_('The payment gateway account in plan {0} is different from the payment gateway account in this payment request'.format(subscription_plan.name))) + + rate = get_plan_rate(subscription_plan.plan, quantity=subscription_plan.qty) + + amount += rate + + if amount != self.grand_total: + frappe.msgprint(_("The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.".format(self.grand_total, amount))) + def on_submit(self): send_mail = self.payment_gateway_validation() ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) @@ -235,6 +253,10 @@ class PaymentRequest(Document): return redirect_to + def create_subscription(self, payment_provider, gateway_controller, data): + if payment_provider == "stripe": + return create_stripe_subscription(gateway_controller, data) + @frappe.whitelist(allow_guest=True) def make_payment_request(**args): """Make payment request""" @@ -375,3 +397,14 @@ def get_dummy_message(doc):

{{ _("Thank you for your business!") }}

""", dict(doc=doc, payment_url = '{{ payment_url }}')) + +@frappe.whitelist() +def get_subscription_details(reference_doctype, reference_name): + if reference_doctype == "Sales Invoice": + subscriptions = frappe.db.sql("""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",reference_name, as_dict=1) + subscription_plans = [] + for subscription in subscriptions: + plans = frappe.get_doc("Subscription", subscription.sub_name).plans + for plan in plans: + subscription_plans.append(plan) + return subscription_plans \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.js b/erpnext/accounts/doctype/payment_request/test_payment_request.js new file mode 100644 index 00000000000..070b595fc6a --- /dev/null +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Payment Request", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Payment Request + () => frappe.tests.make('Payment Request', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json index 5d64d9a1a08..a58ac3e68a5 100644 --- a/erpnext/accounts/doctype/subscription/subscription.json +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -111,39 +111,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "quantity", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -847,7 +814,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-11 19:34:44.582203", + "modified": "2018-07-13 15:18:49.016010", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 72f86f2f60c..fe391619602 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -8,6 +8,7 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.utils.data import nowdate, getdate, cint, add_days, date_diff, get_last_day, add_to_date, flt +from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate class Subscription(Document): @@ -244,7 +245,6 @@ class Subscription(Document): # for that reason items_list = self.get_items_from_plans(self.plans, prorate) for item in items_list: - item['qty'] = self.quantity invoice.append('items', item) # Taxes @@ -272,6 +272,10 @@ class Subscription(Document): discount_on = self.apply_additional_discount invoice.apply_additional_discount = discount_on if discount_on else 'Grand Total' + # Subscription period + invoice.from_date = self.current_invoice_start + invoice.to_date = self.current_invoice_end + invoice.flags.ignore_mandatory = True invoice.save() invoice.submit() @@ -283,28 +287,25 @@ class Subscription(Document): """ Returns the `Customer` linked to the `Subscriber` """ - return frappe.get_value('Subscriber', subscriber_name, 'customer') + return frappe.db.get_value('Subscriber', subscriber_name, 'customer') def get_items_from_plans(self, plans, prorate=0): """ Returns the `Item`s linked to `Subscription Plan` """ - plan_items = [plan.plan for plan in plans] - item_details = None + if prorate: + prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start) - if plan_items: - item_details = frappe.db.sql( - 'select item as item_code, cost as rate from `tabSubscription Plan` where name in %s', - (plan_items,), as_dict=1 - ) + items = [] + customer = self.get_customer(self.subscriber) + for plan in plans: + item_code = frappe.db.get_value("Subscription Plan", plan.plan, "item") + if not prorate: + items.append({'item_code': item_code, 'qty': plan.qty, 'rate': get_plan_rate(plan.plan, plan.qty, customer)}) + else: + items.append({'item_code': item_code, 'qty': plan.qty, 'rate': (get_plan_rate(plan.plan, plan.qty, customer) * prorate_factor)}) - if prorate: - prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start) - - for item in item_details: - item['rate'] = item['rate'] * prorate_factor - - return item_details + return items def process(self): """ @@ -329,7 +330,7 @@ class Subscription(Document): 2. Change the `Subscription` status to 'Past Due Date' 3. Change the `Subscription` status to 'Cancelled' """ - if getdate(nowdate()) > getdate(self.current_invoice_end) and not self.has_outstanding_invoice(): + if getdate(nowdate()) > getdate(self.current_invoice_end) or (getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and not self.has_outstanding_invoice(): self.generate_invoice() if self.current_invoice_is_past_due(): self.status = 'Past Due Date' @@ -363,7 +364,7 @@ class Subscription(Document): else: if self.is_not_outstanding(current_invoice): self.status = 'Active' - self.update_subscription_period(nowdate()) + self.update_subscription_period(add_days(self.current_invoice_end, 1)) else: self.set_status_grace_period() diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 47efa45429a..c42b8e824b9 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -15,6 +15,7 @@ def create_plan(): plan = frappe.new_doc('Subscription Plan') plan.plan_name = '_Test Plan Name' plan.item = '_Test Non Stock Item' + plan.price_determination = "Fixed rate" plan.cost = 900 plan.billing_interval = 'Month' plan.billing_interval_count = 1 @@ -24,6 +25,7 @@ def create_plan(): plan = frappe.new_doc('Subscription Plan') plan.plan_name = '_Test Plan Name 2' plan.item = '_Test Non Stock Item' + plan.price_determination = "Fixed rate" plan.cost = 1999 plan.billing_interval = 'Month' plan.billing_interval_count = 1 @@ -33,6 +35,7 @@ def create_plan(): plan = frappe.new_doc('Subscription Plan') plan.plan_name = '_Test Plan Name 3' plan.item = '_Test Non Stock Item' + plan.price_determination = "Fixed rate" plan.cost = 1999 plan.billing_interval = 'Day' plan.billing_interval_count = 14 @@ -58,7 +61,7 @@ class TestSubscription(unittest.TestCase): subscription.subscriber = '_Test Customer' subscription.trial_period_start = nowdate() subscription.trial_period_end = add_days(nowdate(), 30) - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() self.assertEqual(subscription.trial_period_start, nowdate()) @@ -73,7 +76,7 @@ class TestSubscription(unittest.TestCase): def test_create_subscription_without_trial_with_correct_period(self): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() self.assertEqual(subscription.trial_period_start, None) @@ -91,7 +94,7 @@ class TestSubscription(unittest.TestCase): subscription.subscriber = '_Test Customer' subscription.trial_period_end = nowdate() subscription.trial_period_start = add_days(nowdate(), 30) - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) self.assertRaises(frappe.ValidationError, subscription.save) subscription.delete() @@ -101,8 +104,8 @@ class TestSubscription(unittest.TestCase): subscription.subscriber = '_Test Customer' subscription.trial_period_end = nowdate() subscription.trial_period_start = add_days(nowdate(), 30) - subscription.append('plans', {'plan': '_Test Plan Name'}) - subscription.append('plans', {'plan': '_Test Plan Name 3'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) + subscription.append('plans', {'plan': '_Test Plan Name 3', 'qty': 1}) self.assertRaises(frappe.ValidationError, subscription.save) subscription.delete() @@ -111,7 +114,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' subscription.start = '2018-01-01' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.insert() self.assertEqual(subscription.status, 'Active') @@ -127,7 +130,7 @@ class TestSubscription(unittest.TestCase): def test_status_goes_back_to_active_after_invoice_is_paid(self): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.start = '2018-01-01' subscription.insert() subscription.process() # generate first invoice @@ -144,7 +147,7 @@ class TestSubscription(unittest.TestCase): subscription.process() self.assertEqual(subscription.status, 'Active') - self.assertEqual(subscription.current_invoice_start, nowdate()) + self.assertEqual(subscription.current_invoice_start, add_months(subscription.start, 1)) self.assertEqual(len(subscription.invoices), 1) subscription.delete() @@ -157,7 +160,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.start = '2018-01-01' subscription.insert() subscription.process() # generate first invoice @@ -180,7 +183,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.start = '2018-01-01' subscription.insert() subscription.process() # generate first invoice @@ -198,7 +201,7 @@ class TestSubscription(unittest.TestCase): def test_subscription_invoice_days_until_due(self): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.days_until_due = 10 subscription.start = add_months(nowdate(), -1) subscription.insert() @@ -216,7 +219,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.start = '2018-01-01' subscription.insert() subscription.process() # generate first invoice @@ -240,7 +243,7 @@ class TestSubscription(unittest.TestCase): def test_subscription_remains_active_during_invoice_period(self): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.process() # no changes expected @@ -266,7 +269,7 @@ class TestSubscription(unittest.TestCase): def test_subscription_cancelation(self): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.cancel_subscription() @@ -282,7 +285,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() self.assertEqual(subscription.status, 'Active') @@ -317,7 +320,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.cancel_subscription() invoice = subscription.get_current_invoice() @@ -337,7 +340,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.cancel_subscription() @@ -361,7 +364,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.start = '2018-01-01' subscription.insert() subscription.process() # generate first invoice @@ -395,7 +398,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.start = '2018-01-01' subscription.insert() subscription.process() # generate first invoice @@ -432,7 +435,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.start = '2018-01-01' subscription.insert() subscription.process() # generate first invoice @@ -450,8 +453,9 @@ class TestSubscription(unittest.TestCase): subscription.process() self.assertEqual(subscription.status, 'Active') + # A new invoice is generated subscription.process() - self.assertEqual(subscription.status, 'Active') + self.assertEqual(subscription.status, 'Past Due Date') settings.cancel_after_grace = default_grace_period_action settings.save() @@ -460,7 +464,7 @@ class TestSubscription(unittest.TestCase): def test_restart_active_subscription(self): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() self.assertRaises(frappe.ValidationError, subscription.restart_subscription) @@ -471,7 +475,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' subscription.additional_discount_percentage = 10 - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.cancel_subscription() @@ -486,7 +490,7 @@ class TestSubscription(unittest.TestCase): subscription = frappe.new_doc('Subscription') subscription.subscriber = '_Test Customer' subscription.additional_discount_amount = 11 - subscription.append('plans', {'plan': '_Test Plan Name'}) + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) subscription.save() subscription.cancel_subscription() diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js index f5ea8047c6a..aaa32cfe7ef 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js @@ -1,2 +1,9 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt + +frappe.ui.form.on('Subscription Plan', { + price_determination: function(frm) { + frm.toggle_reqd("cost", frm.doc.price_determination === 'Fixed rate'); + frm.toggle_reqd("price_list", frm.doc.price_determination === 'Based on price list'); + } +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json index ab58e7c3c6f..453521d04de 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json @@ -15,6 +15,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -42,42 +43,11 @@ "search_index": 0, "set_only_once": 0, "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "unique": 1 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -110,10 +80,172 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "item", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Item", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "price_determination", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Price Determination", + "length": 0, + "no_copy": 0, + "options": "\nFixed rate\nBased on price list", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.price_determination==\"Fixed rate\"", "fieldname": "cost", "fieldtype": "Currency", "hidden": 0, @@ -133,7 +265,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -141,6 +273,72 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.price_determination==\"Based on price list\"", + "fieldname": "price_list", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Price List", + "length": 0, + "no_copy": 0, + "options": "Price List", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_11", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -174,6 +372,38 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 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_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -204,6 +434,134 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "payment_plan_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Payment Plan", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "payment_plan_id", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Payment Plan", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_16", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "payment_gateway", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Payment Gateway", + "length": 0, + "no_copy": 0, + "options": "Payment Gateway Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -216,7 +574,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-02-27 09:12:58.330140", + "modified": "2018-06-20 16:59:54.082358", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription Plan", @@ -225,7 +583,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -244,7 +601,7 @@ "write": 1 } ], - "quick_entry": 1, + "quick_entry": 0, "read_only": 0, "read_only_onload": 0, "show_name_in_global_search": 0, diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index 4b8c8fcedd7..d3fef6023ba 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from erpnext.utilities.product import get_price class SubscriptionPlan(Document): def validate(self): @@ -13,3 +14,21 @@ class SubscriptionPlan(Document): def validate_interval_count(self): if self.billing_interval_count < 1: frappe.throw('Billing Interval Count cannot be less than 1') + +@frappe.whitelist() +def get_plan_rate(plan, quantity=1, customer=None): + plan = frappe.get_doc("Subscription Plan", plan) + if plan.price_determination == "Fixed rate": + return plan.cost + + elif plan.price_determination == "Based on price list": + if customer: + customer_group = frappe.db.get_value("Customer", customer, "customer_group") + else: + customer_group = None + + price = get_price(item_code=plan.item, price_list=plan.price_list, customer_group=customer_group, company=None, qty=quantity) + if not price: + return 0 + else: + return price.price_list_rate diff --git a/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.json b/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.json index c1129233974..ca54a167f59 100644 --- a/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.json +++ b/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.json @@ -14,6 +14,39 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "qty", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Quantity", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -55,7 +88,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-02-25 07:35:07.736146", + "modified": "2018-06-20 15:35:13.514699", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription Plan Detail", diff --git a/erpnext/config/erpnext_integrations.py b/erpnext/config/integrations.py similarity index 100% rename from erpnext/config/erpnext_integrations.py rename to erpnext/config/integrations.py diff --git a/erpnext/erpnext_integrations/stripe_integration.py b/erpnext/erpnext_integrations/stripe_integration.py new file mode 100644 index 00000000000..a35ca28e0a3 --- /dev/null +++ b/erpnext/erpnext_integrations/stripe_integration.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.integrations.utils import create_request_log +import stripe + +def create_stripe_subscription(gateway_controller, data): + stripe_settings = frappe.get_doc("Stripe Settings", gateway_controller) + stripe_settings.data = frappe._dict(data) + + stripe.api_key = stripe_settings.get_password(fieldname="secret_key", raise_exception=False) + stripe.default_http_client = stripe.http_client.RequestsClient() + + try: + stripe_settings.integration_request = create_request_log(stripe_settings.data, "Host", "Stripe") + stripe_settings.payment_plans = frappe.get_doc("Payment Request", stripe_settings.data.reference_docname).subscription_plans + return create_subscription_on_stripe(stripe_settings) + + except Exception: + frappe.log_error(frappe.get_traceback()) + return{ + "redirect_to": frappe.redirect_to_message(_('Server Error'), _("It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.")), + "status": 401 + } + + +def create_subscription_on_stripe(stripe_settings): + items = [] + for payment_plan in stripe_settings.payment_plans: + plan = frappe.db.get_value("Subscription Plan", payment_plan.plan, "payment_plan_id") + items.append({"plan": plan, "quantity": payment_plan.qty}) + + try: + customer = stripe.Customer.create(description=stripe_settings.data.payer_name, email=stripe_settings.data.payer_email, source=stripe_settings.data.stripe_token_id) + subscription = stripe.Subscription.create(customer=customer, items=items) + + if subscription.status == "active": + stripe_settings.integration_request.db_set('status', 'Completed', update_modified=False) + stripe_settings.flags.status_changed_to = "Completed" + + else: + stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False) + frappe.log_error('Subscription N°: ' + subscription.id, 'Stripe Payment not completed') + + except Exception: + stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False) + frappe.log_error(frappe.get_traceback()) + + return stripe_settings.finalize_request() \ No newline at end of file diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.html b/erpnext/templates/pages/integrations/gocardless_checkout.html index eb124cafb65..bbe5640d5e4 100644 --- a/erpnext/templates/pages/integrations/gocardless_checkout.html +++ b/erpnext/templates/pages/integrations/gocardless_checkout.html @@ -13,4 +13,4 @@ {{ _("Loading Payment System") }}

-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.py b/erpnext/templates/pages/integrations/gocardless_checkout.py index 3c2466ea2f3..2ba7001a615 100644 --- a/erpnext/templates/pages/integrations/gocardless_checkout.py +++ b/erpnext/templates/pages/integrations/gocardless_checkout.py @@ -73,4 +73,4 @@ def check_mandate(data, reference_doctype, reference_docname): except Exception as e: frappe.log_error(e, "GoCardless Payment Error") - return {"redirect_to": '/integrations/payment-failed'} + return {"redirect_to": '/integrations/payment-failed'} \ No newline at end of file diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.html b/erpnext/templates/pages/integrations/gocardless_confirmation.html index 1baf23be26b..15674876573 100644 --- a/erpnext/templates/pages/integrations/gocardless_confirmation.html +++ b/erpnext/templates/pages/integrations/gocardless_confirmation.html @@ -13,4 +13,4 @@ {{ _("Payment Confirmation") }}

-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.py b/erpnext/templates/pages/integrations/gocardless_confirmation.py index fc564c3df99..069d90050e2 100644 --- a/erpnext/templates/pages/integrations/gocardless_confirmation.py +++ b/erpnext/templates/pages/integrations/gocardless_confirmation.py @@ -82,4 +82,4 @@ def create_mandate(data): }).insert(ignore_permissions=True) except Exception: - frappe.log_error(frappe.get_traceback()) + frappe.log_error(frappe.get_traceback()) \ No newline at end of file