feat: Add shared components, refactor stores, improve data-entry workflow
Shared Components: - Add CompanySelector.vue and PeriodSelector.vue components - Add AppHeader.vue and SlideMenu.vue layout components - Add shared stores factories (companies.js, accountingPeriod.js) - Add shared routes factories (companies.py, calendar.py) - Add shared models (company.py, calendar.py) - Add shared layout styles (header.css, navigation.css) Data Entry App: - Update CLAUDE.md with prod/test server documentation - Improve nomenclature sync service with better error handling - Update receipts router and CRUD operations - Add company/period stores using shared factories - Update App.vue layout with shared components - Fix OCRUploadZone file handling Reports App: - Refactor stores to use shared factories - Update App.vue to use shared layout components Infrastructure: - Replace start-data-entry.sh with separate dev/test scripts - Add .claude/rules for authentication, backend patterns, etc. - Add implementation plan for OCR receipt improvements - Clean up old documentation files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,9 @@ class ReceiptCRUD:
|
||||
session.add(receipt)
|
||||
await session.commit()
|
||||
await session.refresh(receipt)
|
||||
return receipt
|
||||
|
||||
# Reload with relationships to avoid lazy loading issues with async
|
||||
return await ReceiptCRUD.get_by_id(session, receipt.id, include_relations=True)
|
||||
|
||||
@staticmethod
|
||||
async def get_by_id(
|
||||
@@ -175,7 +177,9 @@ class ReceiptCRUD:
|
||||
session.add(receipt)
|
||||
await session.commit()
|
||||
await session.refresh(receipt)
|
||||
return receipt
|
||||
|
||||
# Reload with relationships to avoid lazy loading issues with async
|
||||
return await ReceiptCRUD.get_by_id(session, receipt.id, include_relations=True)
|
||||
|
||||
@staticmethod
|
||||
async def update_status(
|
||||
@@ -206,7 +210,9 @@ class ReceiptCRUD:
|
||||
session.add(receipt)
|
||||
await session.commit()
|
||||
await session.refresh(receipt)
|
||||
return receipt
|
||||
|
||||
# Reload with relationships to avoid lazy loading issues with async
|
||||
return await ReceiptCRUD.get_by_id(session, receipt.id, include_relations=True)
|
||||
|
||||
@staticmethod
|
||||
async def delete(session: AsyncSession, receipt: Receipt) -> bool:
|
||||
|
||||
@@ -132,6 +132,16 @@ from auth.routes import create_auth_router
|
||||
auth_router = create_auth_router(prefix="") # No prefix - we set it in include_router
|
||||
app.include_router(auth_router, prefix="/api/auth", tags=["auth"])
|
||||
|
||||
# Shared routes (companies, calendar)
|
||||
from routes.companies import create_companies_router
|
||||
from routes.calendar import create_calendar_router
|
||||
|
||||
companies_router = create_companies_router(oracle_pool) # No cache for data-entry
|
||||
calendar_router = create_calendar_router(oracle_pool)
|
||||
|
||||
app.include_router(companies_router, prefix="/api/companies", tags=["companies"])
|
||||
app.include_router(calendar_router, prefix="/api/calendar", tags=["calendar"])
|
||||
|
||||
|
||||
# Root endpoint
|
||||
@app.get("/")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Nomenclature API endpoints."""
|
||||
|
||||
from typing import Optional, List
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from typing import Optional, List, Annotated
|
||||
from fastapi import APIRouter, Depends, HTTPException, Header
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -20,6 +20,38 @@ from auth.models import CurrentUser
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ============ Selected Company Dependency ============
|
||||
|
||||
async def get_selected_company(
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
x_selected_company: Annotated[Optional[str], Header()] = None
|
||||
) -> int:
|
||||
"""
|
||||
Get selected company from X-Selected-Company header.
|
||||
Validates user access. Falls back to first company if no header.
|
||||
"""
|
||||
if x_selected_company:
|
||||
try:
|
||||
company_id = int(x_selected_company)
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"Invalid company ID: {x_selected_company}")
|
||||
|
||||
if str(company_id) in current_user.companies:
|
||||
return company_id
|
||||
raise HTTPException(403, f"Nu aveți acces la firma {company_id}")
|
||||
|
||||
if current_user.companies:
|
||||
try:
|
||||
return int(current_user.companies[0])
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
raise HTTPException(400, "Nu aveți nicio firmă asignată")
|
||||
|
||||
|
||||
SelectedCompany = Annotated[int, Depends(get_selected_company)]
|
||||
|
||||
|
||||
# Request/Response Models
|
||||
class SupplierSearchResult(BaseModel):
|
||||
found: bool
|
||||
@@ -70,14 +102,13 @@ async def search_supplier(
|
||||
name: Optional[str] = None,
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Search for supplier by fiscal code or name."""
|
||||
if not fiscal_code and not name:
|
||||
raise HTTPException(status_code=400, detail="Provide fiscal_code or name")
|
||||
|
||||
# Use provided company_id or first from user
|
||||
cid = company_id or (current_user.companies[0] if current_user.companies else 1)
|
||||
cid = company_id or selected_company
|
||||
|
||||
found, supplier, source = await SyncService.search_supplier(
|
||||
session, cid, fiscal_code, name
|
||||
@@ -91,10 +122,10 @@ async def get_suppliers(
|
||||
search: Optional[str] = None,
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Get all suppliers (synced + local) for dropdown/autocomplete."""
|
||||
cid = company_id or (current_user.companies[0] if current_user.companies else 1)
|
||||
cid = company_id or selected_company
|
||||
|
||||
suppliers = await SyncService.get_all_suppliers(session, cid, search)
|
||||
|
||||
@@ -115,10 +146,11 @@ async def create_local_supplier(
|
||||
data: LocalSupplierCreate,
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
selected_company: SelectedCompany = None,
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
"""Create a local supplier from OCR data."""
|
||||
cid = company_id or (current_user.companies[0] if current_user.companies else 1)
|
||||
cid = company_id or selected_company
|
||||
|
||||
supplier = await SyncService.create_local_supplier(
|
||||
session, cid, data.name, data.fiscal_code, data.address, current_user.username
|
||||
@@ -136,10 +168,10 @@ async def create_local_supplier(
|
||||
async def get_cash_registers(
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Get all cash registers for a company."""
|
||||
cid = company_id or (current_user.companies[0] if current_user.companies else 1)
|
||||
cid = company_id or selected_company
|
||||
|
||||
registers = await SyncService.get_all_cash_registers(session, cid)
|
||||
|
||||
@@ -159,10 +191,10 @@ async def get_cash_registers(
|
||||
async def sync_suppliers(
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Manually trigger supplier sync from Oracle."""
|
||||
cid = company_id or (current_user.companies[0] if current_user.companies else 1)
|
||||
cid = company_id or selected_company
|
||||
|
||||
synced, errors = await SyncService.sync_suppliers(session, cid)
|
||||
|
||||
@@ -177,10 +209,10 @@ async def sync_suppliers(
|
||||
async def sync_cash_registers(
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Manually trigger cash register sync from Oracle."""
|
||||
cid = company_id or (current_user.companies[0] if current_user.companies else 1)
|
||||
cid = company_id or selected_company
|
||||
|
||||
synced, errors = await SyncService.sync_cash_registers(session, cid)
|
||||
|
||||
@@ -195,10 +227,10 @@ async def sync_cash_registers(
|
||||
async def sync_all_nomenclatures(
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Sync all nomenclatures (suppliers + cash registers) from Oracle."""
|
||||
cid = company_id or (current_user.companies[0] if current_user.companies else 1)
|
||||
cid = company_id or selected_company
|
||||
|
||||
# Sync suppliers
|
||||
suppliers_synced, suppliers_errors = await SyncService.sync_suppliers(session, cid)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""API endpoints for receipts."""
|
||||
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Annotated
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query, Header
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -39,20 +39,69 @@ from auth.models import CurrentUser
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ============ Helper for current user's company ============
|
||||
# ============ Helper for selected company from header ============
|
||||
|
||||
async def get_selected_company(
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
x_selected_company: Annotated[Optional[str], Header()] = None
|
||||
) -> int:
|
||||
"""
|
||||
Get selected company from X-Selected-Company header.
|
||||
|
||||
Validates that the user has access to the specified company.
|
||||
Falls back to user's first company if no header is provided.
|
||||
|
||||
Raises:
|
||||
HTTPException 403: If user doesn't have access to specified company
|
||||
HTTPException 400: If user has no companies assigned
|
||||
"""
|
||||
if x_selected_company:
|
||||
try:
|
||||
company_id = int(x_selected_company)
|
||||
except ValueError:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Invalid company ID format: {x_selected_company}"
|
||||
)
|
||||
|
||||
# Validate user has access to this company
|
||||
# Auth stores companies as strings
|
||||
if str(company_id) in current_user.companies:
|
||||
return company_id
|
||||
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail=f"Nu aveți acces la firma {company_id}"
|
||||
)
|
||||
|
||||
# No header - use first company from user's list
|
||||
if current_user.companies:
|
||||
try:
|
||||
return int(current_user.companies[0])
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Nu aveți nicio firmă asignată"
|
||||
)
|
||||
|
||||
|
||||
# Dependency for injection
|
||||
SelectedCompany = Annotated[int, Depends(get_selected_company)]
|
||||
|
||||
|
||||
# Legacy function for backwards compatibility (deprecated)
|
||||
def get_current_user_company(current_user: CurrentUser) -> int:
|
||||
"""
|
||||
Get current user's active company.
|
||||
|
||||
Returns the first company from the user's companies list.
|
||||
In future, this can be enhanced to use a session-based active company.
|
||||
DEPRECATED: Use get_selected_company() dependency instead.
|
||||
This function returns the first company, ignoring X-Selected-Company header.
|
||||
"""
|
||||
if current_user.companies:
|
||||
# For data-entry-app, we assume company ID is numeric
|
||||
# If companies are stored as strings, convert to int
|
||||
# For now, return 1 as default (Phase 1)
|
||||
return 1
|
||||
try:
|
||||
return int(current_user.companies[0])
|
||||
except (ValueError, IndexError):
|
||||
return 1
|
||||
return 1
|
||||
|
||||
|
||||
@@ -80,16 +129,14 @@ async def list_receipts(
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Get paginated list of receipts with filters."""
|
||||
from datetime import date as date_type
|
||||
|
||||
current_company = get_current_user_company(current_user)
|
||||
|
||||
filters = ReceiptFilter(
|
||||
status=status,
|
||||
company_id=company_id or current_company,
|
||||
company_id=company_id or selected_company,
|
||||
created_by=created_by,
|
||||
date_from=date_type.fromisoformat(date_from) if date_from else None,
|
||||
date_to=date_type.fromisoformat(date_to) if date_to else None,
|
||||
@@ -105,12 +152,11 @@ async def list_receipts(
|
||||
async def list_pending_receipts(
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Get all receipts pending review (for accountant view)."""
|
||||
current_company = get_current_user_company(current_user)
|
||||
receipts = await ReceiptCRUD.get_pending_review(
|
||||
session, company_id or current_company
|
||||
session, company_id or selected_company
|
||||
)
|
||||
return [ReceiptResponse.model_validate(r) for r in receipts]
|
||||
|
||||
@@ -120,13 +166,13 @@ async def get_receipt_stats(
|
||||
company_id: Optional[int] = None,
|
||||
my_receipts: bool = False,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
selected_company: SelectedCompany = None,
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
"""Get receipt statistics."""
|
||||
current_company = get_current_user_company(current_user)
|
||||
return await ReceiptCRUD.get_stats(
|
||||
session,
|
||||
company_id or current_company,
|
||||
company_id or selected_company,
|
||||
created_by=current_user.username if my_receipts else None,
|
||||
)
|
||||
|
||||
@@ -415,12 +461,11 @@ async def get_partners(
|
||||
search: Optional[str] = None,
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Get partners (suppliers/customers) for dropdown."""
|
||||
current_company = get_current_user_company(current_user)
|
||||
return await NomenclatureService.get_partners(
|
||||
company_id or current_company, search, session
|
||||
company_id or selected_company, search, session
|
||||
)
|
||||
|
||||
|
||||
@@ -428,12 +473,11 @@ async def get_partners(
|
||||
async def get_accounts(
|
||||
prefix: Optional[str] = None,
|
||||
company_id: Optional[int] = None,
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Get chart of accounts for dropdown."""
|
||||
current_company = get_current_user_company(current_user)
|
||||
return await NomenclatureService.get_accounts(
|
||||
company_id or current_company, prefix
|
||||
company_id or selected_company, prefix
|
||||
)
|
||||
|
||||
|
||||
@@ -441,11 +485,10 @@ async def get_accounts(
|
||||
async def get_cash_registers(
|
||||
company_id: Optional[int] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
selected_company: SelectedCompany = None,
|
||||
):
|
||||
"""Get cash registers and bank accounts for dropdown."""
|
||||
current_company = get_current_user_company(current_user)
|
||||
return await NomenclatureService.get_cash_registers(company_id or current_company, session)
|
||||
return await NomenclatureService.get_cash_registers(company_id or selected_company, session)
|
||||
|
||||
|
||||
@router.get("/nomenclature/expense-types", response_model=List[ExpenseTypeOption])
|
||||
|
||||
@@ -18,29 +18,54 @@ from app.db.models.nomenclature import SyncedSupplier, LocalSupplier, SyncedCash
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Company ID to Oracle Schema mapping
|
||||
# TODO: This should come from a config table or environment variable
|
||||
COMPANY_SCHEMAS = {
|
||||
1: "CONTAFIN", # Example mapping - update with real schema names
|
||||
2: "CONTAFIN2",
|
||||
}
|
||||
# Cache for schema lookups (populated dynamically from Oracle)
|
||||
_schema_cache: dict[int, str] = {}
|
||||
|
||||
|
||||
class SyncService:
|
||||
"""Service for syncing nomenclatures from Oracle."""
|
||||
|
||||
@staticmethod
|
||||
def get_schema_for_company(company_id: int) -> Optional[str]:
|
||||
"""Get Oracle schema for company ID."""
|
||||
return COMPANY_SCHEMAS.get(company_id)
|
||||
async def get_schema_for_company(company_id: int) -> Optional[str]:
|
||||
"""
|
||||
Get Oracle schema for company ID from V_NOM_FIRME view.
|
||||
Results are cached in memory for performance.
|
||||
"""
|
||||
# Check cache first
|
||||
if company_id in _schema_cache:
|
||||
return _schema_cache[company_id]
|
||||
|
||||
try:
|
||||
async with oracle_pool.get_connection() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
SELECT SCHEMA
|
||||
FROM CONTAFIN_ORACLE.V_NOM_FIRME
|
||||
WHERE ID_FIRMA = :company_id
|
||||
""", {'company_id': company_id})
|
||||
result = cursor.fetchone()
|
||||
|
||||
if result:
|
||||
schema = result[0]
|
||||
_schema_cache[company_id] = schema
|
||||
logger.info(f"Resolved schema for company {company_id}: {schema}")
|
||||
return schema
|
||||
else:
|
||||
logger.warning(f"No schema found for company {company_id}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching schema for company {company_id}: {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def sync_suppliers(session: AsyncSession, company_id: int) -> Tuple[int, int]:
|
||||
"""
|
||||
Sync suppliers from Oracle NOM_PARTENERI to SQLite.
|
||||
Sync suppliers (furnizori, id_tip_part=17) from Oracle to SQLite.
|
||||
Uses CORESP_TIP_PART joined with VNOM_PARTENERI view.
|
||||
Returns (synced_count, error_count).
|
||||
"""
|
||||
schema = SyncService.get_schema_for_company(company_id)
|
||||
schema = await SyncService.get_schema_for_company(company_id)
|
||||
if not schema:
|
||||
logger.warning(f"No schema mapping for company {company_id}")
|
||||
return 0, 0
|
||||
@@ -51,11 +76,17 @@ class SyncService:
|
||||
try:
|
||||
async with oracle_pool.get_connection() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
# Fetch active partners from Oracle
|
||||
# Fetch active suppliers from Oracle
|
||||
# id_tip_part = 17 means "furnizori" (suppliers)
|
||||
# Using CORESP_TIP_PART to filter by partner type
|
||||
cursor.execute(f"""
|
||||
SELECT ID_PART, DEN_PART, COD_FISCAL, ADRESA
|
||||
FROM {schema}.NOM_PARTENERI
|
||||
WHERE ACTIV = 1
|
||||
SELECT B.ID_PART, B.DENUMIRE, B.COD_FISCAL, B.ADRESA
|
||||
FROM {schema}.CORESP_TIP_PART A
|
||||
INNER JOIN {schema}.VNOM_PARTENERI B ON A.ID_PART = B.ID_PART
|
||||
WHERE A.ID_TIP_PART = 17
|
||||
AND (B.INACTIV = 0 OR B.INACTIV IS NULL)
|
||||
AND B.ID_PART IS NOT NULL
|
||||
ORDER BY B.DENUMIRE
|
||||
""")
|
||||
rows = cursor.fetchall()
|
||||
|
||||
@@ -110,10 +141,16 @@ class SyncService:
|
||||
@staticmethod
|
||||
async def sync_cash_registers(session: AsyncSession, company_id: int) -> Tuple[int, int]:
|
||||
"""
|
||||
Sync cash registers from Oracle to SQLite.
|
||||
Sync cash registers and bank accounts from Oracle to SQLite.
|
||||
Returns (synced_count, error_count).
|
||||
|
||||
Uses CORESP_TIP_PART with:
|
||||
- id_tip_part = 22: CASA LEI
|
||||
- id_tip_part = 23: CASA VALUTA
|
||||
- id_tip_part = 24: BANCA LEI
|
||||
- id_tip_part = 25: BANCA VALUTA
|
||||
"""
|
||||
schema = SyncService.get_schema_for_company(company_id)
|
||||
schema = await SyncService.get_schema_for_company(company_id)
|
||||
if not schema:
|
||||
logger.warning(f"No schema mapping for company {company_id}")
|
||||
return 0, 0
|
||||
@@ -121,25 +158,40 @@ class SyncService:
|
||||
synced = 0
|
||||
errors = 0
|
||||
|
||||
# Partner types mapping
|
||||
# 22=CASA LEI, 23=CASA VALUTA -> cash
|
||||
# 24=BANCA LEI, 25=BANCA VALUTA -> bank
|
||||
partner_types = [22, 23, 24, 25]
|
||||
|
||||
try:
|
||||
async with oracle_pool.get_connection() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
# Fetch cash registers (both cash and bank)
|
||||
# Assuming similar structure to NOM_PARTENERI
|
||||
# TODO: Verify actual table name and structure in Oracle
|
||||
# Fetch cash/bank partners from CORESP_TIP_PART
|
||||
cursor.execute(f"""
|
||||
SELECT ID_CASA, DEN_CASA, CONT
|
||||
FROM {schema}.NOM_CASE
|
||||
WHERE ACTIV = 1
|
||||
SELECT B.ID_PART, B.DENUMIRE, A.ID_TIP_PART
|
||||
FROM {schema}.CORESP_TIP_PART A
|
||||
INNER JOIN {schema}.VNOM_PARTENERI B ON A.ID_PART = B.ID_PART
|
||||
WHERE A.ID_TIP_PART IN (22, 23, 24, 25)
|
||||
AND (B.INACTIV = 0 OR B.INACTIV IS NULL)
|
||||
AND B.ID_PART IS NOT NULL
|
||||
ORDER BY A.ID_TIP_PART, B.DENUMIRE
|
||||
""")
|
||||
rows = cursor.fetchall()
|
||||
|
||||
# Type mapping: 22=CASA LEI, 23=CASA VALUTA -> cash; 24=BANCA LEI, 25=BANCA VALUTA -> bank
|
||||
type_mapping = {
|
||||
22: ("cash", "CASA_LEI"),
|
||||
23: ("cash", "CASA_VALUTA"),
|
||||
24: ("bank", "BANCA_LEI"),
|
||||
25: ("bank", "BANCA_VALUTA"),
|
||||
}
|
||||
|
||||
for row in rows:
|
||||
try:
|
||||
oracle_id, name, account_code = row
|
||||
oracle_id, name, tip_part_id = row
|
||||
|
||||
# Determine type based on account code
|
||||
register_type = "cash" if account_code.startswith("531") else "bank"
|
||||
# Determine type based on partner type
|
||||
register_type, account_code = type_mapping.get(tip_part_id, ("cash", "UNKNOWN"))
|
||||
|
||||
# Check if already exists
|
||||
stmt = select(SyncedCashRegister).where(
|
||||
@@ -152,7 +204,7 @@ class SyncService:
|
||||
if existing:
|
||||
# Update existing record
|
||||
existing.name = name or ""
|
||||
existing.account_code = account_code or ""
|
||||
existing.account_code = account_code
|
||||
existing.register_type = register_type
|
||||
existing.synced_at = datetime.utcnow()
|
||||
logger.debug(f"Updated cash register {oracle_id}: {name}")
|
||||
@@ -162,7 +214,7 @@ class SyncService:
|
||||
oracle_id=oracle_id,
|
||||
company_id=company_id,
|
||||
name=name or "",
|
||||
account_code=account_code or "",
|
||||
account_code=account_code,
|
||||
register_type=register_type,
|
||||
)
|
||||
session.add(cash_register)
|
||||
|
||||
Reference in New Issue
Block a user