Parametros generales SCAII, con endpint y con la mayoria de pestanias hechas y funcionalaes
This commit is contained in:
40
backend/alembic/versions/e76_app_settings_add_table.py
Normal file
40
backend/alembic/versions/e76_app_settings_add_table.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""add_app_settings_table
|
||||
|
||||
Revision ID: e76_app_settings
|
||||
Revises: c1a2b3d4e5f6
|
||||
Create Date: 2026-03-27 16:10:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e76_app_settings'
|
||||
down_revision = 'c1a2b3d4e5f6'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
def upgrade():
|
||||
# Create a76.app_settings table
|
||||
op.create_table(
|
||||
'app_settings',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('tenant_id', sa.Integer(), nullable=True),
|
||||
sa.Column('company_id', sa.Integer(), nullable=True),
|
||||
sa.Column('settings', postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default='{}'),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ),
|
||||
sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('tenant_id', 'company_id', name='uq_app_settings_tenant_company'),
|
||||
schema='a76'
|
||||
)
|
||||
op.create_index(op.f('ix_a76_app_settings_company_id'), 'app_settings', ['company_id'], unique=False, schema='a76')
|
||||
op.create_index(op.f('ix_a76_app_settings_tenant_id'), 'app_settings', ['tenant_id'], unique=False, schema='a76')
|
||||
|
||||
def downgrade():
|
||||
op.drop_index(op.f('ix_a76_app_settings_tenant_id'), table_name='app_settings', schema='a76')
|
||||
op.drop_index(op.f('ix_a76_app_settings_company_id'), table_name='app_settings', schema='a76')
|
||||
op.drop_table('app_settings', schema='a76')
|
||||
1
backend/api/v1/modules/a76/app_settings/__init__.py
Normal file
1
backend/api/v1/modules/a76/app_settings/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Module initialization for app_settings
|
||||
33
backend/api/v1/modules/a76/app_settings/models.py
Normal file
33
backend/api/v1/modules/a76/app_settings/models.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from typing import Optional
|
||||
from sqlalchemy import Integer, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from core.database import Base
|
||||
from api.v1.common.base_models import TimestampMixin
|
||||
|
||||
class AppSetting(Base, TimestampMixin):
|
||||
"""
|
||||
Unified configuration table for Anexo 76.
|
||||
Replaces 14 legacy tables using a hierarchical JSONB override system.
|
||||
"""
|
||||
__tablename__ = "app_settings"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "company_id", name="uq_app_settings_tenant_company"),
|
||||
{"schema": "a76"},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
# Hierarchy levels (Nullable to allow Global/Tenant/Company scoping)
|
||||
tenant_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, ForeignKey("core.tenants.id"), nullable=True, index=True
|
||||
)
|
||||
company_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, ForeignKey("a76.company.id"), nullable=True, index=True
|
||||
)
|
||||
|
||||
# The actual configuration payload
|
||||
settings: Mapped[dict] = mapped_column(JSONB, nullable=False, default={})
|
||||
|
||||
def __repr__(self):
|
||||
return f"<AppSetting(id={self.id}, tenant={self.tenant_id}, company={self.company_id})>"
|
||||
59
backend/api/v1/modules/a76/app_settings/routes.py
Normal file
59
backend/api/v1/modules/a76/app_settings/routes.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from fastapi import APIRouter, Depends, Query, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional, Dict, Any
|
||||
from core.database import get_core_db
|
||||
from .service import AppSettingsService
|
||||
from .schemas import AppSettingRequest, AppSettingResponse
|
||||
|
||||
router = APIRouter(prefix="/a76/app-settings", tags=["a76 / app_settings"])
|
||||
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@router.get("/resolved")
|
||||
def get_resolved_settings(
|
||||
tenant_id: int = Query(...),
|
||||
company_id: int = Query(...),
|
||||
db: Session = Depends(get_core_db)
|
||||
):
|
||||
"""
|
||||
Returns the final merged configuration for a company.
|
||||
Merges Global -> Tenant -> Company levels.
|
||||
"""
|
||||
try:
|
||||
return AppSettingsService.get_resolved_settings(db, tenant_id, company_id)
|
||||
except Exception as e:
|
||||
logger.error(f"RESOLVE ERROR: {str(e)}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/upsert")
|
||||
def upsert_settings(
|
||||
payload: AppSettingRequest,
|
||||
db: Session = Depends(get_core_db)
|
||||
):
|
||||
"""
|
||||
Creates or updates an override for a specific level (Global, Tenant, or Company).
|
||||
"""
|
||||
try:
|
||||
data = payload.settings.model_dump(exclude_unset=True)
|
||||
return AppSettingsService.upsert_settings(
|
||||
db,
|
||||
payload.tenant_id,
|
||||
payload.company_id,
|
||||
data
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"UPSERT ERROR: {str(e)}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.put("/upsert")
|
||||
def update_settings(
|
||||
payload: AppSettingRequest,
|
||||
db: Session = Depends(get_core_db)
|
||||
):
|
||||
"""
|
||||
Alias for upsert_settings.
|
||||
"""
|
||||
return upsert_settings(payload, db)
|
||||
989
backend/api/v1/modules/a76/app_settings/schemas.py
Normal file
989
backend/api/v1/modules/a76/app_settings/schemas.py
Normal file
@@ -0,0 +1,989 @@
|
||||
from typing import Optional, Any, Dict
|
||||
from pydantic import BaseModel, Field
|
||||
from decimal import Decimal
|
||||
|
||||
# --- Legacy Table Domains (Tarea 1) ---
|
||||
|
||||
class SSisGenSettings(BaseModel):
|
||||
"""
|
||||
Legacy Table: SSisGen
|
||||
General system parameters migrated from Clarion/WinDev.
|
||||
"""
|
||||
consecutivo: Optional[int] = None
|
||||
dta: Optional[int] = None
|
||||
dtaexpo: Optional[int] = None
|
||||
subempresa: Optional[str] = None
|
||||
patharch: Optional[str] = None
|
||||
patharchtransmision: Optional[str] = None
|
||||
pathtransexpo: Optional[str] = None
|
||||
pathrespuesta: Optional[str] = None
|
||||
patharchped: Optional[str] = None
|
||||
patharchpedconsm: Optional[str] = None
|
||||
pathgenimpotemp: Optional[str] = None
|
||||
pathgenexpo: Optional[str] = None
|
||||
actseguridad: Optional[int] = None
|
||||
controldes: Optional[int] = None
|
||||
diadesactual: Optional[int] = None
|
||||
diavencimiento: Optional[int] = None
|
||||
mensajevenc: Optional[int] = None
|
||||
fechades: Optional[int] = None
|
||||
factoriva: Optional[Decimal] = None
|
||||
validasifra: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
calvalbasetcped: Optional[int] = None
|
||||
calvalbasetcpedexpo: Optional[int] = None
|
||||
filtrocantidad: Optional[Decimal] = None
|
||||
muestraarchcodbarras: Optional[int] = None
|
||||
datoshist: Optional[int] = None
|
||||
tipovenro: Optional[str] = None
|
||||
cantvenro: Optional[int] = None
|
||||
costoplanta: Optional[Decimal] = None
|
||||
firmapacking: Optional[int] = None
|
||||
advertenciatm: Optional[int] = None
|
||||
tomarsaldosvenc: Optional[int] = None
|
||||
costoimpofijo: Optional[int] = None
|
||||
valparteexiste: Optional[int] = None
|
||||
valmanifusado: Optional[int] = None
|
||||
temporalfechapago: Optional[int] = None
|
||||
asignadiasantdesc: Optional[int] = None
|
||||
diasantdesc: Optional[int] = None
|
||||
deshabilitardescparte: Optional[int] = None
|
||||
deshabilitardescparteing: Optional[int] = None
|
||||
asignafracameparte: Optional[int] = None
|
||||
validadecencant: Optional[int] = None
|
||||
usartranspamedocame: Optional[int] = None
|
||||
mostraradvertenciaro: Optional[int] = None
|
||||
escondaamexpacking: Optional[int] = None
|
||||
calcdutypacking: Optional[int] = None
|
||||
parammultiples: Optional[int] = None
|
||||
fraccnivelpais: Optional[int] = None
|
||||
covefechaemision: Optional[int] = None
|
||||
valordllstcfacturaexpo: Optional[int] = None
|
||||
interfaceaaconsolidada: Optional[int] = None
|
||||
interfaceaatcfpff: Optional[str] = None
|
||||
incluirobscoveobsimpo: Optional[int] = None
|
||||
agregarincreimpo: Optional[int] = None
|
||||
componentebom: Optional[int] = None
|
||||
limitesubensamble: Optional[int] = None
|
||||
actpdfreportes: Optional[int] = None
|
||||
patharchpdfimpo: Optional[str] = None
|
||||
patharchpdfexpo: Optional[str] = None
|
||||
noimprimircons: Optional[int] = None
|
||||
mostraradvertenciarovalor: Optional[int] = None
|
||||
partesypedimentosporcliente: Optional[str] = None
|
||||
mensajesvurfc: Optional[int] = None
|
||||
mostrarpackinglistingles: Optional[int] = None
|
||||
omitirempaqueencodigobarras: Optional[int] = None
|
||||
restringepaisimpo: Optional[int] = None
|
||||
bloqueoaldesactivarnumerodeparte: Optional[int] = None
|
||||
restringpaisexpo: Optional[int] = None
|
||||
geninformeanexo31: Optional[str] = None
|
||||
utilizarfechapagopeddeundiaanterior: Optional[int] = None
|
||||
utilizarequivalenciasdeumpornumerodeparte: Optional[int] = None
|
||||
utilizartitulosalternativosimpresionfactura: Optional[int] = None
|
||||
usarfactorconversionpornumerodeparte: Optional[int] = None
|
||||
usarvude128o256: Optional[int] = None
|
||||
usartcdelafechapagopedimpoendescarga: Optional[int] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
agregarnumeroembarque: Optional[int] = None
|
||||
utilizarumdeexistenciaentransmisionvu: Optional[int] = None
|
||||
utilizarcodigodebrokerdeclienteenmainx30: Optional[int] = None
|
||||
hojacalculosepararincrementablesanexo3: Optional[int] = None
|
||||
hojacalculodesglosefacturaanexo3: Optional[int] = None
|
||||
usarvaloragregadoenfacturaamericana: Optional[int] = None
|
||||
ocultarinformacionfraccion: Optional[int] = None
|
||||
resaltarsaldostempconcolor: Optional[int] = None
|
||||
valoragregadoenfacturamexicana: Optional[int] = None
|
||||
validarsectorprosecr8: Optional[int] = None
|
||||
agregarsubtotalinterfazaa: Optional[int] = None
|
||||
utilizarnombregenericomainx30: Optional[int] = None
|
||||
utilizartcrespectoatipoped: Optional[int] = None
|
||||
tomardecimalescompletos: Optional[int] = None
|
||||
incluirremesaeninterfazaawinsaai: Optional[int] = None
|
||||
cambiarpesosporcostounitario: Optional[int] = None
|
||||
utilizarsolopartesnaftaenco: Optional[int] = None
|
||||
bloqueodeediciondefacturas: Optional[int] = None
|
||||
reasignafraccionclase: Optional[int] = None
|
||||
calcularcostounitarioenbaseavalortotal: Optional[int] = None
|
||||
parametroauxiliar: Optional[int] = None
|
||||
usarcontroldefechasdeversion: Optional[int] = None
|
||||
desactivaciondemodulos: Optional[int] = None
|
||||
imprimirfacturaalterna: Optional[int] = None
|
||||
activarexpedienteelectronico: Optional[int] = None
|
||||
activarcatalogofraccionesamericanassifra: Optional[int] = None
|
||||
informacionamericanasubtotal: Optional[int] = None
|
||||
mostrarprogramaimmexprosec: Optional[int] = None
|
||||
costounitarioporempaquefac: Optional[int] = None
|
||||
transmitirfacalterna: Optional[int] = None
|
||||
|
||||
|
||||
class SSisGen2Settings(BaseModel):
|
||||
"""
|
||||
Legacy Table: SSisGen2
|
||||
Extended system parameters.
|
||||
"""
|
||||
consecutivo: Optional[int] = None
|
||||
actvaloragre: Optional[int] = None
|
||||
valoragregadogen: Optional[Decimal] = None
|
||||
|
||||
|
||||
class SSisGen3Settings(BaseModel):
|
||||
"""
|
||||
Legacy Table: SSisGen3
|
||||
Additional name-value parameters.
|
||||
"""
|
||||
parametro: Optional[str] = None
|
||||
valorparametro: Optional[int] = None
|
||||
|
||||
|
||||
class SSisMexSettings(BaseModel):
|
||||
"""Legacy Table: SSisMex (Mexican Purchases)"""
|
||||
consecutivo: Optional[int] = None
|
||||
prefijocm: Optional[str] = None
|
||||
consecutivocm: Optional[str] = None
|
||||
porparteclasemex: Optional[str] = None
|
||||
porparteclaseame: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
numpartemex: Optional[int] = None
|
||||
firmafmex: Optional[str] = None
|
||||
fraccionimp: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
umequivalentemex: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
umequivalenteame: Optional[int] = None
|
||||
firmafame: Optional[str] = None
|
||||
impordencomp: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
umauxiliarmex: Optional[int] = None
|
||||
umalternamex: Optional[int] = None
|
||||
transportista: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
clavebultos: Optional[str] = None
|
||||
packingordencompra: Optional[int] = None
|
||||
firmaelectronica: Optional[str] = None
|
||||
incluirlineadelpo: Optional[int] = None
|
||||
lineapersonaaa: Optional[int] = None
|
||||
agregarvaenprodterminados: Optional[int] = None
|
||||
ocultarfechahora: Optional[int] = None
|
||||
|
||||
|
||||
class SSisDefSettings(BaseModel):
|
||||
"""Legacy Table: SSisDef (Definitive Import)"""
|
||||
consecutivo: Optional[int] = None
|
||||
prefijoimpo: Optional[str] = None
|
||||
consecutivoimpo: Optional[str] = None
|
||||
porparteclasemex: Optional[str] = None
|
||||
porparteclaseame: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
firmafmex: Optional[str] = None
|
||||
firmafame: Optional[str] = None
|
||||
codigobarras: Optional[int] = None
|
||||
numpartemex: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
fraccionimp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
umequivalentemex: Optional[int] = None
|
||||
umauxiliarmex: Optional[int] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
umequivalenteame: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
umalternamex: Optional[int] = None
|
||||
restringeimpopt: Optional[int] = None
|
||||
partecomplementariamex: Optional[int] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
metvalor: Optional[str] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
packingpedclave: Optional[int] = None
|
||||
porordenporocpack: Optional[str] = None
|
||||
actualizarpartepartida: Optional[int] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
clavebultos: Optional[str] = None
|
||||
partecomplemexpartida: Optional[int] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
packingordencompra: Optional[int] = None
|
||||
firmaelectronica: Optional[str] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
incluirlineadelpo: Optional[int] = None
|
||||
lineapersonaaa: Optional[int] = None
|
||||
agregarvaenprodterminados: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
ocultarfechahora: Optional[int] = None
|
||||
|
||||
|
||||
class SSisExpoSettings(BaseModel):
|
||||
"""Legacy Table: SSisExpo (Exports)"""
|
||||
tipofactura: Optional[str] = None
|
||||
porparteclasemex: Optional[str] = None
|
||||
porparteclaseame: Optional[str] = None
|
||||
prefijoexpo: Optional[str] = None
|
||||
consecutivoexpo: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
vendidopor: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
moneda: Optional[str] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
numpartemex: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
fraccionexp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
costounitmex: Optional[int] = None
|
||||
ordencompmex: Optional[int] = None
|
||||
consfacmex: Optional[int] = None
|
||||
umequivalentemex: Optional[int] = None
|
||||
ocultarempaquemex: Optional[int] = None
|
||||
costounitmpmex: Optional[int] = None
|
||||
codigobarras: Optional[int] = None
|
||||
partecomplementariamex: Optional[int] = None
|
||||
valordllsmexpesos: Optional[int] = None
|
||||
cantpncodbar: Optional[int] = None
|
||||
dtacerocodbar: Optional[int] = None
|
||||
firmafmex: Optional[str] = None
|
||||
numparteame: Optional[int] = None
|
||||
firmafame: Optional[str] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
ordencompame: Optional[int] = None
|
||||
umequivalenteame: Optional[int] = None
|
||||
fdainformacioname: Optional[int] = None
|
||||
paisopcame: Optional[int] = None
|
||||
muestrafree: Optional[int] = None
|
||||
fraccmexdes: Optional[int] = None
|
||||
tipofraccmexdes: Optional[int] = None
|
||||
colvalordes: Optional[int] = None
|
||||
colpesodes: Optional[int] = None
|
||||
descargaclase: Optional[int] = None
|
||||
descargasust: Optional[int] = None
|
||||
descargadef: Optional[int] = None
|
||||
ventqueueact: Optional[int] = None
|
||||
codigoscac: Optional[int] = None
|
||||
formadesperdicio: Optional[str] = None
|
||||
calvalorbasecosto: Optional[int] = None
|
||||
calvabasecapt: Optional[int] = None
|
||||
calpesobasedescarga: Optional[int] = None
|
||||
calempaquebasecapt: Optional[int] = None
|
||||
ocultcantcons: Optional[int] = None
|
||||
prefijofraccmult: Optional[int] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
costamepaisparte: Optional[int] = None
|
||||
basecostompototal: Optional[str] = None
|
||||
umequivalentemex2: Optional[int] = None
|
||||
umequivalenteame2: Optional[int] = None
|
||||
totalequivmex: Optional[int] = None
|
||||
totalequivame: Optional[int] = None
|
||||
infoadicional: Optional[str] = None
|
||||
restringeexpomp: Optional[int] = None
|
||||
calvatotagremp: Optional[str] = None
|
||||
activaadvpedrtv1: Optional[int] = None
|
||||
asignafrac9801: Optional[int] = None
|
||||
valorexcfrac9801: Optional[Decimal] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
scrapsolonoduty: Optional[int] = None
|
||||
descextraamepartes: Optional[int] = None
|
||||
incambioregdesc: Optional[int] = None
|
||||
escambioregimen: Optional[str] = None
|
||||
descnocontemcant: Optional[int] = None
|
||||
metvalor: Optional[str] = None
|
||||
ocultarempaqueame: Optional[int] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
pesonetodesdiffac: Optional[Decimal] = None
|
||||
enviadoporvendidopor: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
clavebultos: Optional[str] = None
|
||||
ocultacolva: Optional[int] = None
|
||||
calccostosamebasemex: Optional[int] = None
|
||||
proveedorexportador: Optional[str] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
partecomplemexpartida: Optional[int] = None
|
||||
validacantparcandes: Optional[int] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
costosamepais: Optional[str] = None
|
||||
incluirfracamebil: Optional[int] = None
|
||||
solicitarpswdactual: Optional[int] = None
|
||||
solicitarpswddesactual: Optional[int] = None
|
||||
buscarsaldosrecientes: Optional[int] = None
|
||||
packingordenventa: Optional[int] = None
|
||||
imprimirordenventa: Optional[int] = None
|
||||
firmaelectronica: Optional[str] = None
|
||||
mpf: Optional[int] = None
|
||||
valormpf: Optional[Decimal] = None
|
||||
colcantporpeso: Optional[int] = None
|
||||
packingpedclave: Optional[int] = None
|
||||
generafacturaimd: Optional[str] = None
|
||||
generafacturaimdenbasea: Optional[str] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
incluirlineadelpo: Optional[int] = None
|
||||
valorpacking1dlls: Optional[int] = None
|
||||
tomarvalormexenfacame: Optional[int] = None
|
||||
lineapersonaaa: Optional[int] = None
|
||||
calamebasemexmp: Optional[int] = None
|
||||
calamebasemexva: Optional[int] = None
|
||||
calamebasemexempaque: Optional[int] = None
|
||||
usarvaenfacturaamericana: Optional[int] = None
|
||||
activarfechacortesaldos: Optional[int] = None
|
||||
fechacortesaldos: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
respaldardescargaendescargam: Optional[int] = None
|
||||
ocultarfechahora: Optional[int] = None
|
||||
calcularvaloresamericanosenbaseapartida: Optional[int] = None
|
||||
validarestatusped: Optional[int] = None
|
||||
descapais: Optional[int] = None
|
||||
|
||||
|
||||
class SSisImpoSettings(BaseModel):
|
||||
"""Legacy Table: SSisImpo (General Import)"""
|
||||
consecutivo: Optional[int] = None
|
||||
prefijoimpo: Optional[str] = None
|
||||
prefijocr: Optional[str] = None
|
||||
consecutivoimpo: Optional[str] = None
|
||||
consecutivocr: Optional[int] = None
|
||||
porparteclasemex: Optional[str] = None
|
||||
porparteclaseame: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
numpartemex: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
firmafmex: Optional[str] = None
|
||||
firmafame: Optional[str] = None
|
||||
codigobarras: Optional[int] = None
|
||||
fraccionimp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
umequivalentemex: Optional[int] = None
|
||||
umauxiliarmex: Optional[int] = None
|
||||
perpagrenglon: Optional[int] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
umequivalenteame: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
valorlimitepar: Optional[Decimal] = None
|
||||
umalternamex: Optional[int] = None
|
||||
restringeimpopt: Optional[int] = None
|
||||
controlremesa: Optional[str] = None
|
||||
remesainicio: Optional[int] = None
|
||||
remesafinal: Optional[int] = None
|
||||
leyendafacame: Optional[str] = None
|
||||
calcigibasecapt: Optional[int] = None
|
||||
umequivalentemex2: Optional[int] = None
|
||||
umequivalenteame2: Optional[int] = None
|
||||
partecomplementariamex: Optional[int] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
metvalor: Optional[str] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
packingpedclave: Optional[int] = None
|
||||
porordenporocpack: Optional[str] = None
|
||||
actualizarpartepartida: Optional[int] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
clavebultos: Optional[str] = None
|
||||
partecomplemexpartida: Optional[int] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
valorlimiteparmin: Optional[Decimal] = None
|
||||
packingordencompra: Optional[int] = None
|
||||
firmaelectronica: Optional[str] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
incluirlineadelpo: Optional[int] = None
|
||||
lineapersonaaa: Optional[int] = None
|
||||
agregarvaenprodterminados: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
ocultarfechahora: Optional[int] = None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QSisCMexSettings(BaseModel):
|
||||
"""Legacy Table: QSisCMex (Fixed Asset Mexican Purchase)"""
|
||||
consecutivo: Optional[int] = None
|
||||
prefijocm: Optional[str] = None
|
||||
consecutivocm: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
firmafmex: Optional[str] = None
|
||||
fraccionimp: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
transportista: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
packingordencompra: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
|
||||
|
||||
class QSisDefSettings(BaseModel):
|
||||
"""Legacy Table: QSisDef (Fixed Asset Definitive Import)"""
|
||||
consecutivo: Optional[int] = None
|
||||
prefijoimpo: Optional[str] = None
|
||||
consecutivoimpo: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
firmafmex: Optional[str] = None
|
||||
firmafame: Optional[str] = None
|
||||
generarassettag: Optional[int] = None
|
||||
codigobarras: Optional[int] = None
|
||||
fraccionimp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
tipomn: Optional[str] = None
|
||||
tipome: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
controlremesa: Optional[str] = None
|
||||
remesainicio: Optional[int] = None
|
||||
remesafinal: Optional[int] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
metvalor: Optional[str] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
numpartemex: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
actualizarpartepartida: Optional[int] = None
|
||||
packingordencompra: Optional[int] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
|
||||
|
||||
class QSisGenSettings(BaseModel):
|
||||
"""Legacy Table: QSisGen (Fixed Asset General)"""
|
||||
consecutivo: Optional[int] = None
|
||||
dta: Optional[int] = None
|
||||
dtaexpo: Optional[int] = None
|
||||
subempresa: Optional[str] = None
|
||||
patharch: Optional[str] = None
|
||||
patharchtransmision: Optional[str] = None
|
||||
pathrespuesta: Optional[str] = None
|
||||
patharchped: Optional[str] = None
|
||||
patharchpedconsm: Optional[str] = None
|
||||
pathgenimpotemp: Optional[str] = None
|
||||
pathgenexpo: Optional[str] = None
|
||||
aplicapermat: Optional[int] = None
|
||||
actseguridad: Optional[int] = None
|
||||
controldes: Optional[int] = None
|
||||
diadesactual: Optional[int] = None
|
||||
fechades: Optional[int] = None
|
||||
ubiplanta: Optional[str] = None
|
||||
datoshistoricos: Optional[int] = None
|
||||
tipovenro: Optional[str] = None
|
||||
cantvenro: Optional[int] = None
|
||||
omitirimposubpcodbarras: Optional[str] = None
|
||||
muestraarchcodbarras: Optional[int] = None
|
||||
calvalbasetcped: Optional[int] = None
|
||||
calvalbasetcpedexpo: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
factoriva: Optional[Decimal] = None
|
||||
filtrocantidad: Optional[Decimal] = None
|
||||
firmapacking: Optional[int] = None
|
||||
advertenciatm: Optional[int] = None
|
||||
calcdepreciacion: Optional[str] = None
|
||||
valmanifusado: Optional[int] = None
|
||||
validadecencant: Optional[int] = None
|
||||
mostraradvertenciaro: Optional[int] = None
|
||||
usartranspamedocame: Optional[int] = None
|
||||
escondaamexpacking: Optional[int] = None
|
||||
cantvscantseries: Optional[int] = None
|
||||
covefechaemision: Optional[int] = None
|
||||
interfaceaaconsolidada: Optional[int] = None
|
||||
interfaceaatcfpff: Optional[str] = None
|
||||
incluirobscoveobsimpo: Optional[int] = None
|
||||
agregarincreimpo: Optional[int] = None
|
||||
repdescargolineal: Optional[int] = None
|
||||
identificadornodoseriecove: Optional[int] = None
|
||||
actpdfreportes: Optional[int] = None
|
||||
patharchpdfimpo: Optional[str] = None
|
||||
patharchpdfexpo: Optional[str] = None
|
||||
mensajesvurfc: Optional[int] = None
|
||||
mostrarpackinglistingles: Optional[int] = None
|
||||
enviarsubpartidascove: Optional[int] = None
|
||||
restringepaisimpo: Optional[int] = None
|
||||
restringpaisexpo: Optional[int] = None
|
||||
utilizarfechapagopeddeundiaanterior: Optional[int] = None
|
||||
utilizartitulosalternativosimpresionfactura: Optional[int] = None
|
||||
usartcdelafechapagopedimpoendescarga: Optional[int] = None
|
||||
usarvude128o256: Optional[int] = None
|
||||
agregarnumeroembarque: Optional[int] = None
|
||||
utilizarumdeexistenciaentransmisionvu: Optional[int] = None
|
||||
utilizarcodigodebrokerdeclienteenmainx30: Optional[int] = None
|
||||
hojacalculosepararincrementablesanexo3: Optional[int] = None
|
||||
hojacalculodesglosefacturaanexo3: Optional[int] = None
|
||||
utilizarnombregenericomainx30: Optional[int] = None
|
||||
utilizartcrespectoatipoped: Optional[int] = None
|
||||
utilizarsolopartesnaftaenco: Optional[int] = None
|
||||
cambiarpesosporcostounitario: Optional[int] = None
|
||||
bloqueodeediciondefacturas: Optional[int] = None
|
||||
calcularcostounitarioenbaseavalortotalscaf: Optional[int] = None
|
||||
impresionfacturaalterna: Optional[int] = None
|
||||
transmitirfacalterna: Optional[int] = None
|
||||
|
||||
|
||||
class QSisExpoRepSettings(BaseModel):
|
||||
"""Legacy Table: QSisExpoRep (Fixed Asset Export Repeat?)"""
|
||||
tipofactura: Optional[str] = None
|
||||
prefijoexpo: Optional[str] = None
|
||||
consecutivoexpo: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
vendidopor: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
codigobarras: Optional[int] = None
|
||||
cantpncodbar: Optional[int] = None
|
||||
dtacerocodbar: Optional[int] = None
|
||||
cbarrasvalor: Optional[str] = None
|
||||
firmafmex: Optional[str] = None
|
||||
fraccionexp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
numpartemex: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
firmafame: Optional[str] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
codigoscac: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
tipomn: Optional[str] = None
|
||||
tipome: Optional[str] = None
|
||||
mostrarvalact: Optional[int] = None
|
||||
pepsporclase: Optional[int] = None
|
||||
pepsporfraccion: Optional[int] = None
|
||||
pepspordescripcion: Optional[int] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
ventqueueact: Optional[int] = None
|
||||
valfacttc: Optional[str] = None
|
||||
metvalor: Optional[str] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
enviadoporvendidopor: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
proveedorexportador: Optional[str] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
solicitarpswdactual: Optional[int] = None
|
||||
solicitarpswddesactual: Optional[int] = None
|
||||
packingordenventa: Optional[int] = None
|
||||
imprimirpo: Optional[int] = None
|
||||
fdainformacioname: Optional[int] = None
|
||||
packingpedclave: Optional[int] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
|
||||
|
||||
class QSisImpoSettings(BaseModel):
|
||||
"""Legacy Table: QSisImpo (Fixed Asset Import)"""
|
||||
consecutivo: Optional[int] = None
|
||||
prefijoimpo: Optional[str] = None
|
||||
consecutivoimpo: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
firmafmex: Optional[str] = None
|
||||
firmafame: Optional[str] = None
|
||||
codigobarras: Optional[int] = None
|
||||
fraccionimp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
generarassettag: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
tipomn: Optional[str] = None
|
||||
tipome: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
controlremesa: Optional[str] = None
|
||||
remesainicio: Optional[int] = None
|
||||
remesafinal: Optional[int] = None
|
||||
perpagrenglon: Optional[int] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
metvalor: Optional[str] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
numpartemex: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
actualizarpartepartida: Optional[int] = None
|
||||
packingordencompra: Optional[int] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
|
||||
|
||||
class QSisImpoRepSettings(BaseModel):
|
||||
"""Legacy Table: QSisImpoRep (Fixed Asset Import Repeat?)"""
|
||||
tipofactura: Optional[str] = None
|
||||
consecutivo: Optional[int] = None
|
||||
prefijoimpo: Optional[str] = None
|
||||
consecutivoimpo: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
firmafmex: Optional[str] = None
|
||||
firmafame: Optional[str] = None
|
||||
codigobarras: Optional[int] = None
|
||||
fraccionimp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
generarassettag: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
tipomn: Optional[str] = None
|
||||
tipome: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
controlremesa: Optional[str] = None
|
||||
remesainicio: Optional[int] = None
|
||||
remesafinal: Optional[int] = None
|
||||
perpagrenglon: Optional[int] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
ventqueueact: Optional[int] = None
|
||||
metvalor: Optional[str] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
numpartemex: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
packingordencompra: Optional[int] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
|
||||
|
||||
class QSisExpoSettings(BaseModel):
|
||||
"""Legacy Table: QSisExpo (Fixed Asset Export)"""
|
||||
tipofactura: Optional[str] = None
|
||||
escambioregimen: Optional[str] = None
|
||||
prefijoexpo: Optional[str] = None
|
||||
consecutivoexpo: Optional[str] = None
|
||||
pedimento: Optional[str] = None
|
||||
tipo: Optional[str] = None
|
||||
proveedor: Optional[str] = None
|
||||
vendidoconsignado: Optional[str] = None
|
||||
vendidoa: Optional[str] = None
|
||||
enviadotransferido: Optional[str] = None
|
||||
enviadoa: Optional[str] = None
|
||||
vendidopor: Optional[str] = None
|
||||
aaduanal: Optional[str] = None
|
||||
incoterm: Optional[str] = None
|
||||
flete: Optional[Decimal] = None
|
||||
identificador: Optional[str] = None
|
||||
codigobarras: Optional[int] = None
|
||||
cantpncodbar: Optional[int] = None
|
||||
dtacerocodbar: Optional[int] = None
|
||||
cbarrasvalor: Optional[str] = None
|
||||
firmafmex: Optional[str] = None
|
||||
fraccionexp: Optional[int] = None
|
||||
tasafraccmex: Optional[int] = None
|
||||
tipofraccmex: Optional[int] = None
|
||||
numpartemex: Optional[int] = None
|
||||
paisorigenmex: Optional[int] = None
|
||||
costounitmex: Optional[int] = None
|
||||
ocultarempaquemex: Optional[int] = None
|
||||
firmafame: Optional[str] = None
|
||||
fraccioname: Optional[int] = None
|
||||
paisorigename: Optional[int] = None
|
||||
fdainformacioname: Optional[int] = None
|
||||
numparteame: Optional[int] = None
|
||||
codigoscac: Optional[int] = None
|
||||
impordencomp: Optional[int] = None
|
||||
decimalespeso: Optional[int] = None
|
||||
decimalescant: Optional[int] = None
|
||||
decimalesvalor: Optional[int] = None
|
||||
decimalescosto: Optional[int] = None
|
||||
tipomoneda: Optional[str] = None
|
||||
clavemoneda: Optional[str] = None
|
||||
tipomn: Optional[str] = None
|
||||
tipome: Optional[str] = None
|
||||
mostrarvalact: Optional[int] = None
|
||||
pepsporclase: Optional[int] = None
|
||||
pepsporfraccion: Optional[int] = None
|
||||
pepspordescripcion: Optional[int] = None
|
||||
pepsporfraccalterna: Optional[int] = None
|
||||
cantlimite: Optional[Decimal] = None
|
||||
pesolimite: Optional[Decimal] = None
|
||||
valorlimite: Optional[Decimal] = None
|
||||
ventqueueact: Optional[int] = None
|
||||
valfacttc: Optional[str] = None
|
||||
metvalor: Optional[str] = None
|
||||
pagoimpuesto: Optional[str] = None
|
||||
formapago: Optional[str] = None
|
||||
colcantporpeso: Optional[int] = None
|
||||
transportista: Optional[str] = None
|
||||
aaduanalame: Optional[str] = None
|
||||
enviadoporvendidopor: Optional[str] = None
|
||||
conductor: Optional[str] = None
|
||||
transporte: Optional[str] = None
|
||||
numtransporte: Optional[str] = None
|
||||
observacione: Optional[str] = None
|
||||
observacioni: Optional[str] = None
|
||||
proveedorexportador: Optional[str] = None
|
||||
cantlimitemin: Optional[Decimal] = None
|
||||
pesolimitemin: Optional[Decimal] = None
|
||||
valorlimitemin: Optional[Decimal] = None
|
||||
solicitarpswdactual: Optional[int] = None
|
||||
solicitarpswddesactual: Optional[int] = None
|
||||
packingordenventa: Optional[int] = None
|
||||
imprimirpo: Optional[int] = None
|
||||
packingpedclave: Optional[int] = None
|
||||
generafacturaimd: Optional[str] = None
|
||||
generafacturaimdenbasea: Optional[str] = None
|
||||
solicitarcontrasenaadministrador: Optional[int] = None
|
||||
imprimirlote: Optional[int] = None
|
||||
imprimirnumentrada: Optional[int] = None
|
||||
|
||||
|
||||
class SettingsPayload(BaseModel):
|
||||
"""
|
||||
Master container for all possible overrides.
|
||||
Using Optional fields so only the delta is required in JSONB.
|
||||
"""
|
||||
# SCAII (Invoices / Inventory)
|
||||
ssisgen: Optional[SSisGenSettings] = None
|
||||
ssisgen2: Optional[SSisGen2Settings] = None
|
||||
ssisgen3: Optional[SSisGen3Settings] = None
|
||||
ssismex: Optional[SSisMexSettings] = None
|
||||
ssisdef: Optional[SSisDefSettings] = None
|
||||
ssisexpo: Optional[SSisExpoSettings] = None
|
||||
ssisimpo: Optional[SSisImpoSettings] = None
|
||||
|
||||
# SCAF (Fixed Assets)
|
||||
qsiscmex: Optional[QSisCMexSettings] = None
|
||||
qsisdef: Optional[QSisDefSettings] = None
|
||||
qsisgen: Optional[QSisGenSettings] = None
|
||||
qsisexporep: Optional[QSisExpoRepSettings] = None
|
||||
qsisimpo: Optional[QSisImpoSettings] = None
|
||||
qsisimporep: Optional[QSisImpoRepSettings] = None
|
||||
qsisexpo: Optional[QSisExpoSettings] = None
|
||||
|
||||
class AppSettingRequest(BaseModel):
|
||||
tenant_id: Optional[int] = None
|
||||
company_id: Optional[int] = None
|
||||
settings: SettingsPayload
|
||||
|
||||
class AppSettingResponse(BaseModel):
|
||||
id: int
|
||||
tenant_id: Optional[int]
|
||||
company_id: Optional[int]
|
||||
settings: Dict[str, Any]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
137
backend/api/v1/modules/a76/app_settings/service.py
Normal file
137
backend/api/v1/modules/a76/app_settings/service.py
Normal file
@@ -0,0 +1,137 @@
|
||||
from typing import Optional, List, Dict, Any
|
||||
from sqlalchemy import or_, and_, select, case, nulls_first
|
||||
from sqlalchemy.orm import Session
|
||||
from .models import AppSetting
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from decimal import Decimal
|
||||
def convert_decimals(obj: Any) -> Any:
|
||||
"""
|
||||
Recursively converts Decimal objects to floats for JSON serialization.
|
||||
"""
|
||||
if isinstance(obj, list):
|
||||
return [convert_decimals(i) for i in obj]
|
||||
elif isinstance(obj, dict):
|
||||
return {k: convert_decimals(v) for k, v in obj.items()}
|
||||
elif isinstance(obj, Decimal):
|
||||
return float(obj)
|
||||
return obj
|
||||
|
||||
def deep_merge(dict1: Dict[str, Any], dict2: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Recursively merges dict2 into dict1.
|
||||
"""
|
||||
for key, value in dict2.items():
|
||||
if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict):
|
||||
deep_merge(dict1[key], value)
|
||||
else:
|
||||
dict1[key] = value
|
||||
return dict1
|
||||
|
||||
class AppSettingsService:
|
||||
"""
|
||||
Service to manage hierarchical configuration overrides.
|
||||
Hierarchy: System (Global) -> Tenant -> Company.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_resolved_settings(db: Session, tenant_id: int, company_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Retrieves settings from all levels (Global -> Tenant -> Company) and merges them.
|
||||
"""
|
||||
stmt = (
|
||||
select(AppSetting)
|
||||
.where(
|
||||
or_(
|
||||
and_(AppSetting.tenant_id.is_(None), AppSetting.company_id.is_(None)),
|
||||
and_(AppSetting.tenant_id == tenant_id, AppSetting.company_id.is_(None)),
|
||||
and_(AppSetting.tenant_id == tenant_id, AppSetting.company_id == company_id),
|
||||
)
|
||||
)
|
||||
.order_by(
|
||||
# Ensure the order is: Global (1) -> Tenant (2) -> Company (3)
|
||||
case(
|
||||
(and_(AppSetting.tenant_id.is_(None), AppSetting.company_id.is_(None)), 1),
|
||||
(and_(AppSetting.tenant_id.is_not(None), AppSetting.company_id.is_(None)), 2),
|
||||
(and_(AppSetting.tenant_id.is_not(None), AppSetting.company_id.is_not(None)), 3),
|
||||
else_=4
|
||||
).asc()
|
||||
)
|
||||
)
|
||||
|
||||
results = db.execute(stmt).scalars().all()
|
||||
logger.info(f"RESOLVE: Found {len(results)} rows for Hierarchy")
|
||||
|
||||
resolved_settings = {}
|
||||
for row in results:
|
||||
level_name = "GLOBAL" if not row.tenant_id else ("TENANT" if not row.company_id else "COMPANY")
|
||||
|
||||
# Use safe data logging to avoid crashes
|
||||
settings_data = row.settings if row.settings else {}
|
||||
keys = list(settings_data.keys()) if isinstance(settings_data, dict) else "not-a-dict"
|
||||
logger.info(f"RESOLVE: Merging {level_name} layer with keys: {keys}")
|
||||
|
||||
if isinstance(settings_data, dict):
|
||||
deep_merge(resolved_settings, settings_data)
|
||||
|
||||
return resolved_settings
|
||||
|
||||
@staticmethod
|
||||
def upsert_settings(db: Session, tenant_id: Optional[int], company_id: Optional[int], settings: Dict[str, Any]) -> AppSetting:
|
||||
"""
|
||||
Inserts or updates settings for a specific level.
|
||||
"""
|
||||
level_label = f"level(tenant={tenant_id}, company={company_id})"
|
||||
logger.info(f"UPSERT Settings START: {level_label}, keys_to_update={list(settings.keys())}")
|
||||
|
||||
# Ensure all Decimals are converted to floats before deep merge and save
|
||||
settings = convert_decimals(settings)
|
||||
|
||||
stmt = select(AppSetting).where(
|
||||
and_(
|
||||
AppSetting.tenant_id == tenant_id if tenant_id is not None else AppSetting.tenant_id.is_(None),
|
||||
AppSetting.company_id == company_id if company_id is not None else AppSetting.company_id.is_(None)
|
||||
)
|
||||
)
|
||||
|
||||
existing = db.execute(stmt).scalar_one_or_none()
|
||||
|
||||
if existing:
|
||||
# Deep merge at the root level (merging categories like ssisgen, ssismex, etc.)
|
||||
logger.info(f"UPSERT: Updating existing row ID={existing.id}")
|
||||
# Create a shallow copy of the top-level dict to ensure SQLAlchemy sees a new reference
|
||||
new_settings = dict(existing.settings) if existing.settings else {}
|
||||
|
||||
# Detailed logging of what's changing
|
||||
for cat, data in settings.items():
|
||||
old_keys = list(new_settings.get(cat, {}).keys())
|
||||
new_keys = list(data.keys()) if isinstance(data, dict) else []
|
||||
logger.info(f"UPSERT: Merging category [{cat}]. Old keys: {old_keys}, New keys to merge/overwrite: {new_keys}")
|
||||
|
||||
deep_merge(new_settings, settings)
|
||||
# Second pass of conversion (merged results might still have Decimals if original row had them)
|
||||
existing.settings = convert_decimals(new_settings)
|
||||
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
flag_modified(existing, "settings")
|
||||
else:
|
||||
logger.info(f"UPSERT: Creating NEW row for {level_label}")
|
||||
existing = AppSetting(
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id,
|
||||
settings=settings
|
||||
)
|
||||
db.add(existing)
|
||||
|
||||
try:
|
||||
db.commit()
|
||||
db.refresh(existing)
|
||||
logger.info(f"UPSERT SUCCESS: Row ID={existing.id}, Final Settings Hash Keys={list(existing.settings.keys())}")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"UPSERT FAILED: {str(e)}")
|
||||
raise e
|
||||
|
||||
return existing
|
||||
@@ -47,6 +47,7 @@ from .reports.exportacion.transmission.MAINX30.routes import router as transmiss
|
||||
from .reports.importacion.transmission.temporal.MAINX30.routes import router as transmission_temporal_router
|
||||
from .reports.importacion.transmission.definitive.MAINX30.routes import router as transmission_definitive_router
|
||||
from .reports.importacion.winsaai.router import router as winsaai_router
|
||||
from .app_settings.routes import router as app_settings_router
|
||||
|
||||
from .manifests.manifest.routes import router as manifests_router
|
||||
from .manifests.driver.routes import router as manifest_drivers_router
|
||||
@@ -176,6 +177,8 @@ router.include_router(
|
||||
tags=["a76 / reports"]
|
||||
)
|
||||
|
||||
router.include_router(app_settings_router)
|
||||
|
||||
# Registrar router de bitácora
|
||||
from .audit_log.router import router as audit_log_router
|
||||
router.include_router(audit_log_router, prefix="/a76/audit-log", tags=["Audit Log"])
|
||||
|
||||
34
frontend/src/lib/api/dashboard/a76/app-settings.ts
Normal file
34
frontend/src/lib/api/dashboard/a76/app-settings.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { BACKEND_URL } from '$lib/config/backend';
|
||||
|
||||
export interface AppSettingsRequest {
|
||||
tenant_id?: number | null;
|
||||
company_id?: number | null;
|
||||
settings: Record<string, any>;
|
||||
}
|
||||
|
||||
export const appSettingsApi = {
|
||||
/**
|
||||
* Resolves settings merging Global -> Tenant -> Company hierarchy
|
||||
*/
|
||||
async getResolved(tenantId: number, companyId: number): Promise<Record<string, any>> {
|
||||
const response = await fetch(`${BACKEND_URL}/v1/a76/app-settings/resolved?tenant_id=${tenantId}&company_id=${companyId}`);
|
||||
if (!response.ok) throw new Error('Error al obtener configuraciones');
|
||||
return response.json();
|
||||
},
|
||||
|
||||
/**
|
||||
* Upserts an override at a specific level
|
||||
*/
|
||||
async upsert(payload: AppSettingsRequest): Promise<any> {
|
||||
const response = await fetch(`${BACKEND_URL}/v1/a76/app-settings/upsert`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || 'Error al guardar configuración');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
||||
<script lang="ts">
|
||||
import { SETTINGS_METADATA } from './settings-metadata';
|
||||
import SettingFormField from './SettingFormField.svelte';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Search, FilterX } from 'lucide-svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
export let category: string;
|
||||
export let currentData: any = {};
|
||||
export let onUpdate: (data: any) => void;
|
||||
|
||||
let searchQuery = '';
|
||||
|
||||
// Local state to manage edits before final save
|
||||
let formData: any = { ...currentData };
|
||||
|
||||
// Update local state when category changes
|
||||
$: {
|
||||
formData = { ...currentData };
|
||||
}
|
||||
|
||||
function handleFieldChange(key: string, value: any) {
|
||||
formData[key] = value;
|
||||
onUpdate(formData); // Propagate changes upward
|
||||
}
|
||||
|
||||
// Filter fields based on metadata and search query
|
||||
$: availableFields = SETTINGS_METADATA[category] || Object.keys(currentData);
|
||||
$: filteredFields = availableFields.filter(f =>
|
||||
f.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
// Derived grouped fields for UI organization
|
||||
$: hasResults = filteredFields.length > 0;
|
||||
</script>
|
||||
|
||||
<div class="space-y-6 flex flex-col h-full overflow-hidden">
|
||||
<!-- Search/Filter Bar -->
|
||||
<div class="relative group mx-2">
|
||||
<Search class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/20 group-focus-within:text-primary transition-colors" />
|
||||
<Input
|
||||
placeholder="Buscar parámetro en {category}..."
|
||||
bind:value={searchQuery}
|
||||
class="pl-10 bg-black/40 border-white/5 h-10 focus-visible:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fields Grid -->
|
||||
<div class="flex-1 overflow-y-auto px-2 custom-scrollbar pr-4">
|
||||
{#if hasResults}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 pb-8">
|
||||
{#each filteredFields as field (field)}
|
||||
<div in:fade={{ duration: 150 }}>
|
||||
<SettingFormField
|
||||
key={field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col items-center justify-center h-48 text-white/20 border-2 border-dashed border-white/5 rounded-2xl mx-2">
|
||||
<FilterX class="w-12 h-12 mb-4 opacity-50" />
|
||||
<p class="text-xs uppercase tracking-widest">No se encontraron parámetros</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
Settings, Package, Truck, Construction,
|
||||
TrendingUp, FileText, ClipboardList, Database,
|
||||
ChevronRight, Layers
|
||||
} from 'lucide-svelte';
|
||||
import { fade, slide } from 'svelte/transition';
|
||||
|
||||
export let activeCategory: string;
|
||||
export let onSelect: (cat: string) => void;
|
||||
|
||||
const groups = [
|
||||
{
|
||||
title: 'SCAII (Inventarios)',
|
||||
icon: Package,
|
||||
items: [
|
||||
{ id: 'ssisgen', label: 'Parámetros Generales', icon: Settings },
|
||||
{ id: 'ssisgen2', label: 'Valor Agregado', icon: TrendingUp },
|
||||
{ id: 'ssisgen3', label: 'Otros Parámetros', icon: ClipboardList },
|
||||
{ id: 'ssismex', label: 'Nacional (MEX)', icon: FileText },
|
||||
{ id: 'ssisdef', label: 'Imp. Definitiva', icon: Truck },
|
||||
{ id: 'ssisimpo', label: 'Imp. Temporal', icon: Database },
|
||||
{ id: 'ssisexpo', label: 'Exportación', icon: Construction },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'SCAF (Activos Fijos)',
|
||||
icon: Layers,
|
||||
items: [
|
||||
{ id: 'qsisgen', label: 'General Activos', icon: Settings },
|
||||
{ id: 'qsiscmex', label: 'Mantenimiento EX', icon: FileText },
|
||||
{ id: 'qsisdef', label: 'Imp. Definitiva', icon: Truck },
|
||||
{ id: 'qsisimpo', label: 'Imp. Temporal', icon: Database },
|
||||
{ id: 'qsisexpo', label: 'Exportación', icon: Construction },
|
||||
{ id: 'qsisimporep', label: 'Rep. Importación', icon: ClipboardList },
|
||||
{ id: 'qsisexporep', label: 'Rep. Exportación', icon: ClipboardList },
|
||||
]
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
{#each groups as group}
|
||||
<div class="space-y-2">
|
||||
<h3 class="text-[10px] font-bold uppercase tracking-widest text-muted-foreground px-4 flex items-center gap-2">
|
||||
<svelte:component this={group.icon} class="w-3 h-3" />
|
||||
{group.title}
|
||||
</h3>
|
||||
<div class="space-y-1">
|
||||
{#each group.items as item}
|
||||
<button
|
||||
class="w-full flex items-center justify-between gap-3 px-4 py-2.5 rounded-lg transition-all group
|
||||
{activeCategory === item.id
|
||||
? 'bg-primary/10 text-primary border border-primary/20 shadow-lg shadow-primary/5'
|
||||
: 'text-white/60 hover:bg-white/5 border border-transparent hover:border-white/5'}"
|
||||
onclick={() => onSelect(item.id)}
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<svelte:component this={item.icon} class="w-4 h-4 {activeCategory === item.id ? 'text-primary' : 'text-white/40 group-hover:text-white/60'}" />
|
||||
<span class="text-sm font-medium">{item.label}</span>
|
||||
</div>
|
||||
{#if activeCategory === item.id}
|
||||
<div in:fade>
|
||||
<ChevronRight class="w-4 h-4 text-primary" />
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -0,0 +1,168 @@
|
||||
<script lang="ts">
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import * as RadioGroup from '$lib/components/ui/radio-group';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { FolderOpen } from 'lucide-svelte';
|
||||
|
||||
interface Props {
|
||||
key: string;
|
||||
label?: string;
|
||||
value: any;
|
||||
onChange: (val: any) => void;
|
||||
options?: { value: any, label: string }[];
|
||||
type?: 'switch' | 'input' | 'select' | 'path' | 'radio' | 'password' | 'number';
|
||||
hint?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
let { key, label, value, onChange, options, type, hint, disabled }: Props = $props();
|
||||
|
||||
// Heuristic to detect toggleable (boolean-like) legacy fields (0/1)
|
||||
const isToggleable = (k: string, v: any) => {
|
||||
const toggles = [
|
||||
'act', 'valid', 'mostr', 'usar', 'utiliz', 'restring', 'bloqueo',
|
||||
'omiti', 'oculta', 'resaltar', 'cal', 'mues', 'mens', 'cont', 'firm',
|
||||
'esc', 'adv', 'val', 'tom', 'nom', 'rev', 'imp', 'temp', 'costo',
|
||||
'des', 'asig', 'calc', 'param', 'fracc', 'valor', 'cove', 'loc', 'inter', 'inclu', 'actvalor', 'trans'
|
||||
];
|
||||
// Be more lenient: if it has the prefix and is 0, 1, or null, it's likely a toggle
|
||||
return (v === 0 || v === 1 || v === null || v === undefined) &&
|
||||
toggles.some(t => k.toLowerCase().startsWith(t));
|
||||
}
|
||||
|
||||
const isPath = $derived(
|
||||
key.toLowerCase().includes('path') ||
|
||||
key.toLowerCase().includes('arch') ||
|
||||
key.toLowerCase().includes('ruta')
|
||||
);
|
||||
|
||||
// Heuristic or explicit type
|
||||
let mode = $derived(type || (options ? 'select' : (isToggleable(key, value) ? 'switch' : (isPath ? 'path' : 'input'))));
|
||||
|
||||
function handleBrowse() {
|
||||
try {
|
||||
if (typeof window !== 'undefined' && 'showDirectoryPicker' in window) {
|
||||
// @ts-ignore
|
||||
window.showDirectoryPicker().then((handle: any) => {
|
||||
onChange(handle.name);
|
||||
}).catch((err: any) => console.log("Browse cancelled:", err));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Browse failed:", err);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2.5 p-4 rounded-xl bg-white/5 border border-white/5 hover:border-primary/30 transition-all group">
|
||||
<div class="flex flex-col gap-2 {mode === 'switch' ? 'flex-row items-center justify-between md:flex-row' : ''}">
|
||||
<Label for={key} class="text-sm font-medium {mode === 'switch' ? 'text-white/80 cursor-pointer lg:max-w-[70%]' : 'text-white/40 ml-1'} {disabled ? 'opacity-30 cursor-not-allowed' : ''}">
|
||||
{label || key}
|
||||
</Label>
|
||||
|
||||
{#if mode === 'select' && options}
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={String(value)}
|
||||
onValueChange={onChange}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Select.Trigger class="w-full bg-[#0f0f0f] border-white/10 text-white rounded-xl focus:ring-primary/40 h-11 px-4">
|
||||
<Select.Value placeholder="Seleccionar..." />
|
||||
</Select.Trigger>
|
||||
<Select.Content class="bg-[#0f0f0f] border-white/10 text-white min-w-[200px]">
|
||||
{#each options as opt}
|
||||
<Select.Item value={String(opt.value)} label={opt.label} class="hover:bg-primary/20 focus:bg-primary/20 text-white">
|
||||
{opt.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
{:else if mode === 'radio'}
|
||||
<RadioGroup.Root
|
||||
value={String(value)}
|
||||
onValueChange={onChange}
|
||||
disabled={disabled}
|
||||
class="flex flex-col gap-3"
|
||||
>
|
||||
{#each options || [] as opt}
|
||||
<div class="flex items-center space-x-3">
|
||||
<RadioGroup.Item value={String(opt.value)} id={`${key}-${opt.value}`} />
|
||||
<Label for={`${key}-${opt.value}`} class="text-sm font-medium text-white/70 cursor-pointer {disabled ? 'cursor-not-allowed' : ''}">
|
||||
{opt.label}
|
||||
</Label>
|
||||
</div>
|
||||
{/each}
|
||||
</RadioGroup.Root>
|
||||
{:else if mode === 'switch'}
|
||||
<Switch
|
||||
id={key}
|
||||
checked={value === 1 || value === '1' || value === true || value === 'true'}
|
||||
onCheckedChange={(val) => onChange(val ? 1 : 0)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{:else}
|
||||
<div class="relative group space-y-1.5">
|
||||
{#if mode === 'path'}
|
||||
<div class="flex gap-2">
|
||||
<Input
|
||||
id={key}
|
||||
value={value || ""}
|
||||
oninput={(e: any) => onChange(e.target.value)}
|
||||
placeholder="Ruta del directorio..."
|
||||
disabled={disabled}
|
||||
class="flex-1 bg-[#0f0f0f] border-white/5 text-white rounded-xl focus:ring-primary/40 h-11 px-4 pr-10 hover:border-white/20 transition-all font-mono text-xs"
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
disabled={disabled}
|
||||
onclick={handleBrowse}
|
||||
class="h-11 w-11 shrink-0 bg-white/[0.03] border-white/10 hover:bg-white/[0.08] hover:border-primary/50 text-white transition-all shadow-xl"
|
||||
>
|
||||
<FolderOpen class="w-4 h-4 text-primary" />
|
||||
</Button>
|
||||
</div>
|
||||
{:else if mode === 'password'}
|
||||
<Input
|
||||
id={key}
|
||||
type="password"
|
||||
value={value || ""}
|
||||
oninput={(e: any) => onChange(e.target.value)}
|
||||
placeholder="••••••••"
|
||||
disabled={disabled}
|
||||
class="bg-[#0f0f0f] border-white/5 text-white rounded-xl focus:ring-primary/40 h-11 px-4 hover:border-white/20 transition-all w-full"
|
||||
/>
|
||||
{:else if mode === 'number'}
|
||||
<Input
|
||||
id={key}
|
||||
type="number"
|
||||
value={value || ""}
|
||||
oninput={(e: any) => onChange(Number(e.target.value))}
|
||||
disabled={disabled}
|
||||
class="bg-[#0f0f0f] border-white/5 text-white rounded-xl focus:ring-primary/40 h-11 px-4 hover:border-white/20 transition-all w-full text-right"
|
||||
/>
|
||||
{:else}
|
||||
<Input
|
||||
id={key}
|
||||
value={value || ""}
|
||||
oninput={(e: any) => onChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
class="bg-[#0f0f0f] border-white/5 text-white rounded-xl focus:ring-primary/40 h-11 px-4 hover:border-white/20 transition-all"
|
||||
/>
|
||||
{/if}
|
||||
{#if hint}
|
||||
<p class="text-[10px] text-white/30 ml-1 font-medium italic">
|
||||
{hint}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Minimal styling for radio group items if needed */
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { Globe, Database, Layers } from 'lucide-svelte';
|
||||
|
||||
export let level: 'global' | 'tenant' | 'company';
|
||||
export let size: 'sm' | 'md' = 'md';
|
||||
|
||||
const config = {
|
||||
global: {
|
||||
label: 'Global',
|
||||
icon: Globe,
|
||||
color: 'text-primary bg-primary/10 border-primary/20'
|
||||
},
|
||||
tenant: {
|
||||
label: 'Tenant',
|
||||
icon: Database,
|
||||
color: 'text-blue-400 bg-blue-400/10 border-blue-400/20'
|
||||
},
|
||||
company: {
|
||||
label: 'Company',
|
||||
icon: Layers,
|
||||
color: 'text-purple-400 bg-purple-400/10 border-purple-400/20'
|
||||
}
|
||||
};
|
||||
|
||||
$: current = config[level];
|
||||
</script>
|
||||
|
||||
<div class="inline-flex items-center gap-1.5 px-2 py-1 rounded-md border {current.color} {size === 'sm' ? 'text-[10px]' : 'text-xs'} font-medium">
|
||||
<svelte:component this={current.icon} class={size === 'sm' ? 'w-3 h-3' : 'w-3.5 h-3.5'} />
|
||||
{current.label.toUpperCase()}
|
||||
</div>
|
||||
@@ -0,0 +1,626 @@
|
||||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import SettingFormField from './SettingFormField.svelte';
|
||||
|
||||
interface Props {
|
||||
currentData: any;
|
||||
onUpdate: (data: any) => void;
|
||||
}
|
||||
|
||||
let { currentData = {}, onUpdate }: Props = $props();
|
||||
|
||||
let formData: any = $state({ ...currentData });
|
||||
|
||||
// Initialize local/loc fields if they don't exist
|
||||
$effect(() => {
|
||||
const defaults = {
|
||||
DownloadFTP: formData.DownloadFTP || 'No',
|
||||
DescargarFTPoLocal: formData.DescargarFTPoLocal || 'FTP',
|
||||
MinsDownLFTP: formData.MinsDownLFTP || 0,
|
||||
};
|
||||
// Object.assign(formData, { ...defaults, ...currentData });
|
||||
formData = { ...defaults, ...currentData };
|
||||
});
|
||||
|
||||
function handleFieldChange(key: string, value: any) {
|
||||
formData[key] = value;
|
||||
onUpdate(formData);
|
||||
}
|
||||
|
||||
// Translation Mapping for SSisGen
|
||||
const FIELD_LABELS: Record<string, string> = {
|
||||
consecutivo: "Número Consecutivo",
|
||||
dta: "DTA (Derecho Trámite Aduanero)",
|
||||
dtaexpo: "DTA Exportación",
|
||||
subempresa: "Sub-Empresa / División",
|
||||
patharch: "Generación de los archivos PDF por",
|
||||
patharchtransmision: "Generación de archivos electrónicos por transmitir en",
|
||||
pathtransexpo: "Ruta: Transmisión Exportación",
|
||||
pathrespuesta: "Ruta: Respuestas SAAI",
|
||||
patharchped: "Generación de archivos electrónicos para pedimentos en",
|
||||
patharchpedconsm: "Ruta de los archivos TXT de los archivos previos para pedimentos consolidados",
|
||||
pathgenimpotemp: "Ruta de los archivos CSV para importaciones",
|
||||
pathgenexpo: "Ruta de los archivos CSV para exportación",
|
||||
actseguridad: "Activar la seguridad del sistema",
|
||||
controldes: "Asignar fecha límite para desactualizar facturas",
|
||||
diadesactual: "Días para desactualizar facturas (Impo/Expo)",
|
||||
diavencimiento: "Días de anticipación (vencimiento)",
|
||||
mensajevenc: "Activar la advertencia de saldos vencidos",
|
||||
fechades: "Fecha de Desactualización Automática",
|
||||
factoriva: "Factor IVA (Aplica en compras)",
|
||||
validasifra: "Validar Fracciones con SIFRA",
|
||||
decimalespeso: "Peso neto y bruto",
|
||||
decimalescant: "Cantidades",
|
||||
decimalesvalor: "Valores y costos",
|
||||
calvalbasetcped: "Cálculo en base al TC fecha pago (Importación)",
|
||||
calvalbasetcpedexpo: "Cálculo en base al TC fecha pago (Exportación)",
|
||||
filtrocantidad: "Omitir Cantidades con balance menor a",
|
||||
muestraarchcodbarras: "Muestra archivo TXT al generar factura/remesa",
|
||||
datoshist: "Permitir Datos Históricos",
|
||||
tipovenro: "Tipo de validación para regla octava",
|
||||
cantvenro: "Cantidad para validación (Porcentaje/Días)",
|
||||
costoplanta: "Costo Directo en Planta",
|
||||
firmapacking: "Esconder la firma en el packing list",
|
||||
escondaamexpacking: "Esconde el agente aduanal mexicano en el packing list",
|
||||
advertenciatm: "Advertencia del uso del tipo de moneda en facturas",
|
||||
costoimpofijo: "Valida como inconsistencia si el costo de la parte es diferente al costo de la partida al actualizar la factura de importación",
|
||||
tomarsaldosvenc: "Activar si desea tomar saldos temporales o vencidos para descargar",
|
||||
valmanifusado: "Activar control de asignación de manifiestos en factura",
|
||||
valparteexiste: "No mostrar ventanas de auto selección de partes y marcar error en caso de que no exista",
|
||||
temporalfechapago: "Tomar temporalidad en base a la fecha de pago del pedimento",
|
||||
muestra_copias_codbarras: "Imprimir código de barras en las demas copias, aparte de la copia del transportista",
|
||||
mostrarpackinglistingles: "Mostrar packing list en inglés",
|
||||
activar_revision_fracciones: "Activar revisión de fracciones",
|
||||
transmitirfacalterna: "Transmitir Factura Alterna en VU",
|
||||
deshabilitardescparte: "Deshabilitar la Descripción en Español del Número de Parte",
|
||||
deshabilitardescparteing: "Deshabilitar la Descripción en Inglés del Número de Parte",
|
||||
asignafracameparte: "Asignar Automáticamente Fracción Americana de Expo US Dutible en Base a la información de la Clase y Parte",
|
||||
validadecencant: "En las Partidas de Factura se Restringe la Captura de Decimales en el Campo de Unidad de Medida Pza",
|
||||
mostraradvertenciaro: "Mostrar Advertencia si el Costo del Permiso de Regla Octava es Diferente al Costo de la Partida al Momento de Guardarla",
|
||||
mostraradvertenciarovalor: "Mostrar Advertencia si el Valor del Permiso de Regla Octava es Diferente al Valor de la Partida al Momento de Guardarla",
|
||||
usartranspamedocame: "Mostrar Transportista Americano en vez del Mexicano en Packing List y Bill Of Lading de Exportación",
|
||||
calcdutypacking: "Agregar el Valor de US Packing como Complemento del Valor Dutible para Exportación.",
|
||||
parammultiples: "Activar Uso de Parámetros Multiples en Catálogos de Facturas Impo/Expo.",
|
||||
fraccnivelpais: "Activar la Asignación de Fracción a Nivel Parte-Pais.",
|
||||
valordllstcfacturaexpo: "Mostrar Valor en dolares conforme al Tipo de Cambio de Exportación (Facturas, Códigos de Barra y COVE)",
|
||||
covefechaemision: "Tomar la fecha de Emision para la generación del COVE.",
|
||||
usarcoveenarchsaaim3: "Usar COVE en Lugar del Número de Factura en el Archivo de Transferencia SAAIM3.",
|
||||
interfaceaaconsolidada: "Generar la Interface Consolidada por Clase para el Agente Aduanal.",
|
||||
incluirobscoveobsimpo: "Incluir las Observaciones del COVE en las Observaciones en Importación.",
|
||||
activarcatalogofraccionesamericanassifra: "Activar Catálogo Fracciones Americanas Sifra",
|
||||
mostrarprogramaimmexprosec: "Mostrar Autorización Prosec en Reportes de Saldos y Descargos",
|
||||
costounitarioporempaquefac: "Mostrar Costo Unitario Por Empaque en Impresión de Facturas de Exportación",
|
||||
actvaloragre: "Activar Valor Agregado General",
|
||||
valoragregadogen: "Valor Agregado General",
|
||||
interfaceaatcfpff: "En Interfaces (WINSAAI, SAAIM3), Usar Tipo de Cambio de:",
|
||||
agregarincreimpo: "Agregar Incrementables, Fecha de Emisión y Factor Tipo de Cambio en Facturas de Importación",
|
||||
componentebom: "No Permitir Capturar Componentes Diferentes en BOMs (Materias Primas)",
|
||||
limitesubensamble: "Límite Sub Ensamble",
|
||||
partesypedimentosporcliente: "Agregar y Filtrar Catálogo de Partes y Pedimentos por Cliente",
|
||||
mensajesvurfc: "Desactivar Mensajes de RFC en transmisión de VU",
|
||||
omitirempaqueencodigobarras: "Omitir en el Código de Barras el Valor del Empaque",
|
||||
actpdfreportes: "Generar PDFs al momento de Actualización",
|
||||
patharchpdfimpo: "Generar Archivos PDF de Importación en:",
|
||||
patharchpdfexpo: "Generar Archivos PDF de Exportación en:",
|
||||
DownloadFTP: "¿Desea Activar Descarga Desde Sitio FTP o Localmente?",
|
||||
MinsDownLFTP: "En Intervalos de (Minutos)",
|
||||
DownloadFTPPath: "Descargar Archivos en",
|
||||
DescargarFTPoLocal: "Descargar Archivos Desde",
|
||||
ServerFTP: "Servidor",
|
||||
UserFTP: "Usuario",
|
||||
PasswordFTP: "Contraseña",
|
||||
DirectorioFTP: "Directorio en FTP",
|
||||
PathLocalParaDescDe: "Ruta Local",
|
||||
AgregarRemplazarAutomatico: "Agregar/Remplazar Aut",
|
||||
ActivarProcesoVEquipment: "Activar/Desactivar proceso para JD Edwards",
|
||||
ActivarProcesoDesperdicioJDEdwards: "Activar proceso de desperdicio para JD Edwards"
|
||||
};
|
||||
|
||||
// Grouped sections for the "General" tab
|
||||
const GENERAL_SECTIONS = [
|
||||
{
|
||||
title: 'Parámetros del Sistema',
|
||||
fields: ['factoriva', 'actseguridad']
|
||||
},
|
||||
{
|
||||
title: 'Advertencias de Vencimiento',
|
||||
fields: ['mensajevenc', 'diavencimiento']
|
||||
},
|
||||
{
|
||||
title: 'Tipos de Cambio',
|
||||
fields: ['calvalbasetcped', 'calvalbasetcpedexpo']
|
||||
},
|
||||
{
|
||||
title: 'Desactualización de Facturas',
|
||||
fields: ['controldes', 'diadesactual']
|
||||
},
|
||||
{
|
||||
title: 'Número de decimales en reportes generales en',
|
||||
fields: ['decimalespeso', 'decimalescant', 'decimalesvalor']
|
||||
},
|
||||
{
|
||||
title: 'Filtrar por la cantidad',
|
||||
fields: ['filtrocantidad']
|
||||
},
|
||||
{
|
||||
title: 'Código de barras',
|
||||
fields: ['muestraarchcodbarras']
|
||||
}
|
||||
];
|
||||
|
||||
// Grouped sections for "Archivos electrónicos" tab
|
||||
const ARCHIVOS_SECTIONS = [
|
||||
{
|
||||
title: 'Gestión de Archivos PDF y Electrónicos',
|
||||
fields: [
|
||||
'patharch',
|
||||
'patharchtransmision',
|
||||
'patharchped',
|
||||
'pathgenimpotemp',
|
||||
'pathgenexpo',
|
||||
'patharchpedconsm',
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Grouped sections for "Continuación" tab
|
||||
const CONTINUACION_SECTIONS = [
|
||||
{
|
||||
title: 'Tipo de validación para regla octava',
|
||||
fields: ['tipovenro', 'cantvenro']
|
||||
},
|
||||
{
|
||||
title: 'Parámetros Adicionales y Visualización',
|
||||
fields: [
|
||||
'firmapacking',
|
||||
'escondaamexpacking',
|
||||
'advertenciatm',
|
||||
'costoimpofijo',
|
||||
'tomarsaldosvenc',
|
||||
'valmanifusado',
|
||||
'valparteexiste',
|
||||
'temporalfechapago',
|
||||
'muestra_copias_codbarras',
|
||||
'mostrarpackinglistingles',
|
||||
'activar_revision_fracciones'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Grouped sections for "Cont 2" tab
|
||||
const CONT2_SECTIONS = [
|
||||
{
|
||||
title: 'Configuraciones de Pantalla y Validación Avanzada',
|
||||
fields: [
|
||||
'deshabilitardescparte',
|
||||
'deshabilitardescparteing',
|
||||
'asignafracameparte',
|
||||
'validadecencant',
|
||||
'mostraradvertenciaro',
|
||||
'mostraradvertenciarovalor',
|
||||
'usartranspamedocame',
|
||||
'calcdutypacking',
|
||||
'parammultiples',
|
||||
'fraccnivelpais',
|
||||
'valordllstcfacturaexpo',
|
||||
'covefechaemision',
|
||||
'usarcoveenarchsaaim3',
|
||||
'interfaceaaconsolidada',
|
||||
'incluirobscoveobsimpo'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Sections for "Cont 3" tab
|
||||
const CONT3_SECTIONS = [
|
||||
{
|
||||
id: 'interface_tc',
|
||||
title: 'Tipo de Cambio en Interfaces',
|
||||
fields: ['interfaceaatcfpff']
|
||||
},
|
||||
{
|
||||
id: 'config_general',
|
||||
title: 'Opciones de Configuración General',
|
||||
fields: [
|
||||
'agregarincreimpo',
|
||||
'componentebom',
|
||||
'limitesubensamble',
|
||||
'partesypedimentosporcliente',
|
||||
'mensajesvurfc',
|
||||
'omitirempaqueencodigobarras'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'rutas_pdf',
|
||||
title: 'Rutas y Archivos PDF',
|
||||
fields: [
|
||||
'actpdfreportes',
|
||||
'patharchpdfimpo',
|
||||
'patharchpdfexpo'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Sections for "Cont 4" tab
|
||||
const CONT4_SECTIONS = [
|
||||
{
|
||||
id: 'ftp_master',
|
||||
title: 'Activación y Frecuencia',
|
||||
fields: ['DownloadFTP', 'MinsDownLFTP']
|
||||
},
|
||||
{
|
||||
id: 'ftp_destino',
|
||||
title: 'Destino de Descarga',
|
||||
fields: ['DownloadFTPPath']
|
||||
},
|
||||
{
|
||||
id: 'ftp_origen',
|
||||
title: 'Origen de Descarga',
|
||||
fields: ['DescargarFTPoLocal']
|
||||
},
|
||||
{
|
||||
id: 'ftp_config',
|
||||
title: 'Configuración FTP',
|
||||
fields: ['ServerFTP', 'UserFTP', 'PasswordFTP', 'DirectorioFTP']
|
||||
},
|
||||
{
|
||||
id: 'local_config',
|
||||
title: 'Configuración de Ruta Local',
|
||||
fields: ['PathLocalParaDescDe']
|
||||
},
|
||||
{
|
||||
id: 'additional_options',
|
||||
title: 'Opciones Adicionales',
|
||||
fields: ['AgregarRemplazarAutomatico', 'ActivarProcesoVEquipment', 'ActivarProcesoDesperdicioJDEdwards']
|
||||
}
|
||||
];
|
||||
|
||||
// Grouped sections for "Cont 7" tab
|
||||
const CONT7_SECTIONS = [
|
||||
{
|
||||
title: 'Configuraciones de Fracciones y Transmisión',
|
||||
fields: [
|
||||
'activarcatalogofraccionesamericanassifra',
|
||||
'mostrarprogramaimmexprosec',
|
||||
'costounitarioporempaquefac',
|
||||
'transmitirfacalterna'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const TABS = [
|
||||
{ id: 'general', label: 'General' },
|
||||
{ id: 'archivos', label: 'Archivos electrónicos' },
|
||||
{ id: 'continuacion', label: 'Continuación' },
|
||||
{ id: 'cont2', label: 'Cont 2' },
|
||||
{ id: 'cont3', label: 'Cont 3' },
|
||||
{ id: 'cont4', label: 'Cont 4' },
|
||||
{ id: 'otros_parms', label: 'Otros Parámetros', fields: ["consecutivo", "dta", "dtaexpo", "subempresa", "fechades", "validasifra", "restringepaisimpo", "restringpaisexpo", "bloqueoaldesactivarnumerodeparte", "asignafracameparte", "validadecencant", "usartranspamedocame", "mostraradvertenciaro", "calcdutypacking", "parammultiples", "fraccnivelpais", "pathtransexpo", "pathrespuesta", "covefechaemision", "interfaceaaconsolidada", "interfaceaatcfpff", "incluirobscoveobsimpo", "agregarincreimpo", "componentebom", "limitesubensamble", "actpdfreportes", "patharchpdfimpo", "patharchpdfexpo", "noimprimircons", "mostraradvertenciarovalor", "partesypedimentosporcliente", "mensajesvurfc", "mostrarpackinglistingles", "omitirempaqueencodigobarras", "geninformeanexo31", "utilizarfechapagopeddeundiaanterior", "utilizarequivalenciasdeumpornumerodeparte", "utilizartitulosalternativosimpresionfactura"] },
|
||||
{ id: 'cont5', label: 'cont 5', fields: ["utilizarnombregenericomainx30", "utilizartcrespectoatipoped", "tomardecimalescompletos", "incluirremesaeninterfazaawinsaai", "cambiarpesosporcostounitario", "utilizarsolopartesnaftaenco", "bloqueodeediciondefacturas"] },
|
||||
{ id: 'cont6', label: 'cont 6', fields: ["reasignafraccionclase", "calcularcostounitarioenbaseavalortotal", "parametroauxiliar", "usarcontroldefechasdeversion", "desactivaciondemodulos"] },
|
||||
{ id: 'cont7', label: 'cont 7' }
|
||||
];
|
||||
|
||||
// Options for the rule validator select
|
||||
const RULE_VALIDATOR_OPTIONS = [
|
||||
{ value: 'Porcentaje', label: 'Porcentaje' },
|
||||
{ value: 'Dias', label: 'Días' },
|
||||
{ value: 'No aplica', label: 'No aplica' }
|
||||
];
|
||||
|
||||
// Options for the interface TC radio buttons
|
||||
const INTERFACE_TC_OPTIONS = [
|
||||
{ value: 'P', label: 'Fecha de Pago' },
|
||||
{ value: 'F', label: 'Fecha de Factura' }
|
||||
];
|
||||
|
||||
// Options for radio groups in Cont 4
|
||||
const YES_NO_OPTIONS = [
|
||||
{ value: 'Si', label: 'Si' },
|
||||
{ value: 'No', label: 'No' }
|
||||
];
|
||||
|
||||
const FTP_LOCAL_OPTIONS = [
|
||||
{ value: 'FTP', label: 'FTP' },
|
||||
{ value: 'Ruta Local', label: 'Ruta Local' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col bg-[#080808] rounded-2xl border border-white/5 overflow-hidden shadow-2xl">
|
||||
<Tabs.Root value="general" class="flex flex-col h-full">
|
||||
<Tabs.List class="flex flex-wrap gap-1 p-3 bg-black/60 border-b border-white/5 h-auto">
|
||||
{#each TABS as tab}
|
||||
<Tabs.Trigger
|
||||
value={tab.id}
|
||||
class="px-5 py-2.5 text-xs font-bold uppercase tracking-wider transition-all data-[state=active]:bg-primary data-[state=active]:text-white rounded-lg opacity-40 data-[state=active]:opacity-100 hover:opacity-70"
|
||||
>
|
||||
{tab.label}
|
||||
</Tabs.Trigger>
|
||||
{/each}
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Content value="general" class="flex-1 overflow-hidden p-0 m-0 bg-transparent">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar p-6 space-y-12 min-h-[600px]">
|
||||
{#each GENERAL_SECTIONS as section}
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-primary/80 whitespace-nowrap">
|
||||
{section.title}
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-primary/20 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{#each section.fields as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="archivos" class="flex-1 overflow-hidden p-0 m-0 bg-transparent">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar p-6 space-y-8 min-h-[600px]">
|
||||
{#each ARCHIVOS_SECTIONS as section}
|
||||
<div class="space-y-6 max-w-3xl mx-auto">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-primary/80 whitespace-nowrap">
|
||||
{section.title}
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-primary/20 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
{#each section.fields as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="continuacion" class="flex-1 overflow-hidden p-0 m-0 bg-transparent">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar p-6 space-y-12 min-h-[600px]">
|
||||
{#each CONTINUACION_SECTIONS as section}
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-primary/80 whitespace-nowrap">
|
||||
{section.title}
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-primary/20 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{#each section.fields as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
options={field === 'tipovenro' ? RULE_VALIDATOR_OPTIONS : undefined}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="cont2" class="flex-1 overflow-hidden p-0 m-0 bg-transparent">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar p-6 space-y-8 min-h-[600px]">
|
||||
{#each CONT2_SECTIONS as section}
|
||||
<div class="space-y-6 max-w-4xl mx-auto">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-primary/80 whitespace-nowrap">
|
||||
{section.title}
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-primary/20 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
{#each section.fields as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="cont3" class="flex-1 overflow-hidden p-0 m-0 bg-transparent">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar p-6 space-y-12 min-h-[600px]">
|
||||
{#each CONT3_SECTIONS as section}
|
||||
<div class="space-y-6 max-w-4xl mx-auto">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-primary/80 whitespace-nowrap">
|
||||
{section.title}
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-primary/20 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
{#each section.fields as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
type={
|
||||
field === 'interfaceaatcfpff' ? 'radio' :
|
||||
(field === 'limitesubensamble' ? 'input' :
|
||||
(['agregarincreimpo', 'componentebom', 'partesypedimentosporcliente'].includes(field) ? 'switch' : undefined))
|
||||
}
|
||||
options={field === 'interfaceaatcfpff' ? INTERFACE_TC_OPTIONS : undefined}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="cont4" class="flex-1 overflow-hidden p-0 m-0 bg-transparent">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar p-6 space-y-8 min-h-[600px]">
|
||||
<div class="max-w-4xl mx-auto space-y-12">
|
||||
<!-- Master Activation (Blue Box Container) -->
|
||||
<div class="space-y-6 p-8 rounded-3xl border-2 border-primary/20 bg-primary/[0.02] shadow-2xl transition-all {formData.DownloadFTP !== 'Si' ? 'grayscale opacity-60' : ''}">
|
||||
|
||||
{#each CONT4_SECTIONS.filter(s => s.id !== 'additional_options') as section}
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-primary/80 whitespace-nowrap">
|
||||
{section.title}
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-primary/20 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{#each section.fields as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
disabled={section.id !== 'ftp_master' && formData.DownloadFTP !== 'Si'}
|
||||
type={
|
||||
field === 'DownloadFTP' || field === 'DescargarFTPoLocal' ? 'radio' :
|
||||
field === 'MinsDownLFTP' ? 'number' :
|
||||
field === 'PasswordFTP' ? 'password' :
|
||||
undefined
|
||||
}
|
||||
options={
|
||||
field === 'DownloadFTP' ? YES_NO_OPTIONS :
|
||||
field === 'DescargarFTPoLocal' ? FTP_LOCAL_OPTIONS :
|
||||
undefined
|
||||
}
|
||||
hint={
|
||||
field === 'DirectorioFTP' ? 'Ejemplo: /Folder 1/SubFolder' :
|
||||
field === 'PathLocalParaDescDe' ? 'Ejemplo: C:\\Aduanas\\SCAIISQL' :
|
||||
undefined
|
||||
}
|
||||
{...(section.id === 'ftp_config' && formData.DescargarFTPoLocal === 'Ruta Local' ? { disabled: true } : {})}
|
||||
{...(section.id === 'local_config' && formData.DescargarFTPoLocal === 'FTP' ? { disabled: true } : {})}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Outside Options -->
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-white/40 whitespace-nowrap">
|
||||
Opciones Adicionales
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-white/10 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{#each CONT4_SECTIONS.find(s => s.id === 'additional_options')?.fields || [] as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="cont7" class="flex-1 overflow-hidden p-0 m-0 bg-transparent">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar p-6 space-y-8 min-h-[600px]">
|
||||
{#each CONT7_SECTIONS as section}
|
||||
<div class="space-y-6 max-w-4xl mx-auto">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-xs font-bold uppercase tracking-[0.2em] text-primary/80 whitespace-nowrap">
|
||||
{section.title}
|
||||
</h3>
|
||||
<div class="h-[1px] w-full bg-gradient-to-r from-primary/20 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<!-- Especial Row: Activar Valor Agregado Gral + Numeric Input -->
|
||||
<div class="flex flex-col md:flex-row items-center gap-4 p-4 rounded-xl border border-white/5 bg-white/[0.02]">
|
||||
<div class="flex-1">
|
||||
<SettingFormField
|
||||
key="actvaloragre"
|
||||
label={FIELD_LABELS.actvaloragre}
|
||||
value={formData.actvaloragre}
|
||||
onChange={(val) => handleFieldChange('actvaloragre', val)}
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full md:w-32">
|
||||
<SettingFormField
|
||||
key="valoragregadogen"
|
||||
label={FIELD_LABELS.valoragregadogen}
|
||||
value={formData.valoragregadogen}
|
||||
onChange={(val) => handleFieldChange('valoragregadogen', val)}
|
||||
type="input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#each section.fields as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
{#each TABS.filter(t => !['general', 'archivos', 'continuacion', 'cont2', 'cont3', 'cont4', 'cont7'].includes(t.id)) as tab}
|
||||
<Tabs.Content value={tab.id} class="flex-1 overflow-hidden p-0 m-0">
|
||||
<div class="h-full w-full overflow-y-scroll custom-scrollbar min-h-[600px]">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-8">
|
||||
{#each tab.fields || [] as field}
|
||||
<SettingFormField
|
||||
key={field}
|
||||
label={FIELD_LABELS[field] || field}
|
||||
value={formData[field]}
|
||||
onChange={(val) => handleFieldChange(field, val)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
{/each}
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global([data-radix-scroll-area-viewport]) {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
:global([data-radix-scroll-area-viewport]::-webkit-scrollbar) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
export const SETTINGS_METADATA: Record<string, string[]> = {
|
||||
ssisgen: [
|
||||
"consecutivo", "dta", "dtaexpo", "subempresa", "patharch", "patharchtransmision", "pathtransexpo", "pathrespuesta",
|
||||
"patharchped", "patharchpedconsm", "pathgenimpotemp", "pathgenexpo", "actseguridad", "controldes", "diadesactual",
|
||||
"diavencimiento", "mensajevenc", "fechades", "factoriva", "validasifra", "decimalespeso", "decimalescant",
|
||||
"decimalesvalor", "calvalbasetcped", "calvalbasetcpedexpo", "filtrocantidad", "muestraarchcodbarras", "datoshist",
|
||||
"tipovenro", "cantvenro", "costoplanta", "firmapacking", "advertenciatm", "tomarsaldosvenc", "costoimpofijo",
|
||||
"valparteexiste", "valmanifusado", "temporalfechapago", "asignadiasantdesc", "diasantdesc", "deshabilitardescparte",
|
||||
"deshabilitardescparteing", "asignafracameparte", "validadecencant", "usartranspamedocame", "mostraradvertenciaro",
|
||||
"escondaamexpacking", "calcdutypacking", "parammultiples", "fraccnivelpais", "covefechaemision", "valordllstcfacturaexpo",
|
||||
"interfaceaaconsolidada", "interfaceaatcfpff", "incluirobscoveobsimpo", "agregarincreimpo", "componentebom",
|
||||
"limitesubensamble", "actpdfreportes", "patharchpdfimpo", "patharchpdfexpo", "noimprimircons", "mostraradvertenciarovalor",
|
||||
"partesypedimentosporcliente", "mensajesvurfc", "mostrarpackinglistingles", "omitirempaqueencodigobarras", "restringepaisimpo",
|
||||
"bloqueoaldesactivarnumerodeparte", "restringpaisexpo", "geninformeanexo31", "utilizarfechapagopeddeundiaanterior",
|
||||
"utilizarequivalenciasdeumpornumerodeparte", "utilizartitulosalternativosimpresionfactura", "usarfactorconversionpornumerodeparte",
|
||||
"usarvude128o256", "usartcdelafechapagopedimpoendescarga", "solicitarcontrasenaadministrador", "agregarnumeroembarque",
|
||||
"utilizarumdeexistenciaentransmisionvu", "utilizarcodigodebrokerdeclienteenmainx30", "hojacalculosepararincrementablesanexo3",
|
||||
"hojacalculodesglosefacturaanexo3", "usarvaloragregadoenfacturaamericana", "ocultarinformacionfraccion",
|
||||
"resaltarsaldostempconcolor", "valoragregadoenfacturamexicana", "validarsectorprosecr8", "agregarsubtotalinterfazaa"
|
||||
],
|
||||
ssismex: [
|
||||
"consecutivo", "prefijocm", "consecutivocm", "porparteclasemex", "porparteclaseame", "proveedor", "vendidoconsignado",
|
||||
"vendidoa", "enviadotransferido", "enviadoa", "flete", "paisorigenmex", "numpartemex", "firmafmex", "fraccionimp",
|
||||
"tipofraccmex", "tasafraccmex", "umequivalentemex", "numparteame", "fraccioname", "paisorigename", "umequivalenteame",
|
||||
"firmafame", "impordencomp", "decimalespeso", "decimalescant", "decimalesvalor", "decimalescosto", "tipomoneda",
|
||||
"clavemoneda", "transportista", "conductor", "transporte", "numtransporte", "observacione", "observacioni",
|
||||
"leyendamex", "leyendaame", "firmaaamex", "firmapmex", "firmaaaame", "firmapame", "firmaeamex", "firmaeame",
|
||||
"firmasamex", "firmasame", "claveregimen", "claveregimename", "numfactura", "fechafactura", "numeropedimento",
|
||||
"fechapedimento", "pedimentomex", "clientemex", "claveregmexo", "claveregmexd", "usarvalorameric", "consecutivoas",
|
||||
"consecutivops", "usatranspfactu", "ocultarfechahora"
|
||||
],
|
||||
qsisgen: [
|
||||
"consecutivo", "actvaloragre", "valoragregadogen"
|
||||
]
|
||||
};
|
||||
@@ -518,6 +518,10 @@ export function getSidebarData(): SidebarData {
|
||||
title: m["sidebar.reference_data.usuarios"](),
|
||||
url: "",
|
||||
},
|
||||
{
|
||||
title: "General",
|
||||
url: "/dashboard/settings/general",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
171
frontend/src/routes/dashboard/settings/general/+page.svelte
Normal file
171
frontend/src/routes/dashboard/settings/general/+page.svelte
Normal file
@@ -0,0 +1,171 @@
|
||||
<script lang="ts">
|
||||
import { appSettingsApi } from '$lib/api/dashboard/a76/app-settings';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import {
|
||||
Settings2, Save, RefreshCw, Layers,
|
||||
Database, Globe, Search, Info, HelpCircle,
|
||||
ArrowRight
|
||||
} from 'lucide-svelte';
|
||||
import { fade, slide, fly } from 'svelte/transition';
|
||||
import { authStore } from '$lib/auth';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
|
||||
// Componentes UI
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
|
||||
// Custom Components
|
||||
import SettingHierarchyBadge from '$lib/components/dashboard/settings/SettingHierarchyBadge.svelte';
|
||||
import SsisGenTabsForm from '$lib/components/dashboard/settings/SsisGenTabsForm.svelte';
|
||||
|
||||
import { currentUser } from '$lib/auth';
|
||||
|
||||
let tenantIdVal = $derived(companyStore.activeCompany?.tenant_id || 0);
|
||||
let companyIdVal = $derived(companyStore.activeCompany?.id || 0);
|
||||
|
||||
let level = $state<'global' | 'tenant' | 'company'>('company');
|
||||
let activeCategory = $state('ssisgen');
|
||||
|
||||
let resolvedSettings = $state<any>(null);
|
||||
let loading = $state(false);
|
||||
let saving = $state(false);
|
||||
|
||||
// Form internal state tracking
|
||||
let currentFormData = $state<any>({});
|
||||
|
||||
// Load when company changes or on mount
|
||||
$effect(() => {
|
||||
if (companyIdVal) {
|
||||
handleResolve();
|
||||
}
|
||||
});
|
||||
|
||||
async function handleSave() {
|
||||
if (!companyIdVal || !tenantIdVal) {
|
||||
toast.error('No hay contexto de empresa o tenant activo');
|
||||
return;
|
||||
}
|
||||
|
||||
saving = true;
|
||||
try {
|
||||
const payload = {
|
||||
tenant_id: tenantIdVal,
|
||||
company_id: companyIdVal,
|
||||
settings: {
|
||||
[activeCategory]: currentFormData
|
||||
}
|
||||
};
|
||||
await appSettingsApi.upsert(payload);
|
||||
toast.success(`Configuraciones de ${activeCategory.toUpperCase()} guardadas correctamente`);
|
||||
await handleResolve();
|
||||
} catch (e: any) {
|
||||
toast.error('Error al guardar: ' + e.message);
|
||||
} finally {
|
||||
saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleResolve() {
|
||||
if (!companyIdVal || !tenantIdVal) return;
|
||||
|
||||
loading = true;
|
||||
try {
|
||||
resolvedSettings = await appSettingsApi.getResolved(tenantIdVal, companyIdVal);
|
||||
if (resolvedSettings && resolvedSettings[activeCategory]) {
|
||||
currentFormData = { ...resolvedSettings[activeCategory] };
|
||||
} else {
|
||||
currentFormData = {};
|
||||
}
|
||||
} catch (e: any) {
|
||||
toast.error('Error al cargar configuraciones: ' + e.message);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleFormUpdate(newData: any) {
|
||||
currentFormData = newData;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Configuración General | Anexo 76</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="flex flex-col h-[calc(100vh-64px)] overflow-hidden bg-[#050505] text-white">
|
||||
<!-- Main Header -->
|
||||
<header class="h-20 shrink-0 px-8 border-b border-white/5 bg-black/40 backdrop-blur-md flex items-center justify-between z-10">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="p-2.5 bg-primary/20 rounded-xl border border-primary/20 shadow-lg shadow-primary/5">
|
||||
<Settings2 class="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h1 class="text-xl font-bold tracking-tight flex items-center gap-2">
|
||||
Configuraciones Generales
|
||||
<span class="text-[10px] font-medium bg-white/5 px-2 py-0.5 rounded-full border border-white/10 text-white/40">SsisGen</span>
|
||||
</h1>
|
||||
<div class="flex items-center gap-2 mt-0.5">
|
||||
<div class="flex items-center gap-1.5 text-xs text-white/30">
|
||||
<Database class="w-3 h-3" />
|
||||
<span>Tenant: {tenantIdVal}</span>
|
||||
</div>
|
||||
<div class="w-1 h-1 rounded-full bg-white/10"></div>
|
||||
<div class="flex items-center gap-1.5 text-xs text-white/30">
|
||||
<Layers class="w-3 h-3" />
|
||||
<span>Empresa: {companyIdVal}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
{#if loading}
|
||||
<div in:fade class="flex items-center gap-2 text-xs text-white/40 bg-white/5 px-3 py-1.5 rounded-lg border border-white/5">
|
||||
<RefreshCw class="w-3 h-3 animate-spin" />
|
||||
Cargando...
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Button variant="default" onclick={handleSave} disabled={saving || loading} class="h-11 px-6 bg-primary hover:bg-primary/90 text-white shadow-xl shadow-primary/20 font-bold transition-all hover:scale-[1.02] active:scale-[0.98]">
|
||||
{#if saving}
|
||||
<RefreshCw class="w-4 h-4 mr-2 animate-spin" />
|
||||
Guardando...
|
||||
{:else}
|
||||
<Save class="w-4 h-4 mr-2" />
|
||||
Guardar Cambios
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Workspace Area -->
|
||||
<main class="flex-1 overflow-hidden p-6 bg-gradient-to-b from-black/20 to-transparent">
|
||||
<div class="h-full flex flex-col max-w-[1600px] mx-auto w-full relative">
|
||||
<!-- Form Background Glow -->
|
||||
<div class="absolute -top-[10%] -left-[10%] w-[40%] h-[40%] bg-primary/5 blur-[120px] rounded-full pointer-events-none"></div>
|
||||
<div class="absolute -bottom-[10%] -right-[10%] w-[40%] h-[40%] bg-purple-500/5 blur-[120px] rounded-full pointer-events-none"></div>
|
||||
|
||||
{#if loading && !resolvedSettings}
|
||||
<div class="flex-1 flex flex-col items-center justify-center gap-4">
|
||||
<div class="p-4 bg-white/5 rounded-2xl border border-white/10">
|
||||
<RefreshCw class="w-10 h-10 animate-spin text-primary" />
|
||||
</div>
|
||||
<p class="text-xs font-bold uppercase tracking-[0.2em] text-white/20">Inicializando Parámetros</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fade={{ duration: 400 }} class="flex-1 h-full shadow-2xl">
|
||||
<SsisGenTabsForm
|
||||
currentData={currentFormData}
|
||||
onUpdate={handleFormUpdate}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(body) { overflow: hidden; }
|
||||
</style>
|
||||
1
frontend/src/routes/dashboard/settings/general/+page.ts
Normal file
1
frontend/src/routes/dashboard/settings/general/+page.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const ssr = false;
|
||||
Reference in New Issue
Block a user