Remove Google Sheets mirror entirely (#13)
Test backoffice (smb-crm) / test (push) Has been cancelled
Test backoffice (smb-crm) / test (push) Has been cancelled
Postgres is now the sole source of truth: delete sheets.py and import_from_sheets.py, strip mirror_entity/mirror_async/_mirror_worker and POST /api/sync from app.py, drop the tab/mirror keys from db.py's TABLES. Re-point n8n/renewal-reminder.json at the CRM's own HTTP API (GET /api/clients, POST /api/activity_log) instead of the Sheets nodes, and drop SHEET_ID/GOOGLE_SA_JSON from deploy env/compose and requests from requirements.txt (PyJWT stays — still used by booking_api.py). Updates docs/README/playbooks accordingly and closes the old #5 (atomic mirror) as moot. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+5
-12
@@ -1,8 +1,8 @@
|
||||
"""Postgres access + the table/column contract shared across the app.
|
||||
|
||||
The TABLES map is the single definition of which entities exist, their columns,
|
||||
and their primary key. Read/CRUD endpoints, the importer and the Sheets mirror
|
||||
all derive from it so they can never drift apart.
|
||||
and their primary key. Read/CRUD endpoints all derive from it so they can
|
||||
never drift apart.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
@@ -13,10 +13,9 @@ from psycopg.rows import dict_row
|
||||
|
||||
DATABASE_URL = os.environ["DATABASE_URL"]
|
||||
|
||||
# entity -> (sheet tab, primary key, ordered columns)
|
||||
# entity -> (primary key, ordered columns)
|
||||
TABLES = {
|
||||
"clients": {
|
||||
"tab": "Clients",
|
||||
"pk": "client_id",
|
||||
"cols": ["client_id", "business_name", "owner_name", "email", "phone",
|
||||
"niche", "tier", "status", "domain", "stack_notes", "vault_ref",
|
||||
@@ -28,7 +27,6 @@ TABLES = {
|
||||
"bools": [],
|
||||
},
|
||||
"leads": {
|
||||
"tab": "Leads",
|
||||
"pk": "lead_id",
|
||||
"cols": ["lead_id", "received_at", "client_id", "source", "name",
|
||||
"contact", "service_interest", "message", "status", "notified"],
|
||||
@@ -38,7 +36,6 @@ TABLES = {
|
||||
"bools": ["notified"],
|
||||
},
|
||||
"projects": {
|
||||
"tab": "Projects",
|
||||
"pk": "project_id",
|
||||
"cols": ["project_id", "client_id", "deliverable", "tier", "checklist",
|
||||
"go_live_date", "status"],
|
||||
@@ -48,7 +45,6 @@ TABLES = {
|
||||
"bools": [],
|
||||
},
|
||||
"bookings": {
|
||||
"tab": "Bookings",
|
||||
"pk": "booking_id",
|
||||
"cols": ["booking_id", "created_at", "client_id", "customer_name",
|
||||
"customer_contact", "service", "start_time", "end_time",
|
||||
@@ -59,7 +55,6 @@ TABLES = {
|
||||
"bools": [],
|
||||
},
|
||||
"invoices": {
|
||||
"tab": "Invoices",
|
||||
"pk": "invoice_id",
|
||||
"cols": ["invoice_id", "client_id", "issued_date", "due_date",
|
||||
"amount_eur", "period", "status", "paid_date"],
|
||||
@@ -69,7 +64,6 @@ TABLES = {
|
||||
"bools": [],
|
||||
},
|
||||
"activity_log": {
|
||||
"tab": "Activity Log",
|
||||
"pk": "id",
|
||||
"cols": ["ts", "workflow", "client_id", "action", "detail", "result"],
|
||||
"dates": [],
|
||||
@@ -78,14 +72,13 @@ TABLES = {
|
||||
"bools": [],
|
||||
},
|
||||
"credentials": {
|
||||
# No "tab": never mirrored to Sheets (see "mirror" below) — secrets stay in Postgres only.
|
||||
# Secrets stay in Postgres only.
|
||||
"pk": "cred_id",
|
||||
"cols": ["cred_id", "client_id", "label", "username", "secret", "notes", "created_at"],
|
||||
"dates": [],
|
||||
"timestamps": ["created_at"],
|
||||
"numbers": [],
|
||||
"bools": [],
|
||||
"mirror": False,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -94,7 +87,7 @@ def connect():
|
||||
return psycopg.connect(DATABASE_URL, row_factory=dict_row)
|
||||
|
||||
|
||||
# ---- coercion: Sheet strings / JSON values -> typed Python for Postgres ----
|
||||
# ---- coercion: JSON request values -> typed Python for Postgres ----
|
||||
|
||||
def parse_date(v):
|
||||
if v in (None, ""):
|
||||
|
||||
Reference in New Issue
Block a user