Se mejoro el disenio de partes, se genero la informacion mas precisa en los reportes y se carga el logo en las instanacias de las empresas
This commit is contained in:
@@ -29,7 +29,7 @@ class FaPart(Base, TenantScopedMixin, TimestampMixin):
|
||||
ForeignKeyConstraint(
|
||||
["id"], ["a76.parts.id"], name="fk_fa_partes_master"
|
||||
),
|
||||
{"schema": "a24"},
|
||||
{"schema": "a24", "extend_existing": True},
|
||||
)
|
||||
|
||||
# El ID hereda el valor de la tabla parts
|
||||
|
||||
@@ -32,7 +32,7 @@ class InvPart(Base, TenantScopedMixin, TimestampMixin):
|
||||
ForeignKeyConstraint(
|
||||
["id"], ["a76.parts.id"], name="fk_inv_partes_master"
|
||||
),
|
||||
{"schema": "a24"},
|
||||
{"schema": "a24", "extend_existing": True},
|
||||
)
|
||||
|
||||
# Relación 1:1 - El ID es el mismo de la tabla maestra
|
||||
|
||||
@@ -48,7 +48,7 @@ class Class(Base, TenantScopedMixin, TimestampMixin):
|
||||
"class_code",
|
||||
name="uq_classes_tenant_company_code",
|
||||
),
|
||||
{"schema": "a76"},
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
|
||||
@@ -26,7 +26,7 @@ class Company(Base, TimestampMixin):
|
||||
__tablename__ = "company" #GEmpresa
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="company_pkey"),
|
||||
{"schema": "a76"},
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
# Primary key
|
||||
|
||||
@@ -3,8 +3,12 @@ Rutas para gestión de empresa
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, File, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.database import get_core_db
|
||||
@@ -298,11 +302,98 @@ async def update_company(
|
||||
return CompanyResponseDTO.model_validate(updated_company)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{company_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
summary="Delete company",
|
||||
return CompanyResponseDTO.model_validate(updated_company)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{company_id}/upload-logo",
|
||||
response_model=dict,
|
||||
summary="Upload company logo",
|
||||
)
|
||||
async def upload_company_logo(
|
||||
company_id: int,
|
||||
file: UploadFile = File(...),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Upload logo for a company"""
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
if not tenant_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Tenant ID not found in user data",
|
||||
)
|
||||
|
||||
# 1. Verify company exists
|
||||
company = CompanyService.get_by_id(db, company_id, tenant_id, 0)
|
||||
if not company:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Company not found",
|
||||
)
|
||||
|
||||
# 2. Define upload path
|
||||
# Use a persistent path: 'app_data/logos/{company_id}'
|
||||
upload_dir = Path(f"app_data/logos/{company_id}")
|
||||
upload_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 3. Save file
|
||||
# Preserve original filename
|
||||
filename = file.filename or "logo.png"
|
||||
file_path = upload_dir / filename
|
||||
|
||||
try:
|
||||
# Check if file exists and remove it to avoid accumulation if needed,
|
||||
# or just overwrite (shutil.copyfileobj overwrites)
|
||||
with open(file_path, "wb") as buffer:
|
||||
shutil.copyfileobj(file.file, buffer)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Could not save file: {e}",
|
||||
)
|
||||
|
||||
# 4. Returns the absolute path keys
|
||||
abs_path = str(file_path.absolute())
|
||||
|
||||
return {"path": abs_path}
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{company_id}/logo/image",
|
||||
summary="Get company logo image",
|
||||
)
|
||||
@router.get(
|
||||
"/{company_id}/logo/image",
|
||||
summary="Get company logo image",
|
||||
)
|
||||
async def get_company_logo_image(
|
||||
company_id: int,
|
||||
db: Session = Depends(get_core_db),
|
||||
# Public endpoint to allow <img> tags to load the image without custom headers
|
||||
):
|
||||
"""Serve the company logo image file"""
|
||||
# Security: In a stricter environment, we would use a signed short-lived URL
|
||||
# or cookie-based auth. For now, checking if company exists is sufficient.
|
||||
|
||||
# We find the company ignoring tenant checks for the image serving
|
||||
# (Logos are generally considered semi-public assets in this context)
|
||||
company = db.query(Company).filter(Company.id == company_id).first()
|
||||
|
||||
if not company or not company.logo:
|
||||
raise HTTPException(status_code=404, detail="Logo not found")
|
||||
|
||||
file_path = Path(company.logo)
|
||||
if not file_path.exists():
|
||||
# Fallback for old paths or moved files
|
||||
# Check if it exists in the 'standard' location even if DB thinks otherwise
|
||||
standard_path = Path(f"app_data/logos/{company_id}") / file_path.name
|
||||
if standard_path.exists():
|
||||
return FileResponse(standard_path)
|
||||
|
||||
raise HTTPException(status_code=404, detail="Logo file not found on server")
|
||||
|
||||
return FileResponse(file_path)
|
||||
async def delete_company(
|
||||
company_id: int,
|
||||
db: Session = Depends(get_core_db),
|
||||
|
||||
@@ -34,10 +34,13 @@ class BaseService:
|
||||
limit: int = 100,
|
||||
filters: Optional[Dict[str, Any]] = None,
|
||||
) -> Tuple[List[Any], int]:
|
||||
query = db.query(cls.model).filter(
|
||||
cls.model.tenant_id == tenant_id,
|
||||
cls.model.company_id == company_id,
|
||||
)
|
||||
query = db.query(cls.model)
|
||||
|
||||
if hasattr(cls.model, "tenant_id"):
|
||||
query = query.filter(cls.model.tenant_id == tenant_id)
|
||||
|
||||
if hasattr(cls.model, "company_id"):
|
||||
query = query.filter(cls.model.company_id == company_id)
|
||||
|
||||
if filters:
|
||||
if filters.get("code"):
|
||||
@@ -56,11 +59,15 @@ class BaseService:
|
||||
def get_by_id(
|
||||
cls, db: Session, id: int, tenant_id: int, company_id: int
|
||||
) -> Optional[Any]:
|
||||
return db.query(cls.model).filter(
|
||||
cls.model.id == id,
|
||||
cls.model.tenant_id == tenant_id,
|
||||
cls.model.company_id == company_id,
|
||||
).first()
|
||||
query = db.query(cls.model).filter(cls.model.id == id)
|
||||
|
||||
if hasattr(cls.model, "tenant_id"):
|
||||
query = query.filter(cls.model.tenant_id == tenant_id)
|
||||
|
||||
if hasattr(cls.model, "company_id"):
|
||||
query = query.filter(cls.model.company_id == company_id)
|
||||
|
||||
return query.first()
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
@@ -70,9 +77,15 @@ class BaseService:
|
||||
tenant_id: int,
|
||||
company_id: int,
|
||||
) -> Any:
|
||||
db_obj = cls.model(
|
||||
**data.model_dump(), tenant_id=tenant_id, company_id=company_id
|
||||
)
|
||||
create_kwargs = data.model_dump()
|
||||
|
||||
if hasattr(cls.model, "tenant_id"):
|
||||
create_kwargs["tenant_id"] = tenant_id
|
||||
|
||||
if hasattr(cls.model, "company_id"):
|
||||
create_kwargs["company_id"] = company_id
|
||||
|
||||
db_obj = cls.model(**create_kwargs)
|
||||
db.add(db_obj)
|
||||
db.commit()
|
||||
db.refresh(db_obj)
|
||||
|
||||
@@ -22,14 +22,16 @@ from sqlalchemy import (
|
||||
# Importante usar relationship y Mapped
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from api.v1.modules.public.reference_data.currency_types.models import CurrencyType
|
||||
from api.v1.modules.a24.fa.fa_parts.models import FaPart
|
||||
from api.v1.modules.a24.inv.inv_parts.models import InvPart
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from api.v1.modules.a76.classes.models import Class
|
||||
from api.v1.modules.public.reference_data.currency_types.models import CurrencyType
|
||||
from api.v1.modules.a76.general_catalogs.units_of_measure.models import (
|
||||
UnitOfMeasure,
|
||||
)
|
||||
from api.v1.modules.a24.fa.fa_parts.models import FaPart
|
||||
from api.v1.modules.a24.inv.inv_parts.models import InvPart
|
||||
|
||||
|
||||
class Part(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import shutil
|
||||
import base64
|
||||
import pdfkit
|
||||
from pathlib import Path
|
||||
from decimal import Decimal
|
||||
from typing import Tuple, List
|
||||
from typing import Tuple, List, Callable, Optional
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||
from fastapi import HTTPException
|
||||
@@ -81,16 +82,19 @@ class FacturaImportacionMexService:
|
||||
autorizacion=prog.program_number if prog else ""
|
||||
)
|
||||
|
||||
def obtener_datos(self, db: Session, invoice_id: int, company_id: int) -> FacturaImportacionCompleta:
|
||||
def obtener_datos(self, db: Session, invoice_id: int, company_id: int, progress_callback: Optional[Callable] = None) -> FacturaImportacionCompleta:
|
||||
try:
|
||||
if progress_callback: progress_callback(10, "Buscando factura...")
|
||||
header = db.query(InvoiceHeader).filter(InvoiceHeader.id == invoice_id, InvoiceHeader.company_id == company_id).first()
|
||||
if not header: raise HTTPException(status_code=404, detail="Factura no encontrada")
|
||||
|
||||
compliance = header.compliance_mx
|
||||
logistics = header.logistics[0] if header.logistics else None
|
||||
logistics = header.logistics if header.logistics else None
|
||||
if progress_callback: progress_callback(20, "Obteniendo datos de pedimento...")
|
||||
pedimento_id = compliance.pedimento_id if (compliance and compliance.pedimento_id) else header.related_doc_id
|
||||
pedimento = db.query(Pedimentos).filter(Pedimentos.id == pedimento_id).first() if pedimento_id else None
|
||||
|
||||
if progress_callback: progress_callback(30, "Obteniendo cliente y proveedor...")
|
||||
proveedor_id = compliance.provider_id if compliance else None
|
||||
cliente_proveedor = self._obtener_datos_cliente(db, proveedor_id, "Proveedor / Supplier") if proveedor_id else ClienteSchema(header="Proveedor", nombre="No Asignado", direccion="", tax_id="", codigo_postal="", ciudad="", estado="", pais="")
|
||||
|
||||
@@ -112,28 +116,35 @@ class FacturaImportacionMexService:
|
||||
remesa_valor = str(compliance.remesa) if (compliance and compliance.remesa) else ""
|
||||
acuse_valor = str(compliance.edocument) if (compliance and compliance.edocument) else "N/A"
|
||||
|
||||
patente_val = ""
|
||||
if pedimento and pedimento.license:
|
||||
patente_val = pedimento.license
|
||||
elif 'broker' in locals() and broker and broker.license:
|
||||
patente_val = broker.license
|
||||
|
||||
factura_schema = FacturaSchema(
|
||||
numero=header.invoice_number or "S/N",
|
||||
fecha=str(header.invoice_date) if header.invoice_date else "",
|
||||
tipo_cambio=float(pedimento.exchange_rate) if pedimento and pedimento.exchange_rate else 1.0,
|
||||
moneda=getattr(header, 'currency', "USD") or "USD",
|
||||
incoterm=logistics.incoterm if logistics else "",
|
||||
incoterm=(logistics.incoterm or "") if logistics else "",
|
||||
observaciones=header.observation_es or header.observation_en or "",
|
||||
pedimento=pedimento.pedimento_number if pedimento else "",
|
||||
clave_pedimento=pedimento.pedimento_code if pedimento else "",
|
||||
regimen=pedimento.regime if pedimento else "",
|
||||
patente=pedimento.license if pedimento else "",
|
||||
patente=patente_val,
|
||||
agente_aduanal=nombre_agente,
|
||||
transporte=str(logistics.transport_type.value) if (logistics and logistics.transport_type) else "",
|
||||
num_transporte=logistics.trailer_num if logistics else "",
|
||||
placas=logistics.license_plate if logistics else "",
|
||||
transportista=logistics.carrier_id if logistics else "",
|
||||
transporte=str(logistics.transport_type) if (logistics and logistics.transport_type) else "",
|
||||
num_transporte=(logistics.trailer_num or "") if logistics else "",
|
||||
placas=(logistics.license_plate or "") if logistics else "",
|
||||
transportista=(logistics.carrier_id or "") if logistics else "",
|
||||
aduana=pedimento.customs_office if pedimento else "",
|
||||
precinto=logistics.seal_number if logistics else "",
|
||||
destino=logistics.destination_goods if logistics else "",
|
||||
precinto=(logistics.seal_number or "") if logistics else "",
|
||||
destino=(logistics.destination_goods or "") if logistics else "",
|
||||
remesa=remesa_valor, acuse_electronico=acuse_valor
|
||||
)
|
||||
|
||||
|
||||
if progress_callback: progress_callback(50, "Procesando partidas...")
|
||||
lines = db.query(LineItem).join(Item, LineItem.item_id == Item.id).filter(Item.invoice_id == header.id).all()
|
||||
partidas_list = []
|
||||
|
||||
@@ -223,16 +234,53 @@ class FacturaImportacionMexService:
|
||||
valor_total_total=self.formatear_numero(valor), valor_total_dolares=self.formatear_numero(valor / tc if tc > 0 else 0)
|
||||
)
|
||||
|
||||
def generar_factura_completa(self, db: Session, invoice_id: int, company_id: int, formato: str = "pdf") -> Tuple[bytes, str, str]:
|
||||
datos = self.obtener_datos(db, invoice_id, company_id)
|
||||
def generar_factura_completa(self, db: Session, invoice_id: int, company_id: int, formato: str = "pdf", progress_callback: Optional[Callable] = None) -> Tuple[bytes, str, str]:
|
||||
if progress_callback: progress_callback(5, "Iniciando servicio de reporte...")
|
||||
datos = self.obtener_datos(db, invoice_id, company_id, progress_callback)
|
||||
|
||||
if progress_callback: progress_callback(80, "Renderizando plantilla...")
|
||||
|
||||
# LOGO LOGIC
|
||||
logo_b64 = None
|
||||
try:
|
||||
# Fetch company to get logo path
|
||||
# We use the passed company_id which corresponds to the active company
|
||||
comp_logo = db.query(Company).filter(Company.id == company_id).first()
|
||||
if comp_logo and comp_logo.logo:
|
||||
p = Path(comp_logo.logo)
|
||||
|
||||
# Logic robusta de búsqueda (igual que en routes.py)
|
||||
target_path = p
|
||||
if not target_path.exists():
|
||||
# Intentar en la ruta estándar: app_data/logos/{id}/{nombre}
|
||||
# Esto cubre el caso donde solo se guardó el nombre del archivo o la ruta absoluta cambió
|
||||
fallback = Path(f"app_data/logos/{company_id}") / p.name
|
||||
if fallback.exists():
|
||||
target_path = fallback
|
||||
|
||||
if target_path.exists():
|
||||
with open(target_path, "rb") as image_file:
|
||||
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
|
||||
# Detect MIME type loosely
|
||||
mime = "image/png"
|
||||
if target_path.suffix.lower() in ['.jpg', '.jpeg']: mime = "image/jpeg"
|
||||
logo_b64 = f"data:{mime};base64,{encoded_string}"
|
||||
except Exception as e:
|
||||
print(f"Error loading logo: {e}")
|
||||
|
||||
context = {
|
||||
'cliente_proveedor': datos.cliente_proveedor.model_dump(), 'cliente_vendido': datos.cliente_vendido.model_dump(),
|
||||
'cliente_enviado': datos.cliente_enviado.model_dump(), 'factura': datos.factura.model_dump(),
|
||||
'partidas': [p.model_dump() for p in datos.partidas], 'totales': datos.totales.model_dump()
|
||||
'partidas': [p.model_dump() for p in datos.partidas], 'totales': datos.totales.model_dump(),
|
||||
'logo_b64': logo_b64
|
||||
}
|
||||
html_content = self.template.render(**context)
|
||||
nombre = f"Factura_{datos.factura.numero}.{formato}"
|
||||
if formato == "html": return html_content.encode('utf-8'), nombre, "text/html"
|
||||
|
||||
if progress_callback: progress_callback(90, "Generando PDF final...")
|
||||
options = {'page-size': 'Letter', 'margin-top': '0.5in', 'margin-right': '0.5in', 'margin-bottom': '0.5in', 'margin-left': '0.5in', 'encoding': "UTF-8", 'enable-local-file-access': None}
|
||||
pdf = pdfkit.from_string(html_content, False, options=options, configuration=self._get_wkhtmltopdf_config())
|
||||
|
||||
if progress_callback: progress_callback(100, "Completado")
|
||||
return pdf, nombre, "application/pdf"
|
||||
@@ -23,13 +23,16 @@ async def get_task_status(
|
||||
response = {
|
||||
"task_id": task_id,
|
||||
"state": task_result.state,
|
||||
"result": None
|
||||
"result": None,
|
||||
"info": None
|
||||
}
|
||||
|
||||
if task_result.state == 'FAILURE':
|
||||
response["result"] = str(task_result.result)
|
||||
elif task_result.state == 'SUCCESS':
|
||||
response["result"] = task_result.result
|
||||
elif task_result.state == 'PROCESSING':
|
||||
response["info"] = task_result.info
|
||||
|
||||
return response
|
||||
|
||||
@@ -42,4 +45,4 @@ async def trigger_descarga_factura(
|
||||
):
|
||||
validate_access_to_resource(db, company_id, current_user)
|
||||
task = generar_pdf_factura_async.delay(invoice_id, company_id)
|
||||
return {"task_id": task.id, "message": "Generación iniciada"}
|
||||
return {"task_id": task.id, "message": "Generación iniciada"}
|
||||
@@ -22,13 +22,17 @@ def generar_pdf_factura_async(self, invoice_id: int, company_id: int):
|
||||
service = FacturaImportacionMexService()
|
||||
|
||||
# Update state to PROCESSING
|
||||
self.update_state(state='PROCESSING', meta={'current': 1, 'total': 1, 'status': 'Generating PDF...'})
|
||||
self.update_state(state='PROCESSING', meta={'current': 5, 'total': 100, 'status': 'Iniciando generación...'})
|
||||
|
||||
def progress_callback(progress: int, status: str):
|
||||
self.update_state(state='PROCESSING', meta={'current': progress, 'total': 100, 'status': status})
|
||||
|
||||
# 3. Generamos los bytes del PDF
|
||||
pdf_bytes, nombre, media_type = service.generar_factura_completa(
|
||||
db=db,
|
||||
invoice_id=invoice_id,
|
||||
company_id=company_id
|
||||
company_id=company_id,
|
||||
progress_callback=progress_callback
|
||||
)
|
||||
|
||||
# 4. Codificamos a base64 para que viaje seguro por Valkey
|
||||
|
||||
@@ -1,79 +1,308 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es" xml:lang="es">
|
||||
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
|
||||
<title>Factura Importacion Mexicana - {{ factura.numero }}</title>
|
||||
<style type="text/css">
|
||||
/* ESTILOS EXACTOS DE SCAPII PARA MANTENER EL FORMATO */
|
||||
* { margin: 0; padding: 0; font-family: Tahoma, sans-serif; color: black; }
|
||||
.titulo { font-size: 14pt; padding: 3pt 0 0 6pt; }
|
||||
.grande { font-size: 13pt; padding-left: 5pt; }
|
||||
.medio-bold { font-size: 9pt; font-weight: bold; padding: 3pt 0 0 3pt; }
|
||||
.normal { font-size: 8pt; }
|
||||
.small-bold { font-size: 8pt; font-weight: bold; }
|
||||
.tiny-bold { font-size: 7pt; font-weight: bold; }
|
||||
.tiny { font-size: 7pt; }
|
||||
.mini { font-size: 5pt; }
|
||||
h1 { font-weight: bold; font-size: 8pt; }
|
||||
p { font-size: 7pt; }
|
||||
.cliente { border-bottom: 1pt solid black; text-decoration: none; display: block; width: 100%}
|
||||
.center { text-align: center; }
|
||||
.right { text-align: right; }
|
||||
.border { border: 1pt solid #808080; }
|
||||
.p-t-1 { padding-top: 1pt; } .p-t-2 { padding-top: 2pt; } .p-t-3 { padding-top: 3pt; }
|
||||
.p-t-4 { padding-top: 4pt; } .p-t-5 { padding-top: 5pt; } .p-t-8 { padding-top: 8pt; }
|
||||
.p-t-9 { padding-top: 9pt; }
|
||||
.p-l-2 { padding-left: 2pt; } .p-l-3 { padding-left: 3pt; } .p-l-5 { padding-left: 5pt; }
|
||||
.p-l-6 { padding-left: 6pt; } .p-l-7 { padding-left: 7pt; } .p-l-8 { padding-left: 8pt; }
|
||||
.p-l-9 { padding-left: 9pt; } .p-l-10 { padding-left: 10pt; }
|
||||
.p-r-1 { padding-right: 1pt; } .p-r-2 { padding-right: 2pt; } .p-r-4 { padding-right: 4pt; }
|
||||
.p-r-5 { padding-right: 5pt; } .p-r-9 { padding-right: 9pt; }
|
||||
.p-b-1 { padding-bottom: 1pt; }
|
||||
.h-10 { height: 10pt; } .h-11 { height: 11pt; } .h-14 { height: 14pt; }
|
||||
.h-15 { height: 15pt; } .h-18 { height: 18pt; } .h-22 { height: 22pt; }
|
||||
.h-82 { height: 82pt; } .h-384 { height: 384pt; }
|
||||
.line-1 { line-height: 1pt; } .line-7 { line-height: 7pt; } .line-8 { line-height: 8pt; }
|
||||
.line-9 { line-height: 9pt; } .line-10 { line-height: 10pt; }
|
||||
.m-l-3 { margin-left: 3pt; } .m-l-5 { margin-left: 5.74pt; }
|
||||
.flex-container { width: 100%; overflow: hidden; }
|
||||
.flex-container-end { width: 100%; overflow: hidden; }
|
||||
.width-48 { width: 48%; float: left; }
|
||||
.width-48-right { width: 48%; float: right; text-align: right; }
|
||||
.position-relative-centered { position: relative; width: 48%; float: left; }
|
||||
.full-width-block { display: block; width: 100%; }
|
||||
.clearfix::after { content: ""; display: table; clear: both; }
|
||||
table, tbody { vertical-align: top; overflow: visible; }
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Tahoma, sans-serif;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.titulo {
|
||||
font-size: 14pt;
|
||||
padding: 3pt 0 0 6pt;
|
||||
}
|
||||
|
||||
.grande {
|
||||
font-size: 13pt;
|
||||
padding-left: 5pt;
|
||||
}
|
||||
|
||||
.medio-bold {
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
padding: 3pt 0 0 3pt;
|
||||
}
|
||||
|
||||
.normal {
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
.small-bold {
|
||||
font-size: 8pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tiny-bold {
|
||||
font-size: 7pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tiny {
|
||||
font-size: 7pt;
|
||||
}
|
||||
|
||||
.mini {
|
||||
font-size: 5pt;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 7pt;
|
||||
}
|
||||
|
||||
.cliente {
|
||||
border-bottom: 1pt solid black;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.border {
|
||||
border: 1pt solid #808080;
|
||||
}
|
||||
|
||||
.p-t-1 {
|
||||
padding-top: 1pt;
|
||||
}
|
||||
|
||||
.p-t-2 {
|
||||
padding-top: 2pt;
|
||||
}
|
||||
|
||||
.p-t-3 {
|
||||
padding-top: 3pt;
|
||||
}
|
||||
|
||||
.p-t-4 {
|
||||
padding-top: 4pt;
|
||||
}
|
||||
|
||||
.p-t-5 {
|
||||
padding-top: 5pt;
|
||||
}
|
||||
|
||||
.p-t-8 {
|
||||
padding-top: 8pt;
|
||||
}
|
||||
|
||||
.p-t-9 {
|
||||
padding-top: 9pt;
|
||||
}
|
||||
|
||||
.p-l-2 {
|
||||
padding-left: 2pt;
|
||||
}
|
||||
|
||||
.p-l-3 {
|
||||
padding-left: 3pt;
|
||||
}
|
||||
|
||||
.p-l-5 {
|
||||
padding-left: 5pt;
|
||||
}
|
||||
|
||||
.p-l-6 {
|
||||
padding-left: 6pt;
|
||||
}
|
||||
|
||||
.p-l-7 {
|
||||
padding-left: 7pt;
|
||||
}
|
||||
|
||||
.p-l-8 {
|
||||
padding-left: 8pt;
|
||||
}
|
||||
|
||||
.p-l-9 {
|
||||
padding-left: 9pt;
|
||||
}
|
||||
|
||||
.p-l-10 {
|
||||
padding-left: 10pt;
|
||||
}
|
||||
|
||||
.p-r-1 {
|
||||
padding-right: 1pt;
|
||||
}
|
||||
|
||||
.p-r-2 {
|
||||
padding-right: 2pt;
|
||||
}
|
||||
|
||||
.p-r-4 {
|
||||
padding-right: 4pt;
|
||||
}
|
||||
|
||||
.p-r-5 {
|
||||
padding-right: 5pt;
|
||||
}
|
||||
|
||||
.p-r-9 {
|
||||
padding-right: 9pt;
|
||||
}
|
||||
|
||||
.p-b-1 {
|
||||
padding-bottom: 1pt;
|
||||
}
|
||||
|
||||
.h-10 {
|
||||
height: 10pt;
|
||||
}
|
||||
|
||||
.h-11 {
|
||||
height: 11pt;
|
||||
}
|
||||
|
||||
.h-14 {
|
||||
height: 14pt;
|
||||
}
|
||||
|
||||
.h-15 {
|
||||
height: 15pt;
|
||||
}
|
||||
|
||||
.h-18 {
|
||||
height: 18pt;
|
||||
}
|
||||
|
||||
.h-22 {
|
||||
height: 22pt;
|
||||
}
|
||||
|
||||
.h-82 {
|
||||
height: 82pt;
|
||||
}
|
||||
|
||||
.h-384 {
|
||||
height: 384pt;
|
||||
}
|
||||
|
||||
.line-1 {
|
||||
line-height: 1pt;
|
||||
}
|
||||
|
||||
.line-7 {
|
||||
line-height: 7pt;
|
||||
}
|
||||
|
||||
.line-8 {
|
||||
line-height: 8pt;
|
||||
}
|
||||
|
||||
.line-9 {
|
||||
line-height: 9pt;
|
||||
}
|
||||
|
||||
.line-10 {
|
||||
line-height: 10pt;
|
||||
}
|
||||
|
||||
.m-l-3 {
|
||||
margin-left: 3pt;
|
||||
}
|
||||
|
||||
.m-l-5 {
|
||||
margin-left: 5.74pt;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flex-container-end {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.width-48 {
|
||||
width: 48%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.width-48-right {
|
||||
width: 48%;
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.position-relative-centered {
|
||||
position: relative;
|
||||
width: 48%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.full-width-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.clearfix::after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
table,
|
||||
tbody {
|
||||
vertical-align: top;
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div class="flex-container clearfix">
|
||||
<div class="width-48">
|
||||
<p class="titulo">Factura de Importacion</p>
|
||||
<div class="cliente" style="height: 1px;"></div>
|
||||
<p class="cliente p-t-1 p-b-1"></p>
|
||||
<div class="width-48" style="position: relative;">
|
||||
<p class="titulo">Factura de Importacion</p>
|
||||
<div class="cliente" style="height: 1px;"></div>
|
||||
<p class="cliente p-t-1 p-b-1"></p>
|
||||
</div>
|
||||
<div class="width-48-right">
|
||||
<p class="p-l-5 line-1"><span></span></p>
|
||||
<p class="p-t-2"><br></p>
|
||||
<p class="p-l-5 line-1"><span></span></p>
|
||||
<p class="p-t-2"><br></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-container-end clearfix">
|
||||
</div>
|
||||
<div class="flex-container-end clearfix" style="overflow: visible;">
|
||||
<div class="position-relative-centered">
|
||||
{% if logo_b64 %}
|
||||
<div style="position: absolute; top: 0; left: 0;">
|
||||
<img src="{{ logo_b64 }}" style="max-height: 70pt; max-width: 120pt;" />
|
||||
</div>
|
||||
{% endif %}
|
||||
<div style="float: right; text-align: left;">
|
||||
<h1 class="cliente">{{ cliente_proveedor.header }}</h1>
|
||||
<p>{{ cliente_proveedor.nombre }}</p>
|
||||
<p>{{ cliente_proveedor.direccion }}
|
||||
{% if cliente_proveedor.num_exterior %} Ext: {{ cliente_proveedor.num_exterior }}{% endif %}
|
||||
{% if cliente_proveedor.num_interior %} Int: {{ cliente_proveedor.num_interior }}{% endif %}
|
||||
<p>{{ cliente_proveedor.direccion }}
|
||||
{% if cliente_proveedor.num_exterior %} Ext: {{ cliente_proveedor.num_exterior }}{% endif %}
|
||||
{% if cliente_proveedor.num_interior %} Int: {{ cliente_proveedor.num_interior }}{% endif %}
|
||||
</p>
|
||||
<p>{{ cliente_proveedor.colonia }} {% if cliente_proveedor.codigo_postal %} CP: {{ cliente_proveedor.codigo_postal }}{% endif %}</p>
|
||||
<p>{{ cliente_proveedor.colonia }} {% if cliente_proveedor.codigo_postal %} CP: {{
|
||||
cliente_proveedor.codigo_postal }}{% endif %}</p>
|
||||
<p>{{ cliente_proveedor.ciudad }}, {{ cliente_proveedor.estado }}, {{ cliente_proveedor.pais }}</p>
|
||||
<p>TAX ID: {{ cliente_proveedor.tax_id }}
|
||||
{% if cliente_proveedor.programa and cliente_proveedor.programa != 'Ninguno' %}
|
||||
{{ cliente_proveedor.programa }}: {{ cliente_proveedor.autorizacion }}
|
||||
{% endif %}
|
||||
<p>TAX ID: {{ cliente_proveedor.tax_id }}
|
||||
{% if cliente_proveedor.programa and cliente_proveedor.programa != 'Ninguno' %}
|
||||
{{ cliente_proveedor.programa }}: {{ cliente_proveedor.autorizacion }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<p><br></p>
|
||||
</div>
|
||||
@@ -82,151 +311,178 @@
|
||||
<div style="position: relative;">
|
||||
<table cellspacing="0" class="m-l-3" style="float: right; text-align: left;">
|
||||
<tbody>
|
||||
<tr class="h-18">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="medio-bold">FACTURA:</p>
|
||||
</td>
|
||||
<td class="border" colspan="3" style="width:176pt">
|
||||
<p class="grande">{{ factura.numero }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-11">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="normal p-l-3 line-9">Fecha:</p>
|
||||
</td>
|
||||
<td class="border" style="width:72pt">
|
||||
<p class="normal p-l-2 line-9">{{ factura.fecha }}</p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="normal p-l-2 line-9">T. Cambio:</p>
|
||||
</td>
|
||||
<td class="border" style="width:52pt">
|
||||
<p class="normal center line-9">{{ factura.tipo_cambio }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-11">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="small-bold p-l-3 line-9">Pedimento:</p>
|
||||
</td>
|
||||
<td class="border" colspan="1" style="width:109pt">
|
||||
<p class="normal p-l-3 line-9">{{ factura.pedimento }}</p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border">
|
||||
<p class="normal p-l-3 line-9">Clave:</p>
|
||||
</td>
|
||||
<td class="border" style="width:37pt">
|
||||
<p class="normal p-r-2 line-9 center">{{ factura.clave_pedimento }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-11">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="normal p-l-3 line-9">Remesa:</p>
|
||||
</td>
|
||||
<td class="border" style="width:38pt">
|
||||
<p class="normal p-l-10 line-9">{{ factura.remesa }}</p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:79pt">
|
||||
<p class="normal p-l-6 line-9">Acuse:</p>
|
||||
</td>
|
||||
<td class="border" style="width:59pt">
|
||||
<p class="normal p-l-6 line-9">{{ factura.acuse_electronico or 'N/A' }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-22">
|
||||
<td class="border" colspan="4" style="width:228pt">
|
||||
<p class="tiny-bold p-l-3 line-7">Agente Aduanal:</p>
|
||||
<p class="tiny p-l-3 line-7">{{ factura.agente_aduanal or '' }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-15">
|
||||
<td class="border" colspan="2">
|
||||
<p><span class="tiny-bold p-l-3 line-7">Patente: </span><span class="tiny p-l-3 line-7">{{ factura.patente or '' }}</span></p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<span class="tiny-bold p-l-3 line-7">Regimen:</span><span class="tiny p-l-3 line-7">{{ factura.regimen or '' }}</span>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="tiny-bold p-l-3 line-7">INCOTERM:</p>
|
||||
<p class="tiny p-l-3 line-7">{{ factura.incoterm or '' }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="border" colspan="2">
|
||||
{% if factura.precinto %}
|
||||
<p class="tiny-bold p-l-3 line-7">Precinto: {{ factura.precinto }}</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="tiny-bold p-l-3 line-7">Aduana: {{ factura.aduana }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
{% if factura.destino %}
|
||||
<p class="tiny-bold p-l-3 line-7">Destino: {{ factura.destino }}</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<tr class="h-18">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="medio-bold">FACTURA:</p>
|
||||
</td>
|
||||
<td class="border" colspan="3" style="width:176pt">
|
||||
<p class="grande">{{ factura.numero }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-11">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="normal p-l-3 line-9">Fecha:</p>
|
||||
</td>
|
||||
<td class="border" style="width:72pt">
|
||||
<p class="normal p-l-2 line-9">{{ factura.fecha }}</p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="normal p-l-2 line-9">T. Cambio:</p>
|
||||
</td>
|
||||
<td class="border" style="width:52pt">
|
||||
<p class="normal center line-9">{{ factura.tipo_cambio }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-11">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="small-bold p-l-3 line-9">Pedimento:</p>
|
||||
</td>
|
||||
<td class="border" colspan="1" style="width:109pt">
|
||||
<p class="normal p-l-3 line-9">{{ factura.pedimento }}</p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border">
|
||||
<p class="normal p-l-3 line-9">Clave:</p>
|
||||
</td>
|
||||
<td class="border" style="width:37pt">
|
||||
<p class="normal p-r-2 line-9 center">{{ factura.clave_pedimento }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-11">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
|
||||
<p class="normal p-l-3 line-9">Remesa:</p>
|
||||
</td>
|
||||
<td class="border" style="width:38pt">
|
||||
<p class="normal p-l-10 line-9">{{ factura.remesa }}</p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:79pt">
|
||||
<p class="normal p-l-6 line-9">Acuse:</p>
|
||||
</td>
|
||||
<td class="border" style="width:59pt">
|
||||
<p class="normal p-l-6 line-9">{{ factura.acuse_electronico or 'N/A' }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-22">
|
||||
<td class="border" colspan="4" style="width:228pt">
|
||||
<p class="small-bold p-l-3 line-7">Agente Aduanal:</p>
|
||||
<p class="normal p-l-3 line-7">{{ factura.agente_aduanal or '' }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-15">
|
||||
<td class="border" colspan="2">
|
||||
<p><span class="small-bold p-l-3 line-7">Patente: </span><span
|
||||
class="normal p-l-3 line-7">{{ factura.patente or '' }}</span></p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<span class="tiny-bold p-l-3 line-7">Regimen:</span><span class="tiny p-l-3 line-7">{{
|
||||
factura.regimen or '' }}</span>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="tiny-bold p-l-3 line-7">INCOTERM:</p>
|
||||
<p class="tiny p-l-3 line-7">{{ factura.incoterm or '' }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="border" colspan="2">
|
||||
{% if factura.precinto %}
|
||||
<p class="tiny-bold p-l-3 line-7">Precinto: {{ factura.precinto }}</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="tiny-bold p-l-3 line-7">Aduana: {{ factura.aduana }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
{% if factura.destino %}
|
||||
<p class="tiny-bold p-l-3 line-7">Destino: {{ factura.destino }}</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-container clearfix">
|
||||
<div class="width-48">
|
||||
<h1 class="p-t-5 p-l-5 line-10 cliente">{{ cliente_vendido.header }}</h1>
|
||||
<p class="p-l-5 line-8">{{ cliente_vendido.nombre }}</p>
|
||||
<p class="p-l-5">{{ cliente_vendido.direccion }}
|
||||
{% if cliente_vendido.num_exterior %} Ext: {{ cliente_vendido.num_exterior }}{% endif %}
|
||||
{% if cliente_vendido.num_interior %} Int: {{ cliente_vendido.num_interior }}{% endif %}
|
||||
<p class="p-l-5">{{ cliente_vendido.direccion }}
|
||||
{% if cliente_vendido.num_exterior %} Ext: {{ cliente_vendido.num_exterior }}{% endif %}
|
||||
{% if cliente_vendido.num_interior %} Int: {{ cliente_vendido.num_interior }}{% endif %}
|
||||
</p>
|
||||
<p class="p-l-5">{{ cliente_vendido.colonia }} {% if cliente_vendido.codigo_postal %} CP: {{ cliente_vendido.codigo_postal }}{% endif %}</p>
|
||||
<p class="p-l-5">{{ cliente_vendido.ciudad }}, {{ cliente_vendido.estado }}, {{ cliente_vendido.pais }}</p>
|
||||
<p class="p-l-5">RFC: {{ cliente_vendido.tax_id }}
|
||||
{% if cliente_vendido.programa and cliente_vendido.programa != 'Ninguno' %}
|
||||
{{ cliente_vendido.programa }}: {{ cliente_vendido.autorizacion }}
|
||||
{% endif %}
|
||||
<p class="p-l-5">{{ cliente_vendido.colonia }} {% if cliente_vendido.codigo_postal %} CP: {{
|
||||
cliente_vendido.codigo_postal }}{% endif %}</p>
|
||||
<p class="p-l-5">{{ cliente_vendido.ciudad }}, {{ cliente_vendido.estado }}, {{ cliente_vendido.pais }}
|
||||
</p>
|
||||
<p class="p-l-5">RFC: {{ cliente_vendido.tax_id }}
|
||||
{% if cliente_vendido.programa and cliente_vendido.programa != 'Ninguno' %}
|
||||
{{ cliente_vendido.programa }}: {{ cliente_vendido.autorizacion }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="width-48-right" style="text-align: left;">
|
||||
<h1 class="p-t-5 p-l-5 line-10 cliente full-width-block">{{ cliente_enviado.header }}</h1>
|
||||
<p class="p-l-5 line-8">{{ cliente_enviado.nombre }}</p>
|
||||
<p class="p-l-5">{{ cliente_enviado.direccion }}
|
||||
{% if cliente_enviado.num_exterior %} Ext: {{ cliente_enviado.num_exterior }}{% endif %}
|
||||
{% if cliente_enviado.num_interior %} Int: {{ cliente_enviado.num_interior }}{% endif %}
|
||||
<p class="p-l-5">{{ cliente_enviado.direccion }}
|
||||
{% if cliente_enviado.num_exterior %} Ext: {{ cliente_enviado.num_exterior }}{% endif %}
|
||||
{% if cliente_enviado.num_interior %} Int: {{ cliente_enviado.num_interior }}{% endif %}
|
||||
</p>
|
||||
<p class="p-l-5">{{ cliente_enviado.colonia }} {% if cliente_enviado.codigo_postal %} CP: {{
|
||||
cliente_enviado.codigo_postal }}{% endif %}</p>
|
||||
<p class="p-l-5">{{ cliente_enviado.ciudad }}, {{ cliente_enviado.estado }}, {{ cliente_enviado.pais }}
|
||||
</p>
|
||||
<p class="p-l-5">{{ cliente_enviado.colonia }} {% if cliente_enviado.codigo_postal %} CP: {{ cliente_enviado.codigo_postal }}{% endif %}</p>
|
||||
<p class="p-l-5">{{ cliente_enviado.ciudad }}, {{ cliente_enviado.estado }}, {{ cliente_enviado.pais }}</p>
|
||||
<p class="p-l-5">RFC: {{ cliente_enviado.tax_id }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="p-t-8"><br /></p>
|
||||
</header>
|
||||
|
||||
<table cellspacing="0" class="m-l-5" style="width: 99%; max-width: 580pt;">
|
||||
<thead>
|
||||
<table cellspacing="0" class="m-l-5" style="width: 99%; max-width: 580pt;">
|
||||
<thead>
|
||||
<tr class="h-10">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:60pt"><p class="tiny line-9">Transportista:</p></td>
|
||||
<td class="border" colspan="4" style="width:120pt"><p class="tiny">{{ factura.transportista }}</p></td>
|
||||
<td class="border" style="width:40pt"><p class="tiny">SCAC: {{ factura.scac }}</p></td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:40pt"><p class="tiny">INCOTERM:</p></td>
|
||||
<td class="border" style="width:40pt"><p class="tiny">{{ factura.incoterm }}</p></td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:60pt">
|
||||
<p class="tiny line-9">Transportista:</p>
|
||||
</td>
|
||||
<td class="border" colspan="4" style="width:120pt">
|
||||
<p class="tiny">{{ factura.transportista }}</p>
|
||||
</td>
|
||||
<td class="border" style="width:40pt">
|
||||
<p class="tiny">SCAC: {{ factura.scac }}</p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:40pt">
|
||||
<p class="tiny">INCOTERM:</p>
|
||||
</td>
|
||||
<td class="border" style="width:40pt">
|
||||
<p class="tiny">{{ factura.incoterm }}</p>
|
||||
</td>
|
||||
<td class="border" style="width:50pt">
|
||||
<p class="tiny line-9">Aduana: <span class="mini">{{ factura.aduana }}</span></p>
|
||||
</td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:20pt"><p><br /></p></td>
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:20pt">
|
||||
<p><br /></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-10">
|
||||
<td bgcolor="#E4E4E4" class="border" style="width:60pt">
|
||||
<p class="tiny line-9">Transporte:</p>
|
||||
</td>
|
||||
<td class="border" colspan="4" style="width:120pt"><p class="tiny">{{ factura.transporte }}: {{ factura.num_transporte }}</p></td>
|
||||
<td class="border" colspan="3" style="width:80pt"><p class="tiny">CAAT: {{ factura.caat }}</p></td>
|
||||
</td>
|
||||
<td class="border" colspan="4" style="width:120pt">
|
||||
<p class="tiny">{{ factura.transporte }}: {{ factura.num_transporte }}</p>
|
||||
</td>
|
||||
<td class="border" colspan="3" style="width:80pt">
|
||||
<p class="tiny">CAAT: {{ factura.caat }}</p>
|
||||
</td>
|
||||
<td class="border" colspan="2" style="width:100pt">
|
||||
<p class="tiny p-t-1 line-8">Placas: {{ factura.placas or 'N/A' }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="h-10">
|
||||
<td class="border" colspan="2" style="width:80pt"><p><br /></p></td>
|
||||
<td class="border" colspan="8"><p><br /></p></td>
|
||||
<td class="border" colspan="2" style="width:80pt">
|
||||
<p><br /></p>
|
||||
</td>
|
||||
<td class="border" colspan="8">
|
||||
<p><br /></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="h-10">
|
||||
@@ -273,51 +529,51 @@
|
||||
<p class="mini center">Total</p>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
</thead>
|
||||
|
||||
<tbody class="h-384" style="width: 100%;">
|
||||
{% for partida in partidas %}
|
||||
<tr>
|
||||
<td class="border">
|
||||
<p class="mini p-t-3 center">{{ loop.index }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-1" style="font-weight: bold;">{{ partida.numero_parte }}</p>
|
||||
<p class="mini">{{ partida.descripcion }}</p>
|
||||
<p class="mini">Frac: {{ partida.fraccion }} / Orig: {{ partida.origen or 'MEX' }}</p>
|
||||
<p class="mini">
|
||||
{% if partida.advalorem %}ADV: {{ partida.advalorem }}{% endif %}
|
||||
{% if partida.preferencia %} / PREF: {{ partida.preferencia }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="border" colspan="2">
|
||||
<p class="mini p-t-2 center">{{ partida.cantidad_importacion }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 center">{{ partida.unidad_medida }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 center">
|
||||
{% if partida.cantidad_bultos != 0 %}{{ partida.cantidad_bultos }}{% endif %}
|
||||
{{ partida.clave_bultos }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-3 center">{{ partida.peso_neto }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-3 center">{{ partida.peso_bruto }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 center">${{ partida.valor_costo_unitario }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 right p-r-2">${{ partida.valor_total }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td class="border">
|
||||
<p class="mini p-t-3 center">{{ loop.index }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-1" style="font-weight: bold;">{{ partida.numero_parte }}</p>
|
||||
<p class="mini">{{ partida.descripcion }}</p>
|
||||
<p class="mini">Frac: {{ partida.fraccion }} / Orig: {{ partida.origen or 'MEX' }}</p>
|
||||
<p class="mini">
|
||||
{% if partida.advalorem %}ADV: {{ partida.advalorem }}{% endif %}
|
||||
{% if partida.preferencia %} / PREF: {{ partida.preferencia }}{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td class="border" colspan="2">
|
||||
<p class="mini p-t-2 center">{{ partida.cantidad_importacion }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 center">{{ partida.unidad_medida }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 center">
|
||||
{% if partida.cantidad_bultos != 0 %}{{ partida.cantidad_bultos }}{% endif %}
|
||||
{{ partida.clave_bultos }}
|
||||
</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-3 center">{{ partida.peso_neto }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-3 center">{{ partida.peso_bruto }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 center">${{ partida.valor_costo_unitario }}</p>
|
||||
</td>
|
||||
<td class="border">
|
||||
<p class="mini p-t-2 right p-r-2">${{ partida.valor_total }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
|
||||
<tfoot>
|
||||
<tr class="h-14">
|
||||
<td bgcolor="#E4E4E4" class="border" colspan="2" style="width:123pt">
|
||||
@@ -332,7 +588,7 @@
|
||||
<td class="border" style="width:22pt"></td>
|
||||
<td class="border" style="width:40pt">
|
||||
<p class="tiny p-t-4 center line-8">
|
||||
{% if totales.bultos_total != 0 %}{{ totales.bultos_total }}{% endif %}
|
||||
{% if totales.bultos_total != 0 %}{{ totales.bultos_total }}{% endif %}
|
||||
<span>{{ totales.clave_bultos or '' }}</span>
|
||||
</p>
|
||||
</td>
|
||||
@@ -355,17 +611,20 @@
|
||||
<p style="border-bottom: 1pt solid black; width: 80%; margin: 0 auto 2pt auto;"></p>
|
||||
<p class="normal center" style="margin-bottom: 0;">{{ cliente_proveedor.nombre }}</p>
|
||||
<p style="margin-bottom: 0;"><br /></p>
|
||||
<p class="small-bold center line-7" style="margin-bottom: 0;">Los valores expresados en esta factura son en: {{ factura.moneda }}</p>
|
||||
<p class="small-bold center line-7" style="margin-bottom: 0;">Los valores expresados en esta factura
|
||||
son en: {{ factura.moneda }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="10" style="width:580pt; vertical-align: top; height: 100%;">
|
||||
<p class="p-t-8"><br /></p>
|
||||
<p class="p-t-8"><br /></p>
|
||||
<p class="p-l-5 line-8 tiny-bold">Normal Por Parte</p>
|
||||
<p class="normal p-l-5 line-9">Declaro bajo protesta de decir verdad que la información contenida en este documento es verdadera y me hago responsable de comprobar lo aquí declarado.</p>
|
||||
<p class="normal p-l-5 line-9">Declaro bajo protesta de decir verdad que la información contenida en
|
||||
este documento es verdadera y me hago responsable de comprobar lo aquí declarado.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -7,11 +7,11 @@ class Container(Base):
|
||||
__tablename__ = "containers" # GContenedores
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("key", name="containers_pkey"),
|
||||
{"schema": "public", "extend_existing": True}, # opcional
|
||||
{"extend_existing": True}, # opcional
|
||||
)
|
||||
|
||||
key: Mapped[str] = mapped_column(
|
||||
String(3), nullable=False
|
||||
String(3), primary_key=True, nullable=False
|
||||
) # mantiene ceros iniciales
|
||||
description: Mapped[str] = mapped_column(
|
||||
String(500), nullable=False
|
||||
|
||||
@@ -11,7 +11,7 @@ class MaterialType(Base):
|
||||
)
|
||||
|
||||
key: Mapped[str] = mapped_column(
|
||||
String(10), nullable=False) # clave del material
|
||||
String(10), primary_key=True, nullable=False) # clave del material
|
||||
type: Mapped[str] = mapped_column(String(15), nullable=False) # tipo
|
||||
description: Mapped[str] = mapped_column(
|
||||
String(256), nullable=False
|
||||
|
||||
BIN
backend/app_data/logos/1/AS.png
Normal file
BIN
backend/app_data/logos/1/AS.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
backend/app_data/logos/1/Agenda.png
Normal file
BIN
backend/app_data/logos/1/Agenda.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
backend/app_data/logos/1/a519dfd146c1bd477bcd841afe02d7de.jpg
Normal file
BIN
backend/app_data/logos/1/a519dfd146c1bd477bcd841afe02d7de.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
backend/app_data/logos/1/footer.png
Normal file
BIN
backend/app_data/logos/1/footer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
BIN
backend/app_data/logos/1/logo2.jpg
Normal file
BIN
backend/app_data/logos/1/logo2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
backend/app_data/logos/company_1_logo.png
Normal file
BIN
backend/app_data/logos/company_1_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Reference in New Issue
Block a user