Files
handoff-pro/lib/db.py

157 lines
3.5 KiB
Python
Raw Normal View History

"""SQLite database initialization and access for Handoff Pro."""
import json
import os
import sqlite3
import uuid
from datetime import datetime
DB_PATH = os.environ.get("HANDOFF_DB",
os.path.join(os.path.dirname(__file__), "..", "data", "handoff.db"))
SCHEMA = """
CREATE TABLE IF NOT EXISTS jobs (
id TEXT PRIMARY KEY,
user_id TEXT DEFAULT 'kellow',
jobtread_job_id TEXT UNIQUE,
client_name TEXT,
jobtread_customer_id TEXT,
address TEXT,
description TEXT,
status TEXT DEFAULT 'proposal',
created_at TEXT,
updated_at TEXT,
synced_to_jobtread_at TEXT
);
CREATE TABLE IF NOT EXISTS estimates (
id TEXT PRIMARY KEY,
job_id TEXT REFERENCES jobs(id),
jobtread_estimate_id TEXT UNIQUE,
labor_hours REAL DEFAULT 0,
labor_rate REAL DEFAULT 0,
materials_cost REAL DEFAULT 0,
markup_percent REAL DEFAULT 20,
total_cost REAL DEFAULT 0,
notes TEXT,
status TEXT DEFAULT 'draft',
created_at TEXT,
synced_to_jobtread_at TEXT
);
CREATE TABLE IF NOT EXISTS line_items (
id TEXT PRIMARY KEY,
estimate_id TEXT REFERENCES estimates(id),
description TEXT,
category TEXT,
quantity REAL DEFAULT 1,
unit TEXT DEFAULT 'ea',
unit_cost REAL DEFAULT 0,
total REAL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS pricing_catalogs (
id TEXT PRIMARY KEY,
user_id TEXT DEFAULT 'kellow',
name TEXT,
labor_rates TEXT,
material_markups TEXT,
created_at TEXT
);
CREATE TABLE IF NOT EXISTS invoices (
id TEXT PRIMARY KEY,
estimate_id TEXT REFERENCES estimates(id),
job_id TEXT REFERENCES jobs(id),
invoice_number TEXT,
jobtread_invoice_id TEXT UNIQUE,
qbo_invoice_id TEXT,
amount_due REAL DEFAULT 0,
amount_paid REAL DEFAULT 0,
due_date TEXT,
status TEXT DEFAULT 'draft',
created_at TEXT,
synced_to_jobtread_at TEXT,
synced_to_qbo_at TEXT
);
CREATE TABLE IF NOT EXISTS daily_logs (
id TEXT PRIMARY KEY,
job_id TEXT REFERENCES jobs(id),
log_date TEXT,
crew_names TEXT,
hours_worked REAL DEFAULT 0,
notes TEXT,
photos TEXT,
created_at TEXT
);
CREATE TABLE IF NOT EXISTS signatures (
id TEXT PRIMARY KEY,
estimate_id TEXT REFERENCES estimates(id),
client_name TEXT,
signature_data TEXT,
signed_at TEXT
);
CREATE TABLE IF NOT EXISTS client_activity (
id TEXT PRIMARY KEY,
estimate_id TEXT REFERENCES estimates(id),
job_id TEXT REFERENCES jobs(id),
action TEXT,
metadata TEXT,
timestamp TEXT
);
CREATE TABLE IF NOT EXISTS jobtread_sync_log (
id TEXT PRIMARY KEY,
local_id TEXT,
jobtread_id TEXT,
entity_type TEXT,
action TEXT,
status TEXT DEFAULT 'pending',
error_message TEXT,
synced_at TEXT
);
CREATE TABLE IF NOT EXISTS project_milestones (
id TEXT PRIMARY KEY,
job_id TEXT REFERENCES jobs(id),
phase_name TEXT,
start_date TEXT,
end_date TEXT,
status TEXT DEFAULT 'pending'
);
"""
def get_db() -> sqlite3.Connection:
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
conn = sqlite3.connect(DB_PATH, timeout=10)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA busy_timeout=5000")
return conn
def init_db():
conn = get_db()
conn.executescript(SCHEMA)
conn.commit()
conn.close()
def new_id():
return str(uuid.uuid4())[:8]
def now():
return datetime.now().astimezone().isoformat()
def row_to_dict(row):
if row is None:
return None
return dict(row)
def rows_to_list(rows):
return [dict(r) for r in rows]
if __name__ == "__main__":
init_db()
print(json.dumps({"ok": True, "db_path": DB_PATH}))