> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yume.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Позиции инвентаря и обмен

> Строки аренды: аренда и продажа единиц и услуг, план и факт выдачи/приёмки, тарифы и периоды, комплекты и группы, механизм замены (обмена) с сохранением истории, а также добавление, массовое обновление и удаление позиций.

Полный перечень полей — в [глоссарии](/ru/logic/rent/inventory-lines-glossary).

## Что описывает эта страница

Позиция инвентаря в аренде — это одна строка внутри аренды: конкретная арендуемая (или продаваемая) единица либо услуга, со своим тарифом, сроками, ценой, скидкой и штрафами. Аренда почти всегда состоит из нескольких таких строк. Здесь разбирается, как эти строки устроены, как они добавляются (по одной, пачкой, подбором по группе или комплекту), как отмечается фактическая выдача и приёмка, и как работает механизм замены (обмена) одной единицы на другую с сохранением истории.

<Info>
  Все операции с позициями идут в контексте одной аренды (заявки). Если конкретная аренда не передана явно, она берётся из контекста запроса (из адреса адреса).
</Info>

## Модель данных: позиция инвентаря в аренде

### Основные поля

| Поле              | Тип          | Назначение                                                                              |
| ----------------- | ------------ | --------------------------------------------------------------------------------------- |
| Аренда (заявка)   | связь        | К какой аренде относится строка. При удалении аренды её позиции удаляются вместе с ней. |
| Клиент            | связь        | Клиент позиции; если пусто, используется клиент самой аренды.                           |
| Тип позиции       | перечисление | «Аренда» (по умолчанию) или «Продажа».                                                  |
| Единица инвентаря | связь        | Конкретная арендуемая/продаваемая единица; может быть пустой у групповой позиции.       |
| Группа инвентаря  | связь        | Товарная группа, если строка не привязана к конкретной единице.                         |
| Склад             | связь        | Склад, к которому привязана позиция.                                                    |
| Контрагент        | связь        | Поставщик или субарендодатель позиции.                                                  |
| Кем создана       | связь        | Сотрудник, добавивший строку.                                                           |
| Доп. данные       | JSON         | Произвольные атрибуты; при обмене хранит идентификатор заменённой позиции.              |

### Тариф, цена и штрафы

У каждой строки хранится применённый тариф и «слепок» его условий на момент добавления: цена тарифа за один период и длительность периода (по умолчанию — один день). Если сам тариф позже удалят, ссылка на него обнулится, но сохранённые цена и длительность останутся.

Итоговые суммы позиции — сумма позиции и сумма со скидкой — не задаются вручную с этого экрана, а рассчитываются автоматическим пересчётом сумм аренды после каждого изменения состава позиций. Помимо этого у позиции хранятся штрафные суммы (итоговая, авто- и ручная) и бонус.

<Note>
  Скидка на позицию хранится ссылкой на применённую скидку; её раскладка по строкам выполняется отдельным пересчётом скидок аренды.
</Note>

### Сроки: план и факт

У позиции четыре временные отметки:

| Поле                | Значение                                                                    |
| ------------------- | --------------------------------------------------------------------------- |
| Плановое начало     | Когда аренда позиции должна начаться.                                       |
| Плановый конец      | Когда аренда позиции должна закончиться.                                    |
| Фактическая выдача  | Момент, когда единицу реально выдали клиенту. Заполнено = позиция выдана.   |
| Фактическая приёмка | Момент, когда единицу реально приняли обратно. Заполнено = позиция принята. |

Для расчётов используется «эффективное» начало и «эффективный» конец: если есть фактическая отметка — берётся она, иначе плановая. Длительность аренды округляется вверх до целого числа суток; если сроки не заданы, длительность считается равной одному часу. Человекочитаемая длительность («N дней») показывается только для арендных позиций — у продажных она пустая.

<Warning>
  На уровне базы данных действует жёсткое правило: плановый конец не может быть раньше планового начала. Поэтому при добавлении и обновлении система сама подставляет в начало меньшую из двух дат (начало/конец), чтобы не нарушить это ограничение.
</Warning>

### Комплекты и группы

Строка может быть частью комплекта. Тогда у неё заполнены: сам комплект, элемент шаблона комплекта, которому она соответствует, и ключ экземпляра комплекта — случайная строка, объединяющая все позиции одного собранного экземпляра. Это позволяет собрать в одной аренде несколько экземпляров одного и того же комплекта и не путать их между собой.

### Связь замены (обмен)

Ключевая особенность модели — связь «замена» на саму себя (один-к-одному). Когда одну единицу меняют на другую, создаётся **новая** строка, а у старой строки поле «замена» указывает на эту новую. У активной (действующей) позиции связь замены пустая, у заменённой — заполнена. Обратная сторона связи позволяет от новой строки вернуться к предыдущей. Идентификатор предыдущей позиции дополнительно дублируется в доп. данных новой строки.

Так каждая замена — это не редактирование строки, а новая строка в цепочке, поэтому история замен внутри аренды сохраняется целиком.

## Список, деталь и фильтры

### Адрес списка позиций

Список позиций отдаётся без разбивки на страницы. Он намеренно ничего не возвращает, если в запросе не указан хотя бы один из ориентиров: аренда, клиент или единица инвентаря. Записи сортируются по единице инвентаря и затем по убыванию даты создания. Создание одной позиции доступно тем же адресом (методом добавления).

### Фильтры списка

| Фильтр                           | Как работает                                                                                                          |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| По списку идентификаторов        | Отбор строк по перечню их id.                                                                                         |
| Пересечение по началу / по концу | Работают как пересечение интервалов: «активные на/после момента» и «активные до момента», а не точное совпадение дат. |
| По аренде                        | Строки одной аренды.                                                                                                  |
| По клиенту                       | Совпадение либо по клиенту позиции, либо по клиенту аренды.                                                           |
| По единице                       | Строки по конкретной единице.                                                                                         |
| Выдана / Принята                 | По заполненности фактической выдачи / фактической приёмки.                                                            |
| Состояние позиции                | Не выдана / выдана / принята / просрочена.                                                                            |
| Признак обмена                   | Есть ли у строки связь замены (то есть заменена она или нет).                                                         |
| Активна на дату                  | Строки, чей плановый интервал охватывает указанный день.                                                              |

## Добавление позиций

### Добавление одной позиции

При добавлении одной позиции система проверяет:

* начало и конец аренды обязательны (кроме режима автопродления);
* начало не может быть позже конца;
* эту же единицу нельзя добавить в аренду повторно;
* единица должна быть свободна на выбранный интервал — иначе выдаётся ошибка о занятости.

Статус аренды напрямую через этот метод менять нельзя — для этого есть отдельные действия по аренде. Но при добавлении разрешено передать целевой статус (заявка, бронь, в аренде): в этом случае создаётся соответствующее действие по аренде, а для статуса «в аренде» дополнительно проставляются фактическое начало аренды и фактическая выдача позиции.

<Tip>
  Если аренда ещё не существует, добавление первой позиции может само создать новую аренду. Её стартовый статус зависит от настроек компании: черновик-заявка, бронь или сразу «в аренде»; туда же переносятся настройки штрафов и налога.
</Tip>

После сохранения запускается автоматический пересчёт состава и сумм аренды и переустановка расписания авто-штрафов.

### Массовое добавление

Массовое добавление кладёт пачку позиций одним запросом. Логика:

1. Формы группируются по паре «начало–конец». Внутри одной такой группы дубли по одной и той же единице схлопываются — остаётся последняя форма для этой единицы; формы без привязки к конкретной единице (групповые) сохраняются все.
2. Для каждого временного интервала проверяется расписание занятости: если единица уже занята пересекающейся бронью, возвращается ошибка конфликта времени со списком конфликтующих единиц.
3. Строки создаются пачкой. Начало каждой строки — меньшая из дат «начало/конец».
4. Фактическая выдача проставляется автоматически только если аренда уже «в аренде»/«просрочка» **и** включён режим единой выдачи; иначе позиция считается ещё не выданной.
5. Если аренда в статусе брони/в аренде и включено бронирование, создаётся действие бронирования на добавленные строки.

После обработки перестраивается расписание занятости инвентаря.

<Warning>
  В завершённой или отменённой аренде массовое добавление (как и обновление, удаление, обмен) запрещено — кроме сотрудников с правом изменять завершённые аренды.
</Warning>

### Подбор позиций по группе

Отдельный метод подбирает свободные единицы из группы (или одну конкретную единицу по её id) и возвращает **готовые формы позиций** — сам он ничего в базу не пишет. Результат затем передаётся в массовое добавление.

Что учитывается при подборе:

* доступность на интервал и статус «свободна»;
* фильтр по дополнительным атрибутам инвентаря;
* точка проката: суперпользователю можно ограничить точкой аренды, обычному сотруднику — его точками;
* приоритет выбора (случайный порядок, либо сначала собственный инвентарь, либо сначала субарендный — по настройке);
* подходящий тариф под выбранный период тарификации (для продажных групп берутся только тарифы без периода).

Если свободных единиц меньше запрошенного количества, возвращается ошибка о нехватке инвентаря.

### Подбор позиций по комплекту

Похожий метод собирает экземпляры комплекта. По каждому элементу шаблона комплекта он подбирает нужное число свободных единиц (количество элемента, умноженное на число экземпляров), присваивает всем строкам одного экземпляра общий ключ экземпляра комплекта и возвращает список форм. Для обязательных элементов комплекта нехватка инвентаря приводит к ошибке; для необязательных нехватка не прерывает подбор — берётся столько единиц, сколько доступно. Тарифы берутся из ценовой раскладки комплекта в заданном порядке.

### Смена периода тарификации

Ещё один вспомогательный метод пересчитывает тариф, цену и длительность для набора единиц (в том числе входящих в комплекты) при смене периода тарификации и возвращает обновлённые формы. В базу изменения он не вносит — только готовит данные. Единицы, у которых нет подходящего тарифа под новый период, в результат не попадают.

## Обмен позиции (замена)

### Через отдельный адрес

Обмен заменяет одну единицу на другую внутри аренды. Все параметры необязательны и по умолчанию наследуются от заменяемой строки: новая единица, тариф, цена тарифа, длительность периода и момент обмена (по умолчанию — текущее время).

Механика:

1. Создаётся **новая** позиция — копия прежней, но с новой единицей (или прочими изменёнными параметрами). Её начало — момент обмена (но не позже планового конца прежней строки), конец совпадает с плановым концом прежней, фактические отметки очищаются, в доп. данных сохраняется идентификатор прежней позиции.
2. У прежней строки заполняется связь замены (указывает на новую) и плановый конец приравнивается к моменту обмена.

После обмена перестраивается расписание занятости.

<Warning>
  Нельзя обменять уже заменённую строку — система потребует обменивать текущую (активную) позицию цепочки. Также у заменяемой строки должны быть заданы и начало, и конец, иначе обмен отклоняется.
</Warning>

### Через массовое обновление

Массовое обновление умеет порождать обмены автоматически. Если в форме позиции сменилась единица инвентаря (и строка сейчас активна), система:

1. создаёт новую строку с признаком замены и ссылкой на предыдущую в доп. данных;
2. проставляет у старой строки связь замены;
3. приравнивает плановый конец каждой заменённой строки к началу пришедшей ей на замену.

Если же в форме сменился только тариф, скидка или сроки, а единица осталась прежней, обмен не создаётся — строка просто обновляется на месте.

<Note>
  Для новой строки-замены начало никогда не устанавливается раньше текущего момента (берётся не раньше «сейчас» и не позже конца). У обычных обновляемых строк такого ограничения нет — там начало равно меньшей из дат «начало/конец».
</Note>

## Массовое обновление и удаление

### Массовое обновление

Каждая форма обязательно содержит идентификатор обновляемой строки. Перед изменениями проверяются конфликты расписания (занятость чужими бронями, исключая текущую аренду). Затем порождаются обмены (см. выше), обновляются поля строк пачкой и пересчитываются концы заменённых строк. В завершённой/отменённой аренде операция запрещена без специального права. После — перестройка расписания занятости.

### Массовое удаление

Удаление принимает список идентификаторов строк и делает несколько вещей до фактического удаления:

1. **Чинит цепочки обмена.** Если удаляется строка, на которую ссылалась заменённая позиция, у заменённой восстанавливается корректный плановый конец.
2. **Перераспределяет цену статичных комплектов.** Для комплектов с фиксированной ценой суммарная стоимость должна сохраниться: цена оставшихся строк умножается на коэффициент «старая сумма / новая сумма», а остаток от округления добавляется к последней строке.
3. **Сбрасывает продажу.** У удаляемых продажных единиц обнуляются дата и цена продажи.

Только после этого строки удаляются, и запускается автоматический пересчёт состава и сумм аренды.

<Warning>
  Перераспределение цены комплекта затрагивает только статичные комплекты. Если удалить часть строк такого комплекта, стоимость оставшихся строк изменится, чтобы суммарная цена комплекта осталась прежней.
</Warning>

## Побочные эффекты и пересчёты

Практически любое изменение состава позиций тянет за собой автоматические пересчёты:

* **пересчёт состава и сумм аренды** — обновляет итоговые суммы позиций, а также, при необходимости, статус и сроки самой аренды;
* **перестройка расписания занятости инвентаря** — синхронизирует брони единиц с новыми сроками позиций (после массового добавления/обновления и обмена);
* **переустановка авто-штрафов** — после добавления одной позиции.

Проверка занятости при добавлении/обмене опирается на доступность единицы на интервал; свободной считается только единица, не пересекающаяся по времени с другими бронями и находящаяся в свободном статусе.

## Особенности поведения

* Список позиций намеренно возвращает пустой результат, если в запросе не указан хотя бы один из ориентиров: аренда, клиент или единица инвентаря.
* Каждая замена — это новая строка, связанная один-к-одному с заменённой; идентификатор предыдущей позиции дополнительно хранится в доп. данных новой строки.
* При массовом добавлении формы с одной и той же единицей в пределах одного интервала «начало–конец» схлопываются — сохраняется только последняя; групповые формы без единицы сохраняются все.
* В массовом обновлении обмен создаётся только когда у активной строки реально сменилась единица инвентаря; смена только тарифа, скидки или сроков обмена не порождает.
* У новой строки-замены начало ограничено снизу текущим моментом (не раньше «сейчас» и не позже конца), тогда как у обычных обновляемых строк начало — просто меньшая из дат начало/конец.
* При массовом удалении у удаляемых продажных единиц обнуляются дата и цена продажи.
* Фактическая выдача при массовом добавлении проставляется автоматически только если аренда уже «в аренде»/«просрочка» и включён режим единой выдачи; иначе позиция создаётся невыданной.
* При смене периода тарификации единицы, у которых нет подходящего тарифа под новый период, молча выпадают из результата.
* На уровне базы данных плановый конец не может быть раньше планового начала, поэтому в начало всюду подставляется меньшая из дат начало/конец.
* Одну и ту же единицу нельзя добавить в аренду дважды при добавлении одной позиции, и единица должна быть свободна на интервал.

## Связанные страницы

<CardGroup cols={2}>
  <Card title="Глоссарий: Позиции инвентаря и обмен" icon="book" href="/ru/logic/rent/inventory-lines-glossary">
    Полный перечень полей и терминов строки аренды: тарифы, сроки, комплекты, связь замены.
  </Card>

  <Card title="Аренда: заявки и жизненный цикл" icon="file-signature" href="/ru/logic/rent">
    Обзорная страница модуля аренды: заявки, статусы и все связанные разделы.
  </Card>

  <Card title="Аренда и статусы" icon="diagram-project" href="/ru/logic/rent/lifecycle">
    Ядро сущности аренды, автомат статусов и статус оплаты, создание, редактирование, закрытие и архивация заявок.
  </Card>

  <Card title="Выдача и приёмка" icon="right-left" href="/ru/logic/rent/actions">
    Действия по аренде: бронирование, сборка, выдача, приёмка, отмена и архив, а также отмена действия с восстановлением статуса.
  </Card>

  <Card title="Услуги и доставка" icon="truck" href="/ru/logic/rent/services-delivery">
    Услуги в составе аренды и доставки: выдача, приёмка, подтверждение и отмена с привязкой к действиям аренды.
  </Card>

  <Card title="Пересчёт сумм и цен" icon="calculator" href="/ru/logic/rent/pricing">
    Движок пересчёта итогов аренды: цены позиций и услуг, скидки, налоги, продление периода и суммарные поля заявки.
  </Card>

  <Card title="Депозиты и штрафы" icon="shield-halved" href="/ru/logic/rent/deposits-penalties">
    Привязка депозитов и штрафов к аренде и позициям, а также генерация штрафных задач.
  </Card>

  <Card title="График оплат, паузы и рабочие дни" icon="calendar-days" href="/ru/logic/rent/schedule">
    График платежей, автосписание, паузы аренды и рабочие/выходные дни, влияющие на длительность и суммы.
  </Card>

  <Card title="Уведомления, бонусы и воронка" icon="bell" href="/ru/logic/rent/triggers">
    Реакции на смену статуса аренды: клиентские уведомления, бонусы, реферальные выплаты и продвижение по воронке.
  </Card>
</CardGroup>
