Capture phone (office) + mobile (cell) on card intake; ship v0.1.0:98

Completes business-card contact capture. The transcription prompt now labels
Phone/Mobile/Fax on separate lines, and the extractor maps an office/main number ->
phone and a cell -> mobile, never a fax. Both carry the same digit-in-source
integrity rule as email/LinkedIn: a number is kept only if its digits literally
appear in the source (or, on revise, the instruction) -- never minted. The proposal
card shows Phone + Mobile and they're editable (aliases phone/tel/office, mobile/cell).

Server: _upsert_contact_from_fundraising now accepts contact.phone + contact.mobile
and writes them to the canonical contact record (contact-level, not grid pills),
shipped in s9pk v0.1.0:98. No schema change -- the contacts columns already exist.

41/41 backend suite green + the matrix_intake units; card flow end-to-end is live-smoke.
This commit is contained in:
Keysat
2026-06-20 11:26:39 -05:00
parent 92ab59de4e
commit e824ff2206
12 changed files with 139 additions and 29 deletions
+7 -4
View File
@@ -826,6 +826,7 @@ def _upsert_contact_from_fundraising(conn, investor_name, contact, actor_user_id
location_query = str(contact.get('location_query') or '').strip()
linkedin_url = str(contact.get('linkedin_url') or '').strip()
phone = str(contact.get('phone') or '').strip()
mobile = str(contact.get('mobile') or '').strip()
if not full_name and not email:
return None
first_name, last_name = _split_full_name(full_name)
@@ -871,20 +872,21 @@ def _upsert_contact_from_fundraising(conn, investor_name, contact, actor_user_id
next_location_query = location_query or str(existing['location_query'] or '')
next_linkedin = linkedin_url or str(existing['linkedin_url'] or '')
next_phone = phone or str(existing['phone'] or '')
next_mobile = mobile or str(existing['mobile'] or '')
next_org = org_id or existing['organization_id']
conn.execute("""
UPDATE contacts
SET first_name = ?, last_name = ?, email = ?, title = ?,
organization_id = ?, source = ?, contact_type = 'investor', city = ?, state = ?, country = ?, location_query = ?, linkedin_url = ?, phone = ?, updated_at = ?
organization_id = ?, source = ?, contact_type = 'investor', city = ?, state = ?, country = ?, location_query = ?, linkedin_url = ?, phone = ?, mobile = ?, updated_at = ?
WHERE id = ?
""", (next_first, next_last, next_email, next_title, next_org, next_source, next_city, next_state, next_country, next_location_query, next_linkedin, next_phone, now(), existing['id']))
""", (next_first, next_last, next_email, next_title, next_org, next_source, next_city, next_state, next_country, next_location_query, next_linkedin, next_phone, next_mobile, now(), existing['id']))
return existing['id']
contact_id = generate_id()
conn.execute("""
INSERT INTO contacts (
id, first_name, last_name, email, title, organization_id, source, contact_type, status, city, state, country, location_query, linkedin_url, phone, created_by, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, 'investor', 'active', ?, ?, ?, ?, ?, ?, ?, ?)
id, first_name, last_name, email, title, organization_id, source, contact_type, status, city, state, country, location_query, linkedin_url, phone, mobile, created_by, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, 'investor', 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
contact_id,
first_name or 'Unknown',
@@ -899,6 +901,7 @@ def _upsert_contact_from_fundraising(conn, investor_name, contact, actor_user_id
location_query,
linkedin_url,
phone,
mobile,
actor_user_id,
now()
))