Skip to content

Plataforma técnica · Sevastopol

Company View Island

Islands Sevastopol Administración

CompanyViewIsland mantiene los datos legales del tenant: razón social, RUT, dirección, contacto, año de inicio de actividades, giros comerciales SII, bandera de PPM estratégico y estado activa/inactiva. Hay un único registro por tenant — la island es un formulario UPDATE; nunca crea ni elimina.

  • Directorysevastopol/src/components/islands/admin/
    • CompanyViewIsland.tsx — componente único, internamente CompanyView
const { tenantId } = useActiveTenant((id) => void fetchData(id));
const [companyData, setCompanyData] = createSignal<CompanyConfig | null>(null);
const [giros, setGiros] = createSignal<GiroComercial[]>([]);
const [isLoading, setLoading] = createSignal(true);
const [isSaving, setSaving] = createSignal(false);
SignalTipoUso
companyDataCompanyConfig | nullRegistro único del tenant (null si no existe o falló la carga).
girosGiroComercial[]Catálogo SII de giros, cargado al montar desde /api/parameters/giros-comerciales.
isLoadingbooleanBloqueo durante carga inicial.
isSavingbooleanBloqueo durante PUT.

Al montar dispara dos fetches:

onMount(() => {
void fetchGiros();
if (tenantId()) void fetchData(tenantId());
});

fetchGiros no requiere tenant — el catálogo SII es global. fetchData sí lo requiere; sin tenant la vista queda en estado vacío.

async function fetchData(tid: string) {
if (!tid) return;
setLoading(true);
try {
const response = await authenticatedFetch(`${API_BASE}?tenant_id=${tid}`);
if (!response.ok) throw new Error("Error loading company config");
setCompanyData(await response.json());
} catch (error) {
toast.push("Error al cargar configuración de empresa", "error");
setCompanyData(null);
}
}

Tres helpers tipan estrictamente el formulario:

const safeString = (value: unknown) => String(value ?? "").trim();
const optionalString = (value: string) => safeString(value) || null;
const parseOptionalNumber = (value: string, mode = "float") => {
const normalized = safeString(value).replace(",", ".");
if (!normalized) return null;
const parsed = mode === "int" ? parseInt(normalized, 10) : parseFloat(normalized);
return Number.isFinite(parsed) ? parsed : null;
};
  • safeString: convierte cualquier valor a string trim-eado; nunca tira por null/undefined.
  • optionalString: vacío → null (deja claro al backend que el campo no fue provisto, no que se borró).
  • parseOptionalNumber: acepta coma decimal latina; rechaza NaN/Infinity.
PillOrigen
TenantPrimeros 8 chars del tenantId.
InventariocompanyData?.giro_codigo?.length — cuántos giros tiene marcados.
Inicioaño_inicio_actividades formateado.
PPMusar_ppm_estrategico ? "Estratégico" : "Base" (tono success vs warning).
Estadoactiva ? "Activa" : "Inactiva".

giro_codigo es un array de strings. El toggle agrega o quita el código del array; si queda vacío, se setea null (no []):

function toggleInventoryGiro(code: string) {
const current = companyData();
if (!current) return;
const selected = current.giro_codigo ?? [];
const next = selected.includes(code)
? selected.filter((item) => item !== code)
: [...selected, code];
updateField("giro_codigo", next.length > 0 ? next : null);
}

El catálogo giros viene del SII vía /api/parameters/giros-comerciales con { codigo, descripcion }.

Single PUT con el CompanyConfig completo:

const payload: UpdateCompanyDTO = { ...data };
const response = await authenticatedFetch(`${API_BASE}?tenant_id=${tid}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
setCompanyData(await response.json());

La respuesta del backend reemplaza companyData en memoria — útil si el backend normaliza o recalcula campos derivados.

Sin validación de cliente — el backend valida formato de RUT (^\d{7,8}-[\dkK]$), email, y existencia de los giros contra el catálogo SII.

MétodoRutaOperación
GET/api/admin/company?tenant_id=...Registro único de la empresa.
PUT/api/admin/company?tenant_id=... body UpdateCompanyDTOGuarda cambios completos.
GET/api/parameters/giros-comercialesCatálogo SII (sin tenant).
ReglaMotivo
Un solo registro UPDATE-onlyEl tenant es la empresa; alta y baja se gestionan en Command › Tenants.
giro_codigo vacío → nullEl backend distingue “sin giros” (null) de “lista vacía” ([]); usar null evita ambigüedad.
Sin validación de clienteEl backend valida formato RUT, email y giros; duplicar en cliente desincronizaría las reglas.
Carga de giros sin tenantEl catálogo SII es global; no esperar al selector.
Toggle sobre giros con next.length > 0 ? next : nullIdem null vs [].
Actualizar companyData con la respuesta del PUTEl backend puede normalizar; reflejarlo evita “guardé X pero veo Y”.
Helpers safeString/optionalString/parseOptionalNumberDisciplinan los tipos: nunca strings vacíos llegan al backend, números mal formados son rechazados antes.