feat(5.12): modal editare + cont obligatoriu la import; design.md + PRD 5.13 revizuit (/autoplan)
5.12 (livrat): editare in modal a randurilor de preview, cont obligatoriu inainte de import, formular editare extras (_form_editare, _editare_preview_modal), plus suita de teste aferenta (preview edit/compact, mapare op, form editare, signup, admin panel). Design + planificare: - docs/design.md: sistem de design (tokeni, breakpoints, scara control, componente, a11y). - docs/prd/prd-5.12-* si prd-5.13-* (5.13 cu raport /autoplan: CEO+Design+Eng, audit trail). Curatare: sterse PNG-urile de test/mockup temporare din radacina. Nota: implementarea CSS 5.13 (responsive compact + sistem butoane) NU e inca facuta — planul revizuit cere refactorul testelor fragile din test_web_responsive.py INAINTE de CSS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,31 +18,49 @@ import sqlite3
|
||||
|
||||
|
||||
def _norm_cui(cui: str | None) -> str | None:
|
||||
"""trim + upper; sir gol -> None (tratat ca „fara CUI")."""
|
||||
"""trim + upper; sir gol -> ValueError daca e string gol, None daca e None."""
|
||||
if cui is None:
|
||||
return None
|
||||
cui = cui.strip().upper()
|
||||
return cui or None
|
||||
if cui == "":
|
||||
raise ValueError("CUI gol (un CUI trebuie sa fie un sir nevid)")
|
||||
return cui
|
||||
|
||||
|
||||
def _norm_email(email: str | None) -> str | None:
|
||||
"""trim + lower; sir gol -> ValueError daca e string gol, None daca e None."""
|
||||
if email is None:
|
||||
return None
|
||||
email = email.strip().lower()
|
||||
if email == "":
|
||||
raise ValueError("email gol (un email trebuie sa fie un sir nevid)")
|
||||
return email
|
||||
|
||||
|
||||
def create_account(
|
||||
conn: sqlite3.Connection, name: str, cui: str | None = None, active: bool = True
|
||||
conn: sqlite3.Connection,
|
||||
name: str,
|
||||
cui: str | None = None,
|
||||
email: str | None = None,
|
||||
active: bool = True,
|
||||
) -> int:
|
||||
"""Insereaza un cont si intoarce id-ul nou (AUTOINCREMENT, deci >=2 — nu atinge default id=1).
|
||||
|
||||
`name` gol/whitespace -> ValueError. `cui` se normalizeaza (trim+upper); un CUI
|
||||
deja folosit -> ValueError cu cauza+fix. Unicitatea e impusa de indexul partial
|
||||
`name` gol/whitespace -> ValueError. `cui` se normalizeaza (trim+upper); sir gol -> ValueError.
|
||||
`email` se normalizeaza (trim+lower); sir gol -> ValueError.
|
||||
Un CUI deja folosit -> ValueError cu cauza+fix. Unicitatea e impusa de indexul partial
|
||||
`ux_accounts_cui` (nu de un check separat), deci e sigura la concurenta.
|
||||
"""
|
||||
name = (name or "").strip()
|
||||
if not name:
|
||||
raise ValueError("name gol (un cont are nevoie de nume)")
|
||||
cui = _norm_cui(cui)
|
||||
email = _norm_email(email)
|
||||
try:
|
||||
# Invariant (5.5): active=1 <=> status='active'; cont creat inactiv = 'pending'.
|
||||
cur = conn.execute(
|
||||
"INSERT INTO accounts (name, cui, active, status) VALUES (?, ?, ?, ?)",
|
||||
(name, cui, 1 if active else 0, "active" if active else "pending"),
|
||||
"INSERT INTO accounts (name, cui, email, active, status) VALUES (?, ?, ?, ?, ?)",
|
||||
(name, cui, email, 1 if active else 0, "active" if active else "pending"),
|
||||
)
|
||||
except sqlite3.IntegrityError:
|
||||
existing = conn.execute("SELECT id FROM accounts WHERE cui=?", (cui,)).fetchone()
|
||||
@@ -54,6 +72,21 @@ def create_account(
|
||||
return int(cur.lastrowid or 0)
|
||||
|
||||
|
||||
def account_is_complete(row: sqlite3.Row | dict) -> bool:
|
||||
"""Returneaza True daca contul are companie (name), email si CUI ne-goale.
|
||||
|
||||
Contul de sistem id=1 (default) este EXCEPTAT si returneaza intotdeauna True
|
||||
(nu are sens sa-l marcam ca incomplet — nu e un cont de client).
|
||||
"""
|
||||
acct_id = row["id"] if "id" in row.keys() else None
|
||||
if acct_id == 1:
|
||||
return True
|
||||
name = (row["name"] or "").strip()
|
||||
cui = (row["cui"] or "").strip()
|
||||
email_val = (row["email"] or "").strip() if "email" in row.keys() else ""
|
||||
return bool(name and cui and email_val)
|
||||
|
||||
|
||||
def set_active(conn: sqlite3.Connection, account_id: int, active: bool) -> None:
|
||||
"""Comuta `accounts.active`. Idempotent (set activ pe activ nu arunca).
|
||||
Cont inexistent -> ValueError.
|
||||
@@ -121,7 +154,7 @@ def list_accounts(conn: sqlite3.Connection) -> list[dict]:
|
||||
"""Metadate conturi (FARA `rar_creds_enc`), ordonate dupa id. Exclude conturile 'deleted'
|
||||
(stergere soft -> invizibile in panou)."""
|
||||
rows = conn.execute(
|
||||
"SELECT id, name, cui, active, status, created_at FROM accounts "
|
||||
"SELECT id, name, cui, email, active, status, created_at FROM accounts "
|
||||
"WHERE status != 'deleted' ORDER BY id"
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
Reference in New Issue
Block a user