Merge pull request #14707 from chdecultot/stripe_improvements_4
Stripe Subscriptions
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js
index afc3804..8820161 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.js
+++ b/erpnext/accounts/doctype/payment_request/payment_request.js
@@ -31,7 +31,7 @@
});
});
}
-
+
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", "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 e21afa4..4148dce 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 a633cc3..c58b185 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.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 @@
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 @@
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 @@
<p>{{ _("Thank you for your business!") }}</p>
""", 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 0000000..070b595
--- /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 5d64d9a..a58ac3e 100644
--- a/erpnext/accounts/doctype/subscription/subscription.json
+++ b/erpnext/accounts/doctype/subscription/subscription.json
@@ -118,39 +118,6 @@
"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,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "subscription_period",
"fieldtype": "Section Break",
"hidden": 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 72f86f2..fe39161 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -8,6 +8,7 @@
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 @@
# 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 @@
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 @@
"""
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 @@
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 @@
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 47efa45..c42b8e8 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -15,6 +15,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 f5ea804..aaa32cf 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 ab58e7c..453521d 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
+ "unique": 1
},
{
"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
- },
- {
- "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 4b8c8fc..d3fef60 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 @@
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 c112923..ca54a16 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 0000000..a35ca28
--- /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 eb124ca..bbe5640 100644
--- a/erpnext/templates/pages/integrations/gocardless_checkout.html
+++ b/erpnext/templates/pages/integrations/gocardless_checkout.html
@@ -13,4 +13,4 @@
<span class='gocardless-loading'>{{ _("Loading Payment System") }}</span>
</p>
-{% 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 3c2466e..2ba7001 100644
--- a/erpnext/templates/pages/integrations/gocardless_checkout.py
+++ b/erpnext/templates/pages/integrations/gocardless_checkout.py
@@ -73,4 +73,4 @@
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 1baf23b..1567487 100644
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.html
+++ b/erpnext/templates/pages/integrations/gocardless_confirmation.html
@@ -13,4 +13,4 @@
<span class='gocardless-loading'>{{ _("Payment Confirmation") }}</span>
</p>
-{% 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 fc564c3..069d900 100644
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.py
+++ b/erpnext/templates/pages/integrations/gocardless_confirmation.py
@@ -82,4 +82,4 @@
}).insert(ignore_permissions=True)
except Exception:
- frappe.log_error(frappe.get_traceback())
+ frappe.log_error(frappe.get_traceback())
\ No newline at end of file