custom_ui/custom_ui/services/client_service.py
2026-01-16 09:06:59 -06:00

155 lines
9.4 KiB
Python

import frappe
from frappe.model.document import Document
from .db_service import DbService
from erpnext.crm.doctype.lead.lead import make_customer
from .address_service import AddressService
from .contact_service import ContactService
from .estimate_service import EstimateService
from .onsite_meeting_service import OnSiteMeetingService
class ClientService:
@staticmethod
def get_client_or_throw(client_name: str) -> Document:
"""Retrieve a Client document (Customer or Lead) or throw an error if it does not exist."""
doctype = ClientService.get_client_doctype(client_name)
return DbService.get_or_throw(doctype, client_name)
@staticmethod
def get_client_doctype(client_name: str) -> str:
"""Determine if the client is a Customer or Lead."""
if DbService.exists("Customer", client_name):
return "Customer"
elif DbService.exists("Lead", client_name):
return "Lead"
else:
raise ValueError(f"Client with name {client_name} does not exist as Customer or Lead.")
@staticmethod
def set_primary_contact(client_name: str, contact_name: str):
"""Set the primary contact for a client (Customer or Lead)."""
print(f"DEBUG: Setting primary contact for client {client_name} to contact {contact_name}")
client_doctype = ClientService.get_client_doctype(client_name)
frappe.db.set_value(client_doctype, client_name, "primary_contact", contact_name)
print(f"DEBUG: Set primary contact for client {client_name} to contact {contact_name}")
@staticmethod
def append_link(client_name: str, field: str, link_doctype: str, link_name: str):
"""Set a link field for a client (Customer or Lead)."""
print(f"DEBUG: Setting link field {field} for client {client_name} to {link_doctype} {link_name}")
client_doctype = ClientService.get_client_doctype(client_name)
client_doc = frappe.get_doc(client_doctype, client_name)
client_doc.append(field, {
link_doctype.lower(): link_name
})
client_doc.save(ignore_permissions=True)
print(f"DEBUG: Set link field {field} for client {client_doc.get('name')} to {link_doctype} {link_name}")
@staticmethod
def append_link_v2(client_name: str, field: str, link: dict):
"""Set a link field for a client (Customer or Lead) using a link dictionary."""
print(f"DEBUG: Setting link field {field} for client {client_name} with link data {link}")
client_doctype = ClientService.get_client_doctype(client_name)
client_doc = DbService.get_or_throw(client_doctype, client_name)
print("DEBUG: Appending link:", link)
client_doc.append(field, link)
print("DEBUG: Saving client document after appending link.")
client_doc.save(ignore_permissions=True)
print(f"DEBUG: Set link field {field} for client {client_doc.get('name')} with link data {link}")
@staticmethod
def convert_lead_to_customer(
lead_name: str,
update_quotations: bool = True,
update_addresses: bool = True,
update_contacts: bool = True,
update_onsite_meetings: bool = True,
update_companies: bool = True
) -> Document:
"""Convert a Lead to a Customer."""
print(f"DEBUG: Converting Lead {lead_name} to Customer")
try:
lead_doc = DbService.get_or_throw("Lead", lead_name)
print(f"DEBUG: Retrieved Lead document: {lead_doc.name}")
print("DEBUG: RUNNING make_customer()")
customer_doc = make_customer(lead_doc.name)
print(f"DEBUG: make_customer() returned document type: {type(customer_doc)}")
print(f"DEBUG: Customer doc name: {customer_doc.name if hasattr(customer_doc, 'name') else 'NO NAME'}")
customer_doc.custom_billing_address = lead_doc.custom_billing_address
print("DEBUG: Calling customer_doc.insert()")
customer_doc.insert(ignore_permissions=True)
print(f"DEBUG: Customer inserted successfully: {customer_doc.name}")
frappe.db.commit()
print("DEBUG: Database committed after customer insert")
print("DEBUG: CREATED CUSTOMER:", customer_doc.as_dict())
if update_addresses:
print("DEBUG: Lead_doc addresses:", lead_doc.get("addresses", []))
print(f"DEBUG: Updating addresses. Count: {len(lead_doc.get('properties', []))}")
for address in lead_doc.get("properties", []):
try:
print(f"DEBUG: Processing address: {address.get('address')}")
ClientService.append_link_v2(customer_doc.name, "properties", {"address": address.get("address")})
address_doc = AddressService.get_or_throw(address.get("address"))
AddressService.link_address_to_customer(address_doc, "Customer", customer_doc.name)
print(f"DEBUG: Linked address {address.get('address')} to customer")
except Exception as e:
print(f"ERROR: Failed to link address {address.get('address')}: {str(e)}")
frappe.log_error(f"Address linking error: {str(e)}", "convert_lead_to_customer")
if update_contacts:
print(f"DEBUG: Updating contacts. Count: {len(lead_doc.get('contacts', []))}")
for contact in lead_doc.get("contacts", []):
try:
print(f"DEBUG: Processing contact: {contact.get('contact')}")
ClientService.append_link_v2(customer_doc.name, "contacts", {"contact": contact.get("contact")})
contact_doc = ContactService.get_or_throw(contact.get("contact"))
ContactService.link_contact_to_customer(contact_doc, "Customer", customer_doc.name)
print(f"DEBUG: Linked contact {contact.get('contact')} to customer")
except Exception as e:
print(f"ERROR: Failed to link contact {contact.get('contact')}: {str(e)}")
frappe.log_error(f"Contact linking error: {str(e)}", "convert_lead_to_customer")
if update_quotations:
print(f"DEBUG: Updating quotations. Count: {len(lead_doc.get('quotations', []))}")
for quotation in lead_doc.get("quotations", []):
try:
print(f"DEBUG: Processing quotation: {quotation.get('quotation')}")
ClientService.append_link_v2(customer_doc.name, "quotations", {"quotation": quotation.get("quotation")})
quotation_doc = EstimateService.get_or_throw(quotation.get("quotation"))
EstimateService.link_estimate_to_customer(quotation_doc, "Customer", customer_doc.name)
print(f"DEBUG: Linked quotation {quotation.get('quotation')} to customer")
except Exception as e:
print(f"ERROR: Failed to link quotation {quotation.get('quotation')}: {str(e)}")
frappe.log_error(f"Quotation linking error: {str(e)}", "convert_lead_to_customer")
if update_onsite_meetings:
print(f"DEBUG: Updating onsite meetings. Count: {len(lead_doc.get('onsite_meetings', []))}")
for meeting in lead_doc.get("onsite_meetings", []):
try:
print(f"DEBUG: Processing onsite meeting: {meeting.get('onsite_meeting')}")
meeting_doc = DbService.get_or_throw("On-Site Meeting",meeting.get("onsite_meeting"))
ClientService.append_link_v2(customer_doc.name, "onsite_meetings", {"onsite_meeting": meeting.get("onsite_meeting")})
OnSiteMeetingService.link_onsite_meeting_to_customer(meeting_doc, "Customer", customer_doc.name)
print(f"DEBUG: Linked onsite meeting {meeting.get('onsite_meeting')} to customer")
except Exception as e:
print(f"ERROR: Failed to link onsite meeting {meeting.get('onsite_meeting')}: {str(e)}")
frappe.log_error(f"Onsite meeting linking error: {str(e)}", "convert_lead_to_customer")
if update_companies:
print(f"DEBUG: Updating companies. Count: {len(lead_doc.get('companies', []))}")
for company in lead_doc.get("companies", []):
try:
print(f"DEBUG: Processing company: {company.get('company')}")
ClientService.append_link_v2(customer_doc.name, "companies", {"company": company.get("company")})
print(f"DEBUG: Linked company {company.get('company')} to customer")
except Exception as e:
print(f"ERROR: Failed to link company {company.get('company')}: {str(e)}")
frappe.log_error(f"Company linking error: {str(e)}", "convert_lead_to_customer")
print(f"DEBUG: Converted Lead {lead_name} to Customer {customer_doc.name}")
return customer_doc
except Exception as e:
print(f"ERROR: Exception in convert_lead_to_customer: {str(e)}")
frappe.log_error(f"convert_lead_to_customer failed: {str(e)}", "ClientService")
raise