Перейти к содержанию

Документы, подписание, отлагательные условия, выдача (T21)

disbursement.py — этапы 10–12 SPEC §6: документы по выбранному предложению, подписание кодом подтверждения (ПЭП), проверка отлагательных условий и выдача. Статус меняется только через transition из workflow.py (WORKFLOW.md), поэтому каждый шаг попадает в журнал. Всё, что относится к этим этапам, хранится в одной колонке loan_applications.closing (миграция 0005_closing).

offered -> accepted -> documents     клиент выбрал предложение; документы формируются сразу, в том же запросе
documents -> signed -> conditions    клиент подписывает все документы одним кодом
conditions -> disbursed              worker проверяет условия (мок) и выдаёт деньги (мок)

Отозвать заявку можно до documents включительно; после подписания нельзя (TRANSITIONS).

Документы

Список зависит от выбранного предложения (LoanApplication.selected_offer_id):

Документ (doc_id) Когда Кто подписывает
loan_agreement — кредитный договор Всегда Заёмщик
guarantee_agreement — договор поручительства Обеспечение owner_guarantee Основной собственник: учредитель с наибольшей долей из ЕГРЮЛ, иначе руководитель
pledge_agreement — договор залога Обеспечение equipment_pledge Заёмщик
msp_guarantee_application — заявка на гарантию Корпорации МСП Обеспечение msp_guarantee Заёмщик

У документа есть номер (КД-…, ДП-…, ДЗ-…, МСП-…, из id заявки), подписант и текст с условиями: продукт, сумма, срок, ставка, платёж, обеспечение, порядок выдачи (на счёт клиента или поставщику). Текст — короткая выжимка условий для экрана, а не юридический договор.

Подписание кодом

  1. issue_code создаёт код из 6 цифр. Он действует 5 минут, на него есть 3 попытки. В базе хранится только хеш кода. Новый код отменяет прежний; на одну заявку выдаётся не больше 5 кодов.
  2. sign проверяет код и подписывает все документы одной подписью: documents → signed → conditions. В closing.signature записываются время, кто подписал, способ pep_code и список документов.

Неверный код тратит попытку. Когда попытки закончились, код сгорает, и нужен новый. Неверный или просроченный код — это не исключение: sign возвращает текст для клиента, и потраченная попытка сохраняется.

Отлагательные условия

Список условий фиксируется вместе с документами. Worker (disbursement.tick(), тот же цикл, что и workflow.tick()) проверяет заявки в статусе conditions:

Условие Когда Проверка (мок)
guarantee_signed Поручительство Договор поручительства подписан
pledge_registered Залог оборудования Номер уведомления в реестре уведомлений о залоге движимого имущества (выдуман из id заявки)
supplier_invoice_checked Выдача поставщику (инвестиционный кредит) Поставщик ≠ заёмщик; сумма кредита ≤ цена − собственный взнос. Номер счёта выдуман
msp_guarantee_issued Гарантия Корпорации МСП Номер гарантии (выдуман)

Каждое условие пишет событие в журнал с результатом. Если все условия выполнены, следует выдача. Если какое-то условие не выполнено, заявка остаётся в conditions с отметкой failed и больше не проверяется: из conditions в таблице переходов есть только disbursed. Что делать с такой заявкой, пока не решено (docs/v2/NOTES.md, Unassigned).

Выдача

closing.payout:

Продукт Куда Что
Инвестиционный кредит (Offer.disbursement = supplier) Поставщику, supplier_inn Оплата оборудования по счёту
Оборотный кредит Счёт клиента Зачисление суммы
Овердрафт, кредитная линия Счёт клиента Установлен лимит, деньги не зачисляются (kind: limit)

Номер счёта клиента и id платежа — мок. Текст выдачи — сообщение перехода в disbursed, его видит клиент.

API (/api/v2)

Метод Кто Что
POST /api/v2/applications/{id}/accept клиент {"offer_id", "revision"}: offered → accepted → documents, в ответе closing.documents
GET /api/v2/applications/{id}/documents/{doc_id} клиент, сотрудник Документ: doc_id, kind, title, signer, text, signed_at
POST /api/v2/applications/{id}/sign/code клиент {"revision"} → {"demo_code", "expires_in", "application"}. 409 не в documents, 422 — лимит кодов
POST /api/v2/applications/{id}/sign клиент {"code", "revision"}: documents → signed → conditions. Неверный или просроченный код — 422 {"detail", "revision"}: попытка потрачена, версия заявки новая

closing в GET /api/v2/applications/{id}: offer_id, documents, code (только expires_at и attempts_left, без хеша), codes_issued, signature, conditions, payout. Клиент видит у условий только code, title, status; подробности проверки видит сотрудник.

Упрощения

  • # MOCK: SMS-шлюза нет: код возвращается в ответе (demo_code), и интерфейс показывает его на экране. В банке код уходит на телефон клиента из договора ПЭП.
  • # SIMPLIFICATION: один код клиента подписывает и договор поручительства. В банке поручитель (основной собственник) подписывает своей ПЭП или ЭП.
  • # SIMPLIFICATION: срок кода, число попыток и лимит кодов — константы в коде, а не условия договора ПЭП.
  • # MOCK: залог в реестре уведомлений, счёт поставщика и гарантия Корпорации МСП — проверки внутри проекта с выдуманными номерами; во внешние системы ничего не отправляется.
  • # MOCK: выдача — запись в closing.payout и событие журнала, без проводок. Номер счёта клиента выводится из ИНН (40702… для ООО, 40802… для ИП).
  • Собственный взнос по инвестиционному кредиту считается оплаченным поставщику клиентом напрямую; проверяется только, что кредит его не покрывает.
  • Срок на подписание не ограничен: документы можно подписать и после valid_until предложения (docs/v2/NOTES.md, Unassigned).