563 lines
23 KiB
Python
563 lines
23 KiB
Python
"""
|
|
Audit Firm ERP -- strict UAT seed patch for FY lock/backup Playwright tests.
|
|
|
|
Run inside ERP container after core UAT seed:
|
|
|
|
cd /app
|
|
PYTHONPATH=/app python scripts/seed_uat_fy_lock_backup_strict.py
|
|
|
|
Purpose:
|
|
Creates deterministic UAT records needed by tests/fy-lock-backup.spec.js without
|
|
weakening test assertions or changing ERP features.
|
|
|
|
Scope:
|
|
- Financial years: 2025-26 current/open and 2024-25 locked for Tenant A
|
|
- Tenant/branch B scenario rows where base seed exists
|
|
- Active FY and locked FY subscriptions/tasks
|
|
- Engagement document and permanent document records
|
|
- Notice case record
|
|
- Billing invoice/payment records
|
|
|
|
The script is idempotent and only upserts UAT-coded records. Intended for UAT/test
|
|
containers, not live production data.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import date, datetime, timedelta, timezone
|
|
|
|
ACTIVE_FY = os.getenv("ACTIVE_FY", "2025-26")
|
|
LOCKED_FY = os.getenv("LOCKED_FY", "2024-25")
|
|
ACTIVE_AY = os.getenv("ACTIVE_AY", "2026-27")
|
|
LOCKED_AY = os.getenv("LOCKED_AY", "2025-26")
|
|
UAT_DOMAIN = os.getenv("UAT_EMAIL_DOMAIN", "vavalam.com")
|
|
|
|
|
|
def fail(msg: str) -> None:
|
|
print(f"\n[fy-strict-seed] ERROR: {msg}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
def utcnow() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
def main() -> None:
|
|
try:
|
|
from sqlalchemy import MetaData, and_, inspect, select, update, text
|
|
from app.core.db.common import CommonSessionLocal
|
|
except Exception as exc:
|
|
fail(f"Run this from /app inside ERP container. Import error: {exc!r}")
|
|
|
|
db = CommonSessionLocal()
|
|
engine = db.get_bind()
|
|
meta = MetaData()
|
|
meta.reflect(bind=engine)
|
|
inspector = inspect(engine)
|
|
table_names = set(inspector.get_table_names())
|
|
notes: list[str] = []
|
|
|
|
def norm(name: str) -> str:
|
|
return name.lower().replace("_", "").replace("-", "")
|
|
|
|
def table(*names: str):
|
|
for name in names:
|
|
if name in meta.tables:
|
|
return meta.tables[name]
|
|
lookup = {norm(t): t for t in table_names}
|
|
for name in names:
|
|
hit = lookup.get(norm(name))
|
|
if hit:
|
|
return meta.tables[hit]
|
|
return None
|
|
|
|
def valid(t, data: dict) -> dict:
|
|
if t is None:
|
|
return {}
|
|
cols = set(t.c.keys())
|
|
return {k: v for k, v in data.items() if k in cols and v is not None}
|
|
|
|
def where(t, lookup: dict):
|
|
lookup = valid(t, lookup)
|
|
if not lookup:
|
|
return None
|
|
return and_(*[t.c[k] == v for k, v in lookup.items()])
|
|
|
|
def one(t, lookup: dict):
|
|
if t is None:
|
|
return None
|
|
clause = where(t, lookup)
|
|
if clause is None:
|
|
return None
|
|
return db.execute(select(t).where(clause)).mappings().first()
|
|
|
|
def first(t, *lookups: dict):
|
|
if t is None:
|
|
return None
|
|
for lookup in lookups:
|
|
row = one(t, lookup)
|
|
if row:
|
|
return row
|
|
return db.execute(select(t).limit(1)).mappings().first()
|
|
|
|
def scalar(sql: str):
|
|
try:
|
|
return db.execute(text(sql)).scalar()
|
|
except Exception:
|
|
return None
|
|
|
|
def py_default(t, col_name: str):
|
|
name = col_name.lower()
|
|
if name.endswith("_at_utc") or name.endswith("_at"):
|
|
return utcnow()
|
|
if name.endswith("_date"):
|
|
return date.today()
|
|
if name in {"status", "export_status"}:
|
|
return "active"
|
|
if name in {"is_active"}:
|
|
return True
|
|
if name in {"is_deleted", "is_locked", "is_current"}:
|
|
return False
|
|
try:
|
|
pytype = t.c[col_name].type.python_type
|
|
except Exception:
|
|
pytype = str
|
|
if pytype is bool:
|
|
return False
|
|
if pytype is int:
|
|
return 0
|
|
if pytype is float:
|
|
return 0.0
|
|
if pytype is date:
|
|
return date.today()
|
|
if pytype is datetime:
|
|
return utcnow()
|
|
return f"UAT {col_name.replace('_', ' ').title()}"
|
|
|
|
def add_required_defaults(t, data: dict) -> dict:
|
|
out = dict(data)
|
|
for col in t.c:
|
|
if col.name in out:
|
|
continue
|
|
if col.primary_key or col.nullable or col.default is not None or col.server_default is not None:
|
|
continue
|
|
out[col.name] = py_default(t, col.name)
|
|
return valid(t, out)
|
|
|
|
def upsert(t, lookup: dict, defaults: dict, label: str):
|
|
if t is None:
|
|
notes.append(f"SKIP {label}: table missing")
|
|
return None
|
|
lookup_v = valid(t, lookup)
|
|
if not lookup_v:
|
|
notes.append(f"SKIP {label}: lookup columns missing in {t.name}")
|
|
return None
|
|
row = one(t, lookup_v)
|
|
values = valid(t, defaults)
|
|
if row:
|
|
if values:
|
|
db.execute(update(t).where(where(t, lookup_v)).values(**values))
|
|
db.flush()
|
|
return one(t, lookup_v)
|
|
data = add_required_defaults(t, {**lookup_v, **values})
|
|
try:
|
|
result = db.execute(t.insert().values(**data))
|
|
db.flush()
|
|
pk = result.inserted_primary_key[0] if result.inserted_primary_key else None
|
|
if pk and "id" in t.c:
|
|
return one(t, {"id": pk})
|
|
return one(t, lookup_v)
|
|
except Exception as exc:
|
|
db.rollback()
|
|
notes.append(f"SKIP {label}: insert failed in {t.name}: {exc.__class__.__name__}: {str(exc).splitlines()[0]}")
|
|
return one(t, lookup_v)
|
|
|
|
def v(row, key: str, default=None):
|
|
return row[key] if row is not None and key in row else default
|
|
|
|
tenants = table("tenants")
|
|
branches = table("branches")
|
|
users = table("users")
|
|
clients = table("clients")
|
|
service_catalogues = table("service_catalogues", "services")
|
|
financial_years = table("financial_years")
|
|
subscriptions = table("client_service_subscriptions")
|
|
tasks = table("client_service_task_instances")
|
|
engagement_documents = table("engagement_documents")
|
|
engagement_document_versions = table("engagement_document_versions")
|
|
permanent_docs = table("permanent_client_documents")
|
|
permanent_versions = table("permanent_client_document_versions")
|
|
notice_cases = table("notice_cases")
|
|
notice_docs = table("notice_case_documents")
|
|
invoices = table("billing_invoices")
|
|
payments = table("billing_payments")
|
|
|
|
tenant_a = first(tenants, {"code": "UAT-A"}, {"tenant_code": "UAT-A"}, {"name": "UAT Tenant A"})
|
|
tenant_b = first(tenants, {"code": "UAT-B"}, {"tenant_code": "UAT-B"}, {"name": "UAT Tenant B"}) or tenant_a
|
|
branch_a = first(branches, {"code": "UAT-BA"}, {"branch_code": "UAT-BA"}, {"name": "UAT Branch A"})
|
|
branch_b = first(branches, {"code": "UAT-BB"}, {"branch_code": "UAT-BB"}, {"name": "UAT Branch B"}) or branch_a
|
|
admin = first(users, {"email": f"uat.admin@{UAT_DOMAIN}"}, {"email": f"uat.firmadmin@{UAT_DOMAIN}"})
|
|
firm_admin = first(users, {"email": f"uat.firmadmin@{UAT_DOMAIN}"}) or admin
|
|
partner = first(users, {"email": f"uat.partner@{UAT_DOMAIN}"}) or firm_admin
|
|
manager = first(users, {"email": f"uat.manager@{UAT_DOMAIN}"}) or firm_admin
|
|
staff = first(users, {"email": f"uat.staff@{UAT_DOMAIN}"}) or firm_admin
|
|
branch_b_staff = first(users, {"email": f"uat.branch2staff@{UAT_DOMAIN}"}) or staff
|
|
client_user = first(users, {"email": f"uat.client@{UAT_DOMAIN}"})
|
|
|
|
if not tenant_a or not branch_a or not admin:
|
|
fail("Core UAT seed missing: tenant A, branch A, or admin user not found.")
|
|
|
|
tenant_a_id = v(tenant_a, "id")
|
|
tenant_b_id = v(tenant_b, "id")
|
|
branch_a_id = v(branch_a, "id")
|
|
branch_b_id = v(branch_b, "id")
|
|
admin_id = v(admin, "id")
|
|
partner_id = v(partner, "id")
|
|
manager_id = v(manager, "id")
|
|
staff_id = v(staff, "id")
|
|
branch_b_staff_id = v(branch_b_staff, "id")
|
|
|
|
# Ensure FY records required by the suite.
|
|
fy_active = upsert(financial_years, {"tenant_id": tenant_a_id, "year_code": ACTIVE_FY}, {
|
|
"assessment_year": ACTIVE_AY,
|
|
"start_date": date(int(ACTIVE_FY[:4]), 4, 1),
|
|
"end_date": date(int(ACTIVE_FY[:4]) + 1, 3, 31),
|
|
"is_current": True,
|
|
"is_locked": False,
|
|
"locked_at_utc": None,
|
|
"locked_by_user_id": None,
|
|
"updated_at_utc": utcnow(),
|
|
}, "active financial year")
|
|
fy_locked = upsert(financial_years, {"tenant_id": tenant_a_id, "year_code": LOCKED_FY}, {
|
|
"assessment_year": LOCKED_AY,
|
|
"start_date": date(int(LOCKED_FY[:4]), 4, 1),
|
|
"end_date": date(int(LOCKED_FY[:4]) + 1, 3, 31),
|
|
"is_current": False,
|
|
"is_locked": True,
|
|
"locked_at_utc": utcnow(),
|
|
"locked_by_user_id": admin_id,
|
|
"updated_at_utc": utcnow(),
|
|
}, "locked financial year")
|
|
if financial_years is not None and fy_active:
|
|
try:
|
|
db.execute(update(financial_years).where(
|
|
and_(financial_years.c.tenant_id == tenant_a_id, financial_years.c.year_code != ACTIVE_FY)
|
|
).values(is_current=False))
|
|
db.execute(update(financial_years).where(
|
|
and_(financial_years.c.tenant_id == tenant_a_id, financial_years.c.year_code == ACTIVE_FY)
|
|
).values(is_current=True, is_locked=False, locked_at_utc=None, locked_by_user_id=None))
|
|
db.flush()
|
|
except Exception as exc:
|
|
notes.append(f"Could not normalize current FY flag: {exc.__class__.__name__}: {exc}")
|
|
|
|
service = first(service_catalogues, {"service_code": "GST-GSTR3B-M"}, {"code": "GST-GSTR3B-M"}) or first(service_catalogues, {})
|
|
if not service:
|
|
fail("No service catalogue/service row available. Run service seed first.")
|
|
service_id = v(service, "id")
|
|
|
|
# Ensure Client A and Client B records where possible.
|
|
client_a = first(clients, {"client_code": "UAT-CL-A"}, {"code": "UAT-CL-A"}, {"client_name": "UAT Client A Pvt Ltd"})
|
|
client_a = client_a or upsert(clients, {"tenant_id": tenant_a_id, "client_code": "UAT-CL-A"}, {
|
|
"branch_id": branch_a_id,
|
|
"client_name": "UAT Client A Pvt Ltd",
|
|
"trade_name": "UAT Client A",
|
|
"pan": "ABCDE1234F",
|
|
"gstin": "33ABCDE1234F1Z5",
|
|
"email": f"uat.client@{UAT_DOMAIN}",
|
|
"mobile": "9000000004",
|
|
"status": "active",
|
|
"is_active": True,
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, "client A")
|
|
client_b = first(clients, {"client_code": "UAT-CL-B"}, {"code": "UAT-CL-B"}, {"client_name": "UAT Client B Pvt Ltd"})
|
|
client_b = client_b or upsert(clients, {"tenant_id": tenant_b_id, "client_code": "UAT-CL-B"}, {
|
|
"branch_id": branch_b_id,
|
|
"client_name": "UAT Client B Pvt Ltd",
|
|
"trade_name": "UAT Client B",
|
|
"pan": "ABCDE5678F",
|
|
"gstin": "33ABCDE5678F1Z5",
|
|
"email": f"uat.client2@{UAT_DOMAIN}",
|
|
"mobile": "9000000005",
|
|
"status": "active",
|
|
"is_active": True,
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, "client B")
|
|
client_a_id = v(client_a, "id")
|
|
client_b_id = v(client_b, "id", client_a_id)
|
|
|
|
# Subscriptions / engagements.
|
|
def sub_seed(label, tenant_id, branch_id, client_id, fy, ay, assignee_staff_id, locked=False):
|
|
return upsert(subscriptions, {"tenant_id": tenant_id, "client_id": client_id, "service_catalogue_id": service_id, "financial_year": fy}, {
|
|
"branch_id": branch_id,
|
|
"assessment_year": ay,
|
|
"assigned_partner_user_id": partner_id,
|
|
"assigned_manager_user_id": manager_id,
|
|
"assigned_staff_user_id": assignee_staff_id,
|
|
"review_partner_user_id": partner_id,
|
|
"engagement_type": "non_assurance",
|
|
"status": "active",
|
|
"is_active": True,
|
|
"is_locked": locked,
|
|
"locked_at_utc": utcnow() if locked else None,
|
|
"locked_by_user_id": admin_id if locked else None,
|
|
"start_date": date(int(fy[:4]), 4, 1),
|
|
"end_date": date(int(fy[:4]) + 1, 3, 31),
|
|
"original_due_date": date(int(fy[:4]) + 1, 4, 20),
|
|
"current_due_date": date(int(fy[:4]) + 1, 4, 20),
|
|
"remarks": f"Strict UAT seed engagement for {label}",
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, label)
|
|
|
|
sub_active = sub_seed("active FY subscription", tenant_a_id, branch_a_id, client_a_id, ACTIVE_FY, ACTIVE_AY, staff_id, False)
|
|
sub_locked = sub_seed("locked FY subscription", tenant_a_id, branch_a_id, client_a_id, LOCKED_FY, LOCKED_AY, staff_id, True)
|
|
sub_branch_b = sub_seed("branch B subscription", tenant_b_id, branch_b_id, client_b_id, ACTIVE_FY, ACTIVE_AY, branch_b_staff_id, False)
|
|
|
|
# Task instances.
|
|
def task_seed(label, sub, tenant_id, branch_id, client_id, fy, ay, task_name, assignee_id, status="pending", locked=False, seq=1):
|
|
return upsert(tasks, {"subscription_id": v(sub, "id"), "financial_year": fy, "task_name": task_name}, {
|
|
"tenant_id": tenant_id,
|
|
"branch_id": branch_id,
|
|
"client_id": client_id,
|
|
"service_catalogue_id": service_id,
|
|
"assessment_year": ay,
|
|
"description": f"Strict UAT seed task for {label}",
|
|
"sequence_no": seq,
|
|
"default_role_name": "Staff",
|
|
"assigned_to_user_id": assignee_id,
|
|
"internal_target_date": date.today() + timedelta(days=10),
|
|
"status": status,
|
|
"priority": "normal",
|
|
"is_active": True,
|
|
"is_locked": locked,
|
|
"locked_at_utc": utcnow() if locked else None,
|
|
"locked_by_user_id": admin_id if locked else None,
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, label)
|
|
|
|
task_active = task_seed("active FY task", sub_active, tenant_a_id, branch_a_id, client_a_id, ACTIVE_FY, ACTIVE_AY, "UAT Active FY Task", staff_id, "pending", False, 10)
|
|
task_partner = task_seed("partner review task", sub_active, tenant_a_id, branch_a_id, client_a_id, ACTIVE_FY, ACTIVE_AY, "UAT Partner Review Task", partner_id, "pending", False, 20)
|
|
task_in_progress = task_seed("in progress task", sub_active, tenant_a_id, branch_a_id, client_a_id, ACTIVE_FY, ACTIVE_AY, "UAT In Progress Task", staff_id, "in_progress", False, 30)
|
|
task_completed = task_seed("completed task", sub_active, tenant_a_id, branch_a_id, client_a_id, ACTIVE_FY, ACTIVE_AY, "UAT Completed Task", staff_id, "completed", False, 40)
|
|
task_locked = task_seed("locked FY task", sub_locked, tenant_a_id, branch_a_id, client_a_id, LOCKED_FY, LOCKED_AY, "UAT Locked FY Task", staff_id, "pending", True, 50)
|
|
task_branch_b = task_seed("branch B active FY task", sub_branch_b, tenant_b_id, branch_b_id, client_b_id, ACTIVE_FY, ACTIVE_AY, "UAT Branch B Active FY Task", branch_b_staff_id, "pending", False, 60)
|
|
|
|
# Engagement document and version.
|
|
doc_content = b"Strict UAT FY engagement document\n"
|
|
eng_doc = upsert(engagement_documents, {"tenant_id": tenant_a_id, "engagement_id": v(sub_active, "id"), "document_code": "UAT-FY-ENG-DOC-001"}, {
|
|
"branch_id": branch_a_id,
|
|
"client_id": client_a_id,
|
|
"task_instance_id": v(task_active, "id"),
|
|
"financial_year": ACTIVE_FY,
|
|
"assessment_year": ACTIVE_AY,
|
|
"document_type": "GENERAL",
|
|
"title": "UAT FY Engagement Document",
|
|
"description": "Strict UAT seed engagement document",
|
|
"current_version_no": 1,
|
|
"status": "active",
|
|
"is_deleted": False,
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, "engagement document")
|
|
eng_doc_ver = upsert(engagement_document_versions, {"document_id": v(eng_doc, "id"), "version_no": 1}, {
|
|
"tenant_id": tenant_a_id,
|
|
"branch_id": branch_a_id,
|
|
"client_id": client_a_id,
|
|
"engagement_id": v(sub_active, "id"),
|
|
"original_filename": "uat-fy-engagement-document.txt",
|
|
"stored_filename": "uat-fy-engagement-document.txt",
|
|
"content_type": "text/plain",
|
|
"file_size_bytes": len(doc_content),
|
|
"file_hash_sha256": hashlib.sha256(doc_content).hexdigest(),
|
|
"storage_backend": "LOCAL_YEAR_WISE",
|
|
"local_relative_path": f"UAT-A/{ACTIVE_FY}/engagements/uat-fy-engagement-document.txt",
|
|
"storage_status": "stored",
|
|
"remarks": "Strict UAT seed engagement document version",
|
|
"uploaded_by_user_id": admin_id,
|
|
"uploaded_at_utc": utcnow(),
|
|
}, "engagement document version")
|
|
|
|
# Permanent document.
|
|
perm_doc = upsert(permanent_docs, {"tenant_id": tenant_a_id, "client_id": client_a_id, "document_code": "UAT-FY-PERM-DOC-001"}, {
|
|
"branch_id": branch_a_id,
|
|
"category": "KYC",
|
|
"title": "UAT FY Permanent Client Document",
|
|
"description": "Strict UAT seed permanent document",
|
|
"current_version_no": 1,
|
|
"status": "active",
|
|
"is_deleted": False,
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, "permanent client document")
|
|
perm_content = b"Strict UAT permanent document\n"
|
|
perm_doc_ver = upsert(permanent_versions, {"document_id": v(perm_doc, "id"), "version_no": 1}, {
|
|
"tenant_id": tenant_a_id,
|
|
"branch_id": branch_a_id,
|
|
"client_id": client_a_id,
|
|
"original_filename": "uat-fy-permanent-document.txt",
|
|
"stored_filename": "uat-fy-permanent-document.txt",
|
|
"content_type": "text/plain",
|
|
"file_size_bytes": len(perm_content),
|
|
"file_hash_sha256": hashlib.sha256(perm_content).hexdigest(),
|
|
"storage_backend": "LOCAL_PERMANENT",
|
|
"local_relative_path": "UAT-A/UAT-CL-A/permanent/uat-fy-permanent-document.txt",
|
|
"storage_status": "stored",
|
|
"remarks": "Strict UAT seed permanent document version",
|
|
"uploaded_by_user_id": admin_id,
|
|
"uploaded_at_utc": utcnow(),
|
|
}, "permanent client document version")
|
|
|
|
# Notice case and document.
|
|
notice_case = upsert(notice_cases, {"tenant_id": tenant_a_id, "case_code": "UAT-FY-NC-001"}, {
|
|
"branch_id": branch_a_id,
|
|
"client_id": client_a_id,
|
|
"engagement_id": v(sub_active, "id"),
|
|
"service_catalogue_id": service_id,
|
|
"financial_year": ACTIVE_FY,
|
|
"assessment_year": ACTIVE_AY,
|
|
"reference_no": "UAT-FY-NOTICE-001",
|
|
"notice_no": "UAT-FY-NOTICE-001",
|
|
"case_title": "UAT FY Notice Case",
|
|
"title": "UAT FY Notice Case",
|
|
"department": "GST",
|
|
"case_type": "notice",
|
|
"status": "open",
|
|
"priority": "normal",
|
|
"notice_date": date.today(),
|
|
"due_date": date.today() + timedelta(days=30),
|
|
"description": "Strict UAT seed notice case",
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
"is_active": True,
|
|
}, "notice case")
|
|
notice_doc = upsert(notice_docs, {"tenant_id": tenant_a_id, "case_id": v(notice_case, "id"), "title": "UAT FY Notice Case Document"}, {
|
|
"branch_id": branch_a_id,
|
|
"document_type": "GENERAL",
|
|
"description": "Strict UAT seed notice case document",
|
|
"original_filename": "uat-fy-notice-document.txt",
|
|
"stored_filename": "uat-fy-notice-document.txt",
|
|
"content_type": "text/plain",
|
|
"file_size_bytes": 29,
|
|
"local_relative_path": "notice_cases/uat-fy-notice-document.txt",
|
|
"version_no": 1,
|
|
"status": "active",
|
|
"is_deleted": False,
|
|
"uploaded_by_user_id": admin_id,
|
|
"uploaded_at_utc": utcnow(),
|
|
}, "notice case document")
|
|
|
|
# Billing invoice and payment.
|
|
invoice = upsert(invoices, {"tenant_id": tenant_a_id, "invoice_no": f"UAT-FY-INV-{ACTIVE_FY}"}, {
|
|
"branch_id": branch_a_id,
|
|
"client_id": client_a_id,
|
|
"financial_year": ACTIVE_FY,
|
|
"invoice_date": date.today(),
|
|
"billing_period_from": date(int(ACTIVE_FY[:4]), 4, 1),
|
|
"billing_period_to": date(int(ACTIVE_FY[:4]) + 1, 3, 31),
|
|
"status": "ISSUED",
|
|
"subtotal_amount": 10000.00,
|
|
"taxable_amount": 10000.00,
|
|
"cgst_amount": 900.00,
|
|
"sgst_amount": 900.00,
|
|
"igst_amount": 0.00,
|
|
"total_tax_amount": 1800.00,
|
|
"total_amount": 11800.00,
|
|
"amount_paid": 1000.00,
|
|
"balance_amount": 10800.00,
|
|
"notes": "Strict UAT seed invoice for FY tests",
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, "billing invoice")
|
|
payment = upsert(payments, {"tenant_id": tenant_a_id, "receipt_no": f"UAT-FY-RCPT-{ACTIVE_FY}"}, {
|
|
"branch_id": branch_a_id,
|
|
"client_id": client_a_id,
|
|
"invoice_id": v(invoice, "id"),
|
|
"financial_year": ACTIVE_FY,
|
|
"payment_date": date.today(),
|
|
"amount_received": 1000.00,
|
|
"tds_deducted": 0.00,
|
|
"mode": "BANK_TRANSFER",
|
|
"reference_no": "UAT-FY-SEED",
|
|
"notes": "Strict UAT seed payment for FY tests",
|
|
"created_by_user_id": admin_id,
|
|
"updated_by_user_id": admin_id,
|
|
"created_at_utc": utcnow(),
|
|
"updated_at_utc": utcnow(),
|
|
}, "billing payment")
|
|
|
|
db.commit()
|
|
|
|
env_ids = {
|
|
"TENANT_A_ID": tenant_a_id,
|
|
"TENANT_B_ID": tenant_b_id,
|
|
"BRANCH_A_ID": branch_a_id,
|
|
"BRANCH_B_ID": branch_b_id,
|
|
"CLIENT_A_ID": client_a_id,
|
|
"CLIENT_B_ID": client_b_id,
|
|
"ENGAGEMENT_A_ID": v(sub_active, "id"),
|
|
"ENGAGEMENT_B_ID": v(sub_branch_b, "id"),
|
|
"SUBSCRIPTION_A_ID": v(sub_active, "id"),
|
|
"ACTIVE_FY_TASK_ID": v(task_active, "id"),
|
|
"TASK_A_ID": v(task_active, "id"),
|
|
"LOCKED_FY_TASK_ID": v(task_locked, "id"),
|
|
"PARTNER_TASK_A_ID": v(task_partner, "id"),
|
|
"BRANCH_B_TASK_ID": v(task_branch_b, "id"),
|
|
"TASK_DOCUMENT_A_ID": v(eng_doc, "id"),
|
|
"ENGAGEMENT_DOCUMENT_A_ID": v(eng_doc, "id"),
|
|
"PERM_DOCUMENT_ID": v(perm_doc, "id"),
|
|
"NOTICE_CASE_A_ID": v(notice_case, "id"),
|
|
"CASE_DOCUMENT_A_ID": v(notice_doc, "id"),
|
|
"INVOICE_A_ID": v(invoice, "id"),
|
|
"PAYMENT_A_ID": v(payment, "id"),
|
|
"ACTIVE_FY": ACTIVE_FY,
|
|
"LOCKED_FY": LOCKED_FY,
|
|
"DEFAULT_YEAR_CODE": ACTIVE_FY,
|
|
"WORK_TRACKER_ROUTE": "/services/work-tracker",
|
|
"WORK_TRACKER_FALLBACK_ROUTE": "/employee/work",
|
|
"DOCUMENTS_ROUTE": "/documents",
|
|
"PERMANENT_DOCUMENTS_ROUTE": "/documents/permanent",
|
|
"NOTICE_CASES_ROUTE": "/notice-cases",
|
|
"BILLING_ROUTE": "/billing",
|
|
"CLIENT_BILLING_ROUTE": "/client/billing",
|
|
}
|
|
|
|
print("\n[fy-strict-seed] Completed strict FY lock/backup seed patch.")
|
|
print("\nCopy/update these in /tests/.env if different:\n")
|
|
for k, val in env_ids.items():
|
|
print(f"{k}={val or ''}")
|
|
|
|
if notes:
|
|
print("\nNotes / non-fatal skips:")
|
|
for note in notes:
|
|
print(f"- {note}")
|
|
else:
|
|
print("\nNotes: no non-fatal seed skips reported.")
|
|
|
|
db.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|