[Enhance] Subscription module (#10089)
* [Enhance] Subscription module * Added view documents from the subscription form * Test cases * documentation * UI Test cases, fixes * Removed child table in the subscription * Provision to make subscription from the document, added subscription in the dashboard for the sales and buying flow * added patch to make subscription from the recurring data * Rename field subscriptio to subscription_id, added new test cases, remove recurring_document from controller * renamed subscription_id to subscription
This commit is contained in:
parent
c79d14190f
commit
166b78f022
@ -1337,6 +1337,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -1382,7 +1443,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-06-13 14:29:09.794076",
|
||||
"modified": "2017-08-31 11:21:09.442695",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry",
|
||||
|
@ -1659,6 +1659,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -1730,7 +1791,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-06-13 14:29:04.244537",
|
||||
"modified": "2017-08-31 11:20:37.578469",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
@ -46,6 +46,12 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
cur_frm.add_custom_button(__('Return / Debit Note'),
|
||||
this.make_debit_note, __("Make"));
|
||||
}
|
||||
|
||||
if(!doc.subscription) {
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(doc.doctype, doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
}
|
||||
|
||||
if(doc.docstatus===0) {
|
||||
@ -343,6 +349,7 @@ frappe.ui.form.on("Purchase Invoice", {
|
||||
'Payment Entry': 'Payment'
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
$.each(["warehouse", "rejected_warehouse"], function(i, field) {
|
||||
frm.set_query(field, "items", function() {
|
||||
@ -370,5 +377,5 @@ frappe.ui.form.on("Purchase Invoice", {
|
||||
erpnext.buying.get_default_bom(frm);
|
||||
}
|
||||
frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
@ -3348,6 +3348,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -3358,7 +3419,7 @@
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_invoice",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
@ -3797,7 +3858,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-07-19 13:53:48.673757",
|
||||
"modified": "2017-08-31 11:22:47.074420",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
@ -667,7 +667,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if account_type != 'Fixed Asset':
|
||||
frappe.throw(_("Row {0}# Account must be of type 'Fixed Asset'").format(d.idx))
|
||||
|
||||
def on_recurring(self, reference_doc):
|
||||
def on_recurring(self, reference_doc, subscription_doc):
|
||||
self.due_date = None
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -8,7 +8,8 @@ def get_data():
|
||||
'Payment Entry': 'reference_name',
|
||||
'Payment Request': 'reference_name',
|
||||
'Landed Cost Voucher': 'receipt_document',
|
||||
'Purchase Invoice': 'return_against'
|
||||
'Purchase Invoice': 'return_against',
|
||||
'Subscription': 'reference_document'
|
||||
},
|
||||
'internal_links': {
|
||||
'Purchase Order': ['items', 'purchase_order'],
|
||||
@ -27,5 +28,9 @@ def get_data():
|
||||
'label': _('Returns'),
|
||||
'items': ['Purchase Invoice']
|
||||
},
|
||||
{
|
||||
'label': _('Subscription'),
|
||||
'items': ['Subscription']
|
||||
},
|
||||
]
|
||||
}
|
@ -256,10 +256,6 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where reference_type='Purchase Invoice' and reference_name=%s""", pi.name))
|
||||
|
||||
def test_recurring_invoice(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
test_recurring_document(self, test_records)
|
||||
|
||||
def test_total_purchase_cost_for_project(self):
|
||||
existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount)
|
||||
from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""")
|
||||
|
@ -86,7 +86,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
this.make_payment_request, __("Make"));
|
||||
}
|
||||
|
||||
|
||||
if(!doc.subscription) {
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(doc.doctype, doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
}
|
||||
|
||||
// Show buttons only when pos view is active
|
||||
|
@ -4175,6 +4175,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -4185,7 +4246,7 @@
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_invoice",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
@ -4688,7 +4749,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-07-07 13:05:37.469682",
|
||||
"modified": "2017-08-31 11:23:08.675028",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
@ -107,7 +107,7 @@ class SalesInvoice(SellingController):
|
||||
def on_submit(self):
|
||||
self.validate_pos_paid_amount()
|
||||
|
||||
if not self.recurring_id:
|
||||
if not self.subscription:
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
self.company, self.base_grand_total, self)
|
||||
|
||||
@ -799,7 +799,7 @@ class SalesInvoice(SellingController):
|
||||
for dn in set(updated_delivery_notes):
|
||||
frappe.get_doc("Delivery Note", dn).update_billing_percentage(update_modified=update_modified)
|
||||
|
||||
def on_recurring(self, reference_doc):
|
||||
def on_recurring(self, reference_doc, subscription_doc):
|
||||
for fieldname in ("c_form_applicable", "c_form_no", "write_off_amount"):
|
||||
self.set(fieldname, reference_doc.get(fieldname))
|
||||
|
||||
|
@ -8,7 +8,8 @@ def get_data():
|
||||
'Journal Entry': 'reference_name',
|
||||
'Payment Entry': 'reference_name',
|
||||
'Payment Request': 'reference_name',
|
||||
'Sales Invoice': 'return_against'
|
||||
'Sales Invoice': 'return_against',
|
||||
'Subscription': 'reference_document',
|
||||
},
|
||||
'internal_links': {
|
||||
'Sales Order': ['items', 'sales_order']
|
||||
@ -26,5 +27,9 @@ def get_data():
|
||||
'label': _('Returns'),
|
||||
'items': ['Sales Invoice']
|
||||
},
|
||||
{
|
||||
'label': _('Subscription'),
|
||||
'items': ['Subscription']
|
||||
},
|
||||
]
|
||||
}
|
@ -809,10 +809,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where reference_name=%s""", si.name))
|
||||
|
||||
def test_recurring_invoice(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
test_recurring_document(self, test_records)
|
||||
|
||||
def test_serialized(self):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
@ -13,6 +13,7 @@ frappe.ui.form.on("Purchase Order", {
|
||||
'Stock Entry': 'Material to Supplier'
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(frm.doc);
|
||||
@ -20,8 +21,7 @@ frappe.ui.form.on("Purchase Order", {
|
||||
|
||||
frm.set_indicator_formatter('item_code',
|
||||
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||
@ -86,8 +86,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
|
||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __("Make"));
|
||||
}
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
|
||||
if(!doc.subscription) {
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(doc.doctype, doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2856,6 +2856,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -2866,7 +2927,7 @@
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_order",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
@ -3335,7 +3396,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-07-19 14:03:51.838328",
|
||||
"modified": "2017-08-31 11:22:30.190589",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
@ -5,7 +5,8 @@ def get_data():
|
||||
'fieldname': 'purchase_order',
|
||||
'non_standard_fieldnames': {
|
||||
'Journal Entry': 'reference_name',
|
||||
'Payment Entry': 'reference_name'
|
||||
'Payment Entry': 'reference_name',
|
||||
'Subscription': 'reference_document'
|
||||
},
|
||||
'internal_links': {
|
||||
'Material Request': ['items', 'material_request'],
|
||||
@ -23,11 +24,11 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Reference'),
|
||||
'items': ['Material Request', 'Supplier Quotation', 'Project']
|
||||
'items': ['Material Request', 'Supplier Quotation', 'Project', 'Subscription']
|
||||
},
|
||||
{
|
||||
'label': _('Sub-contracting'),
|
||||
'items': ['Stock Entry']
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
23
erpnext/buying/doctype/purchase_order/test_purchase_order.js
Normal file
23
erpnext/buying/doctype/purchase_order/test_purchase_order.js
Normal file
@ -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: Purchase Order", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially('Purchase Order', [
|
||||
// insert a new Purchase Order
|
||||
() => frappe.tests.make([
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -22,7 +22,9 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
cur_frm.add_custom_button(__("Quotation"), this.make_quotation,
|
||||
__("Make"));
|
||||
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(me.frm.doc.doctype, me.frm.doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
else if (this.frm.doc.docstatus===0) {
|
||||
|
||||
|
@ -2051,6 +2051,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -2247,7 +2308,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-07-19 13:51:18.929697",
|
||||
"modified": "2017-08-31 11:23:25.268924",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
@ -3,6 +3,9 @@ from frappe import _
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'supplier_quotation',
|
||||
'non_standard_fieldnames': {
|
||||
'Subscription': 'reference_document'
|
||||
},
|
||||
'internal_links': {
|
||||
'Material Request': ['items', 'material_request'],
|
||||
'Request for Quotation': ['items', 'request_for_quotation'],
|
||||
@ -17,6 +20,10 @@ def get_data():
|
||||
'label': _('Reference'),
|
||||
'items': ['Material Request', 'Request for Quotation', 'Project']
|
||||
},
|
||||
{
|
||||
'label': _('Subscription'),
|
||||
'items': ['Subscription']
|
||||
},
|
||||
]
|
||||
|
||||
}
|
||||
|
@ -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: Supplier Quotation", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially('Supplier Quotation', [
|
||||
// insert a new Supplier Quotation
|
||||
() => frappe.tests.make([
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -8,7 +8,6 @@ from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||
from erpnext.controllers.sales_and_purchase_return import validate_return
|
||||
from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
|
||||
from erpnext.exceptions import InvalidCurrency
|
||||
@ -53,13 +52,6 @@ class AccountsController(TransactionBase):
|
||||
self.validate_party()
|
||||
self.validate_currency()
|
||||
|
||||
if self.meta.get_field("is_recurring"):
|
||||
if self.amended_from and self.recurring_id == self.amended_from:
|
||||
self.recurring_id = None
|
||||
if not self.get("__islocal"):
|
||||
validate_recurring_document(self)
|
||||
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
||||
|
||||
if self.doctype == 'Purchase Invoice':
|
||||
self.validate_paid_amount()
|
||||
|
||||
@ -84,11 +76,6 @@ class AccountsController(TransactionBase):
|
||||
else:
|
||||
frappe.db.set(self,'paid_amount',0)
|
||||
|
||||
def on_update_after_submit(self):
|
||||
if self.meta.get_field("is_recurring"):
|
||||
validate_recurring_document(self)
|
||||
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
if frappe.flags.in_test:
|
||||
for fieldname in ["posting_date","transaction_date"]:
|
||||
|
@ -1,230 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import calendar
|
||||
import frappe.utils
|
||||
import frappe.defaults
|
||||
|
||||
from frappe.utils import cint, cstr, getdate, nowdate, \
|
||||
get_first_day, get_last_day, split_emails
|
||||
|
||||
from frappe import _, msgprint, throw
|
||||
|
||||
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
|
||||
date_field_map = {
|
||||
"Sales Order": "transaction_date",
|
||||
"Sales Invoice": "posting_date",
|
||||
"Purchase Order": "transaction_date",
|
||||
"Purchase Invoice": "posting_date"
|
||||
}
|
||||
|
||||
def create_recurring_documents():
|
||||
manage_recurring_documents("Sales Order")
|
||||
manage_recurring_documents("Sales Invoice")
|
||||
manage_recurring_documents("Purchase Order")
|
||||
manage_recurring_documents("Purchase Invoice")
|
||||
|
||||
def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
"""
|
||||
Create recurring documents on specific date by copying the original one
|
||||
and notify the concerned people
|
||||
"""
|
||||
next_date = next_date or nowdate()
|
||||
|
||||
date_field = date_field_map[doctype]
|
||||
|
||||
condition = " and ifnull(status, '') != 'Closed'" if doctype in ("Sales Order", "Purchase Order") else ""
|
||||
|
||||
recurring_documents = frappe.db.sql("""select name, recurring_id
|
||||
from `tab{0}` where is_recurring=1
|
||||
and (docstatus=1 or docstatus=0) and next_date=%s
|
||||
and next_date <= ifnull(end_date, '2199-12-31') {1}""".format(doctype, condition), next_date)
|
||||
|
||||
exception_list = []
|
||||
for ref_document, recurring_id in recurring_documents:
|
||||
if not frappe.db.sql("""select name from `tab%s`
|
||||
where %s=%s and recurring_id=%s and (docstatus=1 or docstatus=0)"""
|
||||
% (doctype, date_field, '%s', '%s'), (next_date, recurring_id)):
|
||||
try:
|
||||
reference_doc = frappe.get_doc(doctype, ref_document)
|
||||
new_doc = make_new_document(reference_doc, date_field, next_date)
|
||||
if reference_doc.notify_by_email:
|
||||
send_notification(new_doc)
|
||||
if commit:
|
||||
frappe.db.commit()
|
||||
except:
|
||||
if commit:
|
||||
frappe.db.rollback()
|
||||
|
||||
frappe.db.begin()
|
||||
frappe.db.sql("update `tab%s` \
|
||||
set is_recurring = 0 where name = %s" % (doctype, '%s'),
|
||||
(ref_document))
|
||||
notify_errors(ref_document, doctype, reference_doc.get("customer") or reference_doc.get("supplier"),
|
||||
reference_doc.owner)
|
||||
frappe.db.commit()
|
||||
|
||||
exception_list.append(frappe.get_traceback())
|
||||
finally:
|
||||
if commit:
|
||||
frappe.db.begin()
|
||||
|
||||
if exception_list:
|
||||
exception_message = "\n\n".join([cstr(d) for d in exception_list])
|
||||
frappe.throw(exception_message)
|
||||
|
||||
def make_new_document(reference_doc, date_field, posting_date):
|
||||
new_document = frappe.copy_doc(reference_doc, ignore_no_copy=False)
|
||||
mcount = month_map[reference_doc.recurring_type]
|
||||
|
||||
from_date = get_next_date(reference_doc.from_date, mcount)
|
||||
|
||||
# get last day of the month to maintain period if the from date is first day of its own month
|
||||
# and to date is the last day of its own month
|
||||
if (cstr(get_first_day(reference_doc.from_date)) == cstr(reference_doc.from_date)) and \
|
||||
(cstr(get_last_day(reference_doc.to_date)) == cstr(reference_doc.to_date)):
|
||||
to_date = get_last_day(get_next_date(reference_doc.to_date, mcount))
|
||||
else:
|
||||
to_date = get_next_date(reference_doc.to_date, mcount)
|
||||
|
||||
new_document.update({
|
||||
date_field: posting_date,
|
||||
"from_date": from_date,
|
||||
"to_date": to_date,
|
||||
"next_date": get_next_date(reference_doc.next_date, mcount,cint(reference_doc.repeat_on_day_of_month))
|
||||
})
|
||||
|
||||
if new_document.meta.get_field('set_posting_time'):
|
||||
new_document.set('set_posting_time', 1)
|
||||
|
||||
# copy document fields
|
||||
for fieldname in ("owner", "recurring_type", "repeat_on_day_of_month",
|
||||
"recurring_id", "notification_email_address", "is_recurring", "end_date",
|
||||
"title", "naming_series", "select_print_heading", "ignore_pricing_rule",
|
||||
"posting_time", "remarks", 'submit_on_creation'):
|
||||
if new_document.meta.get_field(fieldname):
|
||||
new_document.set(fieldname, reference_doc.get(fieldname))
|
||||
|
||||
# copy item fields
|
||||
for i, item in enumerate(new_document.items):
|
||||
for fieldname in ("page_break",):
|
||||
item.set(fieldname, reference_doc.items[i].get(fieldname))
|
||||
|
||||
new_document.run_method("on_recurring", reference_doc=reference_doc)
|
||||
|
||||
if reference_doc.submit_on_creation:
|
||||
new_document.insert()
|
||||
new_document.submit()
|
||||
else:
|
||||
new_document.docstatus=0
|
||||
new_document.insert()
|
||||
|
||||
return new_document
|
||||
|
||||
def get_next_date(dt, mcount, day=None):
|
||||
dt = getdate(dt)
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
dt += relativedelta(months=mcount, day=day)
|
||||
|
||||
return dt
|
||||
|
||||
def send_notification(new_rv):
|
||||
"""Notify concerned persons about recurring document generation"""
|
||||
|
||||
frappe.sendmail(new_rv.notification_email_address,
|
||||
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=new_rv.recurring_print_format)])
|
||||
|
||||
def notify_errors(doc, doctype, party, owner):
|
||||
from frappe.utils.user import get_system_managers
|
||||
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),
|
||||
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
|
||||
"type": doctype,
|
||||
"name": doc,
|
||||
"party": party
|
||||
}))
|
||||
|
||||
assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients)
|
||||
|
||||
def assign_task_to_owner(doc, doctype, msg, users):
|
||||
for d in users:
|
||||
from frappe.desk.form import assign_to
|
||||
args = {
|
||||
'assign_to' : d,
|
||||
'doctype' : doctype,
|
||||
'name' : doc,
|
||||
'description' : msg,
|
||||
'priority' : 'High'
|
||||
}
|
||||
assign_to.add(args)
|
||||
|
||||
def validate_recurring_document(doc):
|
||||
if doc.is_recurring:
|
||||
validate_notification_email_id(doc)
|
||||
if not doc.recurring_type:
|
||||
frappe.throw(_("Please select {0}").format(doc.meta.get_label("recurring_type")))
|
||||
|
||||
elif not (doc.from_date and doc.to_date):
|
||||
frappe.throw(_("Period From and Period To dates mandatory for recurring {0}").format(doc.doctype))
|
||||
|
||||
def validate_recurring_next_date(doc):
|
||||
posting_date = doc.get("posting_date") or doc.get("transaction_date")
|
||||
if getdate(posting_date) > getdate(doc.next_date):
|
||||
frappe.throw(_("Next Date must be greater than Posting Date"))
|
||||
|
||||
next_date = getdate(doc.next_date)
|
||||
if next_date.day != doc.repeat_on_day_of_month:
|
||||
|
||||
# if the repeat day is the last day of the month (31)
|
||||
# and the current month does not have as many days,
|
||||
# then the last day of the current month is a valid date
|
||||
lastday = calendar.monthrange(next_date.year, next_date.month)[1]
|
||||
if doc.repeat_on_day_of_month < lastday:
|
||||
|
||||
# the specified day of the month is not same as the day specified
|
||||
# or the last day of the month
|
||||
frappe.throw(_("Next Date's day and Repeat on Day of Month must be equal"))
|
||||
|
||||
def convert_to_recurring(doc, posting_date):
|
||||
if doc.is_recurring:
|
||||
if not doc.recurring_id:
|
||||
doc.db_set("recurring_id", doc.name)
|
||||
|
||||
set_next_date(doc, posting_date)
|
||||
|
||||
if doc.next_date:
|
||||
validate_recurring_next_date(doc)
|
||||
|
||||
elif doc.recurring_id:
|
||||
doc.db_set("recurring_id", None)
|
||||
|
||||
def validate_notification_email_id(doc):
|
||||
if doc.notify_by_email:
|
||||
if doc.notification_email_address:
|
||||
email_list = split_emails(doc.notification_email_address.replace("\n", ""))
|
||||
|
||||
from frappe.utils import validate_email_add
|
||||
for email in email_list:
|
||||
if not validate_email_add(email):
|
||||
throw(_("{0} is an invalid email address in 'Notification \
|
||||
Email Address'").format(email))
|
||||
|
||||
else:
|
||||
frappe.throw(_("'Notification Email Addresses' not specified for recurring %s") \
|
||||
% doc.doctype)
|
||||
|
||||
def set_next_date(doc, posting_date):
|
||||
""" Set next date on which recurring document will be created"""
|
||||
if not doc.repeat_on_day_of_month:
|
||||
msgprint(_("Please enter 'Repeat on Day of Month' field value"), raise_exception=1)
|
||||
|
||||
next_date = get_next_date(posting_date, month_map[doc.recurring_type],
|
||||
cint(doc.repeat_on_day_of_month))
|
||||
|
||||
doc.db_set('next_date', next_date)
|
||||
|
||||
msgprint(_("Next Recurring {0} will be created on {1}").format(doc.doctype, next_date))
|
@ -1,149 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import frappe.permissions
|
||||
from erpnext.controllers.recurring_document import date_field_map
|
||||
from frappe.utils import get_first_day, get_last_day, add_to_date, nowdate, getdate, add_days
|
||||
|
||||
def test_recurring_document(obj, test_records):
|
||||
frappe.db.set_value("Print Settings", "Print Settings", "send_print_as_pdf", 1)
|
||||
today = nowdate()
|
||||
base_doc = frappe.copy_doc(test_records[0])
|
||||
|
||||
base_doc.update({
|
||||
"is_recurring": 1,
|
||||
"submit_on_create": 1,
|
||||
"recurring_type": "Monthly",
|
||||
"notification_email_address": "test@example.com, test1@example.com, test2@example.com",
|
||||
"repeat_on_day_of_month": getdate(today).day,
|
||||
"due_date": None,
|
||||
"from_date": get_first_day(today),
|
||||
"to_date": get_last_day(today)
|
||||
})
|
||||
|
||||
date_field = date_field_map[base_doc.doctype]
|
||||
base_doc.set(date_field, today)
|
||||
|
||||
if base_doc.doctype == "Sales Order":
|
||||
base_doc.set("delivery_date", add_days(today, 15))
|
||||
|
||||
# monthly
|
||||
doc1 = frappe.copy_doc(base_doc)
|
||||
doc1.insert()
|
||||
doc1.submit()
|
||||
_test_recurring_document(obj, doc1, date_field, True)
|
||||
|
||||
# monthly without a first and last day period
|
||||
if getdate(today).day != 1:
|
||||
doc2 = frappe.copy_doc(base_doc)
|
||||
doc2.update({
|
||||
"from_date": today,
|
||||
"to_date": add_to_date(today, days=30)
|
||||
})
|
||||
doc2.insert()
|
||||
doc2.submit()
|
||||
_test_recurring_document(obj, doc2, date_field, False)
|
||||
|
||||
# quarterly
|
||||
doc3 = frappe.copy_doc(base_doc)
|
||||
doc3.update({
|
||||
"recurring_type": "Quarterly",
|
||||
"from_date": get_first_day(today),
|
||||
"to_date": get_last_day(add_to_date(today, months=3))
|
||||
})
|
||||
doc3.insert()
|
||||
doc3.submit()
|
||||
_test_recurring_document(obj, doc3, date_field, True)
|
||||
|
||||
# quarterly without a first and last day period
|
||||
doc4 = frappe.copy_doc(base_doc)
|
||||
doc4.update({
|
||||
"recurring_type": "Quarterly",
|
||||
"from_date": today,
|
||||
"to_date": add_to_date(today, months=3)
|
||||
})
|
||||
doc4.insert()
|
||||
doc4.submit()
|
||||
_test_recurring_document(obj, doc4, date_field, False)
|
||||
|
||||
# yearly
|
||||
doc5 = frappe.copy_doc(base_doc)
|
||||
doc5.update({
|
||||
"recurring_type": "Yearly",
|
||||
"from_date": get_first_day(today),
|
||||
"to_date": get_last_day(add_to_date(today, years=1))
|
||||
})
|
||||
doc5.insert()
|
||||
doc5.submit()
|
||||
_test_recurring_document(obj, doc5, date_field, True)
|
||||
|
||||
# yearly without a first and last day period
|
||||
doc6 = frappe.copy_doc(base_doc)
|
||||
doc6.update({
|
||||
"recurring_type": "Yearly",
|
||||
"from_date": today,
|
||||
"to_date": add_to_date(today, years=1)
|
||||
})
|
||||
doc6.insert()
|
||||
doc6.submit()
|
||||
_test_recurring_document(obj, doc6, date_field, False)
|
||||
|
||||
# change date field but keep recurring day to be today
|
||||
doc7 = frappe.copy_doc(base_doc)
|
||||
doc7.update({
|
||||
date_field: today,
|
||||
})
|
||||
doc7.insert()
|
||||
doc7.submit()
|
||||
|
||||
# setting so that _test function works
|
||||
# doc7.set(date_field, today)
|
||||
_test_recurring_document(obj, doc7, date_field, True)
|
||||
|
||||
def _test_recurring_document(obj, base_doc, date_field, first_and_last_day):
|
||||
from frappe.utils import add_months, get_last_day
|
||||
from erpnext.controllers.recurring_document import manage_recurring_documents, \
|
||||
get_next_date
|
||||
|
||||
no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_doc.recurring_type]
|
||||
|
||||
def _test(i):
|
||||
obj.assertEquals(i+1, frappe.db.sql("""select count(*) from `tab%s`
|
||||
where recurring_id=%s and (docstatus=1 or docstatus=0)""" % (base_doc.doctype, '%s'),
|
||||
(base_doc.recurring_id))[0][0])
|
||||
|
||||
next_date = get_next_date(base_doc.get(date_field), no_of_months,
|
||||
base_doc.repeat_on_day_of_month)
|
||||
|
||||
manage_recurring_documents(base_doc.doctype, next_date=next_date, commit=False)
|
||||
|
||||
recurred_documents = frappe.db.sql("""select name from `tab%s`
|
||||
where recurring_id=%s and (docstatus=1 or docstatus=0) order by name desc"""
|
||||
% (base_doc.doctype, '%s'), (base_doc.recurring_id))
|
||||
|
||||
obj.assertEquals(i+2, len(recurred_documents))
|
||||
|
||||
new_doc = frappe.get_doc(base_doc.doctype, recurred_documents[0][0])
|
||||
|
||||
for fieldname in ["is_recurring", "recurring_type",
|
||||
"repeat_on_day_of_month", "notification_email_address"]:
|
||||
obj.assertEquals(base_doc.get(fieldname),
|
||||
new_doc.get(fieldname))
|
||||
|
||||
obj.assertEquals(new_doc.get(date_field), getdate(next_date))
|
||||
|
||||
obj.assertEquals(new_doc.from_date, getdate(add_months(base_doc.from_date, no_of_months)))
|
||||
|
||||
if first_and_last_day:
|
||||
obj.assertEquals(new_doc.to_date, getdate(get_last_day(add_months(base_doc.to_date, no_of_months))))
|
||||
else:
|
||||
obj.assertEquals(new_doc.to_date, getdate(add_months(base_doc.to_date, no_of_months)))
|
||||
|
||||
return new_doc
|
||||
|
||||
# if yearly, test 1 repetition, else test 5 repetitions
|
||||
count = 1 if (no_of_months == 12) else 5
|
||||
for i in xrange(count):
|
||||
base_doc = _test(i)
|
0
erpnext/docs/assets/img/subscription/__init__.py
Normal file
0
erpnext/docs/assets/img/subscription/__init__.py
Normal file
BIN
erpnext/docs/assets/img/subscription/subscription.gif
Normal file
BIN
erpnext/docs/assets/img/subscription/subscription.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 818 KiB |
BIN
erpnext/docs/assets/img/subscription/subscription.png
Normal file
BIN
erpnext/docs/assets/img/subscription/subscription.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
@ -9,6 +9,7 @@ manufacturing
|
||||
projects
|
||||
support
|
||||
human-resources
|
||||
subscription
|
||||
customer-portal
|
||||
website
|
||||
using-erpnext
|
||||
|
22
erpnext/docs/user/manual/en/subscription/index.md
Normal file
22
erpnext/docs/user/manual/en/subscription/index.md
Normal file
@ -0,0 +1,22 @@
|
||||
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_base_url}}/assets/img/subscription/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_base_url}}/assets/img/subscription/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
|
0
erpnext/docs/user/manual/en/subscription/index.txt
Normal file
0
erpnext/docs/user/manual/en/subscription/index.txt
Normal file
@ -184,7 +184,7 @@ doc_events = {
|
||||
|
||||
scheduler_events = {
|
||||
"hourly": [
|
||||
"erpnext.controllers.recurring_document.create_recurring_documents",
|
||||
"erpnext.subscription.doctype.subscription.subscription.make_subscription_entry",
|
||||
'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.trigger_emails'
|
||||
],
|
||||
"daily": [
|
||||
|
@ -14,4 +14,5 @@ Hub Node
|
||||
Portal
|
||||
Maintenance
|
||||
Schools
|
||||
Regional
|
||||
Regional
|
||||
Subscription
|
@ -437,4 +437,5 @@ erpnext.patches.v8_7.fix_purchase_receipt_status
|
||||
erpnext.patches.v8_6.rename_bom_update_tool
|
||||
erpnext.patches.v8_9.add_setup_progress_actions
|
||||
erpnext.patches.v8_9.rename_company_sales_target_field
|
||||
erpnext.patches.v8_8.set_bom_rate_as_per_uom
|
||||
erpnext.patches.v8_8.set_bom_rate_as_per_uom
|
||||
erpnext.patches.v8_7.make_subscription_from_recurring_data
|
@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import today
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('subscription', 'doctype', 'subscription')
|
||||
frappe.reload_doc('selling', 'doctype', 'sales_order')
|
||||
frappe.reload_doc('buying', 'doctype', 'purchase_order')
|
||||
frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
|
||||
frappe.reload_doc('accounts', 'doctype', 'purchase_invoice')
|
||||
|
||||
for doctype in ['Sales Order', 'Sales Invoice',
|
||||
'Purchase Invoice', 'Purchase Invoice']:
|
||||
for data in get_data(doctype):
|
||||
make_subscription(doctype, data)
|
||||
|
||||
def get_data(doctype):
|
||||
return frappe.db.sql(""" select name, from_date, end_date, recurring_type,recurring_id
|
||||
next_date, notify_by_email, notification_email_address, recurring_print_format,
|
||||
repeat_on_day_of_month, submit_on_creation
|
||||
from `tab{0}` where is_recurring = 1 and next_date >= %s
|
||||
""".format(doctype), today(), as_dict=1)
|
||||
|
||||
def make_subscription(doctype, data):
|
||||
doc = frappe.get_doc({
|
||||
'doctype': 'Subscription',
|
||||
'reference_doctype': doctype,
|
||||
'reference_document': data.name,
|
||||
'start_date': data.from_date,
|
||||
'end_date': data.end_date,
|
||||
'frequency': data.recurring_type,
|
||||
'repeat_on_day': data.repeat_on_day_of_month,
|
||||
'notify_by_email': data.notify_by_email,
|
||||
'recipients': data.notification_email_address,
|
||||
'next_schedule_date': data.next_date,
|
||||
'submit_on_creation': data.submit_on_creation
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
doc.submit()
|
||||
|
||||
if not doc.subscription:
|
||||
frappe.db.set_value(doctype, data.name, "subscription", doc.name)
|
@ -127,6 +127,20 @@ $.extend(erpnext.utils, {
|
||||
}
|
||||
},
|
||||
|
||||
make_subscription: function(doctype, docname) {
|
||||
frappe.call({
|
||||
method: "erpnext.subscription.doctype.subscription.subscription.make_subscription",
|
||||
args: {
|
||||
doctype: doctype,
|
||||
docname: docname
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the first row of a given child table is empty
|
||||
* @param child_table - Child table Doctype
|
||||
|
@ -10,12 +10,15 @@ frappe.ui.form.on('Quotation', {
|
||||
'Sales Order': 'Make Sales Order'
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.trigger("set_label");
|
||||
},
|
||||
|
||||
quotation_to: function(frm) {
|
||||
frm.trigger("set_label");
|
||||
},
|
||||
|
||||
set_label: function(frm) {
|
||||
frm.fields_dict.customer_address.set_label(__(frm.doc.quotation_to + " Address"));
|
||||
}
|
||||
@ -44,14 +47,22 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
||||
|
||||
if(doc.docstatus == 1 && doc.status!=='Lost') {
|
||||
if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) > 0) {
|
||||
cur_frm.add_custom_button(__('Make Sales Order'),
|
||||
cur_frm.cscript['Make Sales Order']);
|
||||
cur_frm.add_custom_button(__('Sales Order'),
|
||||
cur_frm.cscript['Make Sales Order'], __("Make"));
|
||||
}
|
||||
|
||||
if(doc.status!=="Ordered") {
|
||||
cur_frm.add_custom_button(__('Set as Lost'),
|
||||
cur_frm.cscript['Declare Order Lost']);
|
||||
}
|
||||
|
||||
if(!doc.subscription) {
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(doc.doctype, doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
}
|
||||
|
||||
if (this.frm.doc.docstatus===0) {
|
||||
|
@ -2337,6 +2337,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -2633,7 +2694,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 1,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-08-09 06:35:48.691648",
|
||||
"modified": "2017-08-31 11:22:15.268846",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
|
@ -108,6 +108,9 @@ class Quotation(SellingController):
|
||||
print_lst.append(lst1)
|
||||
return print_lst
|
||||
|
||||
def on_recurring(self, reference_doc, subscription_doc):
|
||||
self.valid_till = None
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
list_context = get_list_context(context)
|
||||
|
@ -3,9 +3,17 @@ from frappe import _
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'prevdoc_docname',
|
||||
'non_standard_fieldnames': {
|
||||
'Subscription': 'reference_document',
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Sales Order'),
|
||||
'items': ['Sales Order']
|
||||
},
|
||||
{
|
||||
'label': _('Subscription'),
|
||||
'items': ['Subscription']
|
||||
},
|
||||
]
|
||||
}
|
@ -50,4 +50,4 @@ QUnit.test("test: quotation", function (assert) {
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -141,6 +141,12 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
function() { me.make_project() }, __("Make"));
|
||||
}
|
||||
|
||||
if(!doc.subscription) {
|
||||
this.frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(doc.doctype, doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
|
||||
} else {
|
||||
if (this.frm.has_perm("submit")) {
|
||||
// un-close
|
||||
|
@ -3179,6 +3179,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Subscription",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -3189,7 +3250,7 @@
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_order",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
@ -3659,7 +3720,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-07 21:27:10.073581",
|
||||
"modified": "2017-08-31 11:21:36.332326",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
@ -11,9 +11,9 @@ from frappe.model.utils import get_fetch_values
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
|
||||
from frappe.desk.notifications import clear_doctype_notifications
|
||||
from erpnext.controllers.recurring_document import month_map, get_next_date
|
||||
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
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@ -346,17 +346,17 @@ class SalesOrder(SellingController):
|
||||
|
||||
return items
|
||||
|
||||
def on_recurring(self, reference_doc):
|
||||
mcount = month_map[reference_doc.recurring_type]
|
||||
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,
|
||||
cint(reference_doc.repeat_on_day_of_month)))
|
||||
cint(subscription_doc.repeat_on_day)))
|
||||
|
||||
for d in self.get("items"):
|
||||
reference_delivery_date = frappe.db.get_value("Sales Order Item",
|
||||
{"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(reference_doc.repeat_on_day_of_month)))
|
||||
get_next_date(reference_delivery_date, mcount, cint(subscription_doc.repeat_on_day)))
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
|
@ -7,7 +7,8 @@ def get_data():
|
||||
'Delivery Note': 'against_sales_order',
|
||||
'Journal Entry': 'reference_name',
|
||||
'Payment Entry': 'reference_name',
|
||||
'Payment Request': 'reference_name'
|
||||
'Payment Request': 'reference_name',
|
||||
'Subscription': 'reference_document',
|
||||
},
|
||||
'internal_links': {
|
||||
'Quotation': ['items', 'prevdoc_docname']
|
||||
@ -31,7 +32,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Reference'),
|
||||
'items': ['Quotation']
|
||||
'items': ['Quotation', 'Subscription']
|
||||
},
|
||||
{
|
||||
'label': _('Payment'),
|
||||
|
23
erpnext/selling/doctype/sales_order/test_sales_order.js
Normal file
23
erpnext/selling/doctype/sales_order/test_sales_order.js
Normal file
@ -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: Sales Order", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially('Sales Order', [
|
||||
// insert a new Sales Order
|
||||
() => frappe.tests.make([
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -166,6 +166,12 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
||||
__("Status"))
|
||||
}
|
||||
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);
|
||||
|
||||
if(doc.docstatus==1 && !doc.subscription) {
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(doc.doctype, doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
},
|
||||
|
||||
make_sales_invoice: function() {
|
||||
|
@ -3251,6 +3251,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -3487,7 +3548,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-08-23 13:25:34.182268",
|
||||
"modified": "2017-08-31 11:21:59.084183",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
@ -5,7 +5,8 @@ def get_data():
|
||||
'fieldname': 'delivery_note',
|
||||
'non_standard_fieldnames': {
|
||||
'Stock Entry': 'delivery_note_no',
|
||||
'Quality Inspection': 'reference_name'
|
||||
'Quality Inspection': 'reference_name',
|
||||
'Subscription': 'reference_document',
|
||||
},
|
||||
'internal_links': {
|
||||
'Sales Order': ['items', 'against_sales_order'],
|
||||
@ -23,5 +24,9 @@ def get_data():
|
||||
'label': _('Returns'),
|
||||
'items': ['Stock Entry']
|
||||
},
|
||||
{
|
||||
'label': _('Subscription'),
|
||||
'items': ['Subscription']
|
||||
},
|
||||
]
|
||||
}
|
@ -34,4 +34,3 @@ QUnit.test("test delivery note", function(assert) {
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
|
@ -86,7 +86,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Type",
|
||||
"length": 0,
|
||||
@ -144,7 +144,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": "Series",
|
||||
"length": 0,
|
||||
@ -211,7 +211,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
@ -369,7 +369,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": "Transaction Date",
|
||||
"length": 0,
|
||||
@ -686,7 +686,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-06-13 14:29:18.032657",
|
||||
"modified": "2017-07-26 19:43:31.823549",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request",
|
||||
|
@ -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: Material Request", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially('Material Request', [
|
||||
// insert a new Material Request
|
||||
() => frappe.tests.make([
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -48,7 +48,7 @@ frappe.ui.form.on("Purchase Receipt", {
|
||||
toggle_display_account_head: function(frm) {
|
||||
var enabled = erpnext.is_perpetual_inventory_enabled(frm.doc.company)
|
||||
frm.fields_dict["items"].grid.set_column_disp(["cost_center"], enabled);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
|
||||
@ -98,6 +98,13 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
||||
if(flt(this.frm.doc.per_billed) < 100) {
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice, __("Make"));
|
||||
}
|
||||
|
||||
if(!this.frm.doc.subscription) {
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(me.frm.doc.doctype, me.frm.doc.name)
|
||||
}, __("Make"))
|
||||
}
|
||||
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
}
|
||||
}
|
||||
|
@ -2611,6 +2611,67 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription_detail",
|
||||
"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 Detail",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
|
@ -5,7 +5,8 @@ def get_data():
|
||||
'fieldname': 'purchase_receipt_no',
|
||||
'non_standard_fieldnames': {
|
||||
'Purchase Invoice': 'purchase_receipt',
|
||||
'Landed Cost Voucher': 'receipt_document'
|
||||
'Landed Cost Voucher': 'receipt_document',
|
||||
'Subscription': 'reference_document'
|
||||
},
|
||||
'internal_links': {
|
||||
'Purchase Order': ['items', 'purchase_order'],
|
||||
@ -25,5 +26,9 @@ def get_data():
|
||||
'label': _('Returns'),
|
||||
'items': ['Stock Entry']
|
||||
},
|
||||
{
|
||||
'label': _('Subscription'),
|
||||
'items': ['Subscription']
|
||||
},
|
||||
]
|
||||
}
|
0
erpnext/subscription/__init__.py
Normal file
0
erpnext/subscription/__init__.py
Normal file
0
erpnext/subscription/doctype/__init__.py
Normal file
0
erpnext/subscription/doctype/__init__.py
Normal file
36
erpnext/subscription/doctype/subscription/subscription.js
Normal file
36
erpnext/subscription/doctype/subscription/subscription.js
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Subscription', {
|
||||
setup: function(frm) {
|
||||
frm.fields_dict['reference_document'].get_query = function() {
|
||||
return {
|
||||
filters: {
|
||||
"docstatus": 1
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
frm.fields_dict['print_format'].get_query = function() {
|
||||
return {
|
||||
filters: {
|
||||
"doc_type": frm.doc.reference_doctype
|
||||
}
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.docstatus == 1) {
|
||||
let label = __('View {0}', [frm.doc.reference_doctype]);
|
||||
frm.add_custom_button(__(label),
|
||||
function() {
|
||||
frappe.route_options = {
|
||||
"subscription": frm.doc.name,
|
||||
};
|
||||
frappe.set_route("List", frm.doc.reference_doctype);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
731
erpnext/subscription/doctype/subscription/subscription.json
Normal file
731
erpnext/subscription/doctype/subscription/subscription.json
Normal file
@ -0,0 +1,731 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "naming_series:",
|
||||
"beta": 0,
|
||||
"creation": "2017-07-18 17:50:43.967266",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_1",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "naming_series",
|
||||
"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": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "SUB-",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"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": "Reference Doctype",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_document",
|
||||
"fieldtype": "Dynamic 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": "Reference Document",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "reference_doctype",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "disabled",
|
||||
"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": "Disabled",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "submit_on_creation",
|
||||
"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": "Submit on Creation",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "start_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "Start Date",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "end_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "End Date",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "next_schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "Next Schedule Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "frequency_detail",
|
||||
"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": "",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "frequency",
|
||||
"fieldtype": "Select",
|
||||
"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": "Frequency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_12",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval: in_list([\"Monthly\", \"Quarterly\", \"Yearly\"], doc.frequency)",
|
||||
"fieldname": "repeat_on_day",
|
||||
"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": "Repeat on Day",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "notification",
|
||||
"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": "Notification",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "notify_by_email",
|
||||
"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": "Notify by Email",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_17",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "notify_by_email",
|
||||
"fieldname": "recipients",
|
||||
"fieldtype": "Small Text",
|
||||
"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": "Recipients",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "notify_by_email",
|
||||
"fieldname": "print_format",
|
||||
"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": "Print Format",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_16",
|
||||
"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": "",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Draft",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nDraft\nSubmitted\nCancelled\nCompleted",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"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": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-29 15:45:16.157643",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subscription",
|
||||
"name": "Subscription",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "reference_document",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "reference_document",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
207
erpnext/subscription/doctype/subscription/subscription.py
Normal file
207
erpnext/subscription/doctype/subscription/subscription.py
Normal file
@ -0,0 +1,207 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import calendar
|
||||
from frappe import _
|
||||
from frappe.desk.form import assign_to
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from frappe.utils.user import get_system_managers
|
||||
from frappe.utils import cstr, getdate, split_emails, add_days, today
|
||||
from frappe.model.document import Document
|
||||
|
||||
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
|
||||
class Subscription(Document):
|
||||
def validate(self):
|
||||
self.update_status()
|
||||
self.validate_dates()
|
||||
self.validate_next_schedule_date()
|
||||
self.validate_email_id()
|
||||
|
||||
def before_submit(self):
|
||||
self.set_next_schedule_date()
|
||||
|
||||
def on_submit(self):
|
||||
self.update_subscription_id()
|
||||
|
||||
def on_update_after_submit(self):
|
||||
self.validate_dates()
|
||||
self.set_next_schedule_date()
|
||||
|
||||
def validate_dates(self):
|
||||
if self.end_date and getdate(self.start_date) > getdate(self.end_date):
|
||||
frappe.throw(_("End date must be greater than start date"))
|
||||
|
||||
def validate_next_schedule_date(self):
|
||||
if self.repeat_on_day and self.next_schedule_date:
|
||||
next_date = getdate(self.next_schedule_date)
|
||||
if next_date.day != self.repeat_on_day:
|
||||
# if the repeat day is the last day of the month (31)
|
||||
# and the current month does not have as many days,
|
||||
# then the last day of the current month is a valid date
|
||||
lastday = calendar.monthrange(next_date.year, next_date.month)[1]
|
||||
if self.repeat_on_day < lastday:
|
||||
|
||||
# the specified day of the month is not same as the day specified
|
||||
# or the last day of the month
|
||||
frappe.throw(_("Next Date's day and Repeat on Day of Month must be equal"))
|
||||
|
||||
def validate_email_id(self):
|
||||
if self.notify_by_email:
|
||||
if self.recipients:
|
||||
email_list = split_emails(self.recipients.replace("\n", ""))
|
||||
|
||||
from frappe.utils import validate_email_add
|
||||
for email in email_list:
|
||||
if not validate_email_add(email):
|
||||
frappe.throw(_("{0} is an invalid email address in 'Recipients'").format(email))
|
||||
else:
|
||||
frappe.throw(_("'Recipients' not specified"))
|
||||
|
||||
def set_next_schedule_date(self):
|
||||
self.next_schedule_date = get_next_schedule_date(self.start_date,
|
||||
self.frequency, self.repeat_on_day)
|
||||
|
||||
def update_subscription_id(self):
|
||||
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
|
||||
if not doc.meta.get_field('subscription'):
|
||||
frappe.throw(_("Add custom field Subscription Id in the doctype {0}").format(self.reference_doctype))
|
||||
|
||||
doc.db_set('subscription', self.name)
|
||||
|
||||
def update_status(self):
|
||||
self.status = {
|
||||
'0': 'Draft',
|
||||
'1': 'Submitted',
|
||||
'2': 'Cancelled'
|
||||
}[cstr(self.docstatus or 0)]
|
||||
|
||||
def get_next_schedule_date(start_date, frequency, repeat_on_day):
|
||||
mcount = month_map.get(frequency)
|
||||
if mcount:
|
||||
next_date = get_next_date(start_date, mcount, repeat_on_day)
|
||||
else:
|
||||
days = 7 if frequency == 'Weekly' else 1
|
||||
next_date = add_days(start_date, days)
|
||||
return next_date
|
||||
|
||||
def make_subscription_entry(date=None):
|
||||
date = date or today()
|
||||
for data in get_subscription_entries(date):
|
||||
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:
|
||||
frappe.db.set_value('Subscription', data.name, 'next_schedule_date', schedule_date)
|
||||
|
||||
def get_subscription_entries(date):
|
||||
return frappe.db.sql(""" select * from `tabSubscription`
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
frappe.db.rollback()
|
||||
frappe.db.begin()
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.db.commit()
|
||||
if data.reference_document and not frappe.flags.in_test:
|
||||
notify_error_to_user(data)
|
||||
|
||||
def notify_error_to_user(data):
|
||||
party = ''
|
||||
party_type = ''
|
||||
|
||||
if data.reference_doctype in ['Sales Order', 'Sales Invoice', 'Delivery Note']:
|
||||
party_type = 'customer'
|
||||
elif data.reference_doctype in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']:
|
||||
party_type = 'supplier'
|
||||
|
||||
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)
|
||||
|
||||
def make_new_document(args, schedule_date):
|
||||
doc = frappe.get_doc(args.reference_doctype, args.reference_document)
|
||||
new_doc = frappe.copy_doc(doc, ignore_no_copy=False)
|
||||
update_doc(new_doc, doc , args, schedule_date)
|
||||
new_doc.insert(ignore_permissions=True)
|
||||
|
||||
if args.submit_on_creation:
|
||||
new_doc.submit()
|
||||
|
||||
return new_doc
|
||||
|
||||
def update_doc(new_document, reference_doc, args, schedule_date):
|
||||
new_document.docstatus = 0
|
||||
if new_document.meta.get_field('set_posting_time'):
|
||||
new_document.set('set_posting_time', 1)
|
||||
|
||||
if new_document.meta.get_field('subscription'):
|
||||
new_document.set('subscription', args.name)
|
||||
|
||||
new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args)
|
||||
for data in new_document.meta.fields:
|
||||
if data.fieldtype == 'Date' and data.reqd:
|
||||
new_document.set(data.fieldname, schedule_date)
|
||||
|
||||
def get_next_date(dt, mcount, day=None):
|
||||
dt = getdate(dt)
|
||||
dt += relativedelta(months=mcount, day=day)
|
||||
|
||||
return dt
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
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),
|
||||
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
|
||||
"type": doctype,
|
||||
"name": doc,
|
||||
"party": party or ""
|
||||
}))
|
||||
|
||||
assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients)
|
||||
|
||||
def assign_task_to_owner(doc, doctype, msg, users):
|
||||
for d in users:
|
||||
args = {
|
||||
'assign_to' : d,
|
||||
'doctype' : doctype,
|
||||
'name' : doc,
|
||||
'description' : msg,
|
||||
'priority' : 'High'
|
||||
}
|
||||
assign_to.add(args)
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_subscription(doctype, docname):
|
||||
doc = frappe.new_doc('Subscription')
|
||||
doc.reference_doctype = doctype
|
||||
doc.reference_document = docname
|
||||
return doc
|
@ -0,0 +1,12 @@
|
||||
frappe.listview_settings['Subscription'] = {
|
||||
add_fields: ["next_schedule_date"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.next_schedule_date >= frappe.datetime.get_today() ) {
|
||||
return [__("Active"), "green"];
|
||||
} else if(doc.docstatus === 0) {
|
||||
return [__("Draft"), "red", "docstatus,=,0"];
|
||||
} else {
|
||||
return [__("Expired"), "darkgrey"];
|
||||
}
|
||||
}
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Subscription", function (assert) {
|
||||
assert.expect(4);
|
||||
let done = assert.async();
|
||||
frappe.run_serially([
|
||||
// insert a new Subscription
|
||||
() => {
|
||||
return frappe.tests.make("Subscription", [
|
||||
{reference_doctype: 'Sales Invoice'},
|
||||
{reference_document: 'SINV-00004'},
|
||||
{start_date: frappe.datetime.month_start()},
|
||||
{end_date: frappe.datetime.month_end()},
|
||||
{frequency: 'Weekly'}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.savesubmit(),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('Yes'),
|
||||
() => frappe.timeout(2),
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.frequency.includes("Weekly"), "Set frequency Weekly");
|
||||
assert.ok(cur_frm.doc.reference_doctype.includes("Sales Invoice"), "Set base doctype Sales Invoice");
|
||||
assert.equal(cur_frm.doc.docstatus, 1, "Submitted subscription");
|
||||
assert.equal(cur_frm.doc.next_schedule_date,
|
||||
frappe.datetime.add_days(frappe.datetime.get_today(), 7), "Set schedule date");
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
});
|
@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils import today, add_days, getdate
|
||||
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
|
||||
|
||||
class TestSubscription(unittest.TestCase):
|
||||
def test_daily_subscription(self):
|
||||
qo = frappe.copy_doc(quotation_records[0])
|
||||
qo.submit()
|
||||
|
||||
doc = make_subscription(reference_document=qo.name)
|
||||
self.assertEquals(doc.next_schedule_date, today())
|
||||
make_subscription_entry()
|
||||
frappe.db.commit()
|
||||
|
||||
quotation = frappe.get_doc(doc.reference_doctype, doc.reference_document)
|
||||
self.assertEquals(quotation.subscription, doc.name)
|
||||
|
||||
new_quotation = frappe.db.get_value('Quotation',
|
||||
{'subscription': doc.name, 'name': ('!=', quotation.name)}, 'name')
|
||||
|
||||
new_quotation = frappe.get_doc('Quotation', new_quotation)
|
||||
|
||||
for fieldname in ['customer', 'company', 'order_type', 'total', 'grand_total']:
|
||||
self.assertEquals(quotation.get(fieldname), new_quotation.get(fieldname))
|
||||
|
||||
for fieldname in ['item_code', 'qty', 'rate', 'amount']:
|
||||
self.assertEquals(quotation.items[0].get(fieldname),
|
||||
new_quotation.items[0].get(fieldname))
|
||||
|
||||
def test_monthly_subscription_for_so(self):
|
||||
current_fiscal_year = get_fiscal_year(today(), as_dict=True)
|
||||
start_date = current_fiscal_year.year_start_date
|
||||
end_date = current_fiscal_year.year_end_date
|
||||
|
||||
for doctype in ['Sales Order', 'Sales Invoice']:
|
||||
if doctype == 'Sales Invoice':
|
||||
docname = create_sales_invoice(posting_date=start_date)
|
||||
else:
|
||||
docname = make_sales_order()
|
||||
|
||||
self.monthly_subscription(doctype, docname.name, start_date, end_date)
|
||||
|
||||
def monthly_subscription(self, doctype, docname, start_date, end_date):
|
||||
doc = make_subscription(reference_doctype=doctype, frequency = 'Monthly',
|
||||
reference_document = docname, start_date=start_date, end_date=end_date)
|
||||
|
||||
doc.disabled = 1
|
||||
doc.save()
|
||||
frappe.db.commit()
|
||||
|
||||
make_subscription_entry()
|
||||
docnames = frappe.get_all(doc.reference_doctype, {'subscription': doc.name})
|
||||
self.assertEquals(len(docnames), 1)
|
||||
|
||||
doc = frappe.get_doc('Subscription', doc.name)
|
||||
doc.disabled = 0
|
||||
doc.save()
|
||||
|
||||
months = get_months(getdate(start_date), getdate(today()))
|
||||
make_subscription_entry()
|
||||
|
||||
docnames = frappe.get_all(doc.reference_doctype, {'subscription': doc.name})
|
||||
self.assertEquals(len(docnames), months)
|
||||
|
||||
quotation_records = frappe.get_test_records('Quotation')
|
||||
|
||||
def make_subscription(**args):
|
||||
args = frappe._dict(args)
|
||||
doc = frappe.get_doc({
|
||||
'doctype': 'Subscription',
|
||||
'reference_doctype': args.reference_doctype or 'Quotation',
|
||||
'reference_document': args.reference_document or \
|
||||
frappe.db.get_value('Quotation', {'docstatus': 1}, 'name'),
|
||||
'frequency': args.frequency or 'Daily',
|
||||
'start_date': args.start_date or add_days(today(), -1),
|
||||
'end_date': args.end_date or add_days(today(), 1),
|
||||
'submit_on_creation': args.submit_on_creation or 0
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
if not args.do_not_submit:
|
||||
doc.submit()
|
||||
|
||||
return doc
|
Loading…
Reference in New Issue
Block a user