feat(core): add address fields to contact service serialization and CRUD

This commit is contained in:
2026-07-04 00:19:23 +00:00
parent 11527c4c95
commit ec0353f469
+19 -23
View File
@@ -14,6 +14,9 @@ from app.models.company import Company
from app.models.contact import CompanyContact, Contact from app.models.contact import CompanyContact, Contact
ADDRESS_FIELDS = ("address_street", "address_city", "address_zip", "address_country", "address_state")
def _contact_to_dict(c: Contact, include_companies: bool = False) -> dict[str, Any]: def _contact_to_dict(c: Contact, include_companies: bool = False) -> dict[str, Any]:
"""Serialize a Contact ORM object to dict.""" """Serialize a Contact ORM object to dict."""
data: dict[str, Any] = { data: dict[str, Any] = {
@@ -27,6 +30,11 @@ def _contact_to_dict(c: Contact, include_companies: bool = False) -> dict[str, A
"department": c.department, "department": c.department,
"linkedin_url": c.linkedin_url, "linkedin_url": c.linkedin_url,
"notes": c.notes, "notes": c.notes,
"address_street": c.address_street,
"address_city": c.address_city,
"address_zip": c.address_zip,
"address_country": c.address_country,
"address_state": c.address_state,
"created_at": c.created_at.isoformat() if c.created_at else None, "created_at": c.created_at.isoformat() if c.created_at else None,
"updated_at": c.updated_at.isoformat() if c.updated_at else None, "updated_at": c.updated_at.isoformat() if c.updated_at else None,
} }
@@ -144,13 +152,17 @@ async def create_contact(
department=data.get("department"), department=data.get("department"),
linkedin_url=data.get("linkedin_url"), linkedin_url=data.get("linkedin_url"),
notes=data.get("notes"), notes=data.get("notes"),
address_street=data.get("address_street"),
address_city=data.get("address_city"),
address_zip=data.get("address_zip"),
address_country=data.get("address_country"),
address_state=data.get("address_state"),
created_by=user_id, created_by=user_id,
updated_by=user_id, updated_by=user_id,
) )
db.add(contact) db.add(contact)
await db.flush() await db.flush()
# Link to companies if company_ids provided
company_ids = data.get("company_ids") company_ids = data.get("company_ids")
linked_companies = [] linked_companies = []
if company_ids: if company_ids:
@@ -159,7 +171,6 @@ async def create_contact(
cid = uuid.UUID(cid_str) if isinstance(cid_str, str) else cid_str cid = uuid.UUID(cid_str) if isinstance(cid_str, str) else cid_str
except (ValueError, TypeError): except (ValueError, TypeError):
continue continue
# Verify company exists in tenant
comp_q = select(Company).where( comp_q = select(Company).where(
Company.id == cid, Company.id == cid,
Company.tenant_id == tenant_id, Company.tenant_id == tenant_id,
@@ -213,17 +224,11 @@ async def update_contact(
return None return None
changes: dict[str, Any] = {} changes: dict[str, Any] = {}
for field in ( all_fields = (
"first_name", "first_name", "last_name", "email", "phone", "mobile",
"last_name", "position", "department", "linkedin_url", "notes",
"email", ) + ADDRESS_FIELDS
"phone", for field in all_fields:
"mobile",
"position",
"department",
"linkedin_url",
"notes",
):
if field in data and data[field] is not None: if field in data and data[field] is not None:
old_val = getattr(contact, field) old_val = getattr(contact, field)
changes[field] = {"old": old_val, "new": data[field]} changes[field] = {"old": old_val, "new": data[field]}
@@ -258,12 +263,7 @@ async def soft_delete_contact(
await db.flush() await db.flush()
await log_audit( await log_audit(
db, db, tenant_id, user_id, "delete", "contact", contact_id,
tenant_id,
user_id,
"delete",
"contact",
contact_id,
changes={"first_name": contact.first_name, "last_name": contact.last_name}, changes={"first_name": contact.first_name, "last_name": contact.last_name},
) )
return True return True
@@ -285,10 +285,8 @@ async def gdpr_hard_delete_contact(
if contact is None: if contact is None:
return False return False
# Capture snapshot before deletion
snapshot = _contact_to_dict(contact) snapshot = _contact_to_dict(contact)
# Delete N:M links first
await db.execute( await db.execute(
delete(CompanyContact).where( delete(CompanyContact).where(
CompanyContact.contact_id == contact_id, CompanyContact.contact_id == contact_id,
@@ -296,11 +294,9 @@ async def gdpr_hard_delete_contact(
) )
) )
# Physical delete
await db.delete(contact) await db.delete(contact)
await db.flush() await db.flush()
# Deletion log (immutable snapshot)
await log_deletion( await log_deletion(
db, db,
tenant_id, tenant_id,