refactor: Code cleanup

This commit is contained in:
ChillarAnand 2022-01-20 11:02:30 +05:30
parent 0c0a9ed96d
commit f423de530a
3 changed files with 0 additions and 424 deletions

View File

@ -1,415 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
from datetime import datetime
import frappe
from frappe import _
from frappe.email import sendmail_to_system_managers
from frappe.model.document import Document
from frappe.utils import add_days, add_months, add_years, get_link_to_form, getdate, nowdate
import erpnext
from erpnext.non_profit.doctype.member.member import create_member
class Membership(Document):
def validate(self):
if not self.member or not frappe.db.exists("Member", self.member):
# for web forms
user_type = frappe.db.get_value("User", frappe.session.user, "user_type")
if user_type == "Website User":
self.create_member_from_website_user()
else:
frappe.throw(_("Please select a Member"))
self.validate_membership_period()
def create_member_from_website_user(self):
member_name = frappe.get_value("Member", dict(email_id=frappe.session.user))
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_name = member.name
if self.get("__islocal"):
self.member = member_name
def validate_membership_period(self):
# get last membership (if active)
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 does not expire in 30 days, then do not allow to renew
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)
elif frappe.session.user == "Administrator":
self.from_date = self.from_date
else:
self.from_date = nowdate()
if frappe.db.get_single_value("Non Profit Settings", "billing_cycle") == "Yearly":
self.to_date = add_years(self.from_date, 1)
else:
self.to_date = add_months(self.from_date, 1)
def on_payment_authorized(self, status_changed_to=None):
if status_changed_to not in ("Completed", "Authorized"):
return
self.load_from_db()
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)
@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"))
if self.invoice:
frappe.throw(_("An invoice is already linked to this document"))
member = frappe.get_doc("Member", self.member)
if not member.customer:
frappe.throw(_("No customer linked to member {0}").format(frappe.bold(self.member)))
plan = frappe.get_doc("Membership Type", self.membership_type)
settings = frappe.get_doc("Non Profit Settings")
self.validate_membership_type_and_settings(plan, settings)
invoice = make_invoice(self, member, plan, settings)
self.reload()
self.invoice = invoice.name
if with_payment_entry:
self.make_payment_entry(settings, invoice)
if save:
self.save()
return invoice
def validate_membership_type_and_settings(self, plan, settings):
settings_link = get_link_to_form("Membership Type", self.membership_type)
if not settings.membership_debit_account:
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))
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)))
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")))
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.membership_payment_account
pe.reference_no = self.name
pe.reference_date = getdate()
pe.flags.ignore_mandatory = True
pe.save()
pe.submit()
@frappe.whitelist()
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")))
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
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))
email_template = frappe.get_doc("Email Template", settings.email_template)
context = { "doc": self, "member": member}
email_args = {
"recipients": [email],
"message": frappe.render_template(email_template.get("response"), context),
"subject": frappe.render_template(email_template.get("subject"), context),
"attachments": attachments,
"reference_doctype": self.doctype,
"reference_name": self.name
}
if not frappe.flags.in_test:
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
else:
frappe.sendmail(**email_args)
def generate_and_send_invoice(self):
self.generate_invoice(save=False)
self.send_acknowlement()
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.set_missing_values()
invoice.insert()
invoice.submit()
frappe.msgprint(_("Sales Invoice created successfully"))
return invoice
def get_member_based_on_subscription(subscription_id, email=None, customer_id=None):
filters = {"subscription_id": subscription_id}
if email:
filters.update({"email_id": email})
if customer_id:
filters.update({"customer_id": customer_id})
members = frappe.get_all("Member", filters=filters, order_by="creation desc")
try:
return frappe.get_doc("Member", members[0]["name"])
except Exception:
return None
def verify_signature(data, endpoint="Membership"):
signature = frappe.request.headers.get("X-Razorpay-Signature")
settings = frappe.get_doc("Non Profit Settings")
key = settings.get_webhook_secret(endpoint)
controller = frappe.get_doc("Razorpay Settings")
controller.verify_signature(data, signature, key)
frappe.set_user(settings.creation_user)
@frappe.whitelist(allow_guest=True)
def trigger_razorpay_subscription(*args, **kwargs):
data = frappe.request.get_data(as_text=True)
data = process_request_data(data)
subscription = data.payload.get("subscription", {}).get("entity", {})
subscription = frappe._dict(subscription)
payment = data.payload.get("payment", {}).get("entity", {})
payment = frappe._dict(payment)
try:
if not data.event == "subscription.charged":
return
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.subscription_id = subscription.id
member.customer_id = payment.customer_id
if subscription.get("notes"):
member = get_additional_notes(member, subscription)
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.flags.ignore_mandatory = True
membership.insert()
# Update membership values
member.subscription_start = datetime.fromtimestamp(subscription.start_at)
member.subscription_end = datetime.fromtimestamp(subscription.end_at)
member.subscription_status = "Active"
member.flags.ignore_mandatory = True
member.save()
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)
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 membership entry for {0}").format(member.name))
notify_failure(log)
return {"status": "Failed", "reason": e}
return {"status": "Success"}
@frappe.whitelist(allow_guest=True)
def update_halted_razorpay_subscription(*args, **kwargs):
"""
When all retries have been exhausted, Razorpay moves the subscription to the halted state.
The customer has to manually retry the charge or change the card linked to the subscription,
for the subscription to move back to the active state.
"""
if frappe.request:
data = frappe.request.get_data(as_text=True)
data = process_request_data(data)
elif frappe.flags.in_test:
data = kwargs.get("data")
data = frappe._dict(data)
else:
return
if not data.event == "subscription.halted":
return
subscription = data.payload.get("subscription", {}).get("entity", {})
subscription = frappe._dict(subscription)
try:
member = get_member_based_on_subscription(subscription.id, customer_id=subscription.customer_id)
if not member:
frappe.throw(_("Member with Razorpay Subscription ID {0} not found").format(subscription.id))
member.subscription_status = "Halted"
member.flags.ignore_mandatory = True
member.save()
if subscription.get("notes"):
member = get_additional_notes(member, subscription)
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))
notify_failure(log)
return {"status": "Failed", "reason": e}
return {"status": "Success"}
def process_request_data(data):
try:
verify_signature(data)
except Exception as e:
log = frappe.log_error(e, "Membership Webhook Verification Error")
notify_failure(log)
return {"status": "Failed", "reason": e}
if isinstance(data, str):
data = json.loads(data)
data = frappe._dict(data)
return data
def get_company_for_memberships():
company = frappe.db.get_single_value("Non Profit Settings", "company")
if not company:
from erpnext.non_profit.utils import get_company
company = get_company()
return company
def get_additional_notes(member, subscription):
if type(subscription.notes) == dict:
for k, v in subscription.notes.items():
notes = "\n".join("{}: {}".format(k, v))
# extract member name from notes
if "name" in k.lower():
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.add_comment("Comment", notes)
elif type(subscription.notes) == str:
member.add_comment("Comment", subscription.notes)
return member
def notify_failure(log):
try:
content = """
Dear System Manager,
Razorpay webhook for creating renewing membership subscription failed due to some reason.
Please check the following error log linked below
Error Log: {0}
Regards, Administrator
""".format(get_link_to_form("Error Log", log.name))
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")
try:
return plan[0]["name"]
except Exception:
return None
def set_expired_status():
frappe.db.sql("""
UPDATE
`tabMembership` SET `membership_status` = 'Expired'
WHERE
`membership_status` not in ('Cancelled') AND `to_date` < %s
""", (nowdate()))

View File

@ -671,14 +671,6 @@ def get_custom_fields():
'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)'
}
],
'Member': [
{
'fieldname': 'pan_number',
'label': 'PAN Details',
'fieldtype': 'Data',
'insert_after': 'email_id'
}
],
'Finance Book': [
{
'fieldname': 'for_income_tax',

View File

@ -195,7 +195,6 @@ def install(country=None):
{'doctype': "Party Type", "party_type": "Customer", "account_type": "Receivable"},
{'doctype': "Party Type", "party_type": "Supplier", "account_type": "Payable"},
{'doctype': "Party Type", "party_type": "Employee", "account_type": "Payable"},
{'doctype': "Party Type", "party_type": "Member", "account_type": "Receivable"},
{'doctype': "Party Type", "party_type": "Shareholder", "account_type": "Payable"},
{'doctype': "Party Type", "party_type": "Student", "account_type": "Receivable"},