Merge pull request #14707 from chdecultot/stripe_improvements_4
Stripe Subscriptions
This commit is contained in:
commit
a638400e5d
@ -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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -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",
|
||||
|
@ -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):
|
||||
|
||||
<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
|
@ -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()
|
||||
]);
|
||||
|
||||
});
|
@ -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",
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
});
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
53
erpnext/erpnext_integrations/stripe_integration.py
Normal file
53
erpnext/erpnext_integrations/stripe_integration.py
Normal file
@ -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()
|
@ -13,4 +13,4 @@
|
||||
<span class='gocardless-loading'>{{ _("Loading Payment System") }}</span>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@ -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'}
|
@ -13,4 +13,4 @@
|
||||
<span class='gocardless-loading'>{{ _("Payment Confirmation") }}</span>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@ -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())
|
Loading…
x
Reference in New Issue
Block a user