Requiply
SDK de TypeScript

Ventas y productos en venta

Lista los productos en venta y su stock, consulta y registra ventas, y vende productos junto con una reserva a través del SDK.

Estos recursos cubren el módulo de Ventas: productos como tarjetas de memoria, baterías o cinta americana que una empresa de alquiler vende con control de stock, junto a sus alquileres.

La organización debe tener el plan Advanced con Enable sales (activar ventas) activado en Funciones. Si no, todos los métodos lanzan un RequiplyError con status403 y reasonfeature_disabled (selling_disabled con una clave publicable).
MétodoClave secretaClave publicable
saleItems.list(), saleItems.get()SíSolo los productos activos, mientras la organización vende a los clientes
saleLines en pricing.quote() y bookings.create()SíSí, dentro del stock disponible, mientras la organización vende a los clientes
sales.list(), sales.get(), sales.create()SíNo

Los productos en venta se mantienen aparte de los productos de alquiler: inventory.list(), la disponibilidad y categories.list() nunca los devuelven.

Con una clave publicable

Una tienda online puede mostrar productos en venta y dejar que los clientes los añadan a una solicitud de reserva, como hace el portal de clientes en su paso Items to buy (artículos para comprar). Una clave publicable solo llega a ellos mientras la organización vende a los clientes, para lo que hacen falta las tres cosas siguientes:

  • la función de ventas en el plan Advanced, como se indica arriba;
  • Selling items (venta de artículos) activado en la configuración del portal de clientes;
  • al menos un producto en venta que no esté archivado.

Si no, la llamada lanza un 403 con reason selling_disabled, así que una tienda online puede ocultar su sección de productos.

Con una clave publicable:

  • saleItems.list() solo devuelve los productos activos, diga lo que diga filter. barcode y purchaseCost son siempre null.
  • saleItems.get() responde 404 para un producto archivado, y soldUnits30d, revenue30d y soldUnitsTotal son 0.
  • saleLines no puede pedir más de stockOnHand. Una línea que supera el stock lanza un 409 con reason out_of_stock; data.saleItemId indica el producto y data.availableQuantity, cuántos quedan (0 cuando está agotado).
try {
  await requiply.pricing.quote({ startDate, endDate, items, saleLines });
} catch (error) {
  if (error instanceof RequiplyError && error.reason === 'out_of_stock') {
    showNotice(`Only ${error.data?.availableQuantity} left`);
  } else if (error instanceof RequiplyError && error.reason === 'selling_disabled') {
    hideGoodsSection();
  } else {
    throw error;
  }
}

saleItems.list()

saleItems.list(
  params?: SaleItemsListParams,
  options?: RequiplyRequestOptions,
): Promise<SaleItemsListResponse>

Parámetros opcionales: search (nombre, SKU o código de barras), category (ID de categoría), filter (active por defecto, archived o all; las claves publicables siempre reciben active), limit (de 1 a 100, por defecto 25) y cursor.

const { items, nextCursor } = await requiply.saleItems.list({ search: 'battery' });

for (const item of items) {
  console.log(item.name, item.salePrice, item.currency, item.stockOnHand);
}
CampoQué contiene
id, name, sku, barcodeEl producto. sku y barcode pueden ser null; barcode siempre es null con una clave publicable.
categoryId, categoryName, imageUrlPueden ser null.
salePrice, currencyPrecio fijo por unidad en la moneda de la organización.
stockOnHandLas unidades en el almacén. Puede ser negativo cuando las ventas han superado el stock registrado.
purchaseCostEl coste neto por unidad, o null salvo que la clave pueda gestionar el stock. Siempre null con una clave publicable.
isArchivedLos productos archivados no se pueden vender.

saleItems.get()

saleItems.get(
  saleItemId: string,
  options?: RequiplyRequestOptions,
): Promise<SaleItemResponse>

Devuelve saleItem con los campos de arriba más soldUnits30d, revenue30d y soldUnitsTotal.

const { saleItem } = await requiply.saleItems.get(itemId);
console.log(saleItem.soldUnits30d, saleItem.revenue30d);

sales.list()

sales.list(
  params?: SalesListParams,
  options?: RequiplyRequestOptions,
): Promise<SalesListResponse>

Lista las ventas, de la más reciente a la más antigua, tanto las independientes como las vendidas con reservas. Filtros opcionales:

ParámetroEfecto
kindstandalone o booking.
statuscompleted o cancelled.
unpaidtrue para las ventas independientes que todavía tienen importe pendiente.
clientIdLas ventas de un cliente.
soldFrom, soldToFechas y horas ISO 8601.
searchNúmero de venta, nombre, email o teléfono del cliente, nombre o SKU del producto.
limit, cursorEl tamaño de página (de 1 a 100, por defecto 25) y el nextCursor de la página anterior.
const { sales, currency } = await requiply.sales.list({ kind: 'standalone', unpaid: true });

Cada venta tiene id, saleOrgId (el número de venta que ve el personal en la app), status, soldAt, total, returnedTotal, net (el total menos las devoluciones), paidAmount, balance, paymentMethod, paidAt, note, el cliente, la reserva a la que pertenece (reservationId, reservationOrgId), itemCount e itemsLabel. balance es lo que todavía debe una venta independiente; siempre es 0 en una venta de una reserva, que se paga a través de la reserva.

sales.get()

sales.get(
  id: string | number,
  params?: { idKind?: 'internal' | 'org' },
  options?: RequiplyRequestOptions,
): Promise<SaleResponse>

id es el UUID de la venta, o el número de venta de la app con idKind: 'org'.

const { sale } = await requiply.sales.get(12, { idKind: 'org' });

La venta también incluye lines (producto, cantidad, returnedQuantity, unitPrice, lineTotal), returns y payments. En una venta de una reserva, payments son los de la reserva, porque la reserva cobra el alquiler y los productos en un único pago.

sales.create()

sales.create(
  input: SaleCreateInput,
  options?: RequiplyRequestOptions,
): Promise<SaleCreateResponse>

Registra una venta independiente, como Nueva venta en la app.

const { sale, stockWarnings, payment } = await requiply.sales.create({
  lines: [{ saleItemId: itemId, quantity: 2 }],
  client: { name: 'Alex', email: 'customer@example.com' },
  paymentMethod: 'monobank',
  note: 'Counter sale',
});

if (payment?.paymentUrl) console.log('Pay here:', payment.paymentUrl);
CampoReglas
linesObligatorio. De 1 a 100 productos, cada uno una sola vez, con una cantidad entera mayor que cero. Cada línea se vende al salePrice del producto.
clientId o clientOpcional. client se busca por email y, después, por teléfono, y se crea cuando no coincide ninguno.
paymentMethodpayOnPickup (por defecto), o monobank, liqpay, wayforpay o stripe con una integración activa. liqpay necesita un cliente con email.
soldAtISO 8601. Por defecto, el momento actual.
noteHasta 2000 caracteres.

El stock baja al momento. Una venta puede dejar el stock por debajo de cero: se registra igualmente, y stockWarnings lista cada producto afectado con su stockOnHand y el stock after (después de) la venta. payment es el cobro pendiente con el que empieza la venta, con un paymentUrl para un método online.

Una venta rechazada lanza un RequiplyError con un reason como sale_item_archived, payment_method_unavailable o client_email_required. Consulta Gestión de errores para ver la lista completa.

Productos en una reserva

pricing.quote() y bookings.create() aceptan saleLines para vender productos junto con un alquiler:

const quote = await requiply.pricing.quote({
  startDate: '2026-07-01T10:00:00',
  endDate: '2026-07-03T18:00:00',
  items: [{ groupId: cameraKitId, quantity: 1 }],
  saleLines: [{ saleItemId: memoryCardId, quantity: 2 }],
});

console.log(quote.saleSubtotal, quote.lineSaleItems[0]?.stockAfter);
  • Una clave secreta puede vender dejando el stock en negativo, igual que puede hacerlo el personal en la app. Una clave publicable no: consulta Con una clave publicable.
  • Los productos se venden a su precio de catálogo y nunca se les aplica descuento ni ajuste. Su total es saleSubtotal, ya incluido en total.
  • lineSaleItems muestra cada producto con stockOnHand y stockAfter, para que puedas avisar antes de que el stock quede en negativo.
  • En bookings.create(), el stock baja al crear la reserva y se recupera si la reserva se cancela. El total de los productos se suma al totalCost de la reserva y a su pago.
  • bookings.get() devuelve los productos como saleItems y su venta como saleId.

Para cambiar los productos de una reserva existente, usa Vender artículos en una reserva en la app.

Copyright © 2026