Merge branch 'develop' into membership-enhancements-2
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 38d8a62..5a5c448 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -132,16 +132,10 @@
return caller
-def get_last_membership():
+def get_last_membership(member):
'''Returns last membership if exists'''
last_membership = frappe.get_all('Membership', 'name,to_date,membership_type',
- dict(member=frappe.session.user, paid=1), order_by='to_date desc', limit=1)
+ dict(member=member, paid=1), order_by='to_date desc', limit=1)
- return last_membership and last_membership[0]
-
-def is_member():
- '''Returns true if the user is still a member'''
- last_membership = get_last_membership()
- if last_membership and getdate(last_membership.to_date) > getdate():
- return True
- return False
+ if last_membership:
+ return last_membership[0]
diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json
index 992ef16..f190cfa 100644
--- a/erpnext/non_profit/doctype/member/member.json
+++ b/erpnext/non_profit/doctype/member/member.json
@@ -12,7 +12,6 @@
"membership_expiry_date",
"column_break_5",
"membership_type",
- "email",
"email_id",
"image",
"customer_section",
@@ -65,13 +64,6 @@
"reqd": 1
},
{
- "fieldname": "email",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "User",
- "options": "User"
- },
- {
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
@@ -178,7 +170,7 @@
],
"image_field": "image",
"links": [],
- "modified": "2020-09-16 23:44:13.596948",
+ "modified": "2020-11-09 12:12:10.174647",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Member",
diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py
index 25d6b53..f40f278 100644
--- a/erpnext/non_profit/doctype/member/member.py
+++ b/erpnext/non_profit/doctype/member/member.py
@@ -18,8 +18,6 @@
def validate(self):
- if self.email:
- self.validate_email_type(self.email)
if self.email_id:
self.validate_email_type(self.email_id)
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index 7d15aba..853d7f5 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -24,7 +24,7 @@
user = frappe.get_doc('User', frappe.session.user)
member = frappe.get_doc(dict(
doctype='Member',
- email=frappe.session.user,
+ email_id=frappe.session.user,
membership_type=self.membership_type,
member_name=user.get_fullname()
)).insert(ignore_permissions=True)
@@ -34,7 +34,7 @@
self.member = member_name
# get last membership (if active)
- last_membership = erpnext.get_last_membership()
+ last_membership = erpnext.get_last_membership(self.member)
# if person applied for offline membership
if last_membership and not frappe.session.user == "Administrator":
@@ -54,9 +54,14 @@
self.to_date = add_months(self.from_date, 1)
def on_payment_authorized(self, status_changed_to=None):
- if status_changed_to in ("Completed", "Authorized"):
- self.load_from_db()
- self.db_set('paid', 1)
+ if status_changed_to not in ("Completed", "Authorized"):
+ return
+ self.load_from_db()
+ self.db_set('paid', 1)
+ settings = frappe.get_doc("Membership Settings")
+ if settings.enable_invoicing and settings.create_for_web_forms:
+ self.generate_invoice(with_payment_entry=settings.make_payment_entry, save=True)
+
def generate_invoice(self, save=True):
if not (self.paid or self.currency or self.amount):
@@ -81,6 +86,20 @@
invoice = make_invoice(self, member, plan, settings)
self.invoice = invoice.name
+ if with_payment_entry:
+ if not settings.payment_account:
+ frappe.throw(_("You need to set <b>Payment Account</b> in Membership Settings"))
+
+ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+ frappe.flags.ignore_account_permission=True
+ pe = get_payment_entry(dt='Sales Invoice', dn=invoice.name, bank_amount=invoice.grand_total)
+ frappe.flags.ignore_account_permission=False
+ pe.paid_to = settings.payment_account
+ pe.reference_no = self.name
+ pe.reference_date = getdate()
+ pe.save(ignore_permissions=True)
+ pe.submit()
+
if save:
self.save()
@@ -92,8 +111,12 @@
frappe.throw(_("You need to enable <b>Send Acknowledge Email</b> in Membership Settings"))
member = frappe.get_doc("Member", self.member)
+
+ if not member.email_id:
+ frappe.throw(_("Email address of member {0} is missing").format(frappe.utils.get_link_to_form("Member", self.member)))
+
plan = frappe.get_doc("Membership Type", self.membership_type)
- email = member.email_id if member.email_id else member.email
+ email = member.email_id
attachments = [frappe.attach_print("Membership", self.name, print_format=settings.membership_print_format)]
if self.invoice and settings.send_invoice:
@@ -153,6 +176,8 @@
return None
def verify_signature(data):
+ if frappe.flags.in_test:
+ return True
signature = frappe.request.headers.get('X-Razorpay-Signature')
settings = frappe.get_doc("Membership Settings")
diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py
index b23f406..ce31b91 100644
--- a/erpnext/non_profit/doctype/membership/test_membership.py
+++ b/erpnext/non_profit/doctype/membership/test_membership.py
@@ -2,8 +2,94 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-
import unittest
+import frappe
+import erpnext
+from erpnext.non_profit.doctype.member.member import create_member
+from frappe.utils import nowdate, getdate, add_months
+from erpnext.stock.doctype.item.test_item import create_item
class TestMembership(unittest.TestCase):
- pass
+ def setUp(self):
+ # Get default company
+ company = frappe.get_doc("Company", erpnext.get_default_company())
+
+ # update membership settings
+ settings = frappe.get_doc("Membership Settings")
+ # Enable razorpay
+ settings.enable_razorpay = 1
+ settings.billing_cycle = "Monthly"
+ settings.billing_frequency = 24
+ # Enable invoicing
+ settings.enable_invoicing = 1
+ settings.make_payment_entry = 1
+ settings.company = company.name
+ settings.payment_account = company.default_cash_account
+ settings.debit_account = company.default_receivable_account
+ settings.save()
+
+ # make test plan
+ plan = frappe.new_doc("Membership Type")
+ plan.membership_type = "_rzpy_test_milythm"
+ plan.amount = 100
+ plan.razorpay_plan_id = "_rzpy_test_milythm"
+ plan.linked_item = create_item("_Test Item for Non Profit Membership").name
+ plan.insert()
+
+ # make test member
+ self.member_doc = create_member(frappe._dict({
+ 'fullname': "_Test_Member",
+ 'email': "_test_member_erpnext@example.com",
+ 'plan_id': plan.name
+ }))
+ self.member_doc.make_customer_and_link()
+ self.member = "self.member_doc.name"
+
+ def test_auto_generate_invoice_and_payment_entry(self):
+ entry = make_membership(self.member)
+
+ # Naive test to see if at all invoice was generated and attached to member
+ # In any case if details were missing, the invoicing would throw an error
+ invoice = entry.generate_invoice(save=True)
+ self.assertEqual(invoice.name, entry.invoice)
+
+ def test_renew_within_30_days(self):
+ # create a membership for two months
+ # Should work fine
+ make_membership(self.member, { "from_date": nowdate() })
+ make_membership(self.member, { "from_date": add_months(nowdate(), 1) })
+
+ from frappe.utils.user import add_role
+ add_role("test@example.com", "Non Profit Manager")
+ frappe.set_user("test@example.com")
+
+ # create next membership with expiry not within 30 days
+ self.assertRaises(frappe.ValidationError, make_membership, self.member, {
+ "from_date": add_months(nowdate(), 2),
+ })
+
+ frappe.set_user("Administrator")
+ # create the same membership but as administrator
+ new_entry = make_membership(self.member, {
+ "from_date": add_months(nowdate(), 2),
+ "to_date": add_months(nowdate(), 3),
+ })
+
+def set_config(key, value):
+ frappe.db.set_value("Membership Settings", None, key, value)
+
+def make_membership(member, payload={}):
+ data = {
+ "doctype": "Membership",
+ "member": member,
+ "membership_status": "Current",
+ "membership_type": "_rzpy_test_milythm",
+ "currency": "INR",
+ "paid": 1,
+ "from_date": nowdate(),
+ "amount": 100
+ }
+ data.update(payload)
+ membership = frappe.get_doc(data)
+ membership.insert(ignore_permissions=True, ignore_if_duplicate=True)
+ return membership
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json
index 5b6bab5..961a9b9 100644
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json
+++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json
@@ -11,9 +11,12 @@
"billing_frequency",
"webhook_secret",
"column_break_6",
- "enable_auto_invoicing",
+ "enable_invoicing",
+ "create_for_web_forms",
+ "make_payment_entry",
"company",
"debit_account",
+ "payment_account",
"column_break_9",
"send_email",
"send_invoice",
@@ -58,14 +61,7 @@
"label": "Invoicing"
},
{
- "default": "0",
- "fieldname": "enable_auto_invoicing",
- "fieldtype": "Check",
- "label": "Enable Auto Invoicing",
- "mandatory_depends_on": "eval:doc.send_invoice"
- },
- {
- "depends_on": "eval:doc.enable_auto_invoicing",
+ "depends_on": "eval:doc.enable_invoicing",
"fieldname": "debit_account",
"fieldtype": "Link",
"label": "Debit Account",
@@ -77,7 +73,7 @@
"fieldtype": "Column Break"
},
{
- "depends_on": "eval:doc.enable_auto_invoicing",
+ "depends_on": "eval:doc.enable_invoicing",
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
@@ -86,7 +82,7 @@
},
{
"default": "0",
- "depends_on": "eval:doc.enable_auto_invoicing && doc.send_email",
+ "depends_on": "eval:doc.enable_invoicing && doc.send_email",
"fieldname": "send_invoice",
"fieldtype": "Check",
"label": "Send Invoice with Email"
@@ -119,11 +115,42 @@
"label": "Email Template",
"mandatory_depends_on": "eval:doc.send_email",
"options": "Email Template"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_invoicing",
+ "fieldtype": "Check",
+ "label": "Enable Invoicing",
+ "mandatory_depends_on": "eval:doc.send_invoice || doc.make_payment_entry"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.enable_invoicing",
+ "fieldname": "make_payment_entry",
+ "fieldtype": "Check",
+ "label": "Make Payment Entry"
+ },
+ {
+ "depends_on": "eval:doc.make_payment_entry",
+ "fieldname": "payment_account",
+ "fieldtype": "Link",
+ "label": "Payment To",
+ "mandatory_depends_on": "eval:doc.make_payment_entry",
+ "options": "Account"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.enable_invoicing",
+ "description": "Automatically create an invoice when payment is authorized from a web form entry",
+ "fieldname": "create_for_web_forms",
+ "fieldtype": "Check",
+ "label": "Auto Create Invoice for Web Forms"
}
],
+ "index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2020-08-05 17:26:37.287395",
+ "modified": "2020-11-09 12:28:49.972434",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Membership Settings",
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.js b/erpnext/non_profit/doctype/membership_type/membership_type.js
index 43311a2..94ccdd8 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.js
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.js
@@ -2,12 +2,12 @@
// For license information, please see license.txt
frappe.ui.form.on('Membership Type', {
- refresh: function(frm) {
+ refresh: function (frm) {
frappe.db.get_single_value("Membership Settings", "enable_razorpay").then(val => {
if (val) frm.set_df_property('razorpay_plan_id', 'hidden', false);
});
- frappe.db.get_single_value("Membership Settings", "enable_auto_invoicing").then(val => {
+ frappe.db.get_single_value("Membership Settings", "enable_invoicing").then(val => {
if (val) frm.set_df_property('linked_item', 'hidden', false);
});
}
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 7b2428e..62e9b5c 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -736,8 +736,9 @@
erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02
erpnext.patches.v13_0.updates_for_multi_currency_payroll
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
-erpnext.patches.v13_0.update_custom_fields_for_shopify
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
+erpnext.patches.v13_0.update_member_email_address
+erpnext.patches.v13_0.update_custom_fields_for_shopify
erpnext.patches.v13_0.updates_for_multi_currency_payroll
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
erpnext.patches.v13_0.add_po_to_global_search
diff --git a/erpnext/patches/v13_0/update_member_email_address.py b/erpnext/patches/v13_0/update_member_email_address.py
new file mode 100644
index 0000000..da7828a
--- /dev/null
+++ b/erpnext/patches/v13_0/update_member_email_address.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ """add value to email_id column from email"""
+
+ if frappe.db.has_column("Member", "email"):
+ # Get all members
+ for member in frappe.db.get_all("Member", pluck="name"):
+ # Check if email_id already exists
+ if not frappe.db.get_value("Member", member, "email_id"):
+ # fetch email id from the user linked field email
+ email = frappe.db.get_value("Member", member, "email")
+
+ # Set the value for it
+ frappe.db.set_value("Member", member, "email_id", email)