import logging from typing import List, Optional, Dict, Any, Tuple from fastapi import HTTPException from sqlalchemy import or_ from sqlalchemy.orm import Session from .models import Manifest from .dtos import ManifestCreateDTO, ManifestUpdateDTO, ManifestResponseDTO logger = logging.getLogger(__name__) class ManifestService: @staticmethod def get_all( db: Session, tenant_id: int, company_id: int, skip: int = 0, limit: int = 100, filters: Optional[Dict[str, Any]] = None, ) -> Tuple[List[Manifest], int]: query = db.query(Manifest).filter( Manifest.tenant_id == tenant_id, Manifest.company_id == company_id, ) if filters: if filters.get("search"): search_pattern = f"%{filters['search']}%" query = query.filter( or_( Manifest.manifest_number.ilike(search_pattern), Manifest.description.ilike(search_pattern), ) ) if filters.get("manifest_number"): query = query.filter( Manifest.manifest_number.ilike(f"%{filters['manifest_number']}%") ) if filters.get("start_date"): query = query.filter(Manifest.entry_date >= filters["start_date"]) if filters.get("end_date"): query = query.filter(Manifest.entry_date <= filters["end_date"]) total = query.count() items = query.offset(skip).limit(limit).all() return items, total @staticmethod def get_by_id( db: Session, manifest_id: int, tenant_id: int, company_id: int ) -> Optional[Manifest]: return ( db.query(Manifest) .filter( Manifest.id == manifest_id, Manifest.tenant_id == tenant_id, Manifest.company_id == company_id, ) .first() ) @staticmethod def create( db: Session, manifest_data: ManifestCreateDTO, tenant_id: int, company_id: int, ) -> Manifest: try: db_manifest = Manifest( **manifest_data.model_dump(exclude_unset=True), tenant_id=tenant_id, company_id=company_id, ) db.add(db_manifest) db.commit() db.refresh(db_manifest) return db_manifest except Exception as e: db.rollback() logger.error(f"Error creating manifest: {str(e)}") raise HTTPException(status_code=500, detail="Error creating manifest") @staticmethod def update( db: Session, manifest_id: int, tenant_id: int, company_id: int, manifest_data: ManifestUpdateDTO, ) -> Optional[Manifest]: manifest = ManifestService.get_by_id(db, manifest_id, tenant_id, company_id) if not manifest: return None try: update_data = manifest_data.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(manifest, field, value) db.commit() db.refresh(manifest) return manifest except Exception as e: db.rollback() logger.error(f"Error updating manifest {manifest_id}: {str(e)}") raise HTTPException(status_code=500, detail="Error updating manifest") @staticmethod def delete( db: Session, manifest_id: int, tenant_id: int, company_id: int ) -> bool: manifest = ManifestService.get_by_id(db, manifest_id, tenant_id, company_id) if not manifest: return False try: db.delete(manifest) db.commit() return True except Exception as e: db.rollback() logger.error(f"Error deleting manifest {manifest_id}: {str(e)}") raise HTTPException(status_code=500, detail="Error deleting manifest") @staticmethod def update_invoice_manifest( db: Session, invoice_id: int, tenant_id: int, company_id: int, manifest_number: Optional[str], ) -> bool: """ Update ONLY the manifest_number of an invoice. Creates compliance_mx record if it doesn't exist. """ # Import models here to avoid circular dependencies if any from api.v1.modules.a76.invoices.models import ( InvoiceHeader, InvoiceComplianceMx, ) invoice = ( db.query(InvoiceHeader) .filter( InvoiceHeader.id == invoice_id, InvoiceHeader.tenant_id == tenant_id, InvoiceHeader.company_id == company_id, ) .first() ) if not invoice: return False try: if invoice.compliance_mx: invoice.compliance_mx.manifest_number = manifest_number else: # Create new compliance record with defaults for required fields new_compliance = InvoiceComplianceMx( invoice_id=invoice.id, tenant_id=tenant_id, company_id=company_id, manifest_number=manifest_number, # Required fields defaults is_pedimento_pending=False, is_regime_change=False, is_owner_of_goods=False, generate_balances=False, is_mixed=False, ) db.add(new_compliance) db.commit() return True except Exception as e: db.rollback() logger.error( f"Error linking invoice {invoice_id} to manifest {manifest_number}: {str(e)}" ) raise HTTPException( status_code=500, detail="Error linking invoice to manifest" )