Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Danielings/Pasantia-Proyecto/llms.txt

Use this file to discover all available pages before exploring further.

Every equipment and peripheral record in the inventory carries location data. Rather than storing free-text addresses, the system uses a structured six-level geographic hierarchy that maps assets to exact physical positions within your organization. Location documents live in the ubicaciones Firestore collection and are referenced by both the procedencia (origin) and asignacion (deployment) fields on asset records.

Location Hierarchy

region  →  estado  →  ciudad  →  sede  →  piso  →  ala (optional)
LevelFieldRequiredDescription
1regionGeographic macro-region (e.g., “Capital”)
2estadoState or province (e.g., “Distrito Capital”)
3ciudadCity (e.g., “Caracas”)
4sedeOffice building or campus name
5pisoFloor number (must be a positive integer)
6alaWing within the floor: Este, Oeste, Norte, or Sur
These required fields are declared in inventory.constants.js:
export const locationRequiredFields = [
  "region",
  "estado",
  "ciudad",
  "sede",
  "piso",
];

Deterministic Document IDs

Location document IDs are derived deterministically from the field values using locationIdFromData(). The same combination of region + estado + ciudad + sede + piso + ala always produces the same Firestore document ID, which prevents duplicate location records from being created even if two users attempt to register the same office simultaneously. This also means the PUT /api/ubicaciones/:id endpoint may trigger a document migration — if the updated fields produce a different ID, the handler opens a transaction that deletes the old document and creates a new one under the new ID:
await db.runTransaction(async (tx) => {
  tx.delete(oldRef);
  tx.set(newRef, {
    ...normUbicacion,
    createdAt: oldData.createdAt || FieldValue.serverTimestamp(),
    updatedAt: FieldValue.serverTimestamp(),
    status: "Activo",
  });
});

Geographic Reference Collections

Regions, states, and cities are seeded into three separate Firestore collections. These collections power the cascading select menus in the frontend forms:
CollectionKey fieldParent link
regionesnombre
estadosnombre, id_regionregiones
ciudadesnombre, id_estadoestados

API Reference

Geographic Data

Returns all documents from the regiones collection ordered alphabetically.
curl http://localhost:3001/api/region
[
  { "id_region": "capital",           "nombre": "Capital" },
  { "id_region": "centro-occidental", "nombre": "Centro Occidental" },
  { "id_region": "zuliana",           "nombre": "Zuliana" }
]
Queries the estados collection filtered by id_region.
curl http://localhost:3001/api/region/capital/estados
[
  { "id_estado": "distrito-capital", "nombre": "Distrito Capital" }
]
Queries the ciudades collection filtered by id_estado.
curl http://localhost:3001/api/estados/distrito-capital/ciudades
[
  { "id_ciudad": "caracas", "nombre": "Caracas" }
]

Locations CRUD

Returns all locations with status: "Activo", deduplicated by their six-level key, sorted by sede + piso.
curl http://localhost:3001/api/ubicaciones
[
  {
    "id": "capital-distrito-capital-caracas-torre-norte-4-este",
    "region": "Capital",
    "estado": "Distrito Capital",
    "ciudad": "Caracas",
    "sede": "Torre Norte",
    "piso": "4",
    "ala": "Este",
    "status": "Activo"
  }
]
Requires Administrador or Superadministrador role (enforced by verificarToken + permitirEscritura middleware).
POST /api/ubicaciones
Content-Type: application/json

{
  "region": "Capital",
  "estado": "Distrito Capital",
  "ciudad": "Caracas",
  "sede": "Torre Norte",
  "piso": "4",
  "ala": "Este"
}
Success response:
HTTP/1.1 201 Created

{
  "message": "Ubicación registrada con éxito",
  "id": "capital-distrito-capital-caracas-torre-norte-4-este",
  "region": "Capital",
  "estado": "Distrito Capital",
  "ciudad": "Caracas",
  "sede": "Torre Norte",
  "piso": "4",
  "ala": "Este"
}
Returns 409 Conflict if a location with the same normalized ID already exists.
Requires write permissions. If the updated fields produce a different document ID, the old document is deleted and a new one is created atomically in a transaction. The createdAt timestamp is preserved.
PUT /api/ubicaciones/capital-distrito-capital-caracas-torre-norte-4-este
Content-Type: application/json

{
  "region": "Capital",
  "estado": "Distrito Capital",
  "ciudad": "Caracas",
  "sede": "Torre Norte",
  "piso": "5",
  "ala": "Oeste"
}
Permanently removes the location document. Requires write permissions.
curl -X DELETE \
  -b 'acceso_token=<token>' \
  http://localhost:3001/api/ubicaciones/capital-distrito-capital-caracas-torre-norte-4-este
Returns 404 if the document does not exist.
Sets status: "inactivo" on the document rather than deleting it. The location is excluded from GET /api/ubicaciones results. This operation is logged to the bitacora collection.
curl -X PUT \
  -b 'acceso_token=<token>' \
  http://localhost:3001/api/ubicaciones/eliminadas/capital-distrito-capital-caracas-torre-norte-4-este
The resulting bitácora entry will have:
  • accion: "Eliminar ubicación"
  • detalles: ["Se inactivó la ubicación: <nombre>"]
  • usuario: the authenticated user’s username
  • sede: the authenticated user’s office

Frontend Validation Schema

The frontend uses ubicacionSchema.ts (Zod) to validate location forms before submission:
import { z } from "zod";

export const ubicacionSchema = z.object({
  region:  requiredSelect("La región"),
  estado:  requiredSelect("El estado"),
  ciudad:  requiredSelect("La ciudad"),
  sede:    z.string().min(1, "La sede es requerida").max(100, "..."),
  piso:    z.string().min(1, "El piso es requerido")
            .regex(/^\d+$/, "El piso debe ser un número entero positivo"),
  ala: z.union([
    z.literal(""),
    z.enum(["Este", "Oeste", "Norte", "Sur"], "Selecciona una ala válida"),
  ]).optional().default(""),
});
The ala field is a union of an empty string (meaning “no wing”) and the four cardinal direction values. This allows the form to leave ala blank while still maintaining type safety — Zod rejects any string that is not one of the four allowed values or an empty string.

Example Location Object

{
  "id": "capital-distrito-capital-caracas-torre-norte-4-este",
  "region": "Capital",
  "estado": "Distrito Capital",
  "ciudad": "Caracas",
  "sede": "Torre Norte",
  "piso": "4",
  "ala": "Este",
  "status": "Activo",
  "createdAt": "2024-09-15T10:23:00.000Z"
}
When registering equipment, you can reference an existing location object as both the procedencia and asignacion values. If the location ID does not yet exist in Firestore, the equipment registration transaction will auto-create the location document with { merge: true } so that no duplicate is written even if two requests race.

Build docs developers (and LLMs) love