Move subscription module to accounts (#10772)
This commit is contained in:
parent
e859671eb3
commit
850eaa73b0
@ -483,8 +483,13 @@ class PaymentEntry(AccountsController):
|
||||
doc = frappe.get_doc("Expense Claim", d.reference_name)
|
||||
update_reimbursed_amount(doc)
|
||||
|
||||
def on_recurring(self, reference_doc, subscription_doc):
|
||||
self.reference_no = reference_doc.name
|
||||
self.reference_date = nowdate()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_outstanding_reference_documents(args):
|
||||
if isinstance(args, basestring):
|
||||
args = json.loads(args)
|
||||
|
||||
party_account_currency = get_account_currency(args.get("party_account"))
|
||||
|
@ -40,6 +40,38 @@ frappe.ui.form.on('Subscription', {
|
||||
frappe.set_route("List", frm.doc.reference_doctype);
|
||||
}
|
||||
);
|
||||
|
||||
if(frm.doc.status != 'Stopped') {
|
||||
frm.add_custom_button(__("Stop"),
|
||||
function() {
|
||||
frm.events.stop_resume_subscription(frm, "Stopped");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if(frm.doc.status == 'Stopped') {
|
||||
frm.add_custom_button(__("Resume"),
|
||||
function() {
|
||||
frm.events.stop_resume_subscription(frm, "Resumed");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stop_resume_subscription: function(frm, status) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.subscription.subscription.stop_resume_subscription",
|
||||
args: {
|
||||
subscription: frm.doc.name,
|
||||
status: status
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frm.set_value("status", r.message);
|
||||
frm.reload_doc();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -148,7 +148,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disabled",
|
||||
"length": 0,
|
||||
@ -619,24 +619,24 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Draft",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nDraft\nSubmitted\nCancelled\nCompleted",
|
||||
"options": "\nDraft\nStopped\nSubmitted\nCancelled\nCompleted",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@ -690,9 +690,9 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-29 15:45:16.157643",
|
||||
"modified": "2017-09-14 12:09:38.471458",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subscription",
|
||||
"module": "Accounts",
|
||||
"name": "Subscription",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
@ -700,7 +700,7 @@
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 1,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
@ -716,6 +716,46 @@
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
@ -71,13 +71,16 @@ class Subscription(Document):
|
||||
|
||||
doc.db_set('subscription', self.name)
|
||||
|
||||
def update_status(self):
|
||||
def update_status(self, status=None):
|
||||
self.status = {
|
||||
'0': 'Draft',
|
||||
'1': 'Submitted',
|
||||
'2': 'Cancelled'
|
||||
}[cstr(self.docstatus or 0)]
|
||||
|
||||
if status and status != 'Resumed':
|
||||
self.status = status
|
||||
|
||||
def get_next_schedule_date(start_date, frequency, repeat_on_day):
|
||||
mcount = month_map.get(frequency)
|
||||
if mcount:
|
||||
@ -93,11 +96,10 @@ def make_subscription_entry(date=None):
|
||||
schedule_date = getdate(data.next_schedule_date)
|
||||
while schedule_date <= getdate(today()):
|
||||
create_documents(data, schedule_date)
|
||||
|
||||
schedule_date = get_next_schedule_date(schedule_date,
|
||||
data.frequency, data.repeat_on_day)
|
||||
|
||||
if schedule_date:
|
||||
if schedule_date and not frappe.db.get_value('Subscription', data.name, 'disabled'):
|
||||
frappe.db.set_value('Subscription', data.name, 'next_schedule_date', schedule_date)
|
||||
|
||||
def get_subscription_entries(date):
|
||||
@ -105,23 +107,29 @@ def get_subscription_entries(date):
|
||||
where docstatus = 1 and next_schedule_date <=%s
|
||||
and reference_document is not null and reference_document != ''
|
||||
and next_schedule_date <= ifnull(end_date, '2199-12-31')
|
||||
and ifnull(disabled, 0) = 0""", (date), as_dict=1)
|
||||
and ifnull(disabled, 0) = 0 and status != 'Stopped' """, (date), as_dict=1)
|
||||
|
||||
def create_documents(data, schedule_date):
|
||||
try:
|
||||
doc = make_new_document(data, schedule_date)
|
||||
if data.notify_by_email:
|
||||
send_notification(doc, data.print_format, data.recipients)
|
||||
if data.notify_by_email and data.recipients:
|
||||
print_format = data.print_format or "Standard"
|
||||
send_notification(doc, print_format, data.recipients)
|
||||
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
frappe.db.rollback()
|
||||
frappe.db.begin()
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
disabled_subscription(data)
|
||||
frappe.db.commit()
|
||||
if data.reference_document and not frappe.flags.in_test:
|
||||
notify_error_to_user(data)
|
||||
|
||||
def disabled_subscription(data):
|
||||
subscription = frappe.get_doc('Subscription', data.name)
|
||||
subscription.db_set('disabled', 1)
|
||||
|
||||
def notify_error_to_user(data):
|
||||
party = ''
|
||||
party_type = ''
|
||||
@ -134,7 +142,7 @@ def notify_error_to_user(data):
|
||||
if party_type:
|
||||
party = frappe.db.get_value(data.reference_doctype, data.reference_document, party_type)
|
||||
|
||||
notify_errors(data.reference_document, data.reference_doctype, party, data.owner)
|
||||
notify_errors(data.reference_document, data.reference_doctype, party, data.owner, data.name)
|
||||
|
||||
def make_new_document(args, schedule_date):
|
||||
doc = frappe.get_doc(args.reference_doctype, args.reference_document)
|
||||
@ -168,32 +176,32 @@ def get_next_date(dt, mcount, day=None):
|
||||
|
||||
def send_notification(new_rv, print_format='Standard', recipients=None):
|
||||
"""Notify concerned persons about recurring document generation"""
|
||||
recipients = recipients or new_rv.notification_email_address
|
||||
print_format = print_format or new_rv.recurring_print_format
|
||||
print_format = print_format
|
||||
|
||||
frappe.sendmail(recipients,
|
||||
subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name),
|
||||
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name),
|
||||
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)])
|
||||
|
||||
def notify_errors(doc, doctype, party, owner):
|
||||
def notify_errors(doc, doctype, party, owner, name):
|
||||
recipients = get_system_managers(only_name=True)
|
||||
frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")],
|
||||
subject="[Urgent] Error while creating recurring %s for %s" % (doctype, doc),
|
||||
subject=_("[Urgent] Error while creating recurring %s for %s" % (doctype, doc)),
|
||||
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
|
||||
"type": doctype,
|
||||
"type": _(doctype),
|
||||
"name": doc,
|
||||
"party": party or ""
|
||||
"party": party or "",
|
||||
"subscription": name
|
||||
}))
|
||||
|
||||
assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients)
|
||||
assign_task_to_owner(name, "Recurring Documents Failed", recipients)
|
||||
|
||||
def assign_task_to_owner(doc, doctype, msg, users):
|
||||
def assign_task_to_owner(name, msg, users):
|
||||
for d in users:
|
||||
args = {
|
||||
'doctype' : 'Subscription',
|
||||
'assign_to' : d,
|
||||
'doctype' : doctype,
|
||||
'name' : doc,
|
||||
'name' : name,
|
||||
'description' : msg,
|
||||
'priority' : 'High'
|
||||
}
|
||||
@ -205,3 +213,16 @@ def make_subscription(doctype, docname):
|
||||
doc.reference_doctype = doctype
|
||||
doc.reference_document = docname
|
||||
return doc
|
||||
|
||||
@frappe.whitelist()
|
||||
def stop_resume_subscription(subscription, status):
|
||||
doc = frappe.get_doc('Subscription', subscription)
|
||||
frappe.msgprint(_("Subscription has been {0}").format(status))
|
||||
if status == 'Resumed':
|
||||
doc.next_schedule_date = get_next_schedule_date(today(),
|
||||
doc.frequency, doc.repeat_on_day)
|
||||
|
||||
doc.update_status(status)
|
||||
doc.save()
|
||||
|
||||
return doc.status
|
@ -1,10 +1,14 @@
|
||||
frappe.listview_settings['Subscription'] = {
|
||||
add_fields: ["next_schedule_date"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.next_schedule_date >= frappe.datetime.get_today() ) {
|
||||
if(doc.disabled) {
|
||||
return [__("Disabled"), "red"];
|
||||
} else if(doc.next_schedule_date >= frappe.datetime.get_today() && doc.status != 'Stopped') {
|
||||
return [__("Active"), "green"];
|
||||
} else if(doc.docstatus === 0) {
|
||||
return [__("Draft"), "red", "docstatus,=,0"];
|
||||
} else if(doc.status === 'Stopped') {
|
||||
return [__("Stopped"), "red"];
|
||||
} else {
|
||||
return [__("Expired"), "darkgrey"];
|
||||
}
|
@ -10,7 +10,7 @@ from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.accounts.report.financial_statements import get_months
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
from erpnext.subscription.doctype.subscription.subscription import make_subscription_entry
|
||||
from erpnext.accounts.doctype.subscription.subscription import make_subscription_entry
|
||||
|
||||
class TestSubscription(unittest.TestCase):
|
||||
def test_daily_subscription(self):
|
@ -32,6 +32,12 @@ def get_data():
|
||||
"label": _("POS"),
|
||||
"description": _("Point of Sale")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Subscription",
|
||||
"label": _("Subscription"),
|
||||
"description": _("To make recurring documents")
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Accounts Receivable",
|
||||
|
Before Width: | Height: | Size: 818 KiB After Width: | Height: | Size: 818 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
@ -6,6 +6,7 @@ purchase-invoice
|
||||
payments
|
||||
journal-entry
|
||||
payment-entry
|
||||
subscription
|
||||
multi-currency-accounting
|
||||
advance-payment-entry
|
||||
payment-request
|
||||
|
24
erpnext/docs/user/manual/en/accounts/subscription.md
Normal file
24
erpnext/docs/user/manual/en/accounts/subscription.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Subscription
|
||||
|
||||
If you have a contract with the Customer where your organization gives bill to the Customer on a monthly, quarterly, half-yearly or annual basis, you can use subscription feature to make auto invoicing.
|
||||
|
||||
<img class="screenshot" alt="Subscription" src="/docs/assets/img/accounts/subscription.png">
|
||||
|
||||
#### Scenario
|
||||
|
||||
Subscription for your hosted ERPNext account requires yearly renewal. We use Sales Invoice for generating proforma invoices. To automate proforma invoicing for renewal, we set original Sales Invoice on the subscription form. Recurring proforma invoice is created automatically just before customer's account is about to expire, and requires renewal. This recurring Proforma Invoice is also emailed automatically to the customer.
|
||||
|
||||
To set the subscription for the sales invoice
|
||||
Goto Subscription > select base doctype "Sales Invoice" > select base docname "Invoice No" > Save
|
||||
|
||||
<img class="screenshot" alt="Subscription" src="/docs/assets/img/accounts/subscription.gif">
|
||||
|
||||
**From Date and To Date**: This defines contract period with the customer.
|
||||
|
||||
**Repeat on Day**: If frequency is set as Monthly, then it will be day of the month on which recurring invoice will be generated.
|
||||
|
||||
**Notify By Email**: If you want to notify the user about auto recurring invoice.
|
||||
|
||||
**Print Format**: Select a print format to define document view which should be emailed to customer.
|
||||
|
||||
**Disabled**: It will stop to make auto recurring documents against the subscription
|
@ -192,7 +192,7 @@ doc_events = {
|
||||
|
||||
scheduler_events = {
|
||||
"hourly": [
|
||||
"erpnext.subscription.doctype.subscription.subscription.make_subscription_entry",
|
||||
"erpnext.accounts.doctype.subscription.subscription.make_subscription_entry",
|
||||
'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.trigger_emails'
|
||||
],
|
||||
"daily": [
|
||||
|
@ -16,4 +16,3 @@ Maintenance
|
||||
Schools
|
||||
Regional
|
||||
Healthcare
|
||||
Subscription
|
@ -439,6 +439,7 @@ erpnext.patches.v8_7.set_offline_in_pos_settings #11-09-17
|
||||
erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017
|
||||
erpnext.patches.v8_9.rename_company_sales_target_field
|
||||
erpnext.patches.v8_8.set_bom_rate_as_per_uom
|
||||
erpnext.patches.v9_0.remove_subscription_module
|
||||
erpnext.patches.v8_7.make_subscription_from_recurring_data
|
||||
erpnext.patches.v8_9.set_print_zero_amount_taxes
|
||||
erpnext.patches.v8_9.set_default_customer_group
|
@ -6,7 +6,7 @@ import frappe
|
||||
from frappe.utils import today
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('subscription', 'doctype', 'subscription')
|
||||
frappe.reload_doc('accounts', 'doctype', 'subscription')
|
||||
frappe.reload_doc('selling', 'doctype', 'sales_order')
|
||||
frappe.reload_doc('buying', 'doctype', 'purchase_order')
|
||||
frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
|
||||
|
1
erpnext/patches/v9_0/__init__.py
Normal file
1
erpnext/patches/v9_0/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
9
erpnext/patches/v9_0/remove_subscription_module.py
Normal file
9
erpnext/patches/v9_0/remove_subscription_module.py
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
if frappe.db.exists('Module Def', 'Subscription'):
|
||||
frappe.db.sql(""" delete from `tabModule Def` where name = 'Subscription'""")
|
@ -129,7 +129,7 @@ $.extend(erpnext.utils, {
|
||||
|
||||
make_subscription: function(doctype, docname) {
|
||||
frappe.call({
|
||||
method: "erpnext.subscription.doctype.subscription.subscription.make_subscription",
|
||||
method: "erpnext.accounts.doctype.subscription.subscription.make_subscription",
|
||||
args: {
|
||||
doctype: doctype,
|
||||
docname: docname
|
||||
|
@ -13,7 +13,7 @@ from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
|
||||
from frappe.desk.notifications import clear_doctype_notifications
|
||||
from frappe.contacts.doctype.address.address import get_company_address
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from erpnext.subscription.doctype.subscription.subscription import month_map, get_next_date
|
||||
from erpnext.accounts.doctype.subscription.subscription import get_next_schedule_date
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@ -347,8 +347,7 @@ class SalesOrder(SellingController):
|
||||
return items
|
||||
|
||||
def on_recurring(self, reference_doc, subscription_doc):
|
||||
mcount = month_map[subscription_doc.frequency]
|
||||
self.set("delivery_date", get_next_date(reference_doc.delivery_date, mcount,
|
||||
self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, subscription_doc.frequency,
|
||||
cint(subscription_doc.repeat_on_day)))
|
||||
|
||||
for d in self.get("items"):
|
||||
@ -356,7 +355,7 @@ class SalesOrder(SellingController):
|
||||
{"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date")
|
||||
|
||||
d.set("delivery_date",
|
||||
get_next_date(reference_delivery_date, mcount, cint(subscription_doc.repeat_on_day)))
|
||||
get_next_schedule_date(reference_delivery_date, subscription_doc.frequency, cint(subscription_doc.repeat_on_day)))
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
|
@ -1,12 +1,11 @@
|
||||
<h2>Recurring {{ type }} Failed</h2>
|
||||
<h2>{{_("Recurring")}} {{ type }} {{ _("Failed")}}</h2>
|
||||
|
||||
<p>An error occured while creating recurring {{ type }} <b>{{ name }}</b> for <b>{{ party }}</b>.</p>
|
||||
<p>This could be because of some invalid Email Addresses in the {{ type }}.</p>
|
||||
<p>To stop sending repetitive error notifications from the system, we have unchecked
|
||||
"Convert into Recurring" field in the {{ type }} {{ name }}.</p>
|
||||
<p><b>Please correct the {{ type }} and make the {{ type }} recurring again.</b></p>
|
||||
<p>To stop sending repetitive error notifications from the system, we have checked "Disabled" field in the subscription {{ subscription}} for the {{ type }} {{ name }}.</p>
|
||||
<p><b>Please correct the {{ type }} and unchcked "Disabled" in the {{ subscription }} for making recurring again.</b></p>
|
||||
<hr>
|
||||
<p><b>It is necessary to take this action today itself for the above mentioned recurring {{ type }}
|
||||
to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field
|
||||
of this {{ type }} for generating the recurring {{ type }}.</b></p>
|
||||
of this {{ type }} for generating the recurring {{ type }} in the subscription {{ subscription }}.</b></p>
|
||||
<p>[This email is autogenerated]</p>
|
||||
|
Loading…
x
Reference in New Issue
Block a user