From f423de530a4140bfb84e93dbfa0f1140dcaa8e9b Mon Sep 17 00:00:00 2001 From: ChillarAnand Date: Thu, 20 Jan 2022 11:02:30 +0530 Subject: [PATCH] refactor: Code cleanup --- .../doctype/membership/membership.py | 415 ------------------ erpnext/regional/india/setup.py | 8 - .../operations/install_fixtures.py | 1 - 3 files changed, 424 deletions(-) delete mode 100644 erpnext/non_profit/doctype/membership/membership.py diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py deleted file mode 100644 index f9b295a223..0000000000 --- a/erpnext/non_profit/doctype/membership/membership.py +++ /dev/null @@ -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 Debit Account in {0}").format(settings_link)) - - if not settings.company: - frappe.throw(_("You need to set Default Company 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 Payment Account 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 Send Acknowledge Email 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())) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index fa4e454f33..ecd9294342 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -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', diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 906db561db..bf3525998c 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -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"},