АРМ бухгалтера и юриста
Концепция рабочего места для финансово-юридического контура: единый учёт денег, документов, расчётов с контрагентами и сотрудниками, договоров и сроков — поверх уже существующих данных проектов, командировок и логистики.
Статус: частично реализовано. Блоки
payments,contracts,payroll, ДДС/дебиторка и НДС-регистр уже доступны через API. Оставшиеся пункты в этом документе являются roadmap/целевой моделью и должны сверяться с более точными страницами блоков.
Зачем это нужно
Сегодня деньги «размазаны» по операционным модулям: затраты возникают в logistics, trips, timeentries, equipmentrequests и стекаются в finance как CostItem; счета/акты/договоры живут в documents; контрагенты — в companies/contacts/requisites. Но нет единого рабочего места, где бухгалтер и юрист ведут учёт целиком: от платежа и зарплатной ведомости до договора и срока его действия.
АРМ — это не один модуль, а сборка над API + дашборды + RBAC-роли. Бэкенд закрывает четыре недостающих контура, frontend собирает из них два рабочих места.
Две роли
| Роль | Что делает в АРМ |
|---|---|
Бухгалтер (accountant) | Реестр платежей и касса, взаиморасчёты с контрагентами, зарплатные ведомости, налоги/НДС, сводная отчётность (ДДС, P&L по организации). |
Юрист (lawyer) | Реестр договоров, допсоглашения, претензии, доверенности, сроки и напоминания, юридическая карточка контрагента. |
Что переиспользуем (фундамент уже есть)
Новый контур не дублирует существующее, а достраивает поверх него:
| Существует | Модуль | Роль в АРМ |
|---|---|---|
| Статьи затрат план/факт, P&L проекта, выручка | finance (CostItem, ProjectSummary, Revenue) | источник фактических расходов и доходов |
| Счета/акты/договоры/сметы, шаблоны, нумерация, статусы, PDF | documents | первичные документы; договоры расширяются юр.-реестром |
| Реквизиты компании, банковские счета, контрагенты, ИНН/dadata | requisites, companybankaccounts, companies, contacts, dadata | стороны платежей и договоров |
Точная десятичная арифметика, НДС-хелперы (VATOnNet, GrossFromNet, VATInGross, NetFromGross) | internal/money | все денежные поля и расчёт НДС |
| Командировки, логистика, KPI, учёт времени | trips, logistics, kpi, timeentries | источники начислений (расходы, труд) |
| Уведомления (HTTP + WS) | notifications | напоминания о сроках договоров и платежей |
Money везде money.Money / *money.Money. Никакого float64. Колонки — NUMERIC(_, 2). См. docs/concepts/money.md.
Что закрывает АРМ
┌───────────────────────────────────────────┐
│ АРМ (frontend) │
│ Бухгалтер • Юрист • Дашборды │
└───────────────────────────────────────────┘
│ REST API
┌───────────────┬────────────────┼────────────────┬───────────────────┐
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ ┌───────────────┐
│payments│ │ payroll │ │ contracts │ │ reporting│ │ existing: │
│касса/ │ │ зарплата │ │ юр. реестр │ │ ДДС/P&L/ │ │ finance/ │
│платежи │ │ │ │ (ext docs) │ │ НДС │ │ documents/... │
└───┬────┘ └────┬─────┘ └─────┬──────┘ └────┬─────┘ └───────────────┘
│ │ │ │
└──── проводки/связи ───────────┴───────────────┘В коде это отдельные bounded-контексты под internal/modules/: payments, contracts, payroll и read-only отчеты в payments/reports. Раскладка стандартная: model.go / service.go / repository_pg.go / transport_http.go / *_test.go. Кросс-модульные вызовы идут через интерфейсы сервисов и адаптеры в internal/app.
Блок 1. Реестр платежей / касса (payments)
Сердце бухгалтерии. Платежи хранят реальные движения денег, частичные разнесения по документам, остатки по счетам и взаиморасчёты. На этом контуре держатся payroll и отчётность.
Модель данных
Account (расчётный счёт / касса организации)
- ID
- Name "Основной р/с", "Касса"
- Kind bank | cash
- CompanyBankAccountID *int64 (связь с companybankaccounts, если банк)
- Currency "RUB" (на будущее; сейчас один)
- OpeningBalance money.Money
- IsActive bool
Payment (факт движения денег)
- ID
- AccountID int64 (с какого/на какой счёт)
- Direction incoming | outgoing
- Amount money.Money (> 0)
- VATAmount *money.Money (НДС в сумме платежа, опц.)
- Date date
- CounterpartyType company | contact | employee | none
- CounterpartyID *int64
- Purpose string (назначение платежа)
- Method bank | cash | card | offset (offset = взаимозачёт)
- Status planned | completed | cancelled
- CreatedBy int64
- CreatedAt / UpdatedAt
PaymentAllocation (разнесение платежа на документы — частичные оплаты)
- ID
- PaymentID int64
- DocumentID int64 (документ из модуля documents)
- Amount money.Money
// сумма разнесений ≤ Payment.Amount; остаток = авансЧто это даёт
- Частичные оплаты: один счёт закрывается несколькими платежами; сумма разнесений участвует во взаиморасчётах. Авто-смена статуса документа
issued → paidпри полном закрытии — запланированная интеграция, а не текущее поведение payments API. - Остаток по счёту:
OpeningBalance + Σ(incoming) − Σ(outgoing)поcompleted-платежам. - Взаиморасчёты с контрагентом: баланс = Σ выставленных документов − Σ разнесённых оплат. Основа для акта сверки.
- Платёжный календарь:
planned-платежи с будущими датами → виджет «к оплате/к получению».
API
GET /api/v1/accounts список счетов/касс
POST /api/v1/accounts
GET /api/v1/accounts/:id/balance текущий остаток
GET /api/v1/payments реестр (фильтры: account, direction,
status, counterparty, date-range)
POST /api/v1/payments создать платёж (+ allocations)
GET /api/v1/payments/:id
PATCH /api/v1/payments/:id/status completed / cancelled
GET /api/v1/counterparties/:type/:id/balance взаиморасчёты
GET /api/v1/counterparties/:type/:id/reconciliation акт сверки (period)Все списки — через transport.RespondList[T]. Разнесения передаются при создании платежа в allocations[]; отдельного публичного endpoint-а POST /payments/:id/allocations в текущем контракте нет.
Блок 2. Зарплата (payroll)
Сейчас salary — только категория CostItem. Полноценного расчёта с сотрудниками нет.
Модель данных
CompensationProfile (условия оплаты сотрудника)
- ID
- UserID int64
- Kind salary | hourly (оклад / почасовая)
- BaseAmount money.Money (оклад в месяц ИЛИ ставка/час)
- EffectiveFrom date
- EffectiveTo *date
// история ставок — новая запись на каждое изменение
PayrollRun (ведомость за период)
- ID
- Period "2026-06" (месяц)
- Status draft | approved | paid
- TotalAccrued money.Money
- TotalWithheld money.Money
- TotalNet money.Money
- CreatedBy / ApprovedBy
- CreatedAt
PayrollItem (строка по сотруднику)
- ID
- PayrollRunID int64
- UserID int64
- Accrued money.Money (начислено: оклад / часы×ставка [+ KPI-бонус])
- Withheld money.Money (удержания: НДФЛ и пр.)
- Net money.Money (к выплате)
- SourceBreakdown jsonb (из чего сложилось: timeentries, kpi, бонусы)
- PaymentID *int64 (ссылка на выплату из payments)Поток
- Бухгалтер создаёт
PayrollRunза месяц → система собираетPayrollItemпо каждому активному сотруднику:- оклад из
CompensationProfileили Σ(timeentries× ставка) для почасовых; - бонусы из
kpi(опционально, по флагу).
- оклад из
draft → approved(фиксация сумм) →paid: на каждую строку создаётся исходящийPayment(CounterpartyType=employee),Netуходит в кассу/банк.- Начисленная зарплата попадает в
financeкакCostItemкатегорииsalary(через интерфейс finance-сервиса), чтобы P&L проектов и организации учитывал фонд оплаты труда.
API
GET/POST /api/v1/compensation-profiles условия оплаты
GET/POST /api/v1/payroll/runs ведомости
GET /api/v1/payroll/runs/:id + items
POST /api/v1/payroll/runs/:id/recalculate пересобрать строки draft
POST /api/v1/payroll/runs/:id/approve утвердить
POST /api/v1/payroll/runs/:id/pay выплатить
PATCH /api/v1/payroll/items/:id скорректировать строку draftБлок 3. Юридический реестр договоров (contracts)
Тип документа contract уже существует в documents, но юридический реестр реализован как отдельный модуль contracts: карточка договора может опционально ссылаться на generated document через document_id, но не обязана быть создана из documents.
Модель данных
Contract
- ID
- DocumentID *int64 (опциональная ссылка на documents.id)
- Number string (юр. номер, может отличаться от сквозного)
- CounterpartyType company | contact
- CounterpartyID int64
- Subject string (предмет договора)
- Amount *money.Money (сумма, опц.)
- SignedAt *date
- EffectiveFrom *date
- EffectiveTo *date (срок действия)
- AutoProlong bool
- State draft | signed | active | expired | terminated
- ResponsibleUserID *int64
ContractLink (связи: допсоглашения, претензии, акты)
- ID
- ContractID int64 (родительский договор)
- DocumentID int64 (связанный документ)
- Relation amendment | annex | claim | act | invoice
ContractDeadline (контролируемые сроки)
- ID
- ContractID int64
- Kind expiry | payment | milestone | custom
- DueDate date
- NotifyDaysBefore int
- Done boolТипы связанных документов в реестре: amendment, annex, claim, act, invoice.
Что это даёт
- Реестр договоров с фильтрами: контрагент, статус, срок, ответственный.
- Сроки и напоминания: фоновый воркер сканирует
ContractDeadline/EffectiveToи шлётnotificationsзаNotifyDaysBeforeдней.AutoProlongсдвигаетEffectiveTo. - Юр. карточка контрагента: агрегирует договоры, документы, взаиморасчёты (из
payments) и реквизиты/проверку ИНН (dadata). - Дерево связей: договор → допсоглашения → акты → счета.
API
GET /api/v1/contracts реестр (фильтры)
POST /api/v1/contracts создать карточку договора
GET /api/v1/contracts/:id карточка + связи + сроки
PATCH /api/v1/contracts/:id обновить поля договора
PATCH /api/v1/contracts/:id/state смена статуса
POST /api/v1/contracts/:id/links привязать документ
POST /api/v1/contracts/:id/deadlines
GET /api/v1/contracts/deadlines/due ближайшие и просроченные сроки
PATCH /api/v1/contracts/deadlines/:id/done закрыть срок
GET /api/v1/contracts/legal-card/:type/:id юр. карточкаБлок 4. Сводная отчётность (reporting)
Сейчас P&L есть только по проекту (finance.GetProjectSummary). Не хватает взгляда «по организации». Этот блок — преимущественно read/aggregation поверх finance + payments + payroll, новые таблицы минимальны (только справочник ставок НДС/налогов при необходимости).
Отчёты
| Отчёт | Источник | Что показывает |
|---|---|---|
| ДДС (Cash Flow) | payments | приход/расход по периодам и счетам, остатки |
| Сводный P&L | finance (все проекты) + payroll | доходы − расходы по организации, по категориям |
| НДС-регистр | documents (VATInGross/VATOnNet) + payments.VATAmount | входящий/исходящий НДС за период, к уплате |
| Дебиторка/кредиторка | взаиморасчёты из payments | кто должен нам / кому должны мы |
API
GET /api/v1/reports/cashflow?from=&to=&account= ДДС
GET /api/v1/reports/financial_overview сводный P&L по проектам
GET /api/v1/reports/vat?from=&to= НДС-регистр
GET /api/v1/reports/receivables дебиторка/авансыРасширяет существующий reports (или новый под-неймспейс). Все суммы — money.Money, JSON-контракт — number.
RBAC
Контур использует роли и permission-коды из docs/rbac_matrix.md и миграций 0110, 0112, 0114.
| Permission code | Где | Роли по умолчанию |
|---|---|---|
accounting.view | GET по payments, accounts, payroll, reports/* | accountant, director, admin |
accounting.manage | POST/PATCH платежей, счетов, ведомостей | accountant, admin |
payroll.view | просмотр условий оплаты и ведомостей | accountant, director, admin |
payroll.manage | ведомости, compensation-profiles, утверждение и выплата | accountant, admin |
legal.view | GET по contracts, юр. карточкам | lawyer, director, admin |
legal.manage | создание/изменение договоров, сроков, связей | lawyer, admin |
Роли контура: accountant, lawyer. admin короткозамыкает (allow), director получает read-доступ через глобальное чтение. Где платёж/документ привязан к департаментному домену, используется дополнительная проверка на уровне сущности.
Миграции
Фактический финансово-юридический контур разложен по миграциям:
0109_payments.sql accounts, payments, payment_allocations + индексы
0110_accounting_rbac.sql роль accountant + accounting.view/manage
0111_contracts.sql contracts, contract_links, contract_deadlines
0112_legal_rbac.sql роль lawyer + legal.view/manage
0113_payroll.sql compensation_profiles, payroll_runs, payroll_items
0114_payroll_rbac.sql payroll.view/manage
0115_logistics_finance_task.sql связь logistics с задачей финансистаКлючевые индексы уже добавлены на платежи, контрагентов, статусы, сроки договоров и строки ведомостей. Новые изменения в этом контуре должны продолжать нумерацию после последней миграции в migrations/.
Поэтапный план
Каждая фаза — самостоятельный вертикальный срез с полной документацией и регенерацией контрактов (Definition of Done из CLAUDE.md).
- Фаза 1 —
payments(касса/реестр платежей). ✅ Реализовано. Счета, платежи, разнесения, остатки, взаиморасчёты, акт сверки. Подробности — Касса, платежи и взаиморасчёты. Авто-смена статусаdocumentsнаpaid— запланированная интеграция. - Фаза 2 — ДДС + дебиторка/кредиторка. ✅ Реализовано. Отчёты
GET /api/v1/reports/cashflowи/api/v1/reports/receivablesповерх реестра платежей, без новых таблиц. См. Касса, платежи и взаиморасчёты. - Фаза 3 —
contracts(юр. реестр). ✅ Реализовано. Реестр договоров, связи документов, сроки + эндпоинтdeadlines/due, юр. карточка контрагента. См. Юридический реестр договоров. Фоновый воркер напоминаний — следующий шаг. - Фаза 4 —
payroll(зарплата). ✅ Реализовано. Условия оплаты, ведомости (начисления из окладов и табеля, НДФЛ 13%), выплата черезpayments(counterparty=employee). См. Зарплата. Попадание ФОТ вfinance— следующий шаг. - Фаза 5 — сводный P&L + НДС-регистр. ✅ Реализовано. НДС-регистр
GET /api/v1/reports/vatповерхpayments.vat_amount; сводный P&L по организации — существующийGET /api/v1/reports/financial_overview. См. Касса, платежи и взаиморасчёты.
Definition of Done (на каждую фазу)
Контур финансово-юридический — изменения по определению substantial. По каждому срезу:
- Концепт в
docs/concepts/(этот файл + специфика блока). - Регенерация контрактов:bashПроверить, что изменились
go run ./cmd/generate_contracts go run ./cmd/generate_endpointsdocs/assets/data/openapi.json,docs/public/openapi.json,docs/assets/data/endpoints.jsonи коллекции вdocs/assets/downloads/. - Обновить
docs/rbac_matrix.md(новые роли/коды). go test ./internal/contractcheck/...— зелёный.- README — если меняется ops/env-контракт.