Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ProcesosAgilesUMSS/sansistore/llms.txt

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

The vendedor (seller) role in SansiStore owns the full lifecycle of every order — from the moment a buyer checks out to final delivery or cancellation. All seller pages live under the /seller/* path and are protected by Firebase Auth role checks; any authenticated user without the vendedor role is denied access. The sidebar navigation is driven by sellerNavGroups and sections constants, grouping pages into Pedidos, Registrar, and Operación sections.

Order Lifecycle Overview

Every order in Firestore moves through a strict sequence of statuses defined in OrderStatus:
// src/features/seller/types.ts
export type OrderStatus =
  | 'CREADO'
  | 'RESERVADO'
  | 'PENDIENTE'
  | 'EMPAQUETADO'
  | 'LISTO'
  | 'ASIGNADO'
  | 'EN CAMINO'
  | 'ENTREGADO'
  | 'PAGADO'
  | 'CANCELADO'
  | 'NO ENTREGADO'
  | 'DEVUELTO'
  | 'CERRADO'
  | 'RECHAZADO'
  | 'PENDIENTE REASIGNACION'
  | 'PENDIENTE-ASIGNACION'
  | 'ACEPTADO';
The happy path runs:
CREADORESERVADOEMPAQUETADOLISTOASIGNADOEN CAMINOENTREGADO / PAGADO
Exceptions branch off into NO ENTREGADO, RECHAZADO, or CANCELADO.

Seller Portal Pages

Created Orders

New orders in status CREADO awaiting seller acceptance. The seller reviews each order and reserves it, transitioning it to RESERVADO.

Pending Orders

Orders in the PENDIENTE state that are currently being processed. Fetched from /api/seller/pending-orders with a bearer token — requires the vendedor role.

Reservations

Orders in RESERVADO status with stock reserved in inventory. The seller confirms stock and prepares items for packaging.

Packaged Orders

Orders in EMPAQUETADO state. The seller verifies the package and marks it Listo to advance the order to the LISTO status.

Ready Orders

Orders in LISTO status, fully packaged and waiting for a courier (mensajero) to be assigned. This page opens the AssignMessengerModal.

Failed Orders

Orders in NO ENTREGADO status. The seller decides to restart the order (back to RESERVADO) or cancel it (to CANCELADO).

Undelivered Orders

Also shows orders in NO ENTREGADO. The useUndeliveredOrderDecision hook exposes restartOrder and cancelOrder actions; restarting re-reserves inventory when stockRestored is true.

Rejected Orders

Orders with status RECHAZADO. These are orders where the seller registered a delivery failure reason via registerDeliveryFailureReason.

Order History

A complete, paginated audit log of all orders for the authenticated seller, sorted by updatedAt descending. Used for reconciliation and dispute resolution.

Core Seller Workflow

1

Receive a Created Order

The buyer places an order; Firestore writes a document to the orders collection with status: 'CREADO' and sellerId set to the seller’s UID. The seller sees it on /seller/created-orders, powered by subscribeConfirmedOrders.
// src/features/seller/services/sellerReservedOrdersService.ts
export function subscribeConfirmedOrders(
  db: Firestore,
  sellerId: string,
  onData: (orders: Order[]) => void,
  onError?: (err: Error) => void,
): Unsubscribe {
  const q = query(
    collection(db, 'orders'),
    where('sellerId', '==', sellerId),
    where('status', '==', 'CREADO'),
  );
  // ...
}
2

Reserve the Order

The seller clicks Reservar. reserveConfirmedOrder runs a Firestore transaction that checks status === 'CREADO' and updates it to RESERVADO, stamping reservedAt. Seller activity is logged via registrarActividadVendedor (HU #160).
tx.update(orderRef, {
  status: 'RESERVADO',
  sellerId,
  reservedAt: serverTimestamp(),
  updatedAt: serverTimestamp(),
});
3

Package the Order

Once items are physically packed, the order transitions to EMPAQUETADO. The /seller/packaged-orders page (PackagedOrdersPanel) lists all such orders and lets the seller confirm they are ready.
4

Mark Order as Ready

The seller clicks Marcar como listo on a packaged order. markOrderReady validates status === 'EMPAQUETADO' inside a transaction then updates it to LISTO.
// src/features/seller/services/markOrderReady.ts
export const markOrderReady = async (
  db: Firestore,
  orderId: string,
  sellerId: string,
): Promise<void> => {
  await runTransaction(db, async (tx) => {
    // ...
    if (current.status !== 'EMPAQUETADO') throw new Error(/* ... */);
    tx.update(orderRef, {
      status: 'LISTO',
      sellerId,
      updatedAt: serverTimestamp(),
    });
  });
};
The useMarkOrderReady hook wraps this and exposes markAsReady, isLoading, isSuccess, and error to the UI.
5

Assign a Courier

With the order in LISTO, the seller opens the Assign Messenger Modal on /seller/ready-orders and picks an available mensajero. assignCourierToDelivery runs an atomic transaction: it creates a new document in the deliveries collection and updates the order’s status to ASIGNADO.
// src/features/seller/services/assignCourierToDelivery.ts
const deliveryCode = `DEL-${Date.now().toString(36).toUpperCase()}`;

tx.set(deliveryRef, {
  orderId,
  courierId: courierId,
  status: 'assigned',
  deliveryCode,
  attemptNumber: 1,
  incidentReason: null,
  evidenceUrl: null,
  failureReason: null,
  amountCollected: null,
  customerConfirmed: false,
  customerConfirmedAt: null,
  pickedUpAt: null,
  inTransitAt: null,
  deliveredAt: null,
  failedAt: null,
  reprogrammedAt: null,
  createdAt: serverTimestamp(),
  assignedAt: serverTimestamp(),
  updatedAt: serverTimestamp(),
});
6

Courier Delivers the Order

The courier picks up and delivers. On success the order reaches ENTREGADO or PAGADO. On failure it moves to NO ENTREGADO, surfacing on /seller/failed-orders and /seller/undelivered-orders.
7

Handle Delivery Outcome

For failed/undelivered orders the seller chooses one of two actions via useFailedOrderDecision or useUndeliveredOrderDecision:
  • Restart — returns the order to RESERVADO and re-reserves stock if stockRestored is true.
  • Cancel — sets status to CANCELADO, releases reserved inventory, and records the cancellation reason in incidentReason.

Order Detail Page

Each order has a dedicated page at /seller/orders/[id] that shows the full Order object including the items subcollection. The getOrders service (getOredrs) uses onSnapshot to keep the list live and enriches each record with buyer info, location label, and order items:
// src/features/seller/services/getOrders.ts
export const getOredrs = ({ status, ordby, db, sellerId, onData, onError }: Params): Unsubscribe => {
  const qOrders = query(
    collection(db, 'orders'),
    where('sellerId', '==', sellerId),
    where('status', '==', status),
    orderBy('updatedAt', ordby),
  );
  return onSnapshot(
    qOrders,
    async (snap) => {
      const orders = snap.docs.map((d) => docToOrder(d.id, d.data() as OrderDoc));
      const enriched = await enrichOrdersWithData(db, orders);
      onData(enriched);
    },
    () => onError('Ocurrio un error al cargar los pedidos'),
  );
};

export const getOrders = getOredrs;
The enrichOrdersWithData function batches lookups for buyers (users collection) and locations (locations collection) and reads orderItems subcollections in parallel, minimising round-trips.

Firestore Order Schema

The orders collection document (OrderDoc) has the following fields:
// src/features/seller/types.ts
export type OrderDoc = {
  orderId: string;
  buyerId: string;
  sellerId: string;
  status: OrderStatus;
  total: number;
  locationId: string;
  paymentId: string | null;
  paymentStatus: string;
  deliveryId: string | null;
  deliveryCode: string | null;
  deliveryStatus: string | null;
  incidentReason: string | null;
  deliveryFailureReason?: string | null;
  deliveryFailureDescription?: string | null;
  confirmedAt: Date | null;
  cancelledAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
};
The enriched Order type extends OrderDoc with resolved fields:
FieldSourceDescription
buyerNameusers/{buyerId}.displayNameResolved buyer display name
buyerEmailusers/{buyerId}.emailBuyer email address
buyerInstitutionalIdusers/{buyerId}.institutionalIdUMSS institutional ID
locationLabellocations/{locationId}.labelHuman-readable delivery location
locationTypelocations/{locationId}.typeLocation category
itemsorders/{orderId}/orderItems subcollectionArray of OrderItem
Each OrderItem includes itemId, productId, productName, unitPrice, quantity, and subtotal.

Daily Collections

The /seller/daily-collections page (rendered by DailyCollectionsPanel) gives the seller a financial summary of cash-on-delivery payments collected by couriers on a given day. It calls fetchDailyCollections from dailyCollectionsService.ts:
// src/features/seller/services/dailyCollectionsService.ts
export async function fetchDailyCollections(
  date?: string,
): Promise<DailyCollectionsSummary> {
  const headers = await getAuthHeader();
  const query = date ? `?date=${encodeURIComponent(date)}` : '';
  const response = await fetch(`/api/seller/daily-collections${query}`, { headers });
  // ...
  return response.json() as Promise<DailyCollectionsSummary>;
}
The summary surface three key metrics:

Total cobrado

Total cash collected across all cash_on_delivery orders for the selected day, formatted in BOB (Bolivianos).

Pedidos cobrados

Count of orders with payments registered by couriers on the selected date.

Confirmados

Number of orders where the buyer confirmed reception (buyerReceptionConfirmed: true).
The date picker defaults to today in the America/La_Paz timezone (UTC-4). Sellers can navigate to any past date to review historical collections.
The seller sidebar is built from sellerNavGroups defined in src/features/seller/constants/navGroups.ts:
export const sellerNavGroups: NavGroup[] = [
  {
    title: 'Pedidos',
    items: [
      { label: 'Mis pedidos',           href: '/seller/orders' },
      { label: 'Pedidos creados',        href: '/seller/created-orders' },
      { label: 'Pedidos listos',         href: '/seller/ready-orders' },
      { label: 'Pedidos rechazados',     href: '/seller/rejected-orders' },
      { label: 'Pedidos no entregados',  href: '/seller/undelivered-orders' },
      { label: 'Historial de pedidos',   href: '/seller/order-history' },
    ],
  },
  {
    title: 'Registrar',
    items: [
      { label: 'Registrar compra',  href: '/seller/purchase' },
      { label: 'Registrar oferta',  href: '/seller/offers' },
    ],
  },
  {
    title: 'Operación',
    items: [
      { label: 'Incidencias',      href: '/seller/incidents' },
      { label: 'Pagos registrados', href: '/seller/daily-collections' },
      { label: 'Motivos de fallo', href: '/seller/failure-reasons' },
    ],
  },
];
Seller activity (reserving orders, marking ready, assigning couriers) is tracked via registrarActividadVendedor as part of HU #160 – Monitoreo de actividad de vendedores. These logs are written asynchronously and will not interrupt the main workflow if they fail.

Build docs developers (and LLMs) love