Продажі та товари на продаж
Ці ресурси охоплюють модуль Продажі: товари на кшталт карт памʼяті, батарей чи гаферного скотчу, які орендний бізнес продає з обліком залишку поруч з орендою.
RequiplyError зі status403 і reasonfeature_disabled (selling_disabled для публічного ключа).| Метод | Секретний ключ | Публічний ключ |
|---|---|---|
saleItems.list(), saleItems.get() | Так | Лише активні товари, поки організація продає клієнтам |
saleLines у pricing.quote() і bookings.create() | Так | Так, у межах наявного залишку, поки організація продає клієнтам |
sales.list(), sales.get(), sales.create() | Так | Ні |
Товари на продаж зберігаються окремо від товарів для оренди: inventory.list(), доступність і categories.list() ніколи їх не повертають.
З публічним ключем
Вітрина може показувати товари на продаж і давати клієнтам додавати їх до запиту на бронювання так само, як це робить клієнтський портал на кроці Додати товари для покупки. Публічний ключ отримує до них доступ, лише поки організація продає клієнтам, для чого потрібні всі три умови:
- функція «Продажі» на тарифі Advanced, як описано вище;
- увімкнений Продаж товарів у налаштуваннях клієнтського порталу;
- щонайменше один товар на продаж, який не архівовано.
Інакше виклик викидає 403 з reason selling_disabled, тож вітрина може приховати розділ товарів.
З публічним ключем:
saleItems.list()повертає лише активні товари, незалежно відfilter.barcodeіpurchaseCostзавждиnull.saleItems.get()повертає404для архівованого товару, аsoldUnits30d,revenue30d,soldUnitsTotalдорівнюють0.saleLinesне може запитувати більше заstockOnHand. Рядок понад залишок викидає409зreasonout_of_stock;data.saleItemIdназиває товар, аdata.availableQuantityпоказує, скільки залишилося (0, коли все продано).
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>
Необовʼязкові параметри: search (назва, артикул чи штрихкод), category (ідентифікатор категорії), filter (за замовчуванням active, також archived або all; публічні ключі завжди отримують active), limit (від 1 до 100, за замовчуванням 25), 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);
}
| Поле | Що містить |
|---|---|
id, name, sku, barcode | Товар. sku і barcode можуть бути null; для публічного ключа barcode завжди null. |
categoryId, categoryName, imageUrl | Можуть бути null. |
salePrice, currency | Фіксована ціна за одиницю у валюті організації. |
stockOnHand | Одиниці на полиці. Значення може бути відʼємним, коли продажі перевищили облікований залишок. |
purchaseCost | Чиста собівартість одиниці або null, якщо ключ не може керувати залишками. Для публічного ключа завжди null. |
isArchived | Архівовані товари продати не можна. |
saleItems.get()
saleItems.get(
saleItemId: string,
options?: RequiplyRequestOptions,
): Promise<SaleItemResponse>
Повертає saleItem з наведеними вище полями, а також полями soldUnits30d, revenue30d, soldUnitsTotal.
const { saleItem } = await requiply.saleItems.get(itemId);
console.log(saleItem.soldUnits30d, saleItem.revenue30d);
sales.list()
sales.list(
params?: SalesListParams,
options?: RequiplyRequestOptions,
): Promise<SalesListResponse>
Перелічує продажі від найновіших, як окремі, так і повʼязані з бронюваннями. Необовʼязкові фільтри:
| Параметр | Дія |
|---|---|
kind | standalone або booking. |
status | completed або cancelled. |
unpaid | true для окремих продажів, за якими ще є заборгованість. |
clientId | Продажі одного клієнта. |
soldFrom, soldTo | Дата й час у форматі ISO 8601. |
search | Номер продажу, імʼя клієнта, email чи телефон, назва товару чи артикул. |
limit, cursor | Розмір сторінки (від 1 до 100, за замовчуванням 25) і nextCursor попередньої сторінки. |
const { sales, currency } = await requiply.sales.list({ kind: 'standalone', unpaid: true });
Кожен продаж має такі поля: id; saleOrgId (номер продажу, який співробітники бачать у застосунку); status; soldAt; total; returnedTotal; net (сума мінус повернення); paidAmount; balance; paymentMethod; paidAt; note; клієнта; бронювання, до якого він належить (reservationId, reservationOrgId); itemCount; itemsLabel. balance показує, скільки ще винен окремий продаж. Для продажу в бронюванні це значення завжди 0, бо його оплачують через бронювання.
sales.get()
sales.get(
id: string | number,
params?: { idKind?: 'internal' | 'org' },
options?: RequiplyRequestOptions,
): Promise<SaleResponse>
id є UUID продажу або номером продажу із застосунку з idKind: 'org'.
const { sale } = await requiply.sales.get(12, { idKind: 'org' });
Продаж також містить lines (товар, кількість, returnedQuantity, unitPrice, lineTotal), а також returns і payments. Для продажу в бронюванні payments належать бронюванню, бо бронювання стягує оренду й товари одним платежем.
sales.create()
sales.create(
input: SaleCreateInput,
options?: RequiplyRequestOptions,
): Promise<SaleCreateResponse>
Записує окремий продаж, як Новий продаж у застосунку.
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);
| Поле | Правила |
|---|---|
lines | Обовʼязкове. Від 1 до 100 товарів, кожен щонайбільше один раз, із цілою кількістю, більшою за нуль. Кожен рядок продається за salePrice товару. |
clientId або client | Необовʼязкове. client зіставляється за email, потім за телефоном, а якщо збігу немає, створюється новий. |
paymentMethod | payOnPickup (за замовчуванням) або один з онлайн-способів (monobank, liqpay, wayforpay, stripe) з активною інтеграцією. liqpay потребує клієнта з email. |
soldAt | ISO 8601. За замовчуванням поточний момент. |
note | До 2000 символів. |
Залишок зменшується одразу. Продаж може опустити залишок нижче нуля: його все одно буде записано, а stockWarnings перелічує кожен такий товар із його stockOnHand і залишком after після продажу. payment є очікуваним платежем, з якого починається продаж, із paymentUrl для онлайн-способу оплати.
Відхилений продаж викидає RequiplyError з reason на кшталт sale_item_archived, payment_method_unavailable чи client_email_required. Повний перелік див. в Обробка помилок.
Товари в бронюванні
pricing.quote() і bookings.create() приймають saleLines, щоб продати товари разом з орендою:
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);
- Секретний ключ може продавати у відʼємний залишок, як це можуть робити співробітники в застосунку. Публічний ключ не може: див. З публічним ключем.
- Товари продаються за каталожною ціною, знижки та коригування до них ніколи не застосовуються. Їхня сума є
saleSubtotalі вже входить уtotal. lineSaleItemsпоказує кожен товар зіstockOnHandіstockAfter, тож можна попередити, перш ніж залишок стане відʼємним.- У
bookings.create()залишок зменшується під час створення бронювання і повертається, якщо бронювання скасують. Сума товарів додається доtotalCostбронювання та його оплати. bookings.get()повертає товари якsaleItems, а їхній продаж якsaleId.
Щоб змінити товари в наявному бронюванні, скористайтеся Продажем товарів у бронюванні в застосунку.