style: bulk format code with black

v13 port because otherwise backports will result in conflicts always
This commit is contained in:
Ankush Menat
2022-03-29 17:29:34 +05:30
parent 7cc84dcbb4
commit c07713b860
1555 changed files with 96709 additions and 66138 deletions

View File

@@ -8,22 +8,21 @@ from frappe.website.website_generator import WebsiteGenerator
class Chapter(WebsiteGenerator):
_website = frappe._dict(
condition_field = "published",
condition_field="published",
)
def get_context(self, context):
context.no_cache = True
context.show_sidebar = True
context.parents = [dict(label='View All Chapters',
route='chapters', title='View Chapters')]
context.parents = [dict(label="View All Chapters", route="chapters", title="View Chapters")]
def validate(self):
if not self.route: #pylint: disable=E0203
self.route = 'chapters/' + self.scrub(self.name)
if not self.route: # pylint: disable=E0203
self.route = "chapters/" + self.scrub(self.name)
def enable(self):
chapter = frappe.get_doc('Chapter', frappe.form_dict.name)
chapter.append('members', dict(enable=self.value))
chapter = frappe.get_doc("Chapter", frappe.form_dict.name)
chapter.append("members", dict(enable=self.value))
chapter.save(ignore_permissions=1)
frappe.db.commit()
@@ -32,9 +31,9 @@ def get_list_context(context):
context.allow_guest = True
context.no_cache = True
context.show_sidebar = True
context.title = 'All Chapters'
context.title = "All Chapters"
context.no_breadcrumbs = True
context.order_by = 'creation desc'
context.order_by = "creation desc"
@frappe.whitelist()

View File

@@ -16,28 +16,30 @@ from erpnext.non_profit.doctype.membership.membership import verify_signature
class Donation(Document):
def validate(self):
if not self.donor or not frappe.db.exists('Donor', self.donor):
if not self.donor or not frappe.db.exists("Donor", self.donor):
# for web forms
user_type = frappe.db.get_value('User', frappe.session.user, 'user_type')
if user_type == 'Website User':
user_type = frappe.db.get_value("User", frappe.session.user, "user_type")
if user_type == "Website User":
self.create_donor_for_website_user()
else:
frappe.throw(_('Please select a Member'))
frappe.throw(_("Please select a Member"))
def create_donor_for_website_user(self):
donor_name = frappe.get_value('Donor', dict(email=frappe.session.user))
donor_name = frappe.get_value("Donor", dict(email=frappe.session.user))
if not donor_name:
user = frappe.get_doc('User', frappe.session.user)
donor = frappe.get_doc(dict(
doctype='Donor',
donor_type=self.get('donor_type'),
email=frappe.session.user,
member_name=user.get_fullname()
)).insert(ignore_permissions=True)
user = frappe.get_doc("User", frappe.session.user)
donor = frappe.get_doc(
dict(
doctype="Donor",
donor_type=self.get("donor_type"),
email=frappe.session.user,
member_name=user.get_fullname(),
)
).insert(ignore_permissions=True)
donor_name = donor.name
if self.get('__islocal'):
if self.get("__islocal"):
self.donor = donor_name
def on_payment_authorized(self, *args, **kwargs):
@@ -45,13 +47,16 @@ class Donation(Document):
self.create_payment_entry()
def create_payment_entry(self, date=None):
settings = frappe.get_doc('Non Profit Settings')
settings = frappe.get_doc("Non Profit Settings")
if not settings.automate_donation_payment_entries:
return
if not settings.donation_payment_account:
frappe.throw(_('You need to set <b>Payment Account</b> for Donation in {0}').format(
get_link_to_form('Non Profit Settings', 'Non Profit Settings')))
frappe.throw(
_("You need to set <b>Payment Account</b> for Donation in {0}").format(
get_link_to_form("Non Profit Settings", "Non Profit Settings")
)
)
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -71,31 +76,31 @@ class Donation(Document):
@frappe.whitelist(allow_guest=True)
def capture_razorpay_donations(*args, **kwargs):
"""
Creates Donation from Razorpay Webhook Request Data on payment.captured event
Creates Donor from email if not found
Creates Donation from Razorpay Webhook Request Data on payment.captured event
Creates Donor from email if not found
"""
data = frappe.request.get_data(as_text=True)
try:
verify_signature(data, endpoint='Donation')
verify_signature(data, endpoint="Donation")
except Exception as e:
log = frappe.log_error(e, 'Donation Webhook Verification Error')
log = frappe.log_error(e, "Donation Webhook Verification Error")
notify_failure(log)
return { 'status': 'Failed', 'reason': e }
return {"status": "Failed", "reason": e}
if isinstance(data, six.string_types):
data = json.loads(data)
data = frappe._dict(data)
payment = data.payload.get('payment', {}).get('entity', {})
payment = data.payload.get("payment", {}).get("entity", {})
payment = frappe._dict(payment)
try:
if not data.event == 'payment.captured':
if not data.event == "payment.captured":
return
# to avoid capturing subscription payments as donations
if payment.description and 'subscription' in str(payment.description).lower():
if payment.description and "subscription" in str(payment.description).lower():
return
donor = get_donor(payment.email)
@@ -103,45 +108,45 @@ def capture_razorpay_donations(*args, **kwargs):
donor = create_donor(payment)
donation = create_razorpay_donation(donor, payment)
donation.run_method('create_payment_entry')
donation.run_method("create_payment_entry")
except Exception as e:
message = '{0}\n\n{1}\n\n{2}: {3}'.format(e, frappe.get_traceback(), _('Payment ID'), payment.id)
log = frappe.log_error(message, _('Error creating donation entry for {0}').format(donor.name))
message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id)
log = frappe.log_error(message, _("Error creating donation entry for {0}").format(donor.name))
notify_failure(log)
return { 'status': 'Failed', 'reason': e }
return {"status": "Failed", "reason": e}
return { 'status': 'Success' }
return {"status": "Success"}
def create_razorpay_donation(donor, payment):
if not frappe.db.exists('Mode of Payment', payment.method):
if not frappe.db.exists("Mode of Payment", payment.method):
create_mode_of_payment(payment.method)
company = get_company_for_donations()
donation = frappe.get_doc({
'doctype': 'Donation',
'company': company,
'donor': donor.name,
'donor_name': donor.donor_name,
'email': donor.email,
'date': getdate(),
'amount': flt(payment.amount) / 100, # Convert to rupees from paise
'mode_of_payment': payment.method,
'payment_id': payment.id
}).insert(ignore_mandatory=True)
donation = frappe.get_doc(
{
"doctype": "Donation",
"company": company,
"donor": donor.name,
"donor_name": donor.donor_name,
"email": donor.email,
"date": getdate(),
"amount": flt(payment.amount) / 100, # Convert to rupees from paise
"mode_of_payment": payment.method,
"payment_id": payment.id,
}
).insert(ignore_mandatory=True)
donation.submit()
return donation
def get_donor(email):
donors = frappe.get_all('Donor',
filters={'email': email},
order_by='creation desc')
donors = frappe.get_all("Donor", filters={"email": email}, order_by="creation desc")
try:
return frappe.get_doc('Donor', donors[0]['name'])
return frappe.get_doc("Donor", donors[0]["name"])
except Exception:
return None
@@ -149,17 +154,19 @@ def get_donor(email):
@frappe.whitelist()
def create_donor(payment):
donor_details = frappe._dict(payment)
donor_type = frappe.db.get_single_value('Non Profit Settings', 'default_donor_type')
donor_type = frappe.db.get_single_value("Non Profit Settings", "default_donor_type")
donor = frappe.new_doc('Donor')
donor.update({
'donor_name': donor_details.email,
'donor_type': donor_type,
'email': donor_details.email,
'contact': donor_details.contact
})
donor = frappe.new_doc("Donor")
donor.update(
{
"donor_name": donor_details.email,
"donor_type": donor_type,
"email": donor_details.email,
"contact": donor_details.contact,
}
)
if donor_details.get('notes'):
if donor_details.get("notes"):
donor = get_additional_notes(donor, donor_details)
donor.insert(ignore_mandatory=True)
@@ -167,9 +174,10 @@ def create_donor(payment):
def get_company_for_donations():
company = frappe.db.get_single_value('Non Profit Settings', 'donation_company')
company = frappe.db.get_single_value("Non Profit Settings", "donation_company")
if not company:
from erpnext.healthcare.setup import get_company
company = get_company()
return company
@@ -177,45 +185,44 @@ def get_company_for_donations():
def get_additional_notes(donor, donor_details):
if type(donor_details.notes) == dict:
for k, v in donor_details.notes.items():
notes = '\n'.join('{}: {}'.format(k, v))
notes = "\n".join("{}: {}".format(k, v))
# extract donor name from notes
if 'name' in k.lower():
donor.update({
'donor_name': donor_details.notes.get(k)
})
if "name" in k.lower():
donor.update({"donor_name": donor_details.notes.get(k)})
# extract pan from notes
if 'pan' in k.lower():
donor.update({
'pan_number': donor_details.notes.get(k)
})
if "pan" in k.lower():
donor.update({"pan_number": donor_details.notes.get(k)})
donor.add_comment('Comment', notes)
donor.add_comment("Comment", notes)
elif type(donor_details.notes) == str:
donor.add_comment('Comment', donor_details.notes)
donor.add_comment("Comment", donor_details.notes)
return donor
def create_mode_of_payment(method):
frappe.get_doc({
'doctype': 'Mode of Payment',
'mode_of_payment': method
}).insert(ignore_mandatory=True)
frappe.get_doc({"doctype": "Mode of Payment", "mode_of_payment": method}).insert(
ignore_mandatory=True
)
def notify_failure(log):
try:
content = '''
content = """
Dear System Manager,
Razorpay webhook for creating donation failed due to some reason.
Please check the error log linked below
Error Log: {0}
Regards, Administrator
'''.format(get_link_to_form('Error Log', log.name))
""".format(
get_link_to_form("Error Log", log.name)
)
sendmail_to_system_managers(_('[Important] [ERPNext] Razorpay donation webhook failed, please check.'), content)
sendmail_to_system_managers(
_("[Important] [ERPNext] Razorpay donation webhook failed, please check."), content
)
except Exception:
pass

View File

@@ -1,17 +1,9 @@
from frappe import _
def get_data():
return {
'fieldname': 'donation',
'non_standard_fieldnames': {
'Payment Entry': 'reference_name'
},
'transactions': [
{
'label': _('Payment'),
'items': ['Payment Entry']
}
]
"fieldname": "donation",
"non_standard_fieldnames": {"Payment Entry": "reference_name"},
"transactions": [{"label": _("Payment"), "items": ["Payment Entry"]}],
}

View File

@@ -11,25 +11,21 @@ from erpnext.non_profit.doctype.donation.donation import create_razorpay_donatio
class TestDonation(unittest.TestCase):
def setUp(self):
create_donor_type()
settings = frappe.get_doc('Non Profit Settings')
settings.company = '_Test Company'
settings.donation_company = '_Test Company'
settings.default_donor_type = '_Test Donor'
settings = frappe.get_doc("Non Profit Settings")
settings.company = "_Test Company"
settings.donation_company = "_Test Company"
settings.default_donor_type = "_Test Donor"
settings.automate_donation_payment_entries = 1
settings.donation_debit_account = 'Debtors - _TC'
settings.donation_payment_account = 'Cash - _TC'
settings.creation_user = 'Administrator'
settings.donation_debit_account = "Debtors - _TC"
settings.donation_payment_account = "Cash - _TC"
settings.creation_user = "Administrator"
settings.flags.ignore_permissions = True
settings.save()
def test_payment_entry_for_donations(self):
donor = create_donor()
create_mode_of_payment()
payment = frappe._dict({
'amount': 100,
'method': 'Debit Card',
'id': 'pay_MeXAmsgeKOhq7O'
})
payment = frappe._dict({"amount": 100, "method": "Debit Card", "id": "pay_MeXAmsgeKOhq7O"})
donation = create_razorpay_donation(donor, payment)
self.assertTrue(donation.name)
@@ -41,37 +37,35 @@ class TestDonation(unittest.TestCase):
donation.reload()
self.assertEqual(donation.paid, 1)
self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name}))
self.assertTrue(frappe.db.exists("Payment Entry", {"reference_no": donation.name}))
def create_donor_type():
if not frappe.db.exists('Donor Type', '_Test Donor'):
frappe.get_doc({
'doctype': 'Donor Type',
'donor_type': '_Test Donor'
}).insert()
if not frappe.db.exists("Donor Type", "_Test Donor"):
frappe.get_doc({"doctype": "Donor Type", "donor_type": "_Test Donor"}).insert()
def create_donor():
donor = frappe.db.exists('Donor', 'donor@test.com')
donor = frappe.db.exists("Donor", "donor@test.com")
if donor:
return frappe.get_doc('Donor', 'donor@test.com')
return frappe.get_doc("Donor", "donor@test.com")
else:
return frappe.get_doc({
'doctype': 'Donor',
'donor_name': '_Test Donor',
'donor_type': '_Test Donor',
'email': 'donor@test.com'
}).insert()
return frappe.get_doc(
{
"doctype": "Donor",
"donor_name": "_Test Donor",
"donor_type": "_Test Donor",
"email": "donor@test.com",
}
).insert()
def create_mode_of_payment():
if not frappe.db.exists('Mode of Payment', 'Debit Card'):
frappe.get_doc({
'doctype': 'Mode of Payment',
'mode_of_payment': 'Debit Card',
'accounts': [{
'company': '_Test Company',
'default_account': 'Cash - _TC'
}]
}).insert()
if not frappe.db.exists("Mode of Payment", "Debit Card"):
frappe.get_doc(
{
"doctype": "Mode of Payment",
"mode_of_payment": "Debit Card",
"accounts": [{"company": "_Test Company", "default_account": "Cash - _TC"}],
}
).insert()

View File

@@ -13,5 +13,6 @@ class Donor(Document):
def validate(self):
from frappe.utils import validate_email_address
if self.email:
validate_email_address(self.email.strip(), True)

View File

@@ -11,12 +11,12 @@ from frappe.website.website_generator import WebsiteGenerator
class GrantApplication(WebsiteGenerator):
_website = frappe._dict(
condition_field = "published",
condition_field="published",
)
def validate(self):
if not self.route: #pylint: disable=E0203
self.route = 'grant-application/' + self.scrub(self.name)
if not self.route: # pylint: disable=E0203
self.route = "grant-application/" + self.scrub(self.name)
def onload(self):
"""Load address and contacts in `__onload`"""
@@ -25,32 +25,35 @@ class GrantApplication(WebsiteGenerator):
def get_context(self, context):
context.no_cache = True
context.show_sidebar = True
context.parents = [dict(label='View All Grant Applications',
route='grant-application', title='View Grants')]
context.parents = [
dict(label="View All Grant Applications", route="grant-application", title="View Grants")
]
def get_list_context(context):
context.allow_guest = True
context.no_cache = True
context.no_breadcrumbs = True
context.show_sidebar = True
context.order_by = 'creation desc'
context.introduction ='''<a class="btn btn-primary" href="/my-grant?new=1">
Apply for new Grant Application</a>'''
context.order_by = "creation desc"
context.introduction = """<a class="btn btn-primary" href="/my-grant?new=1">
Apply for new Grant Application</a>"""
@frappe.whitelist()
def send_grant_review_emails(grant_application):
grant = frappe.get_doc("Grant Application", grant_application)
url = get_url('grant-application/{0}'.format(grant_application))
url = get_url("grant-application/{0}".format(grant_application))
frappe.sendmail(
recipients= grant.assessment_manager,
recipients=grant.assessment_manager,
sender=frappe.session.user,
subject='Grant Application for {0}'.format(grant.applicant_name),
message='<p> Please Review this grant application</p><br>' + url,
subject="Grant Application for {0}".format(grant.applicant_name),
message="<p> Please Review this grant application</p><br>" + url,
reference_doctype=grant.doctype,
reference_name=grant.name
reference_name=grant.name,
)
grant.status = 'In Progress'
grant.status = "In Progress"
grant.email_notification_sent = 1
grant.save()
frappe.db.commit()

View File

@@ -17,20 +17,21 @@ class Member(Document):
"""Load address and contacts in `__onload`"""
load_address_and_contact(self)
def validate(self):
if self.email_id:
self.validate_email_type(self.email_id)
def validate_email_type(self, email):
from frappe.utils import validate_email_address
validate_email_address(email.strip(), True)
def setup_subscription(self):
non_profit_settings = frappe.get_doc('Non Profit Settings')
non_profit_settings = frappe.get_doc("Non Profit Settings")
if not non_profit_settings.enable_razorpay_for_memberships:
frappe.throw(_('Please check Enable Razorpay for Memberships in {0} to setup subscription')).format(
get_link_to_form('Non Profit Settings', 'Non Profit Settings'))
frappe.throw(
_("Please check Enable Razorpay for Memberships in {0} to setup subscription")
).format(get_link_to_form("Non Profit Settings", "Non Profit Settings"))
controller = get_payment_gateway_controller("Razorpay")
settings = controller.get_settings({})
@@ -43,12 +44,10 @@ class Member(Document):
subscription_details = {
"plan_id": plan_id,
"billing_frequency": cint(non_profit_settings.billing_frequency),
"customer_notify": 1
"customer_notify": 1,
}
args = {
'subscription_details': subscription_details
}
args = {"subscription_details": subscription_details}
subscription = controller.setup_subscription(settings, **args)
@@ -59,11 +58,9 @@ class Member(Document):
if self.customer:
frappe.msgprint(_("A customer is already linked to this Member"))
customer = create_customer(frappe._dict({
'fullname': self.member_name,
'email': self.email_id,
'phone': None
}))
customer = create_customer(
frappe._dict({"fullname": self.member_name, "email": self.email_id, "phone": None})
)
self.customer = customer
self.save()
@@ -71,24 +68,29 @@ class Member(Document):
def get_or_create_member(user_details):
member_list = frappe.get_all("Member", filters={'email': user_details.email, 'membership_type': user_details.plan_id})
member_list = frappe.get_all(
"Member", filters={"email": user_details.email, "membership_type": user_details.plan_id}
)
if member_list and member_list[0]:
return member_list[0]['name']
return member_list[0]["name"]
else:
return create_member(user_details)
def create_member(user_details):
user_details = frappe._dict(user_details)
member = frappe.new_doc("Member")
member.update({
"member_name": user_details.fullname,
"email_id": user_details.email,
"pan_number": user_details.pan or None,
"membership_type": user_details.plan_id,
"customer_id": user_details.customer_id or None,
"subscription_id": user_details.subscription_id or None,
"subscription_status": user_details.subscription_status or ""
})
member.update(
{
"member_name": user_details.fullname,
"email_id": user_details.email,
"pan_number": user_details.pan or None,
"membership_type": user_details.plan_id,
"customer_id": user_details.customer_id or None,
"subscription_id": user_details.subscription_id or None,
"subscription_status": user_details.subscription_status or "",
}
)
member.insert(ignore_permissions=True)
member.customer = create_customer(user_details, member.name)
@@ -96,6 +98,7 @@ def create_member(user_details):
return member
def create_customer(user_details, member=None):
customer = frappe.new_doc("Customer")
customer.customer_name = user_details.fullname
@@ -115,16 +118,10 @@ def create_customer(user_details, member=None):
contact.add_email(user_details.email, is_primary=1)
contact.insert(ignore_permissions=True)
contact.append("links", {
"link_doctype": "Customer",
"link_name": customer.name
})
contact.append("links", {"link_doctype": "Customer", "link_name": customer.name})
if member:
contact.append("links", {
"link_doctype": "Member",
"link_name": member
})
contact.append("links", {"link_doctype": "Member", "link_name": member})
contact.save(ignore_permissions=True)
@@ -138,23 +135,24 @@ def create_customer(user_details, member=None):
return customer.name
@frappe.whitelist(allow_guest=True)
def create_member_subscription_order(user_details):
"""Create Member subscription and order for payment
Args:
user_details (TYPE): Description
user_details (TYPE): Description
Returns:
Dictionary: Dictionary with subscription details
{
'subscription_details': {
'plan_id': 'plan_EXwyxDYDCj3X4v',
'billing_frequency': 24,
'customer_notify': 1
},
'subscription_id': 'sub_EZycCvXFvqnC6p'
}
Dictionary: Dictionary with subscription details
{
'subscription_details': {
'plan_id': 'plan_EXwyxDYDCj3X4v',
'billing_frequency': 24,
'customer_notify': 1
},
'subscription_id': 'sub_EZycCvXFvqnC6p'
}
"""
user_details = frappe._dict(user_details)
@@ -162,28 +160,31 @@ def create_member_subscription_order(user_details):
subscription = member.setup_subscription()
member.subscription_id = subscription.get('subscription_id')
member.subscription_id = subscription.get("subscription_id")
member.save(ignore_permissions=True)
return subscription
@frappe.whitelist()
def register_member(fullname, email, rzpay_plan_id, subscription_id, pan=None, mobile=None):
plan = get_membership_type(rzpay_plan_id)
if not plan:
raise frappe.DoesNotExistError
member = frappe.db.exists("Member", {'email': email, 'subscription_id': subscription_id })
member = frappe.db.exists("Member", {"email": email, "subscription_id": subscription_id})
if member:
return member
else:
member = create_member(dict(
fullname=fullname,
email=email,
plan_id=plan,
subscription_id=subscription_id,
pan=pan,
mobile=mobile
))
member = create_member(
dict(
fullname=fullname,
email=email,
plan_id=plan,
subscription_id=subscription_id,
pan=pan,
mobile=mobile,
)
)
return member.name

View File

@@ -1,23 +1,14 @@
from frappe import _
def get_data():
return {
'heatmap': True,
'heatmap_message': _('Member Activity'),
'fieldname': 'member',
'non_standard_fieldnames': {
'Bank Account': 'party'
},
'transactions': [
{
'label': _('Membership Details'),
'items': ['Membership']
},
{
'label': _('Fee'),
'items': ['Bank Account']
}
]
"heatmap": True,
"heatmap_message": _("Member Activity"),
"fieldname": "member",
"non_standard_fieldnames": {"Bank Account": "party"},
"transactions": [
{"label": _("Membership Details"), "items": ["Membership"]},
{"label": _("Fee"), "items": ["Bank Account"]},
],
}

View File

@@ -33,12 +33,14 @@ class Membership(Document):
if not member_name:
user = frappe.get_doc("User", frappe.session.user)
member = frappe.get_doc(dict(
doctype="Member",
email_id=frappe.session.user,
membership_type=self.membership_type,
member_name=user.get_fullname()
)).insert(ignore_permissions=True)
member = frappe.get_doc(
dict(
doctype="Member",
email_id=frappe.session.user,
membership_type=self.membership_type,
member_name=user.get_fullname(),
)
).insert(ignore_permissions=True)
member_name = member.name
if self.get("__islocal"):
@@ -49,9 +51,13 @@ class Membership(Document):
last_membership = erpnext.get_last_membership(self.member)
# if person applied for offline membership
if last_membership and last_membership.name != self.name and not frappe.session.user == "Administrator":
if (
last_membership
and last_membership.name != self.name
and not frappe.session.user == "Administrator"
):
# if last membership does not expire in 30 days, then do not allow to renew
if getdate(add_days(last_membership.to_date, -30)) > getdate(nowdate()) :
if getdate(add_days(last_membership.to_date, -30)) > getdate(nowdate()):
frappe.throw(_("You can only renew if your membership expires within 30 days"))
self.from_date = add_days(last_membership.to_date, 1)
@@ -72,13 +78,16 @@ class Membership(Document):
self.db_set("paid", 1)
settings = frappe.get_doc("Non Profit Settings")
if settings.allow_invoicing and settings.automate_membership_invoicing:
self.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
self.generate_invoice(
with_payment_entry=settings.automate_membership_payment_entries, save=True
)
@frappe.whitelist()
def generate_invoice(self, save=True, with_payment_entry=False):
if not (self.paid or self.currency or self.amount):
frappe.throw(_("The payment for this membership is not paid. To generate invoice fill the payment details"))
frappe.throw(
_("The payment for this membership is not paid. To generate invoice fill the payment details")
)
if self.invoice:
frappe.throw(_("An invoice is already linked to this document"))
@@ -110,21 +119,30 @@ class Membership(Document):
frappe.throw(_("You need to set <b>Debit Account</b> in {0}").format(settings_link))
if not settings.company:
frappe.throw(_("You need to set <b>Default Company</b> for invoicing in {0}").format(settings_link))
frappe.throw(
_("You need to set <b>Default Company</b> for invoicing in {0}").format(settings_link)
)
if not plan.linked_item:
frappe.throw(_("Please set a Linked Item for the Membership Type {0}").format(
get_link_to_form("Membership Type", self.membership_type)))
frappe.throw(
_("Please set a Linked Item for the Membership Type {0}").format(
get_link_to_form("Membership Type", self.membership_type)
)
)
def make_payment_entry(self, settings, invoice):
if not settings.membership_payment_account:
frappe.throw(_("You need to set <b>Payment Account</b> for Membership in {0}").format(
get_link_to_form("Non Profit Settings", "Non Profit Settings")))
frappe.throw(
_("You need to set <b>Payment Account</b> for Membership in {0}").format(
get_link_to_form("Non Profit Settings", "Non Profit 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
frappe.flags.ignore_account_permission = False
pe.paid_to = settings.membership_payment_account
pe.reference_no = self.name
pe.reference_date = getdate()
@@ -136,22 +154,33 @@ class Membership(Document):
def send_acknowlement(self):
settings = frappe.get_doc("Non Profit Settings")
if not settings.send_email:
frappe.throw(_("You need to enable <b>Send Acknowledge Email</b> in {0}").format(
get_link_to_form("Non Profit Settings", "Non Profit Settings")))
frappe.throw(
_("You need to enable <b>Send Acknowledge Email</b> in {0}").format(
get_link_to_form("Non Profit Settings", "Non Profit 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)))
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
attachments = [frappe.attach_print("Membership", self.name, print_format=settings.membership_print_format)]
attachments = [
frappe.attach_print("Membership", self.name, print_format=settings.membership_print_format)
]
if self.invoice and settings.send_invoice:
attachments.append(frappe.attach_print("Sales Invoice", self.invoice, print_format=settings.inv_print_format))
attachments.append(
frappe.attach_print("Sales Invoice", self.invoice, print_format=settings.inv_print_format)
)
email_template = frappe.get_doc("Email Template", settings.email_template)
context = { "doc": self, "member": member}
context = {"doc": self, "member": member}
email_args = {
"recipients": [email],
@@ -159,7 +188,7 @@ class Membership(Document):
"subject": frappe.render_template(email_template.get("subject"), context),
"attachments": attachments,
"reference_doctype": self.doctype,
"reference_name": self.name
"reference_name": self.name,
}
if not frappe.flags.in_test:
@@ -173,21 +202,17 @@ class Membership(Document):
def make_invoice(membership, member, plan, settings):
invoice = frappe.get_doc({
"doctype": "Sales Invoice",
"customer": member.customer,
"debit_to": settings.membership_debit_account,
"currency": membership.currency,
"company": settings.company,
"is_pos": 0,
"items": [
{
"item_code": plan.linked_item,
"rate": membership.amount,
"qty": 1
}
]
})
invoice = frappe.get_doc(
{
"doctype": "Sales Invoice",
"customer": member.customer,
"debit_to": settings.membership_debit_account,
"currency": membership.currency,
"company": settings.company,
"is_pos": 0,
"items": [{"item_code": plan.linked_item, "rate": membership.amount, "qty": 1}],
}
)
invoice.set_missing_values()
invoice.insert()
invoice.submit()
@@ -241,11 +266,15 @@ def trigger_razorpay_subscription(*args, **kwargs):
member = get_member_based_on_subscription(subscription.id, payment.email)
if not member:
member = create_member(frappe._dict({
"fullname": payment.email,
"email": payment.email,
"plan_id": get_plan_from_razorpay_id(subscription.plan_id)
}))
member = create_member(
frappe._dict(
{
"fullname": payment.email,
"email": payment.email,
"plan_id": get_plan_from_razorpay_id(subscription.plan_id),
}
)
)
member.subscription_id = subscription.id
member.customer_id = payment.customer_id
@@ -256,18 +285,20 @@ def trigger_razorpay_subscription(*args, **kwargs):
company = get_company_for_memberships()
# Update Membership
membership = frappe.new_doc("Membership")
membership.update({
"company": company,
"member": member.name,
"membership_status": "Current",
"membership_type": member.membership_type,
"currency": "INR",
"paid": 1,
"payment_id": payment.id,
"from_date": datetime.fromtimestamp(subscription.current_start),
"to_date": datetime.fromtimestamp(subscription.current_end),
"amount": payment.amount / 100 # Convert to rupees from paise
})
membership.update(
{
"company": company,
"member": member.name,
"membership_status": "Current",
"membership_type": member.membership_type,
"currency": "INR",
"paid": 1,
"payment_id": payment.id,
"from_date": datetime.fromtimestamp(subscription.current_start),
"to_date": datetime.fromtimestamp(subscription.current_end),
"amount": payment.amount / 100, # Convert to rupees from paise
}
)
membership.flags.ignore_mandatory = True
membership.insert()
@@ -281,7 +312,9 @@ def trigger_razorpay_subscription(*args, **kwargs):
settings = frappe.get_doc("Non Profit Settings")
if settings.allow_invoicing and settings.automate_membership_invoicing:
membership.reload()
membership.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
membership.generate_invoice(
with_payment_entry=settings.automate_membership_payment_entries, save=True
)
except Exception as e:
message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id)
@@ -328,7 +361,9 @@ def update_halted_razorpay_subscription(*args, **kwargs):
except Exception as e:
message = "{0}\n\n{1}".format(e, frappe.get_traceback())
log = frappe.log_error(message, _("Error updating halted status for member {0}").format(member.name))
log = frappe.log_error(
message, _("Error updating halted status for member {0}").format(member.name)
)
notify_failure(log)
return {"status": "Failed", "reason": e}
@@ -354,6 +389,7 @@ def get_company_for_memberships():
company = frappe.db.get_single_value("Non Profit Settings", "company")
if not company:
from erpnext.healthcare.setup import get_company
company = get_company()
return company
@@ -365,15 +401,11 @@ def get_additional_notes(member, subscription):
# extract member name from notes
if "name" in k.lower():
member.update({
"member_name": subscription.notes.get(k)
})
member.update({"member_name": subscription.notes.get(k)})
# extract pan number from notes
if "pan" in k.lower():
member.update({
"pan_number": subscription.notes.get(k)
})
member.update({"pan_number": subscription.notes.get(k)})
member.add_comment("Comment", notes)
@@ -391,15 +423,21 @@ def notify_failure(log):
Please check the following error log linked below
Error Log: {0}
Regards, Administrator
""".format(get_link_to_form("Error Log", log.name))
""".format(
get_link_to_form("Error Log", log.name)
)
sendmail_to_system_managers("[Important] [ERPNext] Razorpay membership webhook failed , please check.", content)
sendmail_to_system_managers(
"[Important] [ERPNext] Razorpay membership webhook failed , please check.", content
)
except Exception:
pass
def get_plan_from_razorpay_id(plan_id):
plan = frappe.get_all("Membership Type", filters={"razorpay_plan_id": plan_id}, order_by="creation desc")
plan = frappe.get_all(
"Membership Type", filters={"razorpay_plan_id": plan_id}, order_by="creation desc"
)
try:
return plan[0]["name"]
@@ -408,9 +446,12 @@ def get_plan_from_razorpay_id(plan_id):
def set_expired_status():
frappe.db.sql("""
frappe.db.sql(
"""
UPDATE
`tabMembership` SET `membership_status` = 'Expired'
WHERE
`membership_status` not in ('Cancelled') AND `to_date` < %s
""", (nowdate()))
""",
(nowdate()),
)

View File

@@ -17,14 +17,16 @@ class TestMembership(unittest.TestCase):
# make test member
self.member_doc = create_member(
frappe._dict({
"fullname": "_Test_Member",
"email": "_test_member_erpnext@example.com",
"plan_id": plan.name,
"subscription_id": "sub_DEX6xcJ1HSW4CR",
"customer_id": "cust_C0WlbKhp3aLA7W",
"subscription_status": "Active"
})
frappe._dict(
{
"fullname": "_Test_Member",
"email": "_test_member_erpnext@example.com",
"plan_id": plan.name,
"subscription_id": "sub_DEX6xcJ1HSW4CR",
"customer_id": "cust_C0WlbKhp3aLA7W",
"subscription_status": "Active",
}
)
)
self.member_doc.make_customer_and_link()
self.member = self.member_doc.name
@@ -40,30 +42,38 @@ class TestMembership(unittest.TestCase):
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) })
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),
})
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
make_membership(self.member, {
"from_date": add_months(nowdate(), 2),
"to_date": add_months(nowdate(), 3),
})
make_membership(
self.member,
{
"from_date": add_months(nowdate(), 2),
"to_date": add_months(nowdate(), 3),
},
)
def test_halted_memberships(self):
make_membership(self.member, {
"from_date": add_months(nowdate(), 2),
"to_date": add_months(nowdate(), 3)
})
make_membership(
self.member, {"from_date": add_months(nowdate(), 2), "to_date": add_months(nowdate(), 3)}
)
self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Active")
payload = get_subscription_payload()
@@ -73,9 +83,11 @@ class TestMembership(unittest.TestCase):
def tearDown(self):
frappe.db.rollback()
def set_config(key, value):
frappe.db.set_value("Non Profit Settings", None, key, value)
def make_membership(member, payload={}):
data = {
"doctype": "Membership",
@@ -85,13 +97,14 @@ def make_membership(member, payload={}):
"currency": "INR",
"paid": 1,
"from_date": nowdate(),
"amount": 100
"amount": 100,
}
data.update(payload)
membership = frappe.get_doc(data)
membership.insert(ignore_permissions=True, ignore_if_duplicate=True)
return membership
def create_item(item_code):
if not frappe.db.exists("Item", item_code):
item = frappe.new_doc("Item")
@@ -106,6 +119,7 @@ def create_item(item_code):
item = frappe.get_doc("Item", item_code)
return item
def setup_membership():
# Get default company
company = frappe.get_doc("Company", erpnext.get_default_company())
@@ -139,14 +153,13 @@ def setup_membership():
return plan
def get_subscription_payload():
return {
"entity": "event",
"account_id": "acc_BFQ7uQEaa7j2z7",
"event": "subscription.halted",
"contains": [
"subscription"
],
"contains": ["subscription"],
"payload": {
"subscription": {
"entity": {
@@ -155,10 +168,8 @@ def get_subscription_payload():
"plan_id": "_rzpy_test_milythm",
"customer_id": "cust_C0WlbKhp3aLA7W",
"status": "halted",
"notes": {
"Important": "Notes for Internal Reference"
},
"notes": {"Important": "Notes for Internal Reference"},
}
}
}
},
}

View File

@@ -14,5 +14,6 @@ class MembershipType(Document):
if is_stock_item:
frappe.throw(_("The Linked Item should be a service item"))
def get_membership_type(razorpay_id):
return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id})

View File

@@ -18,8 +18,12 @@ class NonProfitSettings(Document):
secret_for = "Membership" if field == "membership_webhook_secret" else "Donation"
frappe.msgprint(
_("Here is your webhook secret for {0} API, this will be shown to you only once.").format(secret_for) + "<br><br>" + key,
_("Webhook Secret")
_("Here is your webhook secret for {0} API, this will be shown to you only once.").format(
secret_for
)
+ "<br><br>"
+ key,
_("Webhook Secret"),
)
@frappe.whitelist()
@@ -28,9 +32,12 @@ class NonProfitSettings(Document):
self.save()
def get_webhook_secret(self, endpoint="Membership"):
fieldname = "membership_webhook_secret" if endpoint == "Membership" else "donation_webhook_secret"
fieldname = (
"membership_webhook_secret" if endpoint == "Membership" else "donation_webhook_secret"
)
return self.get_password(fieldname=fieldname, raise_exception=False)
@frappe.whitelist()
def get_plans_for_membership(*args, **kwargs):
controller = get_payment_gateway_controller("Razorpay")

View File

@@ -11,18 +11,37 @@ def execute(filters=None):
data = get_data(filters)
return columns, data
def get_columns(filters):
return [
_("Membership Type") + ":Link/Membership Type:100", _("Membership ID") + ":Link/Membership:140",
_("Member ID") + ":Link/Member:140", _("Member Name") + ":Data:140", _("Email") + ":Data:140",
_("Expiring On") + ":Date:120"
_("Membership Type") + ":Link/Membership Type:100",
_("Membership ID") + ":Link/Membership:140",
_("Member ID") + ":Link/Member:140",
_("Member Name") + ":Data:140",
_("Email") + ":Data:140",
_("Expiring On") + ":Date:120",
]
def get_data(filters):
filters["month"] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].index(filters.month) + 1
filters["month"] = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
].index(filters.month) + 1
return frappe.db.sql("""
return frappe.db.sql(
"""
select ms.membership_type,ms.name,m.name,m.member_name,m.email,ms.max_membership_date
from `tabMember` m
inner join (select name,membership_type,max(to_date) as max_membership_date,member
@@ -31,4 +50,6 @@ def get_data(filters):
group by member
order by max_membership_date asc) ms
on m.name = ms.member
where month(max_membership_date) = %(month)s and year(max_membership_date) = %(year)s """,{'month': filters.get('month'),'year':filters.get('fiscal_year')})
where month(max_membership_date) = %(month)s and year(max_membership_date) = %(year)s """,
{"month": filters.get("month"), "year": filters.get("fiscal_year")},
)

View File

@@ -1,5 +1,3 @@
def get_context(context):
# do your magic here
pass

View File

@@ -1,5 +1,3 @@
def get_context(context):
# do your magic here
pass

View File

@@ -1,6 +1,3 @@
def get_context(context):
context.no_cache = True
context.parents = [dict(label='View All ',
route='grant-application', title='View All')]
context.parents = [dict(label="View All ", route="grant-application", title="View All")]