Remove Instructions/Feedback + lp_profiles; sync retry, purge, mobile fixes (v0.1.0:104)
Removals (net -570 lines): - Delete the Instructions and Feedback (feature_requests) pages + backend. - Retire lp_profiles + investor_type across server, ingest, and seeds; migration 0008 drops both empty tables (a sanctioned one-off exception to never-hard-delete). 0001's lp_profiles ALTER is removed so a fresh DB doesn't break the migration chain (live DBs already applied it). Fixes: - Email sync: a transient timeout no longer terminally parks a mailbox; the scheduler retries 'retrying' each cycle and re-includes errored accounts on an hourly backoff, so stuck mailboxes self-heal. - Mobile Contacts: page through the full directory (server caps 500/page) -- one fetch silently truncated at 720, hiding people from the list and from search. - Mobile email review: clock icon to set a reminder inline; approval cards show date/time. New: - Admin-only purge of soft-deleted rows (Settings -> Admin; type-to-confirm, refuses any row still linked to live data). Tests: 45/45 (adds test_sync_ready + test_purge_soft_deleted). Reviewer pass applied (NULL reminders.contact_id on contact purge). Bumped to v0.1.0:104.
This commit is contained in:
@@ -11,8 +11,8 @@ What it builds (into a SEPARATE dev DB, never crm.db):
|
||||
core migration (backend/migrations/), so the canonical/interaction/graph
|
||||
tables exist.
|
||||
* A classic-model dataset: organizations, contacts (investors + prospects),
|
||||
opportunities across pipeline stages, communications with entity-rich prose
|
||||
notes, and lp_profiles.
|
||||
opportunities across pipeline stages, and communications with entity-rich
|
||||
prose notes.
|
||||
* A fundraising grid (fundraising_state.grid_json) populated via the real
|
||||
sync_fundraising_relational() code path, so the normalized mirror + the
|
||||
grid->classic bridge behave exactly as in production.
|
||||
@@ -179,7 +179,7 @@ def main():
|
||||
f"Prospect sourced via {random.choice(['X DM', 'warm intro', 'podcast'])}.", uid, now()))
|
||||
contacts.append((cid, first, last, org_name, "prospect"))
|
||||
|
||||
# ── opportunities + lp_profiles + communications ──
|
||||
# ── opportunities + communications ──
|
||||
stages = server.PIPELINE_STAGES
|
||||
for idx, (cid, first, last, org_name, ctype) in enumerate(contacts):
|
||||
person = f"{first} {last}"
|
||||
@@ -199,19 +199,6 @@ def main():
|
||||
random.choice(["Send deck", "Schedule call", "Await IC", "Send subdocs"]),
|
||||
uid, random.choice(["low", "medium", "high"]), now()))
|
||||
|
||||
# lp_profile for ~closed investors
|
||||
if ctype == "investor" and idx % 2 == 0:
|
||||
amt = random.choice(AMOUNTS)
|
||||
conn.execute(
|
||||
"INSERT INTO lp_profiles (id, contact_id, commitment_amount, funded_amount, commitment_date, "
|
||||
"fund_name, investor_type, accredited, legal_docs_signed, wire_received, k1_sent, notes, updated_at) "
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
(gen(), cid, amt, amt if idx % 4 == 0 else 0, past(120),
|
||||
random.choice(list(FUND_LABELS.values())),
|
||||
random.choice(["family_office", "institutional", "endowment", "individual"]),
|
||||
1, 1 if idx % 3 else 0, 1 if idx % 4 == 0 else 0, 0,
|
||||
f"Closed LP. Accreditation on file. Primary contact {person}.", now()))
|
||||
|
||||
# 2-4 communications each, entity-rich prose
|
||||
for k in range(random.randint(2, 4)):
|
||||
ctype_comm, subj, body = random.choice(COMM_TEMPLATES)
|
||||
@@ -275,7 +262,7 @@ def main():
|
||||
|
||||
print(f"\nSynthetic dev DB written to: {db}")
|
||||
print(" Classic model:")
|
||||
for t in ("organizations", "contacts", "opportunities", "communications", "lp_profiles"):
|
||||
for t in ("organizations", "contacts", "opportunities", "communications"):
|
||||
print(f" {t:<24} {count(t)}")
|
||||
print(" Fundraising grid (after real sync):")
|
||||
for t in ("fundraising_investors", "fundraising_contacts", "fundraising_funds",
|
||||
|
||||
Reference in New Issue
Block a user