Paykeeper Adapter — Data Model
База данных: paykeeper_adapter_db
erDiagram paykeeper_accounts ||--o{ paykeeper_terminals : "owns" paykeeper_accounts ||--o{ paykeeper_invoices : "routes" paykeeper_accounts ||--o{ paykeeper_payments : "routes" paykeeper_accounts ||--o{ paykeeper_refunds : "routes" paykeeper_accounts ||--o{ pk_outbox : "routes" paykeeper_accounts ||--o{ webhook_log : "routes" paykeeper_accounts ||--|| pk_token_cache : "has" paykeeper_accounts ||--o{ paykeeper_products : "syncs" paykeeper_accounts ||--o{ pk_catalog_sync_runs : "tracks" paykeeper_accounts ||--o{ paykeeper_users : "imported" paykeeper_accounts ||--o{ paykeeper_user_imports : "tracks" paykeeper_invoices ||--o{ pk_invoice_check_schedule : "polls" paykeeper_payments ||--o{ paykeeper_receipts : "has" paykeeper_payments ||--o{ paykeeper_refunds : "has" paykeeper_accounts { uuid id PK uuid legal_entity_id UNIQUE "NOT NULL" varchar pk_server_host "NOT NULL, (255)" varchar pk_login "NOT NULL, (100)" bytea pk_password_enc "NOT NULL — AES-GCM" bytea informer_seed_enc "NOT NULL — AES-GCM" varchar paykeeper_id "NULL, (50)" varchar status "NOT NULL, default active, (20)" timestamp onboarded_at "NOT NULL" timestamp last_token_at "NULL" timestamp created_at "NOT NULL" timestamp updated_at "NOT NULL" timestamp deleted_at "NULL" } paykeeper_terminals { uuid id PK uuid account_id FK "NOT NULL" uuid store_id UNIQUE "NOT NULL" varchar pk_terminal_id UNIQUE "NOT NULL, (100)" varchar pk_mpos_merchant_id "NOT NULL, (100)" varchar label "NULL, (100)" varchar status "NOT NULL, default active, (20)" timestamp created_at "NOT NULL" timestamp updated_at "NOT NULL" } paykeeper_invoices { uuid id PK uuid account_id FK "NOT NULL" uuid order_id UNIQUE "NOT NULL — ссылка на Order Service" varchar pk_invoice_id UNIQUE "NOT NULL, (50)" text pk_invoice_url "NOT NULL" varchar pk_status "NOT NULL, (20)" decimal pay_amount "NOT NULL, (12,2)" varchar orderid_bridge "NOT NULL — packed store_id:order_number" timestamp created_at "NOT NULL" timestamp paid_at "NULL" timestamp expiry_at "NULL" } paykeeper_payments { uuid id PK uuid account_id FK "NOT NULL" uuid order_id "NOT NULL" varchar pk_payment_id "NOT NULL, (50)" varchar pk_unique_id "NULL, (100)" decimal pay_amount "NOT NULL, (12,2)" integer payment_system_id "NULL" varchar status "NOT NULL, (20)" varchar bank_id "NULL, (100)" varchar card_last4 "NULL, (4)" timestamp pending_datetime "NULL" timestamp obtain_datetime "NULL" timestamp success_datetime "NULL" jsonb raw_informer_json "NOT NULL" boolean reconciled "NOT NULL, default false" timestamp received_at "NOT NULL" } paykeeper_receipts { uuid id PK uuid payment_id FK "NOT NULL" varchar pk_receipt_id "NOT NULL, (50)" varchar type "NOT NULL, (20)" boolean is_post_sale "NOT NULL, default false" boolean is_correction "NOT NULL, default false" varchar status "NOT NULL, (20)" decimal sum_cashless "NULL, (12,2)" decimal sum_cash "NULL, (12,2)" varchar fpd "NULL, (50)" varchar fnd "NULL, (50)" varchar fn "NULL, (50)" varchar rnkkt "NULL, (50)" integer shift_number "NULL" integer receipt_number "NULL" varchar fop_receipt_key "NULL, (100)" text fop_url "NULL" varchar ts "NULL, (20)" jsonb cart_json "NULL" jsonb error_json "NULL" timestamp received_at "NOT NULL" } paykeeper_refunds { uuid id PK uuid account_id FK "NOT NULL" uuid order_id "NOT NULL" uuid payment_id FK "NULL" varchar pk_refund_id "NULL, (50)" decimal amount "NOT NULL, (12,2)" decimal refund_total_so_far "NULL, (12,2)" boolean is_full_refund "NOT NULL" varchar status "NOT NULL, (20)" varchar initiated_by "NOT NULL, (10)" text reason "NULL" jsonb refund_cart "NULL" timestamp datetime "NULL" timestamp created_at "NOT NULL" } pk_outbox { uuid id PK uuid account_id FK "NOT NULL" varchar op_type "NOT NULL, (50)" jsonb payload_json "NOT NULL" varchar status "NOT NULL, default pending, (20)" integer attempts "NOT NULL, default 0" timestamp next_attempt_at "NOT NULL" text last_error "NULL" timestamp created_at "NOT NULL" timestamp sent_at "NULL" } webhook_log { uuid id PK uuid account_id FK "NULL" varchar endpoint "NOT NULL, (50)" text raw_body "NOT NULL" jsonb headers_json "NOT NULL" boolean signature_valid "NOT NULL" varchar dedup_key "NULL, (100)" boolean processed "NOT NULL, default false" text processing_error "NULL" timestamp created_at "NOT NULL" } pk_token_cache { uuid account_id PK varchar token "NOT NULL, (255)" timestamp expires_at "NOT NULL" } pk_invoice_check_schedule { uuid id PK uuid invoice_id FK "NOT NULL" timestamp next_run_at "NOT NULL" integer interval_min "NOT NULL, default 5" integer attempts "NOT NULL, default 0" varchar status "NOT NULL, default active, (20)" timestamp created_at "NOT NULL" } paykeeper_products { uuid id PK uuid account_id FK "NOT NULL" uuid erp_product_id "NOT NULL" uuid erp_structural_option_id "NULL" uuid erp_free_option_id "NULL" varchar variant_kind "NOT NULL, (20)" varchar sku "NOT NULL, (120)" varchar pk_product_id "NOT NULL, (100)" varchar hash "NOT NULL, (64)" varchar status "NOT NULL, default active, (20)" timestamp last_synced_at "NOT NULL" text last_error "NULL" timestamp created_at "NOT NULL" timestamp updated_at "NOT NULL" } pk_catalog_sync_runs { uuid id PK uuid account_id FK "NOT NULL" varchar trigger "NOT NULL, (20)" timestamp started_at "NOT NULL" timestamp finished_at "NULL" integer products_upserted "NOT NULL, default 0" integer products_deleted "NOT NULL, default 0" integer errors_count "NOT NULL, default 0" varchar status "NOT NULL, (20)" text last_error "NULL" jsonb errors_json "NULL" } paykeeper_users { uuid id PK uuid account_id FK "NOT NULL" varchar pk_user_id "NOT NULL, (50)" varchar pk_login "NOT NULL, (100)" uuid employee_id "NOT NULL" timestamp created_at "NOT NULL" timestamp updated_at "NOT NULL" } paykeeper_user_imports { uuid id PK uuid account_id FK "NOT NULL" varchar trigger "NOT NULL, default manual, (20)" uuid initiated_by_user_id "NOT NULL" timestamp started_at "NOT NULL" timestamp finished_at "NULL" integer users_total "NOT NULL, default 0" integer users_created "NOT NULL, default 0" integer users_linked "NOT NULL, default 0" integer users_updated "NOT NULL, default 0" integer users_skipped "NOT NULL, default 0" integer users_errored "NOT NULL, default 0" varchar status "NOT NULL, default running, (20)" text last_error "NULL" jsonb errors_json "NULL" }
Таблицы
paykeeper_accounts
Аккаунт PayKeeper — привязка ЛК PK к юрлицу.
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
legal_entity_id | uuid | NOT NULL | — | FK → User Service legal_entities (логический). UNIQUE |
pk_server_host | varchar(255) | NOT NULL | — | URL ЛК PK {tsp}.server.paykeeper.ru |
pk_login | varchar(100) | NOT NULL | — | Логин ЛК PK |
pk_password_enc | bytea | NOT NULL | — | Пароль, AES-GCM зашифровано |
informer_seed_enc | bytea | NOT NULL | — | Секретное слово для MD5-подписи webhook’ов, AES-GCM |
paykeeper_id | varchar(50) | NULL | — | Номер договора PK (отображается в ЛК) |
status | varchar(20) | NOT NULL | 'active' | active / suspended |
onboarded_at | timestamp | NOT NULL | now() | Дата подключения |
last_token_at | timestamp | NULL | — | Последний успешный рефреш security token |
created_at | timestamp | NOT NULL | now() | |
updated_at | timestamp | NOT NULL | now() | |
deleted_at | timestamp | NULL | — | Soft delete |
Индексы:
uq_pk_accounts_legal_entity— UNIQUElegal_entity_idWHEREdeleted_at IS NULLidx_pk_accounts_status— поstatus
paykeeper_terminals
Привязка ТТ к физическому mPOS-терминалу у PK.
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts |
store_id | uuid | NOT NULL | — | FK → Store Service stores (логический). UNIQUE |
pk_terminal_id | varchar(100) | NOT NULL | — | Заводской ID терминала. UNIQUE глобально |
pk_mpos_merchant_id | varchar(100) | NOT NULL | — | ID мерчанта у PK |
label | varchar(100) | NULL | — | «Касса 1» |
status | varchar(20) | NOT NULL | 'active' | active / inactive |
created_at | timestamp | NOT NULL | now() | |
updated_at | timestamp | NOT NULL | now() |
Индексы:
uq_pk_terminals_store— UNIQUEstore_iduq_pk_terminals_pk_id— UNIQUEpk_terminal_ididx_pk_terminals_account—account_id
paykeeper_invoices
Запись об инвойсе, созданном в PK для нашего заказа.
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts |
order_id | uuid | NOT NULL | — | Наш заказ (логический FK → Order Service). UNIQUE |
pk_invoice_id | varchar(50) | NOT NULL | — | ID счёта у PK. UNIQUE |
pk_invoice_url | text | NOT NULL | — | URL для оплаты (отдаётся клиенту / терминалу) |
pk_status | varchar(20) | NOT NULL | 'created' | created / sent / paid / expired |
pay_amount | decimal(12,2) | NOT NULL | — | Сумма |
orderid_bridge | varchar(255) | NOT NULL | — | Packed bridge-ID (base64url(store_id:order_number)) — передаётся в PK как orderid, возвращается в webhook |
created_at | timestamp | NOT NULL | now() | |
paid_at | timestamp | NULL | — | Когда PK отметил как оплачен |
expiry_at | timestamp | NULL | — | TTL инвойса |
Индексы:
uq_pk_invoices_order— UNIQUEorder_iduq_pk_invoices_pk_id— UNIQUEpk_invoice_ididx_pk_invoices_status—pk_status
paykeeper_payments
Informer success — факт оплаты, принятый от PK.
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts |
order_id | uuid | NOT NULL | — | Логический FK → Order Service |
pk_payment_id | varchar(50) | NOT NULL | — | ID платежа у PK |
pk_unique_id | varchar(100) | NULL | — | Уникальный ID транзакции у банка |
pay_amount | decimal(12,2) | NOT NULL | — | |
payment_system_id | integer | NULL | — | ID платёжной системы PK |
status | varchar(20) | NOT NULL | 'paid' | paid / stuck (эквивалент PK success / stuck) |
bank_id | varchar(100) | NULL | — | ID привязки карты |
card_last4 | varchar(4) | NULL | — | Последние 4 цифры карты |
pending_datetime | timestamp | NULL | — | Из PK |
obtain_datetime | timestamp | NULL | — | Из PK |
success_datetime | timestamp | NULL | — | Из PK |
raw_informer_json | jsonb | NOT NULL | — | Полный payload informer’а для аудита |
reconciled | boolean | NOT NULL | false | true если запись создана через reconciliation, а не прямой informer |
received_at | timestamp | NOT NULL | now() |
Индексы:
uq_pk_payments_dedup— UNIQUE(account_id, pk_payment_id)idx_pk_payments_order—order_id
paykeeper_receipts
Фискальный чек из PK (догружается после оплаты / приходит через callback §8.13).
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
payment_id | uuid | NOT NULL | — | FK → paykeeper_payments |
pk_receipt_id | varchar(50) | NOT NULL | — | ID чека у PK |
type | varchar(20) | NOT NULL | — | sale / refund / expense / expense-refund |
is_post_sale | boolean | NOT NULL | false | Чек окончательного расчёта |
is_correction | boolean | NOT NULL | false | Чек коррекции ФФД 1.2 |
status | varchar(20) | NOT NULL | — | created / request_sent / success / timeout / failed / rejected |
sum_cashless, sum_cash | decimal(12,2) | NULL | — | |
fpd | varchar(50) | NULL | — | Фискальный признак |
fnd | varchar(50) | NULL | — | Номер ФД |
fn | varchar(50) | NULL | — | Номер ФН |
rnkkt | varchar(50) | NULL | — | Регистрационный номер ККТ |
shift_number | integer | NULL | — | |
receipt_number | integer | NULL | — | Номер чека в смене |
fop_receipt_key | varchar(100) | NULL | — | Ключ чека |
fop_url | text | NULL | — | QR-контент |
ts | varchar(20) | NULL | — | Время формирования YYYYMMDDTHHmm |
cart_json | jsonb | NULL | — | Корзина |
error_json | jsonb | NULL | — | Ошибка если rejected/failed |
received_at | timestamp | NOT NULL | now() |
Индексы:
uq_pk_receipts_pk_id— UNIQUEpk_receipt_ididx_pk_receipts_payment—payment_ididx_pk_receipts_status—status
paykeeper_refunds
Возврат платежа.
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts |
order_id | uuid | NOT NULL | — | Логический FK |
payment_id | uuid | NULL | — | FK → paykeeper_payments |
pk_refund_id | varchar(50) | NULL | — | ID возврата у PK (если есть) |
amount | decimal(12,2) | NOT NULL | — | Сумма возврата |
refund_total_so_far | decimal(12,2) | NULL | — | Сумма всех возвратов по платежу на момент события |
is_full_refund | boolean | NOT NULL | — | |
status | varchar(20) | NOT NULL | 'started' | started / done / failed |
initiated_by | varchar(10) | NOT NULL | — | admin / pos / pk_external (через ЛК PK) |
reason | text | NULL | — | |
refund_cart | jsonb | NULL | — | Корзина возврата для частичных |
datetime | timestamp | NULL | — | Когда PK выполнил |
created_at | timestamp | NOT NULL | now() |
Индексы:
idx_pk_refunds_payment—payment_ididx_pk_refunds_order—order_ididx_pk_refunds_status—status
pk_outbox
Исходящие команды в PK (для гарантии доставки при временной недоступности).
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | На какой ЛК PK слать |
op_type | varchar(50) | NOT NULL | — | Платежи: create_invoice / reverse_payment / capture / repeatcnt / post_sale_receipt. Каталог (BR 3.4): upsert_product / delete_product (включая виртуальные продукты из развёрнутых модификаторов) / sync_catalog_snapshot (full re-sync marker) |
payload_json | jsonb | NOT NULL | — | Тело запроса |
status | varchar(20) | NOT NULL | 'pending' | pending / in_progress / done / dead_letter |
attempts | integer | NOT NULL | 0 | |
next_attempt_at | timestamp | NOT NULL | now() | |
last_error | text | NULL | — | |
created_at | timestamp | NOT NULL | now() | |
sent_at | timestamp | NULL | — |
Индексы:
idx_pk_outbox_next_attempt—(status, next_attempt_at)для worker’аidx_pk_outbox_account—account_id
webhook_log
Лог всех входящих webhook’ов от PK — для аудита и дебага.
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NULL | — | NULL если аккаунт не найден по URL |
endpoint | varchar(50) | NOT NULL | — | informer / refund / receipt |
raw_body | text | NOT NULL | — | Полное тело запроса |
headers_json | jsonb | NOT NULL | — | Заголовки |
signature_valid | boolean | NOT NULL | — | MD5/HMAC проверка прошла |
dedup_key | varchar(100) | NULL | — | pk_payment_id или pk_receipt_id |
processed | boolean | NOT NULL | false | Опубликовано в Kafka |
processing_error | text | NULL | — | |
created_at | timestamp | NOT NULL | now() |
Индексы:
idx_webhook_log_dedup—(account_id, endpoint, dedup_key)— для дедупаidx_webhook_log_created—created_at(для ретеншена, чистка >90 дней)
pk_token_cache
Кэш security token’ов PK (TTL 24ч).
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
account_id | uuid | NOT NULL | — | PK |
token | varchar(255) | NOT NULL | — | |
expires_at | timestamp | NOT NULL | — |
Альтернатива — Redis (быстрее). На выбор команды при реализации.
pk_invoice_check_schedule
Планировщик per-invoice поллинга (reconciliation когда informer задерживается).
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
invoice_id | uuid | NOT NULL | — | FK → paykeeper_invoices |
next_run_at | timestamp | NOT NULL | — | Первый запуск = invoice.created_at + 25 min |
interval_min | integer | NOT NULL | 5 | Интервал между проверками |
attempts | integer | NOT NULL | 0 | |
status | varchar(20) | NOT NULL | 'active' | active / completed / cancelled |
created_at | timestamp | NOT NULL | now() |
Индексы:
idx_pk_invoice_check_next—(status, next_run_at)для worker’а
paykeeper_products (BR 3.4)
Mapping «виртуальный PK-продукт ↔ ERP-товар/его вариант» в пределах одного PK-аккаунта. Одна запись = один товар в ЛК PK. Один erp_product_id может разворачиваться в N записей (базовый + структурные варианты + свободные addon’ы) — см. правило развёртывания.
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts |
erp_product_id | uuid | NOT NULL | — | products.id — корневой товар ERP |
erp_structural_option_id | uuid | NULL | — | modifier_options.id если запись представляет структурный вариант (размер 30см и т.п.) |
erp_free_option_id | uuid | NULL | — | modifier_options.id если запись — свободный addon (доп. сыр) |
variant_kind | varchar(20) | NOT NULL | — | base / structural_variant / free_addon |
sku | varchar(120) | NOT NULL | — | Человеко-читаемый ключ: "{product_id}[:{struct_opt_id}][:+{free_opt_id}]". Кладём в поле sku ims-api, используется для reverse lookup |
pk_product_id | varchar(100) | NOT NULL | — | ID товара в ЛК PK (возвращается PK при upsert). Используется для обратного webhook’а когда PK его реализует |
hash | varchar(64) | NOT NULL | — | SHA-256 от сериализованного состояния (name + price + tax + variant-specific fields) |
status | varchar(20) | NOT NULL | 'active' | active / deleted (soft при удалении товара в ERP или смене набора модификаторов) |
last_synced_at | timestamp | NOT NULL | — | Последний успешный push в PK |
last_error | text | NULL | — | Текст последней ошибки (если sync упал) |
created_at | timestamp | NOT NULL | now() | |
updated_at | timestamp | NOT NULL | now() |
Индексы:
uq_pk_products_variant— UNIQUE(account_id, erp_product_id, COALESCE(erp_structural_option_id, '00000000-0000-0000-0000-000000000000'), COALESCE(erp_free_option_id, '00000000-0000-0000-0000-000000000000'))— одна запись на каждую комбинацию (product, struct_opt, free_opt)uq_pk_products_pk— UNIQUE(account_id, pk_product_id)— для обратного webhook lookupidx_pk_products_status—(account_id, status)idx_pk_products_erp_root—(account_id, erp_product_id)— все варианты одного товара одним запросомidx_pk_products_stale—last_synced_at(для алерта об устаревших)
Нет отдельных таблиц
paykeeper_categoriesиpaykeeper_modifier_groupsPK ims-api не имеет сущностей категорий и модификаторов. Категория попадает в PK как часть имени товара (
category_pathprefix). Группа модификаторов разворачивается в набор записейpaykeeper_productsсvariant_kind≠base. Отдельных mapping-таблиц для них не заводим.
pk_catalog_sync_runs (BR 3.4)
История полных прогонов синхронизации каталога — для аудита и UI «Журнал прогонов».
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts |
trigger | varchar(20) | NOT NULL | — | cron / manual / webhook_missed |
started_at | timestamp | NOT NULL | now() | |
finished_at | timestamp | NULL | — | NULL пока status=running |
products_upserted | integer | NOT NULL | 0 | Счётчик PK-товаров добавленных/обновлённых (включая виртуальные варианты) |
products_deleted | integer | NOT NULL | 0 | Счётчик удалённых PK-товаров |
errors_count | integer | NOT NULL | 0 | |
status | varchar(20) | NOT NULL | 'running' | running / success / partial / failed |
last_error | text | NULL | — | Топ-уровневая ошибка прогона (если прервался целиком) |
errors_json | jsonb | NULL | — | Детальные ошибки: [{ erp_product_id, variant_kind, variant_sku, message }] |
Индексы:
idx_pk_catalog_runs_account—(account_id, started_at DESC)— выборка последних прогоновidx_pk_catalog_runs_status—(account_id, status)— для детектированияrunning(блокировка параллельных запусков)
paykeeper_users (BR 3.5)
Mapping «наш сотрудник ↔ пользователь ЛК PK». Создаётся при импорте через wizard «Выгрузить из PK». Используется при получении чека от PK по полю user_login для построения отчётов «выручка по кассиру».
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts ON DELETE CASCADE |
pk_user_id | varchar(50) | NOT NULL | — | id из PK API (GET /info/organization/users/) |
pk_login | varchar(100) | NOT NULL | — | login из PK API. Используется для матча с user_login в receipt webhook’ах |
employee_id | uuid | NOT NULL | — | Логический FK → User Service employees. При удалении employee mapping удаляется через webhook user.deleted |
created_at | timestamp | NOT NULL | now() | |
updated_at | timestamp | NOT NULL | now() |
Индексы:
uq_pk_users_user— UNIQUE(account_id, pk_user_id)— один pk_user в одном ЛК = одна записьuq_pk_users_employee— UNIQUE(account_id, employee_id)— один employee связан только с одним pk_user в данном ЛК (employee может быть связан с разными pk_user в разных ЛК)idx_pk_users_employee—employee_id— для lookup при чеке от PKidx_pk_users_login—(account_id, pk_login)— для матча по login если PK API не вернул user_id в чеке
paykeeper_user_imports (BR 3.5)
История прогонов импорта сотрудников из ЛК PK — для аудита и UI «Журнал импортов».
| Колонка | Тип | Nullable | Default | Описание |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
account_id | uuid | NOT NULL | — | FK → paykeeper_accounts |
trigger | varchar(20) | NOT NULL | 'manual' | В P0 только manual (нет cron / webhook-trigger) |
initiated_by_user_id | uuid | NOT NULL | — | Логический FK → employees (кто нажал «Выгрузить из PK») |
started_at | timestamp | NOT NULL | now() | |
finished_at | timestamp | NULL | — | NULL пока status=running |
users_total | integer | NOT NULL | 0 | Размер decisions[] в request |
users_created | integer | NOT NULL | 0 | Создано новых employees (action create_new или create_with_alt_email) |
users_linked | integer | NOT NULL | 0 | Связано с существующими employees (action link_existing) |
users_updated | integer | NOT NULL | 0 | Обновлено существующих employees (action update_existing) |
users_skipped | integer | NOT NULL | 0 | Пропущено по решению владельца (action skip) |
users_errored | integer | NOT NULL | 0 | С ошибками при создании/обновлении |
status | varchar(20) | NOT NULL | 'running' | running / success / partial / failed |
last_error | text | NULL | — | Топ-уровневая ошибка (если прогон прервался целиком) |
errors_json | jsonb | NULL | — | Детали: [{ pk_user_id, pk_login, action, message }] |
Индексы:
idx_pk_user_imports_account—(account_id, started_at DESC)— выборка последних прогоновidx_pk_user_imports_status—(account_id, status)
Правила
- Все суммы —
decimal(12,2)для соответствия PK (PK возвращает строки с точкой-разделителем, парсим в BigDecimal). - Все секреты (
pk_password_enc,informer_seed_enc) — AES-GCM at rest; дешифровка только в момент отправки запроса. - Retention
webhook_log— 90 дней (cron-чистка). - Retention
pk_outboxstatus=done— 30 дней. - Retention
pk_catalog_sync_runs— 90 дней (BR 3.4). - Retention
paykeeper_products— бессрочно: храним историю mapping’ов на случай восстановления soft-deleted товаров и для обратного webhook lookup (BR 3.4). - Retention
paykeeper_users— бессрочно: mapping employee ↔ pk_user используется в построении отчётов по кассирам, не удаляется (BR 3.5). - Retention
paykeeper_user_imports— 1 год (история импортов для аудита, потом cron-чистка) (BR 3.5). - Soft delete
paykeeper_accounts(не CASCADE) — исторические платежи остаются. - При soft-delete
paykeeper_accounts— mapping-записи (paykeeper_productsи др.) остаютсяstatusкак есть; sync не выполняется пока account неactive.