Backend: - service_auto module complet: router, service, schemas, 5 teste suites (22/22 passed) - 5 endpoints: GET /ping, /firme, /tip-deviz, /masini, POST /comenzi - SP_CREEAZA_COMANDA_PROTOTIP creat în MARIUSM_AUTO (VALID, 5.9ms) - oracle_pool.py: session_callback backward-compat patch - ROA_WEB user: grants SP-only confirmate (H3), mariusm_test pool switchat - pyproject.toml: integration pytest marker înregistrat Frontend: - ComandaNoua.vue: date reale din Oracle (firme/tip-deviz/masini), nu hardcodate - src/modules/service-auto/services/api.js: axios service cu Bearer token - src/router/index.js: rută /service-auto/comanda-noua Docs: - decision-log.md: verdict MERGE, toate 6 ipoteze CONFIRMED - learnings.md: 7 patterns reutilizabile - grants-audit.md: arhitectura multi-tenant + proxy auth analysis + V_NOM_FIRME loop - template-modul-oracle.md: rețetă completă pentru module Oracle noi - TODO-phase2.md: 7 items concrete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
3.9 KiB
Python
110 lines
3.9 KiB
Python
import re
|
|
from typing import NoReturn
|
|
|
|
import oracledb
|
|
from fastapi import HTTPException
|
|
from shared.database.oracle_pool import oracle_pool
|
|
from ..schemas.comanda import ComandaRequest, ComandaResponse
|
|
from .. import logger
|
|
|
|
|
|
def _handle_oracle_error(e: Exception) -> NoReturn:
|
|
"""
|
|
Map Oracle error codes to FastAPI HTTPExceptions. Always raises.
|
|
|
|
Code ranges:
|
|
20001-20999 → 422 Unprocessable (business rule errors from RAISE_APPLICATION_ERROR)
|
|
12541/12170/12154/12560 → 503 Service Unavailable (Oracle unreachable / network)
|
|
1017 → 500 + CRITICAL log (bad credentials — config error)
|
|
942 → 500 + CRITICAL log (missing object/grant — deployment error)
|
|
* → 500 + ERROR log (unexpected)
|
|
"""
|
|
err = e.args[0]
|
|
code = getattr(err, "code", 0)
|
|
raw_message = getattr(err, "message", str(e))
|
|
|
|
if 20001 <= code <= 20999:
|
|
# Strip "ORA-2xxxx: " prefix injected by Oracle; expose the business message only
|
|
clean = re.sub(r"^ORA-\d+:\s*", "", raw_message).strip()
|
|
raise HTTPException(status_code=422, detail=clean)
|
|
|
|
if code in (12541, 12170, 12154, 12560):
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail="Serviciul bazei de date e temporar indisponibil",
|
|
)
|
|
|
|
if code == 1017:
|
|
logger.critical("Oracle credentials rejected (ORA-01017)", exc_info=True)
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail="Eroare de configurare. Contactați administratorul.",
|
|
)
|
|
|
|
if code == 942:
|
|
logger.critical("Oracle object not found or grant missing (ORA-00942)", exc_info=True)
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail="Eroare internă. Contactați administratorul.",
|
|
)
|
|
|
|
logger.error("Unexpected Oracle error ORA-%05d", code, exc_info=True)
|
|
raise HTTPException(status_code=500, detail="Eroare internă neașteptată")
|
|
|
|
|
|
class ComandaService:
|
|
@staticmethod
|
|
async def creeaza_comanda(
|
|
data: ComandaRequest,
|
|
username: str,
|
|
) -> ComandaResponse:
|
|
logger.info(
|
|
"service_auto.create_comanda START",
|
|
extra={
|
|
"user": username,
|
|
"tip": data.tip_id,
|
|
"client_id": data.id_masiniclient,
|
|
"id_firma": data.id_firma,
|
|
},
|
|
)
|
|
|
|
async with oracle_pool.get_connection("mariusm_test") as connection:
|
|
try:
|
|
with connection.cursor() as cursor:
|
|
out_id_ordl = cursor.var(oracledb.NUMBER)
|
|
out_nrord = cursor.var(oracledb.STRING)
|
|
|
|
cursor.callproc(
|
|
"MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP",
|
|
[
|
|
data.tip_id, # p_tip IN NUMBER
|
|
data.id_masiniclient, # p_id_masiniclient IN NUMBER
|
|
data.solicitari, # p_solicitari IN VARCHAR2
|
|
data.id_firma, # p_id_firma IN NUMBER
|
|
out_id_ordl, # p_id_ordl OUT NUMBER
|
|
out_nrord, # p_nrord OUT VARCHAR2
|
|
],
|
|
)
|
|
|
|
connection.commit()
|
|
|
|
id_ordl = int(out_id_ordl.getvalue())
|
|
nrord = out_nrord.getvalue() or ""
|
|
except oracledb.DatabaseError as e:
|
|
try:
|
|
connection.rollback()
|
|
except Exception:
|
|
pass # connection may be dead on network errors; ignore
|
|
_handle_oracle_error(e)
|
|
|
|
logger.info(
|
|
"service_auto.create_comanda OK",
|
|
extra={"user": username, "id_ordl": id_ordl, "nrord": nrord},
|
|
)
|
|
|
|
return ComandaResponse(
|
|
id_ordl=id_ordl,
|
|
nrord=nrord,
|
|
mesaj=f"Comanda {nrord} creata cu succes.",
|
|
)
|