---
title: "Sprzedaż i towary na sprzedaż"
description: "Wyświetlaj towary na sprzedaż i ich stany, odczytuj i rejestruj sprzedaż oraz sprzedawaj towary razem z rezerwacją za pomocą SDK."
canonical_url: "https://requiply.com/docs/pl/developers/typescript-sdk/sales"
---
# Sprzedaż i towary na sprzedaż

> Wyświetlaj towary na sprzedaż i ich stany, odczytuj i rejestruj sprzedaż oraz sprzedawaj towary razem z rezerwacją za pomocą SDK.

Te zasoby obsługują moduł [Sprzedaż](https://requiply.com/docs/pl/guides/sales): towary, takie jak karty pamięci, akumulatory czy taśma gaffer, które wypożyczalnia sprzedaje z ewidencją stanów obok wynajmu.

<warning>

Organizacja musi mieć plan Advanced i włączone **Włącz sprzedaż** w [Funkcje](https://requiply.com/docs/pl/guides/settings/organization/features#w%C5%82%C4%85cz-sprzeda%C5%BC). W przeciwnym razie każda metoda zgłasza `RequiplyError` ze `status` `403` i `reason` `feature_disabled` (`selling_disabled` dla klucza publicznego).

</warning>

<table>
<thead>
  <tr>
    <th>
      Metoda
    </th>
    
    <th>
      Klucz tajny
    </th>
    
    <th>
      Klucz publiczny
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        saleItems.list()
      </code>
      
      , <code>
        saleItems.get()
      </code>
    </td>
    
    <td>
      Tak
    </td>
    
    <td>
      Tylko aktywne towary, gdy organizacja sprzedaje klientom
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        saleLines
      </code>
      
       w <code>
        pricing.quote()
      </code>
      
       i <code>
        bookings.create()
      </code>
    </td>
    
    <td>
      Tak
    </td>
    
    <td>
      Tak, w granicach stanu magazynowego, gdy organizacja sprzedaje klientom
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sales.list()
      </code>
      
      , <code>
        sales.get()
      </code>
      
      , <code>
        sales.create()
      </code>
    </td>
    
    <td>
      Tak
    </td>
    
    <td>
      Nie
    </td>
  </tr>
</tbody>
</table>

Towary na sprzedaż są oddzielone od pozycji do wynajęcia: `inventory.list()`, dostępność i `categories.list()` nigdy ich nie zwracają.

## Z kluczem publicznym

Sklep internetowy może pokazywać towary na sprzedaż i pozwalać klientom dodawać je do zapytania o rezerwację, tak jak [portal klienta](https://requiply.com/docs/pl/guides/online-booking/booking-flow) robi to w kroku **Produkty do zakupu**. Klucz publiczny ma do nich dostęp tylko wtedy, gdy organizacja **sprzedaje klientom**, co wymaga spełnienia wszystkich trzech warunków:

- funkcja Sprzedaż włączona w planie Advanced, jak wyżej;
- włączona opcja **Sprzedaż towarów** w [ustawieniach portalu klienta](https://requiply.com/docs/pl/guides/online-booking/portal-settings#akcje);
- co najmniej jeden niezarchiwizowany towar na sprzedaż.

W przeciwnym razie wywołanie zgłasza `403` z `reason` `selling_disabled`, więc sklep internetowy może ukryć sekcję z towarami.

Z kluczem publicznym:

- `saleItems.list()` zwraca tylko aktywne towary, niezależnie od wartości `filter`. `barcode` i `purchaseCost` mają zawsze wartość `null`.
- `saleItems.get()` zwraca `404` dla zarchiwizowanego towaru, a `soldUnits30d`, `revenue30d` i `soldUnitsTotal` mają wartość `0`.
- `saleLines` nie może prosić o więcej niż `stockOnHand`. Pozycja ponad stan zgłasza `409` z `reason` `out_of_stock`. `data.saleItemId` wskazuje towar, a `data.availableQuantity` mówi, ile zostało (`0`, gdy towar jest wyprzedany).

```ts
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()`

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

Parametry opcjonalne: `search` (nazwa, SKU lub kod kreskowy), `category` (identyfikator kategorii), `filter` (domyślnie `active`, a także `archived` lub `all`; klucze publiczne zawsze dostają `active`), `limit` (od 1 do 100, domyślnie 25) i `cursor`.

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

for (const item of items) {
  console.log(item.name, item.salePrice, item.currency, item.stockOnHand);
}
```

<table>
<thead>
  <tr>
    <th>
      Pole
    </th>
    
    <th>
      Co zawiera
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        id
      </code>
      
      , <code>
        name
      </code>
      
      , <code>
        sku
      </code>
      
      , <code>
        barcode
      </code>
    </td>
    
    <td>
      Towar. <code>
        sku
      </code>
      
       i <code>
        barcode
      </code>
      
       mogą mieć wartość <code>
        null
      </code>
      
      . Dla klucza publicznego <code>
        barcode
      </code>
      
       ma zawsze wartość <code>
        null
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        categoryId
      </code>
      
      , <code>
        categoryName
      </code>
      
      , <code>
        imageUrl
      </code>
    </td>
    
    <td>
      Mogą mieć wartość <code>
        null
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        salePrice
      </code>
      
      , <code>
        currency
      </code>
    </td>
    
    <td>
      Stała cena za sztukę w walucie organizacji.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stockOnHand
      </code>
    </td>
    
    <td>
      Liczba sztuk na półce. Może być ujemna, gdy sprzedaż przekroczyła zapisany stan.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        purchaseCost
      </code>
    </td>
    
    <td>
      Koszt netto za sztukę albo <code>
        null
      </code>
      
      , chyba że klucz może zarządzać stanem magazynowym. Dla klucza publicznego zawsze <code>
        null
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isArchived
      </code>
    </td>
    
    <td>
      Zarchiwizowanych towarów nie można sprzedawać.
    </td>
  </tr>
</tbody>
</table>

## `saleItems.get()`

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

Zwraca `saleItem` z powyższymi polami oraz `soldUnits30d`, `revenue30d` i `soldUnitsTotal`.

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

## `sales.list()`

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

Wymienia sprzedaże od najnowszej, zarówno samodzielne, jak i w ramach rezerwacji. Filtry opcjonalne:

<table>
<thead>
  <tr>
    <th>
      Parametr
    </th>
    
    <th>
      Działanie
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        kind
      </code>
    </td>
    
    <td>
      <code>
        standalone
      </code>
      
       lub <code>
        booking
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        status
      </code>
    </td>
    
    <td>
      <code>
        completed
      </code>
      
       lub <code>
        cancelled
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unpaid
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
      
       dla samodzielnych sprzedaży, które nie są jeszcze w pełni opłacone.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clientId
      </code>
    </td>
    
    <td>
      Sprzedaże jednego klienta.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        soldFrom
      </code>
      
      , <code>
        soldTo
      </code>
    </td>
    
    <td>
      Daty z godziną w formacie ISO 8601.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search
      </code>
    </td>
    
    <td>
      Numer sprzedaży, nazwa klienta, e-mail lub telefon, nazwa towaru lub SKU.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        limit
      </code>
      
      , <code>
        cursor
      </code>
    </td>
    
    <td>
      Rozmiar strony (od 1 do 100, domyślnie 25) i <code>
        nextCursor
      </code>
      
       poprzedniej strony.
    </td>
  </tr>
</tbody>
</table>

```ts
const { sales, currency } = await requiply.sales.list({ kind: 'standalone', unpaid: true });
```

Każda sprzedaż ma `id`, `saleOrgId` (numer sprzedaży, który pracownicy widzą w aplikacji), `status`, `soldAt`, `total`, `returnedTotal`, `net` (suma pomniejszona o zwroty), `paidAmount`, `balance`, `paymentMethod`, `paidAt`, `note`, klienta, rezerwację, do której należy (`reservationId`, `reservationOrgId`), `itemCount` i `itemsLabel`. `balance` to kwota, której samodzielna sprzedaż nadal wymaga. Dla sprzedaży w ramach rezerwacji ma zawsze wartość `0`, bo płaci się za nią w rezerwacji.

## `sales.get()`

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

`id` to UUID sprzedaży albo numer sprzedaży z aplikacji z `idKind: 'org'`.

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

Sprzedaż zawiera też `lines` (towar, ilość, `returnedQuantity`, `unitPrice`, `lineTotal`), `returns` i `payments`. W przypadku sprzedaży w ramach rezerwacji `payments` to płatności rezerwacji, bo rezerwacja pobiera opłatę za wynajem i towary w jednej płatności.

## `sales.create()`

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

Rejestruje samodzielną sprzedaż, tak jak [Nowa sprzedaż](https://requiply.com/docs/pl/guides/sales/new-sale) w aplikacji.

```ts
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);
```

<table>
<thead>
  <tr>
    <th>
      Pole
    </th>
    
    <th>
      Zasady
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        lines
      </code>
    </td>
    
    <td>
      Wymagane. Od 1 do 100 towarów, każdy najwyżej raz, z ilością będącą liczbą całkowitą większą od zera. Każda pozycja jest sprzedawana po cenie <code>
        salePrice
      </code>
      
       towaru.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clientId
      </code>
      
       lub <code>
        client
      </code>
    </td>
    
    <td>
      Opcjonalne. <code>
        client
      </code>
      
       jest dopasowywany po adresie e-mail, potem po telefonie, a gdy żaden nie pasuje, zostaje utworzony.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paymentMethod
      </code>
    </td>
    
    <td>
      <code>
        payOnPickup
      </code>
      
       (domyślnie) albo <code>
        monobank
      </code>
      
      , <code>
        liqpay
      </code>
      
      , <code>
        wayforpay
      </code>
      
       lub <code>
        stripe
      </code>
      
       przy aktywnej integracji. <code>
        liqpay
      </code>
      
       wymaga klienta z adresem e-mail.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        soldAt
      </code>
    </td>
    
    <td>
      ISO 8601. Domyślnie bieżący moment.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        note
      </code>
    </td>
    
    <td>
      Do 2000 znaków.
    </td>
  </tr>
</tbody>
</table>

Stan magazynowy spada od razu. Sprzedaż może zejść ze stanem poniżej zera: nadal zostaje zarejestrowana, a `stockWarnings` wymienia każdy taki towar z jego `stockOnHand` i stanem `after` po sprzedaży. `payment` to oczekująca płatność, od której zaczyna się sprzedaż, z `paymentUrl` dla metody online.

Odrzucona sprzedaż zgłasza `RequiplyError` z `reason`, takim jak `sale_item_archived`, `payment_method_unavailable` lub `client_email_required`. Pełną listę zawiera [Obsługa błędów](https://requiply.com/docs/pl/developers/typescript-sdk/error-handling#przyczyny-b%C5%82%C4%99d%C3%B3w).

## Towary w rezerwacji

`pricing.quote()` i `bookings.create()` przyjmują `saleLines`, aby sprzedać towary razem z wynajmem:

```ts
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);
```

- Klucz tajny może sprzedawać poniżej zerowego stanu, tak jak pracownicy w aplikacji. Klucz publiczny nie może: zobacz [Z kluczem publicznym](#z-kluczem-publicznym).
- Towary są sprzedawane po cenie katalogowej i nigdy nie są objęte rabatem ani korektą. Ich suma to `saleSubtotal`, już uwzględniona w `total`.
- `lineSaleItems` pokazuje każdy towar z `stockOnHand` i `stockAfter`, więc możesz ostrzec, zanim stan spadnie poniżej zera.
- W `bookings.create()` stan magazynowy spada po utworzeniu rezerwacji i wraca, jeśli rezerwacja zostanie anulowana. Suma za towary jest doliczana do `totalCost` rezerwacji i jej płatności.
- `bookings.get()` zwraca towary jako `saleItems`, a ich sprzedaż jako `saleId`.

Aby zmienić towary w istniejącej rezerwacji, użyj w aplikacji opcji [Sprzedaż towarów w rezerwacji](https://requiply.com/docs/pl/guides/bookings/sell-items).


## Sitemap

See the full [sitemap](https://requiply.com/docs/sitemap.md) for all pages.
