Инструменты пользователя

Инструменты сайта


sharixopen:knowledge:scenarios:service_request_assigned

SERVICE_REQUEST в ASSIGNED: раунд подтверждения и принятие назначения

Назначение: Сценарий процесса. Продукт: ShariX Open.

Для кого: Разработчик · Интегратор · Поддержка · Рецензент и приёмщик · Нейросеть.

Темы: Заказы → Назначение · Заказы → Жизненный цикл · Заказы → Финализация оплаты.

Область: ShariX Open. Обработчик: open_service_request_assigned, тип SERVICE_REQUEST (2), рабочий статус ASSIGNED (212). 31.10.2026 исходники повторно получены из Git. Обе Python-копии совпали с новой канонической генерацией Blockly; проверены 11 исходников генераторов. Сам обработчик и его внешние действия не запускались. Выбранная версия, настройки и доступность подключений конкретной установки проверяются отдельно.

Назначение и границы

Обработчик получает заказ с уже назначенным исполнителем:

  • SERVICE_REQUEST_ASSIGNED_AUTO_ACCEPT включён: пропустить человеческий раунд и вызвать каноническое принятие.
  • Выключен: дождаться исхода раунда текущего provider и применить принятие либо отказ.

Новый кандидат здесь не выбирается. Backend заново проверяет текущего исполнителя.

Составное accept_prepare:

  1. Сохранить ASSIGNED → ACCEPTED.
  2. Попробовать перейти в PRE_START через каноническую подготовку.
  3. Если подготовка заблокирована ожидаемым доменным условием — сохранить принятие и вернуть success с prepared=false.

Платёжное ограничение или конфликт активных заказов не отменяет уже выполненное принятие. Положительный ответ может означать ACCEPTED, а не PRE_START.

BPMN имеют isExecutable=false. Wiki не исполняет обработчик; runtime запускает выбранный проверенный Python из Blockly. Раунды и счётчики находятся в памяти процесса, не в Ticket.json.

Решения внутри текущей стадии (BPMN)

Схема начинается после проверки ASSIGNED и успешного scoped order context. Чужой статус запрашивает очистку Ticket/комнаты; 404 контекста — локальную очистку устаревшего scope, отсутствие/неоднозначность связанного Orders — ошибку API.

Схема BPMN

Загрузка схемы…

Исходник схемы
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_service_assigned_logic" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="assigned_Process_assigned" name="open_service_request_assigned: auto, результат раунда и каноническое действие" isExecutable="false">
    <bpmn:documentation>{"notes": ["Git built-in, BPMN isExecutable=false; код handler при генерации не исполняется.", "winner/no_winner проверяются до и сразу после отправки попытки.", "Счётчик доменных неуспехов accept и счётчик попыток рассылки — разные состояния в памяти.", "SingleConfirmation timeout → no_winner; FirstAccept timeout → timed_out, не одинаковый исход."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_service_request_assigned.py", "repository": "sharix-open-handlers-examples", "sha256": "b0037fc0562e9e574fef1becaa454130ab9441c00d572023df78a69c98344a00"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_service_request_assigned.json", "repository": "sharix-open-handlers-examples", "sha256": "74c3fb45aa8863ab2ee03efab3f553c340693c9cb51b10566df64900e47f1c7b"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_action_messages.py", "repository": "sharix-open-handlers-backend", "sha256": "046c262c0aabb02104fefca4e887324f99df22731e7726063f614aaf8839ca5c"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_order_workflow.py", "repository": "sharix-open-backend", "sha256": "35624ded6e67110437a25dd07caef214a04251eb0136194ae939fa32fef17e84"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/open_api.py", "repository": "sharix-open-backend", "sha256": "cb8fdda304d29c2cc5c0fc938759a13c60022871b7b93d77883d521820021ad3"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_handler_relay.py", "repository": "sharix-open-backend", "sha256": "e49a7876169403970d995af464357e2134af19a398372c55db2a5b40681d731e"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/handler_order_context.py", "repository": "sharix-open-backend", "sha256": "f3c0731e74d30dbe5e59bd53040a06ee108f14f7aad04771e65d741cffeb7179"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_workflow_policy.py", "repository": "sharix-open-backend", "sha256": "3266c6ecab490a0e19c8bbc02041387b86fe4b14068750e545ba9bde8bf51326"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_actions.py", "repository": "sharix-open-backend", "sha256": "27ce243408a46f1b700d94bf78a25c68ef21d51078bb03627174e4a424d82ef0"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_permissions.py", "repository": "sharix-open-backend", "sha256": "9c1636daa80c4da68c3ae5cf347e279127d142eeff91e969559e8bd54ca3d81d"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "urls.py", "repository": "sharix-open-backend", "sha256": "61ffd2f0563291da2dcc9488a5443cb5e2c0395108740a6700034cfd90548158"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_lifecycle.py", "repository": "sharix-open-backend", "sha256": "66dd2c4db9da3aaf90cb080946c1920a4f9953d62d1e6c9e224dd52564c4d661"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_provider_requirements.py", "repository": "sharix-open-backend", "sha256": "392f1f9180a098a4749b5fe5b7948e34530ccdb2befed646e538cdb7a5ddcada"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_rounds.py", "repository": "sharix-open-handlers-backend", "sha256": "e27a9190374c3888930f60e26686c11f69069bbbefd4511d21ad7267301321eb"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "settings_schema.py", "repository": "sharix-open-handlers-backend", "sha256": "8ea8494405c6808834f4be7b171ffb8a969e1dd9b1654d36a8c21fbc9e3599c3"}]}</bpmn:documentation>
    <bpmn:laneSet id="assigned_Lanes_assigned">
      <bpmn:lane id="assigned_Lane_worker" name="Один проход Python; defer завершает проход и освобождает worker">
        <bpmn:flowNodeRef>assigned_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_reason</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_auto</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_outcome</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_attempts</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_send</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_after</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_accept</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_accepted</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_accept_end</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_failures</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_decline</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_declined</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_decline_end</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_defer</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_waiting</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_accept_error</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_decline_error</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>assigned_retry</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="assigned_start" name="Свой статус ASSIGNED и context получен">
      <bpmn:outgoing>assigned_Flow_0</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:serviceTask id="assigned_reason" name="provider_confirmation_round">
      <bpmn:incoming>assigned_Flow_0</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_1</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="assigned_auto" name="ASSIGNED_AUTO_ACCEPT?">
      <bpmn:incoming>assigned_Flow_1</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_2</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_3</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:exclusiveGateway id="assigned_outcome" name="Текущий исход раунда?">
      <bpmn:incoming>assigned_Flow_3</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_4</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_5</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_6</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:exclusiveGateway id="assigned_attempts" name="Попытки раунда исчерпаны?">
      <bpmn:incoming>assigned_Flow_6</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_7</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_8</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sendTask id="assigned_send" name="Создать/отправить попытку; разобрать queued intents">
      <bpmn:incoming>assigned_Flow_8</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_9</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:exclusiveGateway id="assigned_after" name="Исход сразу после отправки?">
      <bpmn:incoming>assigned_Flow_9</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_10</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_11</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_12</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="assigned_accept" name="Narrow action: accept-assigned">
      <bpmn:incoming>assigned_Flow_2</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_4</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_10</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_13</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="assigned_accepted" name="Успех / stale?">
      <bpmn:incoming>assigned_Flow_13</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_14</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_15</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:endEvent id="assigned_accept_end" name="Текущий Ticket исключён">
      <bpmn:incoming>assigned_Flow_14</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:exclusiveGateway id="assigned_failures" name="Доменные неуспехи accept достигли лимита?">
      <bpmn:incoming>assigned_Flow_15</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_16</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_17</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="assigned_decline" name="Narrow action: decline-provider">
      <bpmn:incoming>assigned_Flow_5</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_7</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_11</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_16</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_18</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="assigned_declined" name="Успех / stale?">
      <bpmn:incoming>assigned_Flow_18</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_19</bpmn:outgoing>
      <bpmn:outgoing>assigned_Flow_20</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:endEvent id="assigned_decline_end" name="Текущий Ticket исключён">
      <bpmn:incoming>assigned_Flow_19</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:serviceTask id="assigned_defer" name="Defer по ASSIGNED_NOTIFICATION_INTERVAL_SECONDS">
      <bpmn:incoming>assigned_Flow_12</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_17</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_20</bpmn:incoming>
      <bpmn:outgoing>assigned_Flow_21</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="assigned_waiting" name="Worker свободен; следующий проход позже/по событию">
      <bpmn:incoming>assigned_Flow_21</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:boundaryEvent id="assigned_accept_error" name="Ошибка API" attachedToRef="assigned_accept" cancelActivity="true">
      <bpmn:outgoing>assigned_Flow_22</bpmn:outgoing>
      <bpmn:errorEventDefinition />
    </bpmn:boundaryEvent>
    <bpmn:boundaryEvent id="assigned_decline_error" name="Ошибка API" attachedToRef="assigned_decline" cancelActivity="true">
      <bpmn:outgoing>assigned_Flow_23</bpmn:outgoing>
      <bpmn:errorEventDefinition />
    </bpmn:boundaryEvent>
    <bpmn:serviceTask id="assigned_retry" name="processing_error + общий defer">
      <bpmn:incoming>assigned_Flow_22</bpmn:incoming>
      <bpmn:incoming>assigned_Flow_23</bpmn:incoming>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="assigned_Flow_0" sourceRef="assigned_start" targetRef="assigned_reason" name="" />
    <bpmn:sequenceFlow id="assigned_Flow_1" sourceRef="assigned_reason" targetRef="assigned_auto" name="" />
    <bpmn:sequenceFlow id="assigned_Flow_2" sourceRef="assigned_auto" targetRef="assigned_accept" name="Да" />
    <bpmn:sequenceFlow id="assigned_Flow_3" sourceRef="assigned_auto" targetRef="assigned_outcome" name="Нет" />
    <bpmn:sequenceFlow id="assigned_Flow_4" sourceRef="assigned_outcome" targetRef="assigned_accept" name="winner" />
    <bpmn:sequenceFlow id="assigned_Flow_5" sourceRef="assigned_outcome" targetRef="assigned_decline" name="no_winner" />
    <bpmn:sequenceFlow id="assigned_Flow_6" sourceRef="assigned_outcome" targetRef="assigned_attempts" name="Иное / нет раунда" />
    <bpmn:sequenceFlow id="assigned_Flow_7" sourceRef="assigned_attempts" targetRef="assigned_decline" name="Да" />
    <bpmn:sequenceFlow id="assigned_Flow_8" sourceRef="assigned_attempts" targetRef="assigned_send" name="Нет" />
    <bpmn:sequenceFlow id="assigned_Flow_9" sourceRef="assigned_send" targetRef="assigned_after" name="" />
    <bpmn:sequenceFlow id="assigned_Flow_10" sourceRef="assigned_after" targetRef="assigned_accept" name="winner" />
    <bpmn:sequenceFlow id="assigned_Flow_11" sourceRef="assigned_after" targetRef="assigned_decline" name="no_winner" />
    <bpmn:sequenceFlow id="assigned_Flow_12" sourceRef="assigned_after" targetRef="assigned_defer" name="Иное" />
    <bpmn:sequenceFlow id="assigned_Flow_13" sourceRef="assigned_accept" targetRef="assigned_accepted" name="" />
    <bpmn:sequenceFlow id="assigned_Flow_14" sourceRef="assigned_accepted" targetRef="assigned_accept_end" name="Да" />
    <bpmn:sequenceFlow id="assigned_Flow_15" sourceRef="assigned_accepted" targetRef="assigned_failures" name="Нет" />
    <bpmn:sequenceFlow id="assigned_Flow_16" sourceRef="assigned_failures" targetRef="assigned_decline" name="Да" />
    <bpmn:sequenceFlow id="assigned_Flow_17" sourceRef="assigned_failures" targetRef="assigned_defer" name="Нет" />
    <bpmn:sequenceFlow id="assigned_Flow_18" sourceRef="assigned_decline" targetRef="assigned_declined" name="" />
    <bpmn:sequenceFlow id="assigned_Flow_19" sourceRef="assigned_declined" targetRef="assigned_decline_end" name="Да" />
    <bpmn:sequenceFlow id="assigned_Flow_20" sourceRef="assigned_declined" targetRef="assigned_defer" name="Нет" />
    <bpmn:sequenceFlow id="assigned_Flow_21" sourceRef="assigned_defer" targetRef="assigned_waiting" name="" />
    <bpmn:sequenceFlow id="assigned_Flow_22" sourceRef="assigned_accept_error" targetRef="assigned_retry" name="" />
    <bpmn:sequenceFlow id="assigned_Flow_23" sourceRef="assigned_decline_error" targetRef="assigned_retry" name="" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_service_assigned_logic">
    <bpmn:participant id="assigned_Pool_assigned" name="open_service_request_assigned: auto, результат раунда и каноническое действие" processRef="assigned_Process_assigned" />
  </bpmn:collaboration>
  <bpmndi:BPMNDiagram id="Diagram_service_assigned_logic">
    <bpmndi:BPMNPlane id="Plane_service_assigned_logic" bpmnElement="Collaboration_service_assigned_logic">
      <bpmndi:BPMNShape id="assigned_DI_Pool_assigned" bpmnElement="assigned_Pool_assigned" isHorizontal="true">
        <dc:Bounds x="0.0" y="0.0" width="2470" height="870" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_Lane_worker" bpmnElement="assigned_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="0.0" width="2440" height="870" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_start" bpmnElement="assigned_start">
        <dc:Bounds x="132.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_reason" bpmnElement="assigned_reason">
        <dc:Bounds x="315.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_auto" bpmnElement="assigned_auto">
        <dc:Bounds x="625.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_outcome" bpmnElement="assigned_outcome">
        <dc:Bounds x="625.0" y="295.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_attempts" bpmnElement="assigned_attempts">
        <dc:Bounds x="905.0" y="295.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_send" bpmnElement="assigned_send">
        <dc:Bounds x="1135.0" y="285.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_after" bpmnElement="assigned_after">
        <dc:Bounds x="1465.0" y="295.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_accept" bpmnElement="assigned_accept">
        <dc:Bounds x="1695.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_accepted" bpmnElement="assigned_accepted">
        <dc:Bounds x="2015.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_accept_end" bpmnElement="assigned_accept_end">
        <dc:Bounds x="2292.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_failures" bpmnElement="assigned_failures">
        <dc:Bounds x="2015.0" y="295.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_decline" bpmnElement="assigned_decline">
        <dc:Bounds x="1695.0" y="505.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_declined" bpmnElement="assigned_declined">
        <dc:Bounds x="2015.0" y="515.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_decline_end" bpmnElement="assigned_decline_end">
        <dc:Bounds x="2292.0" y="522.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_defer" bpmnElement="assigned_defer">
        <dc:Bounds x="1415.0" y="735.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_waiting" bpmnElement="assigned_waiting">
        <dc:Bounds x="1752.0" y="752.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_accept_error" bpmnElement="assigned_accept_error">
        <dc:Bounds x="1802.0" y="117.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_decline_error" bpmnElement="assigned_decline_error">
        <dc:Bounds x="1802.0" y="557.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="assigned_DI_retry" bpmnElement="assigned_retry">
        <dc:Bounds x="2235.0" y="735.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_0" bpmnElement="assigned_Flow_0">
        <di:waypoint x="168.0" y="100.0" />
        <di:waypoint x="315.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_1" bpmnElement="assigned_Flow_1">
        <di:waypoint x="465.0" y="100.0" />
        <di:waypoint x="625.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_2" bpmnElement="assigned_Flow_2">
        <di:waypoint x="675.0" y="100.0" />
        <di:waypoint x="1695.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_3" bpmnElement="assigned_Flow_3">
        <di:waypoint x="650.0" y="125.0" />
        <di:waypoint x="650.0" y="295.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_4" bpmnElement="assigned_Flow_4">
        <di:waypoint x="675.0" y="320.0" />
        <di:waypoint x="770.0" y="320.0" />
        <di:waypoint x="840.0" y="320.0" />
        <di:waypoint x="840.0" y="30.0" />
        <di:waypoint x="1770.0" y="30.0" />
        <di:waypoint x="1770.0" y="65.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_5" bpmnElement="assigned_Flow_5">
        <di:waypoint x="675.0" y="320.0" />
        <di:waypoint x="770.0" y="320.0" />
        <di:waypoint x="770.0" y="470.0" />
        <di:waypoint x="1770.0" y="470.0" />
        <di:waypoint x="1770.0" y="505.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_6" bpmnElement="assigned_Flow_6">
        <di:waypoint x="675.0" y="320.0" />
        <di:waypoint x="905.0" y="320.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_7" bpmnElement="assigned_Flow_7">
        <di:waypoint x="955.0" y="320.0" />
        <di:waypoint x="1050.0" y="320.0" />
        <di:waypoint x="1050.0" y="470.0" />
        <di:waypoint x="1770.0" y="470.0" />
        <di:waypoint x="1770.0" y="505.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_8" bpmnElement="assigned_Flow_8">
        <di:waypoint x="955.0" y="320.0" />
        <di:waypoint x="1135.0" y="320.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_9" bpmnElement="assigned_Flow_9">
        <di:waypoint x="1285.0" y="320.0" />
        <di:waypoint x="1465.0" y="320.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_10" bpmnElement="assigned_Flow_10">
        <di:waypoint x="1515.0" y="320.0" />
        <di:waypoint x="1610.0" y="320.0" />
        <di:waypoint x="1680.0" y="320.0" />
        <di:waypoint x="1680.0" y="30.0" />
        <di:waypoint x="1770.0" y="30.0" />
        <di:waypoint x="1770.0" y="65.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_11" bpmnElement="assigned_Flow_11">
        <di:waypoint x="1515.0" y="320.0" />
        <di:waypoint x="1610.0" y="320.0" />
        <di:waypoint x="1610.0" y="470.0" />
        <di:waypoint x="1770.0" y="470.0" />
        <di:waypoint x="1770.0" y="505.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_12" bpmnElement="assigned_Flow_12">
        <di:waypoint x="1490.0" y="345.0" />
        <di:waypoint x="1490.0" y="735.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_13" bpmnElement="assigned_Flow_13">
        <di:waypoint x="1845.0" y="100.0" />
        <di:waypoint x="2015.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_14" bpmnElement="assigned_Flow_14">
        <di:waypoint x="2065.0" y="100.0" />
        <di:waypoint x="2292.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_15" bpmnElement="assigned_Flow_15">
        <di:waypoint x="2040.0" y="125.0" />
        <di:waypoint x="2040.0" y="295.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_16" bpmnElement="assigned_Flow_16">
        <di:waypoint x="2065.0" y="320.0" />
        <di:waypoint x="2160.0" y="320.0" />
        <di:waypoint x="2200.0" y="320.0" />
        <di:waypoint x="2200.0" y="470.0" />
        <di:waypoint x="1770.0" y="470.0" />
        <di:waypoint x="1770.0" y="505.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_17" bpmnElement="assigned_Flow_17">
        <di:waypoint x="2065.0" y="320.0" />
        <di:waypoint x="2160.0" y="320.0" />
        <di:waypoint x="2160.0" y="700.0" />
        <di:waypoint x="1490.0" y="700.0" />
        <di:waypoint x="1490.0" y="735.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_18" bpmnElement="assigned_Flow_18">
        <di:waypoint x="1845.0" y="540.0" />
        <di:waypoint x="2015.0" y="540.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_19" bpmnElement="assigned_Flow_19">
        <di:waypoint x="2065.0" y="540.0" />
        <di:waypoint x="2292.0" y="540.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_20" bpmnElement="assigned_Flow_20">
        <di:waypoint x="2065.0" y="540.0" />
        <di:waypoint x="2160.0" y="540.0" />
        <di:waypoint x="2160.0" y="700.0" />
        <di:waypoint x="1490.0" y="700.0" />
        <di:waypoint x="1490.0" y="735.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_21" bpmnElement="assigned_Flow_21">
        <di:waypoint x="1565.0" y="770.0" />
        <di:waypoint x="1752.0" y="770.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_22" bpmnElement="assigned_Flow_22">
        <di:waypoint x="1838.0" y="135.0" />
        <di:waypoint x="1880.0" y="135.0" />
        <di:waypoint x="1880.0" y="690.0" />
        <di:waypoint x="2310.0" y="690.0" />
        <di:waypoint x="2310.0" y="735.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="assigned_DI_Flow_23" bpmnElement="assigned_Flow_23">
        <di:waypoint x="1838.0" y="575.0" />
        <di:waypoint x="1880.0" y="575.0" />
        <di:waypoint x="2310.0" y="575.0" />
        <di:waypoint x="2310.0" y="735.0" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

Сначала записывается provider_confirmation_round, в том числе перед auto-ветвью: сама эта причина не доказывает отправки человеку. В auto-режиме сразу вызывается accept-assigned.

Ручной режим в одном проходе:

  1. Проверить существующий winner/no_winner.
  2. Если результата нет — проверить исчерпание попыток.
  3. Отправить попытку и разобрать web intents, пришедшие до создания раунда.
  4. Снова проверить winner/no_winner в том же проходе.
  5. При ином исходе назначить SERVICE_REQUEST_ASSIGNED_NOTIFICATION_INTERVAL_SECONDS.
  • winner → попытка принятия назначения.
  • no_winner либо предусмотренное исчерпание попыток → decline-provider.
  • Неуспешный доменный accept отдельно увеличивает счётчик неуспехов действия. При достижении порога выполняется попытка decline; до порога — повтор.
  • Успех/stale канонического действия исключает Ticket из текущей очереди. Обработанный отрицательный decline остаётся на обычном повторе; исключение API идёт в общий processing_error.

Учитываются два разных счётчика:

  • Send-попытки: сколько приглашений пытались отправить.
  • Доменные неуспехи accept: сколько обработанных ответов действия не привели к успеху; успех/stale сбрасывает этот счётчик.

Для порога действия используется SERVICE_REQUEST_ASSIGNED_NOTIFICATION_ATTEMPTS. Исключение транспорта или авторизации не является отрицательным ответом исполнителя.

Исполнитель, сообщение, ответ и таймер (BPMN)

Схема BPMN

Загрузка схемы…

Исходник схемы
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_service_assigned_round" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="round_Process_round" name="Handler: один именованный исполнитель, окно ответа и валидатор раунда" isExecutable="false">
    <bpmn:documentation>{"notes": ["Git built-in, BPMN isExecutable=false; код handler при генерации не исполняется.", "winner/no_winner проверяются до и сразу после отправки попытки.", "Счётчик доменных неуспехов accept и счётчик попыток рассылки — разные состояния в памяти.", "SingleConfirmation timeout → no_winner; FirstAccept timeout → timed_out, не одинаковый исход.", "ASSIGNED адресует одного текущего provider по JID из backend context.", "Раунд и ответы — в памяти процесса. Restart не восстанавливает старый round id."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_service_request_assigned.py", "repository": "sharix-open-handlers-examples", "sha256": "b0037fc0562e9e574fef1becaa454130ab9441c00d572023df78a69c98344a00"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_service_request_assigned.json", "repository": "sharix-open-handlers-examples", "sha256": "74c3fb45aa8863ab2ee03efab3f553c340693c9cb51b10566df64900e47f1c7b"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_action_messages.py", "repository": "sharix-open-handlers-backend", "sha256": "046c262c0aabb02104fefca4e887324f99df22731e7726063f614aaf8839ca5c"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_order_workflow.py", "repository": "sharix-open-backend", "sha256": "35624ded6e67110437a25dd07caef214a04251eb0136194ae939fa32fef17e84"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/open_api.py", "repository": "sharix-open-backend", "sha256": "cb8fdda304d29c2cc5c0fc938759a13c60022871b7b93d77883d521820021ad3"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_handler_relay.py", "repository": "sharix-open-backend", "sha256": "e49a7876169403970d995af464357e2134af19a398372c55db2a5b40681d731e"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/handler_order_context.py", "repository": "sharix-open-backend", "sha256": "f3c0731e74d30dbe5e59bd53040a06ee108f14f7aad04771e65d741cffeb7179"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_workflow_policy.py", "repository": "sharix-open-backend", "sha256": "3266c6ecab490a0e19c8bbc02041387b86fe4b14068750e545ba9bde8bf51326"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_actions.py", "repository": "sharix-open-backend", "sha256": "27ce243408a46f1b700d94bf78a25c68ef21d51078bb03627174e4a424d82ef0"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_permissions.py", "repository": "sharix-open-backend", "sha256": "9c1636daa80c4da68c3ae5cf347e279127d142eeff91e969559e8bd54ca3d81d"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "urls.py", "repository": "sharix-open-backend", "sha256": "61ffd2f0563291da2dcc9488a5443cb5e2c0395108740a6700034cfd90548158"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_lifecycle.py", "repository": "sharix-open-backend", "sha256": "66dd2c4db9da3aaf90cb080946c1920a4f9953d62d1e6c9e224dd52564c4d661"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_provider_requirements.py", "repository": "sharix-open-backend", "sha256": "392f1f9180a098a4749b5fe5b7948e34530ccdb2befed646e538cdb7a5ddcada"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_rounds.py", "repository": "sharix-open-handlers-backend", "sha256": "e27a9190374c3888930f60e26686c11f69069bbbefd4511d21ad7267301321eb"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "settings_schema.py", "repository": "sharix-open-handlers-backend", "sha256": "8ea8494405c6808834f4be7b171ffb8a969e1dd9b1654d36a8c21fbc9e3599c3"}]}</bpmn:documentation>
    <bpmn:laneSet id="round_Lanes_round">
      <bpmn:lane id="round_Lane_worker" name="Подготовка / проверка внешнего ответа">
        <bpmn:flowNodeRef>round_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_create</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_send</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_wait</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_response</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_validate</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_valid</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_decision</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_ready</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_reject</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_timer</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_timeout</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>round_timeout_end</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="round_start" name="Для ручного раунда есть provider JID">
      <bpmn:outgoing>round_Flow_0</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:serviceTask id="round_create" name="Получить provider JID, выбрать strategy/delivery/лимиты">
      <bpmn:incoming>round_Flow_0</bpmn:incoming>
      <bpmn:outgoing>round_Flow_1</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sendTask id="round_send" name="Попытка order.action.required: accept / decline">
      <bpmn:incoming>round_Flow_1</bpmn:incoming>
      <bpmn:outgoing>round_Flow_2</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:eventBasedGateway id="round_wait" name="Ответ или истечение окна?">
      <bpmn:incoming>round_Flow_2</bpmn:incoming>
      <bpmn:outgoing>round_Flow_3</bpmn:outgoing>
      <bpmn:outgoing>round_Flow_9</bpmn:outgoing>
    </bpmn:eventBasedGateway>
    <bpmn:intermediateCatchEvent id="round_response" name="Получен прямой XMPP ответ либо доверенный web intent">
      <bpmn:incoming>round_Flow_3</bpmn:incoming>
      <bpmn:outgoing>round_Flow_4</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:intermediateCatchEvent>
    <bpmn:serviceTask id="round_validate" name="Проверить round/order/status/JID/action/offer и повтор">
      <bpmn:incoming>round_Flow_4</bpmn:incoming>
      <bpmn:outgoing>round_Flow_5</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="round_valid" name="Ответ учтён?">
      <bpmn:incoming>round_Flow_5</bpmn:incoming>
      <bpmn:outgoing>round_Flow_6</bpmn:outgoing>
      <bpmn:outgoing>round_Flow_8</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="round_decision" name="Сохранить решение стратегии">
      <bpmn:incoming>round_Flow_6</bpmn:incoming>
      <bpmn:outgoing>round_Flow_7</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="round_ready" name="Сделать Ticket ready для бизнес-действия">
      <bpmn:incoming>round_Flow_7</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:endEvent id="round_reject" name="Отклонить ответ, не менять заказ">
      <bpmn:incoming>round_Flow_8</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:intermediateCatchEvent id="round_timer" name="Наступил round.expires_at">
      <bpmn:incoming>round_Flow_9</bpmn:incoming>
      <bpmn:outgoing>round_Flow_10</bpmn:outgoing>
      <bpmn:timerEventDefinition>
        <bpmn:timeDate xsi:type="bpmn:tFormalExpression">round_expires_at</bpmn:timeDate>
      </bpmn:timerEventDefinition>
    </bpmn:intermediateCatchEvent>
    <bpmn:serviceTask id="round_timeout" name="Вычислить on_timeout выбранной strategy">
      <bpmn:incoming>round_Flow_10</bpmn:incoming>
      <bpmn:outgoing>round_Flow_11</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="round_timeout_end" name="Новый проход учитывает этот исход">
      <bpmn:incoming>round_Flow_11</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="round_Flow_0" sourceRef="round_start" targetRef="round_create" name="" />
    <bpmn:sequenceFlow id="round_Flow_1" sourceRef="round_create" targetRef="round_send" name="" />
    <bpmn:sequenceFlow id="round_Flow_2" sourceRef="round_send" targetRef="round_wait" name="" />
    <bpmn:sequenceFlow id="round_Flow_3" sourceRef="round_wait" targetRef="round_response" name="" />
    <bpmn:sequenceFlow id="round_Flow_4" sourceRef="round_response" targetRef="round_validate" name="" />
    <bpmn:sequenceFlow id="round_Flow_5" sourceRef="round_validate" targetRef="round_valid" name="" />
    <bpmn:sequenceFlow id="round_Flow_6" sourceRef="round_valid" targetRef="round_decision" name="Да" />
    <bpmn:sequenceFlow id="round_Flow_7" sourceRef="round_decision" targetRef="round_ready" name="" />
    <bpmn:sequenceFlow id="round_Flow_8" sourceRef="round_valid" targetRef="round_reject" name="Нет" />
    <bpmn:sequenceFlow id="round_Flow_9" sourceRef="round_wait" targetRef="round_timer" name="" />
    <bpmn:sequenceFlow id="round_Flow_10" sourceRef="round_timer" targetRef="round_timeout" name="" />
    <bpmn:sequenceFlow id="round_Flow_11" sourceRef="round_timeout" targetRef="round_timeout_end" name="" />
  </bpmn:process>
  <bpmn:process id="provider_Process_provider" name="Назначенный исполнитель: ответ на актуальное предложение" isExecutable="false">
    <bpmn:documentation>{"notes": ["Git built-in, BPMN isExecutable=false; код handler при генерации не исполняется.", "winner/no_winner проверяются до и сразу после отправки попытки.", "Счётчик доменных неуспехов accept и счётчик попыток рассылки — разные состояния в памяти.", "SingleConfirmation timeout → no_winner; FirstAccept timeout → timed_out, не одинаковый исход.", "ASSIGNED адресует одного текущего provider по JID из backend context.", "Раунд и ответы — в памяти процесса. Restart не восстанавливает старый round id."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_service_request_assigned.py", "repository": "sharix-open-handlers-examples", "sha256": "b0037fc0562e9e574fef1becaa454130ab9441c00d572023df78a69c98344a00"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_service_request_assigned.json", "repository": "sharix-open-handlers-examples", "sha256": "74c3fb45aa8863ab2ee03efab3f553c340693c9cb51b10566df64900e47f1c7b"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_action_messages.py", "repository": "sharix-open-handlers-backend", "sha256": "046c262c0aabb02104fefca4e887324f99df22731e7726063f614aaf8839ca5c"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_order_workflow.py", "repository": "sharix-open-backend", "sha256": "35624ded6e67110437a25dd07caef214a04251eb0136194ae939fa32fef17e84"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/open_api.py", "repository": "sharix-open-backend", "sha256": "cb8fdda304d29c2cc5c0fc938759a13c60022871b7b93d77883d521820021ad3"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_handler_relay.py", "repository": "sharix-open-backend", "sha256": "e49a7876169403970d995af464357e2134af19a398372c55db2a5b40681d731e"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/handler_order_context.py", "repository": "sharix-open-backend", "sha256": "f3c0731e74d30dbe5e59bd53040a06ee108f14f7aad04771e65d741cffeb7179"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_workflow_policy.py", "repository": "sharix-open-backend", "sha256": "3266c6ecab490a0e19c8bbc02041387b86fe4b14068750e545ba9bde8bf51326"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_actions.py", "repository": "sharix-open-backend", "sha256": "27ce243408a46f1b700d94bf78a25c68ef21d51078bb03627174e4a424d82ef0"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_permissions.py", "repository": "sharix-open-backend", "sha256": "9c1636daa80c4da68c3ae5cf347e279127d142eeff91e969559e8bd54ca3d81d"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "urls.py", "repository": "sharix-open-backend", "sha256": "61ffd2f0563291da2dcc9488a5443cb5e2c0395108740a6700034cfd90548158"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_lifecycle.py", "repository": "sharix-open-backend", "sha256": "66dd2c4db9da3aaf90cb080946c1920a4f9953d62d1e6c9e224dd52564c4d661"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_provider_requirements.py", "repository": "sharix-open-backend", "sha256": "392f1f9180a098a4749b5fe5b7948e34530ccdb2befed646e538cdb7a5ddcada"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_rounds.py", "repository": "sharix-open-handlers-backend", "sha256": "e27a9190374c3888930f60e26686c11f69069bbbefd4511d21ad7267301321eb"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "settings_schema.py", "repository": "sharix-open-handlers-backend", "sha256": "8ea8494405c6808834f4be7b171ffb8a969e1dd9b1654d36a8c21fbc9e3599c3"}]}</bpmn:documentation>
    <bpmn:laneSet id="provider_Lanes_provider">
      <bpmn:lane id="provider_Lane_person" name="Действие человека">
        <bpmn:flowNodeRef>provider_notice</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>provider_decision</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>provider_reply</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>provider_end</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="provider_notice" name="Получено актуальное предложение">
      <bpmn:outgoing>provider_Flow_0</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:startEvent>
    <bpmn:userTask id="provider_decision" name="Подтвердить или отказаться">
      <bpmn:incoming>provider_Flow_0</bpmn:incoming>
      <bpmn:outgoing>provider_Flow_1</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sendTask id="provider_reply" name="Ответ с round id / action">
      <bpmn:incoming>provider_Flow_1</bpmn:incoming>
      <bpmn:outgoing>provider_Flow_2</bpmn:outgoing>
    </bpmn:sendTask>
    <bpmn:endEvent id="provider_end" name="Ответ отправлен, бизнес-результат ещё проверяется">
      <bpmn:incoming>provider_Flow_2</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="provider_Flow_0" sourceRef="provider_notice" targetRef="provider_decision" name="" />
    <bpmn:sequenceFlow id="provider_Flow_1" sourceRef="provider_decision" targetRef="provider_reply" name="" />
    <bpmn:sequenceFlow id="provider_Flow_2" sourceRef="provider_reply" targetRef="provider_end" name="" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_service_assigned_round">
    <bpmn:participant id="round_Pool_round" name="Handler: один именованный исполнитель, окно ответа и валидатор раунда" processRef="round_Process_round" />
    <bpmn:participant id="provider_Pool_provider" name="Назначенный исполнитель: ответ на актуальное предложение" processRef="provider_Process_provider" />
    <bpmn:messageFlow id="Message_service_assigned_round_0" sourceRef="round_send" targetRef="provider_notice" name="XMPP send не доказывает получение приложением" />
    <bpmn:messageFlow id="Message_service_assigned_round_1" sourceRef="provider_reply" targetRef="round_response" name="Прямой ответ либо web relay с actor identity" />
  </bpmn:collaboration>
  <bpmndi:BPMNDiagram id="Diagram_service_assigned_round">
    <bpmndi:BPMNPlane id="Plane_service_assigned_round" bpmnElement="Collaboration_service_assigned_round">
      <bpmndi:BPMNShape id="round_DI_Pool_round" bpmnElement="round_Pool_round" isHorizontal="true">
        <dc:Bounds x="0.0" y="0.0" width="2600" height="580" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_Lane_worker" bpmnElement="round_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="0.0" width="2570" height="580" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_start" bpmnElement="round_start">
        <dc:Bounds x="132.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_create" bpmnElement="round_create">
        <dc:Bounds x="355.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_send" bpmnElement="round_send">
        <dc:Bounds x="655.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_wait" bpmnElement="round_wait">
        <dc:Bounds x="1005.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_response" bpmnElement="round_response">
        <dc:Bounds x="1292.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_validate" bpmnElement="round_validate">
        <dc:Bounds x="1525.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_valid" bpmnElement="round_valid">
        <dc:Bounds x="1855.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_decision" bpmnElement="round_decision">
        <dc:Bounds x="2085.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_ready" bpmnElement="round_ready">
        <dc:Bounds x="2422.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_reject" bpmnElement="round_reject">
        <dc:Bounds x="1862.0" y="292.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_timer" bpmnElement="round_timer">
        <dc:Bounds x="1292.0" y="462.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_timeout" bpmnElement="round_timeout">
        <dc:Bounds x="1525.0" y="445.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="round_DI_timeout_end" bpmnElement="round_timeout_end">
        <dc:Bounds x="1862.0" y="462.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="round_DI_Flow_0" bpmnElement="round_Flow_0">
        <di:waypoint x="168.0" y="100.0" />
        <di:waypoint x="355.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_1" bpmnElement="round_Flow_1">
        <di:waypoint x="505.0" y="100.0" />
        <di:waypoint x="655.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_2" bpmnElement="round_Flow_2">
        <di:waypoint x="805.0" y="100.0" />
        <di:waypoint x="1005.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_3" bpmnElement="round_Flow_3">
        <di:waypoint x="1055.0" y="100.0" />
        <di:waypoint x="1292.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_4" bpmnElement="round_Flow_4">
        <di:waypoint x="1328.0" y="100.0" />
        <di:waypoint x="1525.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_5" bpmnElement="round_Flow_5">
        <di:waypoint x="1675.0" y="100.0" />
        <di:waypoint x="1855.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_6" bpmnElement="round_Flow_6">
        <di:waypoint x="1905.0" y="100.0" />
        <di:waypoint x="2085.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_7" bpmnElement="round_Flow_7">
        <di:waypoint x="2235.0" y="100.0" />
        <di:waypoint x="2422.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_8" bpmnElement="round_Flow_8">
        <di:waypoint x="1880.0" y="125.0" />
        <di:waypoint x="1880.0" y="292.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_9" bpmnElement="round_Flow_9">
        <di:waypoint x="1055.0" y="100.0" />
        <di:waypoint x="1173.5" y="100.0" />
        <di:waypoint x="1173.5" y="480.0" />
        <di:waypoint x="1292.0" y="480.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_10" bpmnElement="round_Flow_10">
        <di:waypoint x="1328.0" y="480.0" />
        <di:waypoint x="1525.0" y="480.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="round_DI_Flow_11" bpmnElement="round_Flow_11">
        <di:waypoint x="1675.0" y="480.0" />
        <di:waypoint x="1862.0" y="480.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="provider_DI_Pool_provider" bpmnElement="provider_Pool_provider" isHorizontal="true">
        <dc:Bounds x="0.0" y="780.0" width="1190" height="200" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="provider_DI_Lane_person" bpmnElement="provider_Lane_person" isHorizontal="true">
        <dc:Bounds x="30.0" y="780.0" width="1160" height="200" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="provider_DI_notice" bpmnElement="provider_notice">
        <dc:Bounds x="132.0" y="862.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="provider_DI_decision" bpmnElement="provider_decision">
        <dc:Bounds x="355.0" y="845.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="provider_DI_reply" bpmnElement="provider_reply">
        <dc:Bounds x="655.0" y="845.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="provider_DI_end" bpmnElement="provider_end">
        <dc:Bounds x="1012.0" y="862.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="provider_DI_Flow_0" bpmnElement="provider_Flow_0">
        <di:waypoint x="168.0" y="880.0" />
        <di:waypoint x="355.0" y="880.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="provider_DI_Flow_1" bpmnElement="provider_Flow_1">
        <di:waypoint x="505.0" y="880.0" />
        <di:waypoint x="655.0" y="880.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="provider_DI_Flow_2" bpmnElement="provider_Flow_2">
        <di:waypoint x="805.0" y="880.0" />
        <di:waypoint x="1012.0" y="880.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_service_assigned_round_0" bpmnElement="Message_service_assigned_round_0">
        <di:waypoint x="730" y="135" />
        <di:waypoint x="730" y="650" />
        <di:waypoint x="150" y="650" />
        <di:waypoint x="150" y="862" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="180" y="615" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="DI_Message_service_assigned_round_1" bpmnElement="Message_service_assigned_round_1">
        <di:waypoint x="730" y="915" />
        <di:waypoint x="2530" y="915" />
        <di:waypoint x="2530" y="30" />
        <di:waypoint x="1310" y="30" />
        <di:waypoint x="1310" y="82" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="2000" y="700" width="260" height="42" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

Для ASSIGNED получатель — один текущий provider из backend context при наличии JID. Выбираются SERVICE_REQUEST_ASSIGNED_ROUND_STRATEGY и SERVICE_REQUEST_ASSIGNED_DELIVERY_POLICY, число попыток, интервал и окно ответа. Пустой список адресатов не создаёт успешного раунда: send возвращает no_recipients, и handler продолжает ожидание. Одно наличие provider id не доказывает доступность XMPP.

Окно раунда рассчитывается от его создания: (attempts−1) × notification_interval + response_window. Истечение оценивается runtime при работе с раундом; это граница допустимости ответа, а не гарантия бизнес-действия ровно в эту секунду. Один факт отправки последней попытки до конца окна не равен немедленному отказу.

Стратегия определяет исход тайм-аута

У встроенных стратегий различается тайм-аут:

  • single_confirmation: один получатель; accept → winner, decline → no_winner, timeout → no_winner.
  • first_accept: положительный ответ → winner, все отрицательные → no_winner, timeout → timed_out.

Timed_out не равен no_winner в этом Python. Закрытый раунд не считается активным при проверке исчерпания, а следующий send может создать новый раунд.

timed_out не приравнивается этим Python к no_winner. Закрытый раунд не считается активным при проверке исчерпания попыток; при следующем send может создаться новый раунд. Поэтому нельзя обещать одинаковое автоматическое отклонение при любом выборе strategy. Фактическую настройку нужно сверять отдельно; схема сохраняет ветку «иной исход».

Доставка и проверка ответа

Сообщение order.action.required содержит round/order/ticket/stage, accept/decline, номер попытки, сроки и popup_policy=always. Это параметры сообщения, а не доказательство, что приложение открыло окно или человек прочитал его. Попытка регистрируется до отправки; неуспешная доставка также может расходовать лимит. Ошибки отдельных XMPP-send логируются, но сами по себе не становятся ответом decline от человека.

Прямой order.action.response связывает actor с JID отправителя. Web intent допускается только через доверенный relay и сохраняет actor. Валидатор сопоставляет round/order/ticket/status, участника, action, offer id при наличии, срок и повтор ответа. Неправильный/поздний/повторный ответ не является новым согласием. Прямой клиент может получить order.action.response-result о приёме ответа, но это ещё не подтверждение выполнения канонического бизнес-действия.

Учет ответа делает Ticket готовым к новому проходу; backend позже заново проверяет текущую привязку provider. После рестарта старые round id и счетчики не восстанавливаются из Ticket.json, а запоздалое согласие на старый раунд не следует считать согласием на новый.

Backend: принятие/подготовка и отказ (BPMN)

Схема BPMN

Загрузка схемы…

Исходник схемы
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_service_assigned_actions" targetNamespace="urn:sharix:knowledge:handlers">
  <bpmn:process id="actions_Process_actions" name="Backend: expected provider, принятие/подготовка и отказ" isExecutable="false">
    <bpmn:documentation>{"notes": ["Git built-in, BPMN isExecutable=false; код handler при генерации не исполняется.", "winner/no_winner проверяются до и сразу после отправки попытки.", "Счётчик доменных неуспехов accept и счётчик попыток рассылки — разные состояния в памяти.", "SingleConfirmation timeout → no_winner; FirstAccept timeout → timed_out, не одинаковый исход.", "Показаны канонические действия, которые использует ASSIGNED handler.", "Ожидаемый доменный отказ PRE_START после принятия возвращает success с prepared=false и сохраняет ACCEPTED."], "sources": [{"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "handlers/open_service_request_assigned.py", "repository": "sharix-open-handlers-examples", "sha256": "b0037fc0562e9e574fef1becaa454130ab9441c00d572023df78a69c98344a00"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "generated_handler_blocks/open_service_request_assigned.json", "repository": "sharix-open-handlers-examples", "sha256": "74c3fb45aa8863ab2ee03efab3f553c340693c9cb51b10566df64900e47f1c7b"}, {"commit": "76e0c693b628ed4a1fa34515a3f8abdff26149bb", "path": "catalog.py", "repository": "sharix-open-handlers-examples", "sha256": "e3dcb72b7e620e2837adacd7af053f35b04cdff475789496d9c114e17d57dfde"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/blockly_runtime.py", "repository": "sharix-open-handlers-backend", "sha256": "3e222150cbff315077c6c9b160d46e59856cedf6d6591d213e958c63cb2f2428"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/requesters.py", "repository": "sharix-open-handlers-backend", "sha256": "965a348cea477722bbaf4c8c6b06023eb238b71664a332f7ea8c90bfe72ea808"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_action_messages.py", "repository": "sharix-open-handlers-backend", "sha256": "046c262c0aabb02104fefca4e887324f99df22731e7726063f614aaf8839ca5c"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_blockly_codegen.py", "repository": "sharix-open-handlers-backend", "sha256": "bce71eab622922f8dd0954222842349799b60e18a0ab6310cc3e5f305603661e"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "ticket_codegen_contract.json", "repository": "sharix-open-handlers-backend", "sha256": "0bfd2b0ebb07bd380ed3c867386536ab88ef7dfa6e13f1cd8c91d32bb02c1ae9"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "lib/tickets.py", "repository": "sharix-open-tickets", "sha256": "0d260c17947f9a186d07af2749159780e3c7354a95b4bafe9d67493663c96883"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "api/handler_auth.py", "repository": "sharix-open-tickets", "sha256": "ac76dc45aaa9742e5e1da0b98aae914bb2e287048d33e97b4a245a6997bc8425"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "signals.py", "repository": "sharix-open-tickets", "sha256": "39aad8690a2e7d35cebfb052127a8130a951cbb4eea316c4f767a0a59fc5fa7e"}, {"commit": "fd64704d7d9f01a5ec5b7b89cbc5afe5102b2c14", "path": "services/handler_wakeup.py", "repository": "sharix-open-tickets", "sha256": "c9fdc62a18301fd7c075395c697d5b1c738c77713aa71fcbb5ab097e173d98de"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/handler_order_workflow.py", "repository": "sharix-open-backend", "sha256": "35624ded6e67110437a25dd07caef214a04251eb0136194ae939fa32fef17e84"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "apiviews/open_api.py", "repository": "sharix-open-backend", "sha256": "cb8fdda304d29c2cc5c0fc938759a13c60022871b7b93d77883d521820021ad3"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_handler_relay.py", "repository": "sharix-open-backend", "sha256": "e49a7876169403970d995af464357e2134af19a398372c55db2a5b40681d731e"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/handler_order_context.py", "repository": "sharix-open-backend", "sha256": "f3c0731e74d30dbe5e59bd53040a06ee108f14f7aad04771e65d741cffeb7179"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_workflow_policy.py", "repository": "sharix-open-backend", "sha256": "3266c6ecab490a0e19c8bbc02041387b86fe4b14068750e545ba9bde8bf51326"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_actions.py", "repository": "sharix-open-backend", "sha256": "27ce243408a46f1b700d94bf78a25c68ef21d51078bb03627174e4a424d82ef0"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_permissions.py", "repository": "sharix-open-backend", "sha256": "9c1636daa80c4da68c3ae5cf347e279127d142eeff91e969559e8bd54ca3d81d"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "urls.py", "repository": "sharix-open-backend", "sha256": "61ffd2f0563291da2dcc9488a5443cb5e2c0395108740a6700034cfd90548158"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_lifecycle.py", "repository": "sharix-open-backend", "sha256": "66dd2c4db9da3aaf90cb080946c1920a4f9953d62d1e6c9e224dd52564c4d661"}, {"commit": "778a3280cee115ba0efe3fdff7b0e708b0c10093", "path": "lib/order_provider_requirements.py", "repository": "sharix-open-backend", "sha256": "392f1f9180a098a4749b5fe5b7948e34530ccdb2befed646e538cdb7a5ddcada"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "core/order_rounds.py", "repository": "sharix-open-handlers-backend", "sha256": "e27a9190374c3888930f60e26686c11f69069bbbefd4511d21ad7267301321eb"}, {"commit": "50baf376e224d915a87d63240f3f339b3dda9afa", "path": "settings_schema.py", "repository": "sharix-open-handlers-backend", "sha256": "8ea8494405c6808834f4be7b171ffb8a969e1dd9b1654d36a8c21fbc9e3599c3"}]}</bpmn:documentation>
    <bpmn:laneSet id="actions_Lanes_actions">
      <bpmn:lane id="actions_Lane_worker" name="accept-assigned → accept_prepare">
        <bpmn:flowNodeRef>actions_start</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_check</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_accept</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_prepare</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_ok</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_prepared</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_partial</bpmn:flowNodeRef>
      </bpmn:lane>
      <bpmn:lane id="actions_Lane_decline" name="decline-provider">
        <bpmn:flowNodeRef>actions_decline</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_decline_check</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_save</bpmn:flowNodeRef>
        <bpmn:flowNodeRef>actions_end</bpmn:flowNodeRef>
      </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:startEvent id="actions_start" name="Scoped accept-assigned">
      <bpmn:outgoing>actions_Flow_0</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:startEvent>
    <bpmn:serviceTask id="actions_check" name="Capability/scope/lock; expected provider и eligibility">
      <bpmn:incoming>actions_Flow_0</bpmn:incoming>
      <bpmn:outgoing>actions_Flow_1</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="actions_accept" name="Сохранить переход ASSIGNED → ACCEPTED">
      <bpmn:incoming>actions_Flow_1</bpmn:incoming>
      <bpmn:outgoing>actions_Flow_2</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="actions_prepare" name="Попытаться PRE_START: concurrency и платёжный hook">
      <bpmn:incoming>actions_Flow_2</bpmn:incoming>
      <bpmn:outgoing>actions_Flow_3</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:exclusiveGateway id="actions_ok" name="Подготовка разрешена?">
      <bpmn:incoming>actions_Flow_3</bpmn:incoming>
      <bpmn:outgoing>actions_Flow_4</bpmn:outgoing>
      <bpmn:outgoing>actions_Flow_5</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:endEvent id="actions_prepared" name="Успех: PRE_START, provider offline/prediction hooks">
      <bpmn:incoming>actions_Flow_4</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:endEvent id="actions_partial" name="Успех принятия: ACCEPTED, prepared=false">
      <bpmn:incoming>actions_Flow_5</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:startEvent id="actions_decline" name="Scoped decline-provider">
      <bpmn:outgoing>actions_Flow_6</bpmn:outgoing>
      <bpmn:messageEventDefinition />
    </bpmn:startEvent>
    <bpmn:serviceTask id="actions_decline_check" name="Capability/scope/lock; expected provider">
      <bpmn:incoming>actions_Flow_6</bpmn:incoming>
      <bpmn:outgoing>actions_Flow_7</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:serviceTask id="actions_save" name="Сохранить DECLINED, очистить provider">
      <bpmn:incoming>actions_Flow_7</bpmn:incoming>
      <bpmn:outgoing>actions_Flow_8</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="actions_end" name="Успех: прежний handler освобождается">
      <bpmn:incoming>actions_Flow_8</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="actions_Flow_0" sourceRef="actions_start" targetRef="actions_check" name="" />
    <bpmn:sequenceFlow id="actions_Flow_1" sourceRef="actions_check" targetRef="actions_accept" name="" />
    <bpmn:sequenceFlow id="actions_Flow_2" sourceRef="actions_accept" targetRef="actions_prepare" name="" />
    <bpmn:sequenceFlow id="actions_Flow_3" sourceRef="actions_prepare" targetRef="actions_ok" name="" />
    <bpmn:sequenceFlow id="actions_Flow_4" sourceRef="actions_ok" targetRef="actions_prepared" name="Да" />
    <bpmn:sequenceFlow id="actions_Flow_5" sourceRef="actions_ok" targetRef="actions_partial" name="Нет: доменное условие" />
    <bpmn:sequenceFlow id="actions_Flow_6" sourceRef="actions_decline" targetRef="actions_decline_check" name="" />
    <bpmn:sequenceFlow id="actions_Flow_7" sourceRef="actions_decline_check" targetRef="actions_save" name="" />
    <bpmn:sequenceFlow id="actions_Flow_8" sourceRef="actions_save" targetRef="actions_end" name="" />
  </bpmn:process>
  <bpmn:collaboration id="Collaboration_service_assigned_actions">
    <bpmn:participant id="actions_Pool_actions" name="Backend: expected provider, принятие/подготовка и отказ" processRef="actions_Process_actions" />
  </bpmn:collaboration>
  <bpmndi:BPMNDiagram id="Diagram_service_assigned_actions">
    <bpmndi:BPMNPlane id="Plane_service_assigned_actions" bpmnElement="Collaboration_service_assigned_actions">
      <bpmndi:BPMNShape id="actions_DI_Pool_actions" bpmnElement="actions_Pool_actions" isHorizontal="true">
        <dc:Bounds x="0.0" y="0.0" width="1800" height="670" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_Lane_worker" bpmnElement="actions_Lane_worker" isHorizontal="true">
        <dc:Bounds x="30.0" y="0.0" width="1770" height="440.0" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_Lane_decline" bpmnElement="actions_Lane_decline" isHorizontal="true">
        <dc:Bounds x="30.0" y="440.0" width="1770" height="230.0" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_start" bpmnElement="actions_start">
        <dc:Bounds x="132.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_check" bpmnElement="actions_check">
        <dc:Bounds x="365.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_accept" bpmnElement="actions_accept">
        <dc:Bounds x="665.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_prepare" bpmnElement="actions_prepare">
        <dc:Bounds x="965.0" y="65.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_ok" bpmnElement="actions_ok">
        <dc:Bounds x="1315.0" y="75.0" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_prepared" bpmnElement="actions_prepared">
        <dc:Bounds x="1622.0" y="82.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_partial" bpmnElement="actions_partial">
        <dc:Bounds x="1322.0" y="292.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_decline" bpmnElement="actions_decline">
        <dc:Bounds x="132.0" y="552.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_decline_check" bpmnElement="actions_decline_check">
        <dc:Bounds x="365.0" y="535.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_save" bpmnElement="actions_save">
        <dc:Bounds x="665.0" y="535.0" width="150" height="70" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="actions_DI_end" bpmnElement="actions_end">
        <dc:Bounds x="1022.0" y="552.0" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="actions_DI_Flow_0" bpmnElement="actions_Flow_0">
        <di:waypoint x="168.0" y="100.0" />
        <di:waypoint x="365.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_1" bpmnElement="actions_Flow_1">
        <di:waypoint x="515.0" y="100.0" />
        <di:waypoint x="665.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_2" bpmnElement="actions_Flow_2">
        <di:waypoint x="815.0" y="100.0" />
        <di:waypoint x="965.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_3" bpmnElement="actions_Flow_3">
        <di:waypoint x="1115.0" y="100.0" />
        <di:waypoint x="1315.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_4" bpmnElement="actions_Flow_4">
        <di:waypoint x="1365.0" y="100.0" />
        <di:waypoint x="1622.0" y="100.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_5" bpmnElement="actions_Flow_5">
        <di:waypoint x="1340.0" y="125.0" />
        <di:waypoint x="1340.0" y="292.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_6" bpmnElement="actions_Flow_6">
        <di:waypoint x="168.0" y="570.0" />
        <di:waypoint x="365.0" y="570.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_7" bpmnElement="actions_Flow_7">
        <di:waypoint x="515.0" y="570.0" />
        <di:waypoint x="665.0" y="570.0" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="actions_DI_Flow_8" bpmnElement="actions_Flow_8">
        <di:waypoint x="815.0" y="570.0" />
        <di:waypoint x="1022.0" y="570.0" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

Backend accept-assigned:

  1. Проверить order.accept_assigned и текущий Ticket scope.
  2. Под lock сверить актуальное назначение и expected provider id из context.
  3. Проверить eligibility исполнителя.
  4. Сохранить ACCEPTED и попробовать PRE_START.

Согласие, принятое раундом, не заменяет эти повторные backend-проверки.

В вызове от имени handler нет отдельного ожидания prepare_available_at/pre_start_at и ASAP не является обязательным условием составного принятия. Подготовка всё равно проходит канонические ограничения одновременных заказов и платёжный hook. При успехе сохраняется PRE_START, применяются предусмотренные offline/prediction hooks. При ожидаемом доменном блокировании подготовки ответ фиксирует успешное принятие, prepared=false и ACCEPTED; последующий ACCEPTED handler продолжает процесс. Неожиданное исключение транзакции — другой случай, его нельзя приравнивать к такому успешному частичному ответу.

decline-provider требует собственного capability, scope и expected provider. Backend допускает отказ только в ASSIGNED/ACCEPTED при текущем назначенном исполнителе, сохраняет DECLINED и очищает provider. Это не прямой возврат в BOOKED; следующий handler определяет дальнейший процесс.

Операции handler не используют generic ticket.transition. Успех/stale освобождает текущую очередь; уже сохранённая стадия не откатывается из-за потери commit-события. Нового владельца будит ticket.changed, прежний получает ticket.moved; periodic reconciliation восстанавливает состав при потере доставки.

Настройки и диагностика

Назначение Имя
Пропустить человеческий раунд SERVICE_REQUEST_ASSIGNED_AUTO_ACCEPT
Стратегия / доставка SERVICE_REQUEST_ASSIGNED_ROUND_STRATEGY, SERVICE_REQUEST_ASSIGNED_DELIVERY_POLICY
Число попыток и порог доменных неуспехов accept SERVICE_REQUEST_ASSIGNED_NOTIFICATION_ATTEMPTS
Повтор / интервал попыток SERVICE_REQUEST_ASSIGNED_NOTIFICATION_INTERVAL_SECONDS
Окно ответа SERVICE_REQUEST_ASSIGNED_RESPONSE_WINDOW_SECONDS
Общее исключение DEFERRED_TICKET_RECHECK_SECONDS, processing_error
Сверка состава INTERVAL_TIME_BD_REQ

Реальные значения не предполагаются. Defer освобождает worker; внешнее событие может сделать Ticket готовым раньше обычного срока. Раунд, ответы, рассылка и счётчик доменных неуспехов — процессная память. После рестарта нужно различать сохранённое назначение/статус и новый локальный раунд.

Порядок диагностики:

  1. Прочитать актуального provider и проверить его JID.
  2. Сверить auto-настройку, strategy и round outcome.
  3. Разделить попытку send, фактический ответ человека и решение стратегии.
  4. Прочитать результат accept/decline и фактическую конечную стадию.
  5. При ACCEPTED с prepared=false разобрать платёжное условие либо конфликт подготовки.

Частично успешное принятие не следует автоматически считать потерянным ответом исполнителя.

Проверенные источники

Git SHA/SHA-256 и параметры моделей закреплены в metadata/BPMN. Код handler и бизнес-запросы при анализе не исполнялись. Источники заново сверены при сборке пакета; это проверка закреплённых файлов авторского процесса, не общий автоматический hook всех Wiki-обновлений.

Связанные материалы

sharixopen/knowledge/scenarios/service_request_assigned.txt · Последнее изменение: — sx_knowledge_api_open

© 2022 ShariX