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