diff --git a/rangeldigital/__pycache__/__init__.cpython-310.pyc b/rangeldigital/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8f40bbc Binary files /dev/null and b/rangeldigital/__pycache__/__init__.cpython-310.pyc differ diff --git a/rangeldigital/__pycache__/hooks.cpython-310.pyc b/rangeldigital/__pycache__/hooks.cpython-310.pyc new file mode 100644 index 0000000..4438fd2 Binary files /dev/null and b/rangeldigital/__pycache__/hooks.cpython-310.pyc differ diff --git a/rangeldigital/hooks.py b/rangeldigital/hooks.py index 830f2b6..5525498 100644 --- a/rangeldigital/hooks.py +++ b/rangeldigital/hooks.py @@ -11,8 +11,31 @@ override_doctype_class = { } +# Lead: Add javascript to add "Add to Email Campaign to Action Dropdown" +doctype_js = { + "Lead": "public/js/lead.js" +} +# Add cron scheduler for email campaign sending + # Send email at 08:15 AM every day +scheduler_events = { + "cron": { + "15 8 * * *": [ + "rangeldigital.utilities.scheduler.scheduler.send_email_to_leads_or_contacts" + ] + } +} + +# Disable default send_email_to_leads_or_contacts +after_install = "rangeldigital.utilities.install.after_install" + + +# Enable default send_email_to_leads_or_contacts +before_uninstall = "rangeldigital.utilities.uninstall.before_uninstall" + + +#app_include_js = "/assets/rangeldigital/js/lead.js" web_include_js = [ diff --git a/rangeldigital/install.py b/rangeldigital/install.py new file mode 100644 index 0000000..ba4203b --- /dev/null +++ b/rangeldigital/install.py @@ -0,0 +1,26 @@ +import frappe + +def after_install(): + stop_erpnext_scheduled_send_email_job() + +def stop_erpnext_scheduled_send_email_job(): + # Get list of Scheduled Job Type docs with that method + job_docs = frappe.get_all( + "Scheduled Job Type", + filters={"method": "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts"}, + limit_page_length=1 + ) + + if not job_docs: + frappe.msgprint("Scheduled Job Type not found.") + return + + job_name = job_docs[0].name + job_doc = frappe.get_doc("Scheduled Job Type", job_name) + + # Set stopped = 1 + job_doc.stopped = 1 + job_doc.save(ignore_permissions=True) + frappe.db.commit() + + frappe.msgprint(f"Scheduled Job Type '{job_doc.method}' stopped successfully.") diff --git a/rangeldigital/public/js/lead.js b/rangeldigital/public/js/lead.js new file mode 100644 index 0000000..ec65adb --- /dev/null +++ b/rangeldigital/public/js/lead.js @@ -0,0 +1,65 @@ +frappe.ui.form.on('Lead', { + refresh(frm) { + // Only show the button if the Lead has been saved and has an email + if (!frm.doc.__islocal && frm.doc.email_id) { + frm.add_custom_button(__('Add to Email Campaign'), () => { + open_email_campaign_dialog(frm); + }, __('Action')); + } else if (!frm.doc.email_id) { + // Optional: give user visual feedback + frm.dashboard.set_headline(__('This Lead has no email address — cannot add to Email Campaign.')); + } + } +}); + +function open_email_campaign_dialog(frm) { + frappe.prompt([ + { + fieldname: 'campaign_name', + label: 'Campaign', + fieldtype: 'Link', + options: 'Campaign', + reqd: 1 + }, + { + fieldname: 'start_date', + label: 'Start Date', + fieldtype: 'Date', + default: frappe.datetime.get_today(), + reqd: 1 + } + ], + (values) => { + frappe.call({ + method: 'rangeldigital.utilities.lead.lead_api.add_lead_to_campaign', + args: { + lead_name: frm.doc.name, + campaign_name: values.campaign_name, + start_date: values.start_date + }, + freeze: true, + freeze_message: __('Creating Email Campaign...'), + callback: (r) => { + if (r.message && r.message.status === 'success') { + frappe.show_alert({ + message: __('Email Campaign created successfully'), + indicator: 'green' + }); + } else { + frappe.msgprint(__('Something went wrong. Please try again.')); + } + }, + error: (err) => { + if (err && err.message) { + frappe.msgprint(err.message); + } else { + frappe.msgprint(__('Unexpected error. Check the console.')); + console.error(err); + } + } + }); + }, + __('New Email Campaign'), + __('Create') + ); +} diff --git a/rangeldigital/rangel_digital/__pycache__/__init__.cpython-310.pyc b/rangeldigital/rangel_digital/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6df4fd1 Binary files /dev/null and b/rangeldigital/rangel_digital/__pycache__/__init__.cpython-310.pyc differ diff --git a/rangeldigital/utilities/__init__.py b/rangeldigital/utilities/__init__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/rangeldigital/utilities/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/rangeldigital/utilities/__pycache__/__init__.cpython-310.pyc b/rangeldigital/utilities/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4664743 Binary files /dev/null and b/rangeldigital/utilities/__pycache__/__init__.cpython-310.pyc differ diff --git a/rangeldigital/utilities/__pycache__/install.cpython-310.pyc b/rangeldigital/utilities/__pycache__/install.cpython-310.pyc new file mode 100644 index 0000000..bf73c5a Binary files /dev/null and b/rangeldigital/utilities/__pycache__/install.cpython-310.pyc differ diff --git a/rangeldigital/utilities/__pycache__/uninstall.cpython-310.pyc b/rangeldigital/utilities/__pycache__/uninstall.cpython-310.pyc new file mode 100644 index 0000000..81bde6a Binary files /dev/null and b/rangeldigital/utilities/__pycache__/uninstall.cpython-310.pyc differ diff --git a/rangeldigital/utilities/install.py b/rangeldigital/utilities/install.py new file mode 100644 index 0000000..ba4203b --- /dev/null +++ b/rangeldigital/utilities/install.py @@ -0,0 +1,26 @@ +import frappe + +def after_install(): + stop_erpnext_scheduled_send_email_job() + +def stop_erpnext_scheduled_send_email_job(): + # Get list of Scheduled Job Type docs with that method + job_docs = frappe.get_all( + "Scheduled Job Type", + filters={"method": "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts"}, + limit_page_length=1 + ) + + if not job_docs: + frappe.msgprint("Scheduled Job Type not found.") + return + + job_name = job_docs[0].name + job_doc = frappe.get_doc("Scheduled Job Type", job_name) + + # Set stopped = 1 + job_doc.stopped = 1 + job_doc.save(ignore_permissions=True) + frappe.db.commit() + + frappe.msgprint(f"Scheduled Job Type '{job_doc.method}' stopped successfully.") diff --git a/rangeldigital/utilities/lead/__init__.py b/rangeldigital/utilities/lead/__init__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/rangeldigital/utilities/lead/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/rangeldigital/utilities/lead/__pycache__/__init__.cpython-310.pyc b/rangeldigital/utilities/lead/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3c6f408 Binary files /dev/null and b/rangeldigital/utilities/lead/__pycache__/__init__.cpython-310.pyc differ diff --git a/rangeldigital/utilities/lead/__pycache__/lead_api.cpython-310.pyc b/rangeldigital/utilities/lead/__pycache__/lead_api.cpython-310.pyc new file mode 100644 index 0000000..4afff3c Binary files /dev/null and b/rangeldigital/utilities/lead/__pycache__/lead_api.cpython-310.pyc differ diff --git a/rangeldigital/utilities/lead/lead_api.py b/rangeldigital/utilities/lead/lead_api.py new file mode 100644 index 0000000..b4ff908 --- /dev/null +++ b/rangeldigital/utilities/lead/lead_api.py @@ -0,0 +1,31 @@ +import frappe +from frappe.utils import today + +@frappe.whitelist() +def add_lead_to_campaign(lead_name, campaign_name, start_date=None): + if not frappe.db.exists("Lead", lead_name): + frappe.throw("Lead does not exist") + + # Check if there's already a scheduled/in-progress campaign for this Lead with same name + existing = frappe.db.exists("Email Campaign", { + "recipient": lead_name, + "campaign_name": campaign_name, + "email_campaign_for": "Lead", + "status": ["in", ["Scheduled", "In Progress"]] + }) + + if existing: + frappe.throw("This Lead is already part of an Email Campaign with this name.") + + doc = frappe.new_doc("Email Campaign") + doc.update({ + "campaign_name": campaign_name, + "email_campaign_for": "Lead", + "recipient": lead_name, + "start_date": start_date or today(), + "sender": frappe.session.user # or replace with a specific default sender + }) + doc.insert(ignore_permissions=True) + frappe.db.commit() + + return {"status": "success", "message": "Email Campaign created"} diff --git a/rangeldigital/utilities/scheduler/__init__.py b/rangeldigital/utilities/scheduler/__init__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/rangeldigital/utilities/scheduler/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/rangeldigital/utilities/scheduler/__pycache__/__init__.cpython-310.pyc b/rangeldigital/utilities/scheduler/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c2557f9 Binary files /dev/null and b/rangeldigital/utilities/scheduler/__pycache__/__init__.cpython-310.pyc differ diff --git a/rangeldigital/utilities/scheduler/__pycache__/scheduler.cpython-310.pyc b/rangeldigital/utilities/scheduler/__pycache__/scheduler.cpython-310.pyc new file mode 100644 index 0000000..b97b32c Binary files /dev/null and b/rangeldigital/utilities/scheduler/__pycache__/scheduler.cpython-310.pyc differ diff --git a/rangeldigital/utilities/scheduler/scheduler.py b/rangeldigital/utilities/scheduler/scheduler.py new file mode 100644 index 0000000..4b3ddf3 --- /dev/null +++ b/rangeldigital/utilities/scheduler/scheduler.py @@ -0,0 +1,4 @@ +from erpnext.crm.doctype.email_campaign.email_campaign import send_email_to_leads_or_contacts as erpnext_send_email + +def send_email_to_leads_or_contacts(): + erpnext_send_email() diff --git a/rangeldigital/utilities/uninstall.py b/rangeldigital/utilities/uninstall.py new file mode 100644 index 0000000..e30e436 --- /dev/null +++ b/rangeldigital/utilities/uninstall.py @@ -0,0 +1,26 @@ +import frappe + +def before_uninstall(): + start_erpnext_scheduled_send_email_job() + +def start_erpnext_scheduled_send_email_job(): + # Get list of Scheduled Job Type docs with that method + job_docs = frappe.get_all( + "Scheduled Job Type", + filters={"method": "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts"}, + limit_page_length=1 + ) + + if not job_docs: + frappe.msgprint("Scheduled Job Type not found.") + return + + job_name = job_docs[0].name + job_doc = frappe.get_doc("Scheduled Job Type", job_name) + + # Set stopped = 1 + job_doc.stopped = 0 + job_doc.save(ignore_permissions=True) + frappe.db.commit() + + frappe.msgprint(f"Scheduled Job Type '{job_doc.method}' stopped successfully.")