Files
service-finder/backend/scripts/seed_financial_ledger.py
2026-07-27 08:39:18 +00:00

156 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""
Seed script: Populate audit.financial_ledger with 20 dummy records.
Uses psycopg2 (sync) to bypass asyncpg prepared statement enum validation issues.
"""
import uuid
import random
import os
from datetime import datetime, timedelta, timezone
import psycopg2
TRANSACTION_TYPES = [
"SUBSCRIPTION", "COMMISSION", "REFUND", "MANUAL_ADJUSTMENT",
"PURCHASE", "WITHDRAWAL", "BONUS", "FEE", "REWARD",
]
DB_ENTRY_TYPES = ["CREDIT", "DEBIT"]
DB_WALLET_TYPES = ["EARNED", "PURCHASED", "SERVICE_COINS", "VOUCHER"]
DB_STATUSES = ["PENDING", "SUCCESS", "FAILED", "REFUNDED"]
TEST_USER_IDS = [1, 2, 28, 29, 55, 75, 79, 85, 86, 88, 100, 102, 103, 104, 105, 106]
NOW = datetime.now(timezone.utc)
def get_dsn_params():
"""Extract connection parameters from DATABASE_URL."""
db_url = os.environ.get("DATABASE_URL", "postgresql+asyncpg://service_finder_app:AppSafePass_2026@shared-postgres:5432/service_finder")
db_url = db_url.replace("postgresql+asyncpg://", "postgresql://")
# Parse URL
parts = db_url.replace("postgresql://", "").split("@")
user_pass = parts[0].split(":")
host_db = parts[1].split("/")
host_port = host_db[0].split(":")
return {
"user": user_pass[0],
"password": user_pass[1],
"host": host_port[0],
"port": int(host_port[1]) if len(host_port) > 1 else 5432,
"dbname": host_db[1].split("?")[0],
}
def seed():
params = get_dsn_params()
conn = psycopg2.connect(**params)
conn.autocommit = False
try:
with conn.cursor() as cur:
# Count existing
cur.execute("SELECT COUNT(*) FROM audit.financial_ledger")
print(f"Existing records: {cur.fetchone()[0]}")
# Get person_ids
person_map = {}
for uid in TEST_USER_IDS:
cur.execute("SELECT person_id FROM identity.users WHERE id = %s", (uid,))
pid = cur.fetchone()
if pid and pid[0]:
person_map[uid] = pid[0]
if not person_map:
print("No users with person_id found!")
return
print(f"Users with person records: {list(person_map.keys())}")
inserted = 0
for i in range(20):
uid = random.choice(list(person_map.keys()))
pid = person_map[uid]
entry_t = random.choice(DB_ENTRY_TYPES)
wallet_t = random.choice(DB_WALLET_TYPES)
stat = random.choice(DB_STATUSES)
txn_t = random.choice(TRANSACTION_TYPES)
cur_r = random.choice(["HUF", "EUR", "USD", "GBP"])
amt = round(random.uniform(5.0, 5000.0), 2)
days_ago = random.randint(0, 30)
created_ts = NOW - timedelta(
days=random.randint(0, days_ago),
hours=random.randint(0, 23),
minutes=random.randint(0, 59),
)
ref = "TXN-" + uuid.uuid4().hex[:8].upper()
desc = txn_t.lower().replace("_", " ").title() + f" for user {uid}"
net_amt = round(amt / 1.27, 2) if random.random() > 0.3 else None
tax_amt = round(amt - net_amt, 2) if net_amt else None
balance = round(random.uniform(100, 50000), 2)
inv_stat = "PAID" if stat == "completed" else stat.upper()
txn_id = str(uuid.uuid4())
# psycopg2 handles text → enum cast correctly with simple execute
cur.execute(
"""
INSERT INTO audit.financial_ledger
(user_id, person_id, amount, currency, transaction_type, details,
created_at, entry_type, balance_after, wallet_type, status,
transaction_id, net_amount, tax_amount, gross_amount, invoice_status)
VALUES (
%s, %s, %s, %s, %s,
%s::jsonb,
%s::timestamptz,
%s::audit.ledger_entry_type,
%s,
%s::audit.wallet_type,
%s::audit.ledger_status,
%s::uuid,
%s, %s, %s, %s
)
""",
(
uid, pid, amt, cur_r, txn_t,
f'{{"description": "{desc}", "reference": "{ref}"}}',
created_ts.isoformat(),
entry_t, balance, wallet_t, stat, txn_id,
net_amt, tax_amt, amt, inv_stat,
),
)
inserted += 1
conn.commit()
print(f"✅ Inserted {inserted} records successfully!")
cur.execute("SELECT COUNT(*) FROM audit.financial_ledger")
print(f"Total records now: {cur.fetchone()[0]}")
cur.execute("""
SELECT
fl.entry_type::text,
fl.wallet_type::text,
fl.status::text,
COUNT(*)::int,
ROUND(AVG(fl.amount)::numeric, 2),
u.email
FROM audit.financial_ledger fl
LEFT JOIN identity.users u ON u.id = fl.user_id
GROUP BY fl.entry_type, fl.wallet_type, fl.status, u.email
ORDER BY fl.entry_type, fl.wallet_type
""")
rows = cur.fetchall()
total_sum = sum(r[3] for r in rows) if rows else 0
print(f"\n📊 Summary (total {total_sum} records):")
print(f" {'Entry':8} {'Wallet':15} {'Status':10} {'Cnt':5} {'Avg Amt':10} {'User'}")
print(f" {'-'*8} {'-'*15} {'-'*10} {'-'*5} {'-'*10} {'-'*30}")
for r in rows:
print(f" {r[0]:8} {r[1]:15} {r[2]:10} {r[3]:5} {r[4]:10} {r[5] or 'N/A':30}")
finally:
conn.close()
if __name__ == "__main__":
seed()