[ledger2][@ant] refactor(ledger2): wallet IDs uint64_t → eosio::name

Сквозная замена числовых идентификаторов кошельков ledger2 (1001/2001/9001/...)
на eosio::name с префиксом w.<contract>.<waltype> по аналогии с операциями
(o.<contract>.<verb>) и процессами (p.<contract>.<noun>).

Маппинг:
  2001 → w.wal.share    (паевые деньгами)
  2002 → w.reg.minshr   (минимальный паевой)
  2003 → w.wal.sharid   (паевой фонд РИД)
  3001 → w.reg.entry    (вступительные)
  3002 → w.sov.member   (членские)
  3003 → w.sov.delgte   (делегатские)
  4001 → w.wal.wthdrw   (возвраты пайщикам)
  4002 → w.mkt.payout   (выплаты поставщикам)
  4051 → w.cap.loan     (займы)
  5001 → w.led.adjust   (ручные корректировки)
  9001 → w.cap.bginv    (Благорост — деньги)
  9002 → w.cap.bgrid    (Благорост — РИД)
  9003 → w.cap.bgprop   (Благорост — имущество)
  9004 → w.cap.bgmem    (Благорост — членские)
  10001 → w.cap.gncom   (Генератор — коммит)
  10002 → w.cap.gnmem   (Генератор — членские)
  11001 → w.mkt.fund    (Стол Заказов)
  0     → eosio::name{} (sentinel «вне системы» для ISSUE/REVOKE)

Затронуто:
  • C++ контракты: wallets.hpp, operations.hpp, table_ledger2_wallet.hpp
    (id: uint64_t → eosio::name, primary_key() = id.value), actions
    walletop/walmove/revert.
  • cooptypes: WalletMeta { name: IName, human_name }, OperationMeta
    wallet_from/to: IName | null, IWallet2.id, IWalmove, IRevert.
  • controller: DTO Int → String, getLedger2History разделён на accountId
    (число) + walletName (строка), repository без ::bigint для wallet полей.
  • SDK zeus/index.ts: 4 секции типов под String + walletName поле.
  • desktop: WalletIdCell prop wallet-name, store getWalletByName, query
    param wallet_name=, OperationsPage filter walletName.
  • YAML стандарты: 10 файлов processes capital/marketplace/registrator/
    soviet/wallet — wallet_from/wallet_to ('' для sentinel).
  • boot tests: walmove/migrate/read-layer на eosio::name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
coopops
2026-04-27 07:40:31 +00:00
parent 77734368b9
commit 184530dab7
43 changed files with 974 additions and 580 deletions
+2 -2
View File
@@ -23,8 +23,8 @@ export async function walmove(
login: LoginResult,
coopname: string,
username: string,
fromWallet: number,
toWallet: number,
fromWallet: string,
toWallet: string,
quantity: string,
memo: string,
): Promise<{ processHash: string; transactionId: string }> {
@@ -8,15 +8,16 @@ import { LedgerAccountType } from './wallet/walletUtils'
// =============================================================================
// Epic 1 addendum (пересмотр 2026-04-20): миграция переведена на 6 прямых
// TRANSIT_* проводок без транзитного счёта 99 и без кошелька CASH_MAIN (1001).
// TRANSIT_* проводок без транзитного счёта 99 и без кошелька CASH_MAIN.
// 2026-04-27: numeric wallet id → eosio::name w.<contract>.<waltype>.
//
// Семантика:
// TRANSIT_MIN_SHARE → Dr 51 / Cr 80, ISSUE MIN_SHARE_FUND 2002 (N × min_share)
// TRANSIT_BLAGOROST → Dr 51 / Cr 80, ISSUE BLAGOROST_INVEST 9001
// TRANSIT_SHARE → Dr 51 / Cr 80, ISSUE SHARE_FUND_PAY 2001 (остаток money)
// TRANSIT_ENTRY → Dr 51 / Cr 86, ISSUE ENTRANCE_FEES 3001
// TRANSIT_COMMITMENT → Dr 08 / Cr 80, ISSUE GENERATOR_COMMIT 10001
// TRANSIT_RID → Dr 04 / Cr 80, ISSUE SHARE_FUND_RID 2003
// TRANSIT_MIN_SHARE → Dr 51 / Cr 80, ISSUE MIN_SHARE_FUND (w.reg.minshr)
// TRANSIT_BLAGOROST → Dr 51 / Cr 80, ISSUE BLAGOROST_INVEST (w.cap.bginv)
// TRANSIT_SHARE → Dr 51 / Cr 80, ISSUE SHARE_FUND_PAY (w.wal.share)
// TRANSIT_ENTRY → Dr 51 / Cr 86, ISSUE ENTRANCE_FEES (w.reg.entry)
// TRANSIT_COMMITMENT → Dr 08 / Cr 80, ISSUE GENERATOR_COMMIT (w.cap.gncom)
// TRANSIT_RID → Dr 04 / Cr 80, ISSUE SHARE_FUND_RID (w.wal.sharid)
//
// План счетов: 04, 08, 51, 58, 80, 86 (99 удалён как лишний транзит).
// =============================================================================
@@ -32,10 +33,10 @@ const LEGACY_ACCOUNTS = {
} as const
const LEDGER2_WALLETS = {
SHARE_FUND_PAY: 2001,
MIN_SHARE_FUND: 2002,
SHARE_FUND_RID: 2003,
ENTRANCE_FEES: 3001,
SHARE_FUND_PAY: 'w.wal.share',
MIN_SHARE_FUND: 'w.reg.minshr',
SHARE_FUND_RID: 'w.wal.sharid',
ENTRANCE_FEES: 'w.reg.entry',
} as const
const LEDGER2_ACCOUNTS = {
@@ -70,9 +71,9 @@ async function getLegacyLaccount(accountId: number) {
return rows.find((row: any) => Number(row.id) === accountId)
}
async function getLedger2Wallet(walletId: number) {
async function getLedger2Wallet(walletName: string) {
const rows = await blockchain.getTableRows(LEDGER2, COOP, 'wallets', 200)
return rows.find((row: any) => Number(row.id) === walletId)
return rows.find((row: any) => String(row.id) === walletName)
}
async function getLedger2Account(accountId: number) {
@@ -150,7 +151,7 @@ describe('ledger2::migrate (пересмотр 2026-04-20: 6 TRANSIT_* без 99
baselineShareAcc = parseAssetAmount((await getLedger2Account(LEDGER2_ACCOUNTS.SHARE_FUND))?.credit_balance)
baselineEntryAcc = parseAssetAmount((await getLedger2Account(LEDGER2_ACCOUNTS.TARGET_RECEIPTS))?.credit_balance)
// Baseline Σ wallets (2001+2002+3001+2003): testnet-boot мог создать
// Baseline Σ wallets (w.wal.share + w.reg.minshr + w.reg.entry + w.wal.sharid): testnet-boot мог создать
// начальные балансы для пайщиков. AC8 проверяет что seed добавил ровно
// seedCash сверху baseline.
const sharePayW = await getLedger2Wallet(LEDGER2_WALLETS.SHARE_FUND_PAY)
@@ -241,15 +242,15 @@ describe('ledger2::migrate (пересмотр 2026-04-20: 6 TRANSIT_* без 99
expect(parseAssetAmount(entryAcc.balance)).toBeCloseTo(parseAssetAmount(entryAcc.credit_balance) - parseAssetAmount(entryAcc.debit_balance), 4)
})
it('AC7: wallet CASH_MAIN (1001) больше НЕ существует — убран из плана кошельков', async () => {
it('AC7: wallet CASH_MAIN больше НЕ существует — убран из плана кошельков', async () => {
// Пересмотр 2026-04-20: CASH_MAIN удалён, счёт 51 ведётся только в accounts2.
// Никаких wallet-записей под id=1001 быть не должно.
// Никаких wallet-записей под устаревшим именем быть не должно.
const rows = await blockchain.getTableRows(LEDGER2, COOP, 'wallets', 200)
const legacyCashMain = rows.find((row: any) => Number(row.id) === 1001)
expect(legacyCashMain, 'wallet 1001 (CASH_MAIN) не должен существовать после migrate').toBeUndefined()
const legacyCashMain = rows.find((row: any) => String(row.id) === 'w.wal.cash')
expect(legacyCashMain, 'wallet CASH_MAIN не должен существовать после migrate').toBeUndefined()
})
it('AC8: Σ wallets (2001 + 2002 + 3001 + 2003) == baseline + seedCash (инвариант миграции)', async () => {
it('AC8: Σ wallets (w.wal.share + w.reg.minshr + w.reg.entry + w.wal.sharid) == baseline + seedCash (инвариант миграции)', async () => {
if (migrateWasAlreadyDone) {
console.log('⏩ migrate был ранее — AC8 проверка Σ wallets пропущена (неизвестен baseline)')
return
@@ -267,8 +268,8 @@ describe('ledger2::migrate (пересмотр 2026-04-20: 6 TRANSIT_* без 99
// Инвариант: seed добавил ровно seedCash в Σ money-кошельков над baseline.
// Baseline снимался ДО seed (в первом it) — теперь baseline + seedCash.
// Благорост (9001) и Генератор (10001) в эту сумму не входят — они emplace-ятся
// отдельно из soviet::progwallets без участия в бух-проводках.
// Благорост (w.cap.bginv) и Генератор (w.cap.gncom) в эту сумму не входят — они
// emplace-ятся отдельно из soviet::progwallets без участия в бух-проводках.
expect(totalWallets).toBeCloseTo(baselineWalletsTotal + seedCash, 4)
})
@@ -24,9 +24,10 @@ const ACCOUNT_TARGET = 86_000
const ACCOUNT_TYPE_ACTIVE = 0
const ACCOUNT_TYPE_PASSIVE = 1
// Кошельки кооператива после миграции (пересмотр 2026-04-20: CASH_MAIN удалён).
const WALLET_SHARE_FUND_PAY = 2001
const WALLET_ENTRANCE_FEES = 3001
// Кошельки кооператива после миграции (пересмотр 2026-04-20: CASH_MAIN удалён;
// 2026-04-27: numeric id → eosio::name w.<contract>.<waltype>).
const WALLET_SHARE_FUND_PAY = 'w.wal.share'
const WALLET_ENTRANCE_FEES = 'w.reg.entry'
const ACCOUNTS_QUERY = `query($c:String!){
getLedger2Accounts(coopname:$c){ id name balance debitBalance creditBalance accountType }
@@ -125,15 +126,15 @@ describe('Ledger2 read-layer (Story 1.23)', () => {
expect(Array.isArray(wallets)).toBe(true)
expect(wallets.length).toBeGreaterThanOrEqual(3)
const byId = new Map<number, any>(wallets.map((w) => [Number(w.id), w]))
expect(byId.get(WALLET_SHARE_FUND_PAY), 'кошелёк паевых взносов 2001').toBeDefined()
expect(byId.get(WALLET_ENTRANCE_FEES), 'кошелёк вступ.взносов 3001').toBeDefined()
// CASH_MAIN (1001) удалён пересмотром 2026-04-20 — счёт 51 ведётся только в accounts2.
expect(byId.get(1001), 'wallet 1001 (CASH_MAIN) удалён, проверка на отсутствие').toBeUndefined()
const byId = new Map<string, any>(wallets.map((w) => [String(w.id), w]))
expect(byId.get(WALLET_SHARE_FUND_PAY), 'кошелёк паевых взносов w.wal.share').toBeDefined()
expect(byId.get(WALLET_ENTRANCE_FEES), 'кошелёк вступ.взносов w.reg.entry').toBeDefined()
// CASH_MAIN удалён пересмотром 2026-04-20 — счёт 51 ведётся только в accounts2.
expect(byId.get('w.wal.cash'), 'устаревший CASH_MAIN отсутствует в реестре').toBeUndefined()
const onchain = await bc.getTableRows('ledger2', COOP, 'wallets', 500)
for (const w of onchain) {
const match = byId.get(Number(w.id))
const match = byId.get(String(w.id))
if (!match) continue
expect(parseAssetAmount(match.available)).toBeCloseTo(parseAssetAmount(w.available), 4)
expect(parseAssetAmount(match.blocked)).toBeCloseTo(parseAssetAmount(w.blocked), 4)
@@ -148,9 +149,10 @@ describe('Ledger2 read-layer (Story 1.23)', () => {
expect(resp.totalCount).toBeGreaterThan(0)
expect(resp.items.length).toBeGreaterThan(0)
// Каждая операция — одного из 4 типов blockchain-actions ledger2.
// Каждая операция — одного из типов blockchain-actions ledger2:
// штатное трио apply/walletop/debit/credit + adjustment-actions walmove/revert.
for (const op of resp.items) {
expect(['apply', 'walletop', 'debit', 'credit']).toContain(op.action)
expect(['apply', 'walletop', 'debit', 'credit', 'walmove', 'revert']).toContain(op.action)
}
// Фильтр по operationCodes: если есть o.mig.share (транзит паевого фонда), то
+14 -13
View File
@@ -1,13 +1,14 @@
/**
* Integration: walmove (operation `o.adj.walmove`) — перевод между кошельками
* одного бух.счёта (без Dr/Cr). Кейс: после регистрации пайщика на 2002
* (MIN_SHARE_FUND) и 2001 (SHARE_FUND_PAY) лежит вместе X RUB на 80-м счёте,
* нам нужно перенести часть с 2002 на 2001 (например, реструктуризация
* минимального паевого взноса в обычный после criteria-аудита).
* одного бух.счёта (без Dr/Cr). Кейс: после регистрации пайщика на
* `w.reg.minshr` (MIN_SHARE_FUND) и `w.wal.share` (SHARE_FUND_PAY) лежит
* вместе X RUB на 80-м счёте, нам нужно перенести часть с MIN_SHARE_FUND на
* SHARE_FUND_PAY (например, реструктуризация минимального паевого взноса
* в обычный после criteria-аудита).
*
* Проверяет:
* - mutation walmoveWallets отрабатывает успешно
* - balance кошельков 2002 (источник) и 2001 (получатель) изменился на amount
* - balance кошельков `w.reg.minshr` и `w.wal.share` изменился на amount
* - account 80 (Cr SHARE_FUND) ≡ не изменился (WALLET_ONLY не трогает счета)
* - в getLedger2History появилась запись с operation_code='o.adj.walmove'
* - попытка перевода между кошельками разных счетов отбивается на backend
@@ -24,9 +25,9 @@ import { generateRandomUsername } from '../utils/randomUsername'
const COOP = 'voskhod'
const WALLET_MIN_SHARE_FUND = 2002
const WALLET_SHARE_FUND_PAY = 2001
const WALLET_ENTRANCE_FEES = 3001
const WALLET_MIN_SHARE_FUND = 'w.reg.minshr'
const WALLET_SHARE_FUND_PAY = 'w.wal.share'
const WALLET_ENTRANCE_FEES = 'w.reg.entry'
const ACCOUNT_SHARE_FUND = 80_000
@@ -60,15 +61,15 @@ function parseAsset(raw: string | null | undefined): number {
* Читаем балансы кошельков напрямую с блокчейна — parser отстаёт от head_block
* на минуты, GraphQL-снимок ненадёжен для проверок «сразу после транзакции».
*/
async function getWalletAvailable(walletId: number): Promise<number> {
async function getWalletAvailable(walletName: string): Promise<number> {
const rows = await bc.getTableRows('ledger2', COOP, 'wallets', 200)
const w = rows.find((r: { id: number | string }) => Number(r.id) === walletId)
const w = rows.find((r: { id: string }) => String(r.id) === walletName)
return w ? parseAsset((w as { available: string }).available) : 0
}
describe('walmove (operation o.adj.walmove) — корректировка между кошельками одного бух.счёта', () => {
it('переводит сумму между двумя кошельками счёта 80 без изменения balance счёта', async () => {
// 1. Регистрируем нового пайщика — пополняет 2002 (MIN_SHARE_FUND) и 3001 (ENTRANCE_FEES)
// 1. Регистрируем нового пайщика — пополняет w.reg.minshr (MIN_SHARE_FUND) и w.reg.entry (ENTRANCE_FEES)
const username = generateRandomUsername()
const participant = await registerUser(bc, COOP, username)
expect(participant).toBeDefined()
@@ -85,7 +86,7 @@ describe('walmove (operation o.adj.walmove) — корректировка ме
expect(moveAmount).toBeGreaterThan(0)
expect(fromBefore).toBeGreaterThanOrEqual(moveAmount)
// 3. Перевод 2002 → 2001 (оба кошелька на счёте 80)
// 3. Перевод w.reg.minshr → w.wal.share (оба кошелька на счёте 80)
const memo = `walmove test: ${username}`
const { processHash } = await walmove(
login, COOP, COOP,
@@ -122,7 +123,7 @@ describe('walmove (operation o.adj.walmove) — корректировка ме
}, 240_000)
it('отказывает в переводе между кошельками разных бух.счетов (валидация в backend)', async () => {
// 2002 (MIN_SHARE_FUND, на счёте 80) → 3001 (ENTRANCE_FEES, на счёте 86) — разные счета.
// w.reg.minshr (MIN_SHARE_FUND, на счёте 80) → w.reg.entry (ENTRANCE_FEES, на счёте 86) — разные счета.
let errMessage = ''
try {
await walmove(
@@ -3,8 +3,8 @@
# фонда кооператива и его возврат через акт приёма-передачи проекта.
#
# Многоактовый процесс с авторизацией советом. Две операции:
# • o.cap.lend — выдача займа на момент авторизации (ISSUE → 4051, Дт 58 / Кт 51)
# • o.cap.repay — возврат займа из акта-2 (TRANSFER 4051 → 2001, Дт 80 / Кт 58)
# • o.cap.lend — выдача займа на момент авторизации (ISSUE → w.cap.loan, Дт 58 / Кт 51)
# • o.cap.repay — возврат займа из акта-2 (TRANSFER w.cap.loan → w.wal.share, Дт 80 / Кт 58)
#
# Источники правды в коде:
# • cpp/capital/capital.hpp — actions
@@ -29,8 +29,8 @@ purpose: >
Gateway выплачивает деньги пайщику — в этот момент применяется операция
o.cap.lend (выдача займа со счёта 51 в счёт 58). Возврат займа происходит
на этапе сдачи акта-2 проекта: операция o.cap.repay переводит средства
с кошелька «Выданные займы» (4051) на «ЦПП Цифровой Кошелёк» пайщика
(2001) с проводкой Дт 80 / Кт 58 — паевой фонд закрывает заём.
с кошелька «Выданные займы» (w.cap.loan) на «ЦПП Цифровой Кошелёк» пайщика
(w.wal.share) с проводкой Дт 80 / Кт 58 — паевой фонд закрывает заём.
roles:
- contributor
- chairman
@@ -204,7 +204,7 @@ scenario:
action: capital::debtauthcnfr
description: >
Совет авторизует выплату. В ledger2 применяется операция o.cap.lend
(ISSUE → 4051, Дт 58 / Кт 51), Gateway получает поручение на
(ISSUE → w.cap.loan, Дт 58 / Кт 51), Gateway получает поручение на
исходящий платёж. Статус → `authorized`.
- step: 4
@@ -262,8 +262,8 @@ operations:
- ledger_code: o.cap.lend
human_name: Выдача пайщику беспроцентного займа
wallet_op: ISSUE
wallet_from: null
wallet_to: 4051 # Выданные пайщикам беспроцентные займы
wallet_from: ''
wallet_to: w.cap.loan # Выданные пайщикам беспроцентные займы
debit: 58 # Финансовые вложения
credit: 51 # Расчётный счёт
amount_ref: debt.amount
@@ -271,14 +271,14 @@ operations:
description: >
Выдача беспроцентного займа пайщику. Деньги уходят с расчётного счёта
кооператива (Кт 51) и появляются как финансовое вложение Дт 58.
Кошелёк «Выданные пайщикам беспроцентные займы» (4051) фиксирует
Кошелёк «Выданные пайщикам беспроцентные займы» (w.cap.loan) фиксирует
обязательство пайщика перед кооперативом.
- ledger_code: o.cap.repay
human_name: Возврат беспроцентного займа пайщика по акту-2
wallet_op: TRANSFER
wallet_from: 4051 # Выданные пайщикам беспроцентные займы
wallet_to: 2001 # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_from: w.cap.loan # Выданные пайщикам беспроцентные займы
wallet_to: w.wal.share # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
debit: 80 # Паевой фонд (складочный капитал)
credit: 58 # Финансовые вложения
amount_ref: debt.amount
@@ -1,7 +1,7 @@
# ─────────────────────────────────────────────────────────────────────────────
# Стандарт «Инвестиция в ЦПП «Благорост»» — перенос ранее внесённого паевого
# взноса с кошелька SHARE_FUND_PAY (2001) на инвестиционный кошелёк программы
# «Благорост» (9001) без бухгалтерских проводок.
# взноса с кошелька SHARE_FUND_PAY (w.wal.share) на инвестиционный кошелёк программы
# «Благорост» (w.cap.bginv) без бухгалтерских проводок.
#
# Одноактовый процесс. В ledger2 — одна операция o.cap.invest c wallet_op =
# WALLET_ONLY: средства переходят между двумя аналитическими кошельками
@@ -27,8 +27,8 @@ summary: >
своими кошельками.
purpose: >
Одноактовый процесс. Пайщик подписывает заявление об инвестировании,
контракт переносит сумму с его кошелька SHARE_FUND_PAY (2001, паевые
взносы деньгами) на BLAGOROST_INVEST (9001, инвестиции в «Благорост»).
контракт переносит сумму с его кошелька SHARE_FUND_PAY (w.wal.share, паевые
взносы деньгами) на BLAGOROST_INVEST (w.cap.bginv, инвестиции в «Благорост»).
Бухгалтерские проводки не делаются: это аналитическое перемещение в рамках
одной общей суммы паевого фонда. Денежные средства фактически остаются на
расчётном счёте кооператива, но в учёте пайщика они теперь считаются
@@ -45,8 +45,8 @@ actions:
purpose: >
Закрывающее одношаговое действие: пайщик подписывает заявление об
инвестировании в «Благорост», контракт применяет ledger2-операцию
o.cap.invest и средства аналитически переходят с SHARE_FUND_PAY (2001)
на BLAGOROST_INVEST (9001).
o.cap.invest и средства аналитически переходят с SHARE_FUND_PAY (w.wal.share)
на BLAGOROST_INVEST (w.cap.bginv).
# ── Секция 3. Граф состояний ────────────────────────────────────────────────
entity: capital::invest
@@ -57,8 +57,8 @@ states:
- name: invested
human: Инвестировано
description: >
Сумма перенесена с кошелька SHARE_FUND_PAY (2001) пайщика на
BLAGOROST_INVEST (9001). Бухгалтерия не затронута — это аналитический
Сумма перенесена с кошелька SHARE_FUND_PAY (w.wal.share) пайщика на
BLAGOROST_INVEST (w.cap.bginv). Бухгалтерия не затронута — это аналитический
сдвиг внутри паевого фонда.
kind: final
@@ -72,7 +72,7 @@ transitions:
- o.cap.invest
guards:
- Пайщик имеет статус active.
- Сумма ≤ доступного остатка на SHARE_FUND_PAY (2001).
- Сумма ≤ доступного остатка на SHARE_FUND_PAY (w.wal.share).
- invest_hash уникален.
- Заявление об инвестировании подписано пайщиком.
@@ -86,7 +86,7 @@ scenario:
description: >
Пайщик подписывает заявление об инвестировании и вызывает
capital::createpinv. Контракт переводит указанную сумму с кошелька
SHARE_FUND_PAY (2001) на BLAGOROST_INVEST (9001) — операция
SHARE_FUND_PAY (w.wal.share) на BLAGOROST_INVEST (w.cap.bginv) — операция
o.cap.invest, wallet_op WALLET_ONLY (без проводок). Запись в
реестре фиксирует факт инвестиции.
pre:
@@ -94,7 +94,7 @@ scenario:
- Достаточный остаток на SHARE_FUND_PAY.
- Заявление подписано ЭЦП.
post:
- Сумма переведена 2001 → 9001 (аналитический сдвиг).
- Сумма переведена w.wal.share → w.cap.bginv (аналитический сдвиг).
- В ledger2 применена операция o.cap.invest.
- Создана запись инвестиции в таблице.
@@ -115,8 +115,8 @@ operations:
- ledger_code: o.cap.invest
human_name: Инвестиция в ЦПП «Благорост» (перенос между кошельками)
wallet_op: WALLET_ONLY
wallet_from: 2001 # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_to: 9001 # ЦПП «Благорост» — инвестиции деньгами
wallet_from: w.wal.share # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_to: w.cap.bginv # ЦПП «Благорост» — инвестиции деньгами
debit: null
credit: null
amount_ref: invest.quantity
@@ -4,7 +4,7 @@
#
# Шестиактовый процесс с двумя последовательными актами и одной операцией.
# В ledger2 — одна операция o.cap.actprp на закрывающем действии акта-2
# (ISSUE → 9003, Дт 51 / Кт 80).
# (ISSUE → w.cap.bgprop, Дт 51 / Кт 80).
#
# Источники правды в коде:
# • cpp/capital/capital.hpp — actions
@@ -29,7 +29,7 @@ purpose: >
его одобряет, совет авторизует. Передача оформляется двумя актами:
пайщик ставит первую подпись на акте приёма-передачи, председатель —
вторую (закрывающее действие). На второй подписи применяется ledger2-операция
o.cap.actprp (ISSUE → 9003, Дт 51 / Кт 80) — имущество учитывается
o.cap.actprp (ISSUE → w.cap.bgprop, Дт 51 / Кт 80) — имущество учитывается
как паевой взнос на кошельке «Благорост — имущественные паевые взносы».
roles:
- contributor
@@ -71,7 +71,7 @@ actions:
role: closer
purpose: >
Председатель ставит вторую подпись на акте приёма-передачи — закрывающее
действие. Применяется ledger2-операция o.cap.actprp (ISSUE → 9003, Дт 51 / Кт 80).
действие. Применяется ledger2-операция o.cap.actprp (ISSUE → w.cap.bgprop, Дт 51 / Кт 80).
- name: capital::declinepgprp
human: Отклонить
actor: chairman
@@ -108,7 +108,7 @@ states:
human: Имущество принято
description: >
Председатель поставил вторую подпись на акте приёма-передачи: имущество учтено как паевой взнос на
кошельке «Благорост — имущественные паевые взносы» (9003) с проводкой
кошельке «Благорост — имущественные паевые взносы» (w.cap.bgprop) с проводкой
Дт 51 / Кт 80.
kind: final
- name: removed
@@ -212,7 +212,7 @@ scenario:
description: >
Председатель ставит вторую подпись на акте приёма-передачи —
закрывающее действие. Применяется
ledger2-операция o.cap.actprp (ISSUE → 9003, Дт 51 / Кт 80) — имущество
ledger2-операция o.cap.actprp (ISSUE → w.cap.bgprop, Дт 51 / Кт 80) — имущество
учтено как паевой взнос.
alternatives:
@@ -263,15 +263,15 @@ operations:
- ledger_code: o.cap.actprp
human_name: Паевой взнос (имущественный) по программе «Благорост»
wallet_op: ISSUE
wallet_from: null
wallet_to: 9003 # ЦПП «Благорост» — имущественные паевые взносы
wallet_from: ''
wallet_to: w.cap.bgprop # ЦПП «Благорост» — имущественные паевые взносы
debit: 51 # Расчётный счёт
credit: 80 # Паевой фонд (складочный капитал)
amount_ref: property.amount
triggered_by: capital::act2pgprp
description: >
Зачисление имущества (по сумме оценки) на кошелёк «Благорост —
имущественные паевые взносы» (9003). Двойная запись Дт 51 / Кт 80 —
имущественные паевые взносы» (w.cap.bgprop). Двойная запись Дт 51 / Кт 80 —
кооператив принял имущество и оно учтено как часть паевого фонда.
# ── Секция 7. Связи ─────────────────────────────────────────────────────────
@@ -4,8 +4,8 @@
# паевого взноса.
#
# Многоэтапный процесс. Три ledger2-операции:
# • o.cap.commit — коммит РИД при одобрении (Дт 8 / Кт 80, кошелёк 10001)
# • o.cap.accept — приём РИД в паевой фонд по акту-2 (TRANSFER 10001 → 9002, Дт 4 / Кт 8)
# • o.cap.commit — коммит РИД при одобрении (Дт 8 / Кт 80, кошелёк w.cap.gncom)
# • o.cap.accept — приём РИД в паевой фонд по акту-2 (TRANSFER w.cap.gncom → w.cap.bgrid, Дт 4 / Кт 8)
# • o.cap.repay — опционально, если у участника есть заём (см. p.cap.debt)
#
# Источники правды в коде:
@@ -35,7 +35,7 @@ purpose: >
(Дт 8 / Кт 80, кошелёк «Генератор — принятый коммит»). Затем РИД
передаётся через акт приёма-передачи: первую подпись ставит участник,
вторую — председатель; на второй подписи применяется o.cap.accept
(TRANSFER 10001 → 9002, Дт 4 / Кт 8) —
(TRANSFER w.cap.gncom → w.cap.bgrid, Дт 4 / Кт 8) —
РИД переходит в паевой фонд «Благорост — принятые РИД». Если у участника
был заём проекта (см. p.cap.debt), на акте-2 дополнительно срабатывает
o.cap.repay (зачёт займа в паевой фонд).
@@ -62,7 +62,7 @@ actions:
purpose: >
Мастер проекта одобряет коммит. На этом этапе применяется ledger2-
операция o.cap.commit — РИД зачисляется на «Генератор — принятый
коммит» (10001) с проводкой Дт 8 / Кт 80.
коммит» (w.cap.gncom) с проводкой Дт 8 / Кт 80.
- name: capital::declinecmmt
human: Отклонить коммит
actor: master
@@ -110,7 +110,7 @@ states:
human: Коммит одобрен
description: >
Мастер одобрил коммит. РИД зачислен на кошелёк «Генератор — принятый
коммит» (10001) с проводкой Дт 8 / Кт 80. Ожидается сдача актами.
коммит» (w.cap.gncom) с проводкой Дт 8 / Кт 80. Ожидается сдача актами.
kind: normal
- name: pushed
human: Результат сдан
@@ -127,8 +127,8 @@ states:
- name: accepted
human: РИД принят в паевой фонд
description: >
Председатель поставил вторую подпись на акте приёма-передачи: РИД переведён с «Генератора» (10001)
на «Благорост — принятые РИД» (9002), Дт 4 / Кт 8. Если у участника
Председатель поставил вторую подпись на акте приёма-передачи: РИД переведён с «Генератора» (w.cap.gncom)
на «Благорост — принятые РИД» (w.cap.bgrid), Дт 4 / Кт 8. Если у участника
был заём проекта — он закрылся (o.cap.repay). РИД считается
имущественным паевым взносом.
kind: final
@@ -210,7 +210,7 @@ scenario:
description: >
Мастер проекта одобряет коммит. Применяется ledger2-операция
o.cap.commit — РИД зачисляется на «Генератор — принятый коммит»
(10001) с проводкой Дт 8 / Кт 80.
(w.cap.gncom) с проводкой Дт 8 / Кт 80.
- step: 3
title: Сдача результата
@@ -236,9 +236,9 @@ scenario:
действие. Применяются
две ledger2-операции:
(1) o.cap.accept — РИД переходит с «Генератор — принятый коммит»
(10001) на «Благорост — принятые РИД» (9002), Дт 4 / Кт 8;
(w.cap.gncom) на «Благорост — принятые РИД» (w.cap.bgrid), Дт 4 / Кт 8;
(2) опционально o.cap.repay — если у участника был заём проекта,
заём закрывается (4051 → 2001, Дт 80 / Кт 58).
заём закрывается (w.cap.loan → w.wal.share, Дт 80 / Кт 58).
alternatives:
- branch: Отказ мастера
@@ -280,22 +280,22 @@ operations:
- ledger_code: o.cap.commit
human_name: Коммит РИД по программе «Благорост»
wallet_op: ISSUE
wallet_from: null
wallet_to: 10001 # ЦПП «Генератор» — принятый коммит (имущество)
wallet_from: ''
wallet_to: w.cap.gncom # ЦПП «Генератор» — принятый коммит (имущество)
debit: 8 # Вложения во внеоборотные активы
credit: 80 # Паевой фонд (складочный капитал)
amount_ref: commit.delta_amount
triggered_by: capital::approvecmmt
description: >
Зачисление РИД-коммита на кошелёк «Генератор — принятый коммит» (10001)
Зачисление РИД-коммита на кошелёк «Генератор — принятый коммит» (w.cap.gncom)
при одобрении мастером. Двойная запись Дт 8 / Кт 80 — РИД учитывается
как вложение во внеоборотные активы и формирует паевой фонд.
- ledger_code: o.cap.accept
human_name: Приём РИД в паевой фонд
wallet_op: TRANSFER
wallet_from: 10001 # ЦПП «Генератор» — принятый коммит
wallet_to: 9002 # ЦПП «Благорост» — принятые РИД
wallet_from: w.cap.gncom # ЦПП «Генератор» — принятый коммит
wallet_to: w.cap.bgrid # ЦПП «Благорост» — принятые РИД
debit: 4 # Нематериальные активы
credit: 8 # Вложения во внеоборотные активы
amount_ref: result.amount
@@ -308,8 +308,8 @@ operations:
- ledger_code: o.cap.repay
human_name: Возврат беспроцентного займа пайщика по акту-2
wallet_op: TRANSFER
wallet_from: 4051 # Выданные пайщикам беспроцентные займы
wallet_to: 2001 # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_from: w.cap.loan # Выданные пайщикам беспроцентные займы
wallet_to: w.wal.share # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
debit: 80 # Паевой фонд (складочный капитал)
credit: 58 # Финансовые вложения
amount_ref: debt.amount
+7 -7
View File
@@ -85,8 +85,8 @@ public:
*/
[[eosio::action]] void walletop(eosio::name coopname,
uint8_t op_code,
uint64_t wallet_from,
uint64_t wallet_to,
eosio::name wallet_from,
eosio::name wallet_to,
eosio::asset amount,
eosio::checksum256 process_hash,
std::string memo);
@@ -164,8 +164,8 @@ public:
[[eosio::action]] void walmove(eosio::name coopname,
eosio::name initiator,
eosio::name username,
uint64_t from_wallet,
uint64_t to_wallet,
eosio::name from_wallet,
eosio::name to_wallet,
eosio::asset amount,
eosio::checksum256 process_hash,
std::string memo);
@@ -194,7 +194,7 @@ public:
* @param amount сумма (как в оригинале)
* @param mirror_wallet_op тип wallet-операции зеркала (REVOKE/TRANSFER/WALLET_ONLY)
* @param mirror_wallet_from кошелёк-источник зеркала
* @param mirror_wallet_to кошелёк-получатель зеркала (0 для REVOKE)
* @param mirror_wallet_to кошелёк-получатель зеркала (пустое имя для REVOKE)
* @param mirror_debit_account_id Dr-счёт зеркала (0 для WALLET_ONLY)
* @param mirror_credit_account_id Cr-счёт зеркала (0 для WALLET_ONLY)
* @param process_hash уникальный хэш для зеркальной операции
@@ -209,8 +209,8 @@ public:
eosio::name username,
eosio::asset amount,
uint8_t mirror_wallet_op,
uint64_t mirror_wallet_from,
uint64_t mirror_wallet_to,
eosio::name mirror_wallet_from,
eosio::name mirror_wallet_to,
uint64_t mirror_debit_account_id,
uint64_t mirror_credit_account_id,
eosio::checksum256 process_hash,
@@ -29,8 +29,8 @@ void ledger2::revert(eosio::name coopname,
eosio::name username,
eosio::asset amount,
uint8_t mirror_wallet_op,
uint64_t mirror_wallet_from,
uint64_t mirror_wallet_to,
eosio::name mirror_wallet_from,
eosio::name mirror_wallet_to,
uint64_t mirror_debit_account_id,
uint64_t mirror_credit_account_id,
eosio::checksum256 process_hash,
@@ -75,13 +75,13 @@ void ledger2::revert(eosio::name coopname,
"revert: BLOCK/UNBLOCK не подлежат откату через revert (они симметричны сами себе)");
// -------- validate mirror wallets/accounts --------
if (mirror_wallet_from != 0) {
eosio::check(!ledger2_get_wallet_name_by_id(mirror_wallet_from).empty(),
std::string{"revert: неизвестный mirror_wallet_from "} + std::to_string(mirror_wallet_from));
if (mirror_wallet_from.value != 0) {
eosio::check(ledger2_is_known_wallet(mirror_wallet_from),
std::string{"revert: неизвестный mirror_wallet_from "} + mirror_wallet_from.to_string());
}
if (mirror_wallet_to != 0) {
eosio::check(!ledger2_get_wallet_name_by_id(mirror_wallet_to).empty(),
std::string{"revert: неизвестный mirror_wallet_to "} + std::to_string(mirror_wallet_to));
if (mirror_wallet_to.value != 0) {
eosio::check(ledger2_is_known_wallet(mirror_wallet_to),
std::string{"revert: неизвестный mirror_wallet_to "} + mirror_wallet_to.to_string());
}
const bool wallet_only = (mirror_wallet_op == static_cast<uint8_t>(WalletOp::WALLET_ONLY));
@@ -18,8 +18,8 @@
void ledger2::walmove(eosio::name coopname,
eosio::name initiator,
eosio::name username,
uint64_t from_wallet,
uint64_t to_wallet,
eosio::name from_wallet,
eosio::name to_wallet,
eosio::asset amount,
eosio::checksum256 process_hash,
std::string memo) {
@@ -44,13 +44,13 @@ void ledger2::walmove(eosio::name coopname,
"walmove: некорректный символ валюты");
// -------- validate wallets --------
eosio::check(from_wallet != 0, "walmove: from_wallet обязателен");
eosio::check(to_wallet != 0, "walmove: to_wallet обязателен");
eosio::check(from_wallet.value != 0, "walmove: from_wallet обязателен");
eosio::check(to_wallet.value != 0, "walmove: to_wallet обязателен");
eosio::check(from_wallet != to_wallet, "walmove: from_wallet == to_wallet");
eosio::check(!ledger2_get_wallet_name_by_id(from_wallet).empty(),
std::string{"walmove: неизвестный from_wallet "} + std::to_string(from_wallet));
eosio::check(!ledger2_get_wallet_name_by_id(to_wallet).empty(),
std::string{"walmove: неизвестный to_wallet "} + std::to_string(to_wallet));
eosio::check(ledger2_is_known_wallet(from_wallet),
std::string{"walmove: неизвестный from_wallet "} + from_wallet.to_string());
eosio::check(ledger2_is_known_wallet(to_wallet),
std::string{"walmove: неизвестный to_wallet "} + to_wallet.to_string());
// -------- validate memo (обязательный для adjustment) --------
eosio::check(!memo.empty(), "walmove: memo обязателен — укажите обоснование");
@@ -22,8 +22,8 @@
[[eosio::action]]
void ledger2::walletop(eosio::name coopname,
uint8_t op_code,
uint64_t wallet_from,
uint64_t wallet_to,
eosio::name wallet_from,
eosio::name wallet_to,
eosio::asset amount,
eosio::checksum256 process_hash,
std::string memo) {
@@ -49,16 +49,16 @@ void ledger2::walletop(eosio::name coopname,
wallets2_index wallets(get_self(), coopname.value);
const eosio::name payer = get_self();
auto upsert_wallet = [&](uint64_t wallet_id) -> wallets2_index::const_iterator {
auto it = wallets.find(wallet_id);
auto upsert_wallet = [&](eosio::name wallet_id) -> wallets2_index::const_iterator {
auto it = wallets.find(wallet_id.value);
if (it == wallets.end()) {
const auto name_view = ledger2_get_wallet_name_by_id(wallet_id);
eosio::check(!name_view.empty(),
std::string{"walletop: unknown wallet id "} + std::to_string(wallet_id));
const std::string name{name_view};
const auto human_view = ledger2_get_wallet_human_name(wallet_id);
eosio::check(!human_view.empty(),
std::string{"walletop: unknown wallet "} + wallet_id.to_string());
const std::string human{human_view};
it = wallets.emplace(payer, [&](auto& w) {
w.id = wallet_id;
w.name = name;
w.name = human;
w.available = eosio::asset(0, amount.symbol);
w.blocked = eosio::asset(0, amount.symbol);
});
@@ -66,8 +66,8 @@ void ledger2::walletop(eosio::name coopname,
return it;
};
auto cleanup_if_empty = [&](uint64_t wallet_id) {
auto it = wallets.find(wallet_id);
auto cleanup_if_empty = [&](eosio::name wallet_id) {
auto it = wallets.find(wallet_id.value);
if (it != wallets.end() && it->is_empty()) {
wallets.erase(it);
}
@@ -75,8 +75,8 @@ void ledger2::walletop(eosio::name coopname,
switch (static_cast<WalletOp>(op_code)) {
case WalletOp::ISSUE: {
eosio::check(wallet_from == 0, "walletop ISSUE: wallet_from должен быть 0");
eosio::check(wallet_to != 0, "walletop ISSUE: требуется wallet_to");
eosio::check(wallet_from.value == 0, "walletop ISSUE: wallet_from должен быть пустым");
eosio::check(wallet_to.value != 0, "walletop ISSUE: требуется wallet_to");
auto it = upsert_wallet(wallet_to);
wallets.modify(it, payer, [&](auto& w) { w.available += amount; });
break;
@@ -85,14 +85,14 @@ void ledger2::walletop(eosio::name coopname,
case WalletOp::WALLET_ONLY: {
// Оба типа — перенос available между кошельками. WALLET_ONLY отличается
// только отсутствием inline debit/credit (см. apply.cpp).
eosio::check(wallet_from != 0 && wallet_to != 0,
eosio::check(wallet_from.value != 0 && wallet_to.value != 0,
"walletop TRANSFER/WALLET_ONLY: требуются wallet_from и wallet_to");
eosio::check(wallet_from != wallet_to,
"walletop TRANSFER/WALLET_ONLY: wallet_from == wallet_to");
auto from_it = wallets.find(wallet_from);
auto from_it = wallets.find(wallet_from.value);
eosio::check(from_it != wallets.end() && from_it->available >= amount,
std::string{"walletop TRANSFER: недостаточно средств на кошельке "} +
std::to_string(wallet_from));
wallet_from.to_string());
wallets.modify(from_it, payer, [&](auto& w) { w.available -= amount; });
auto to_it = upsert_wallet(wallet_to);
wallets.modify(to_it, payer, [&](auto& w) { w.available += amount; });
@@ -100,12 +100,12 @@ void ledger2::walletop(eosio::name coopname,
break;
}
case WalletOp::BLOCK: {
eosio::check(wallet_from != 0, "walletop BLOCK: требуется wallet_from");
eosio::check(wallet_to == 0, "walletop BLOCK: wallet_to должен быть 0");
auto it = wallets.find(wallet_from);
eosio::check(wallet_from.value != 0, "walletop BLOCK: требуется wallet_from");
eosio::check(wallet_to.value == 0, "walletop BLOCK: wallet_to должен быть пустым");
auto it = wallets.find(wallet_from.value);
eosio::check(it != wallets.end() && it->available >= amount,
std::string{"walletop BLOCK: недостаточно available на кошельке "} +
std::to_string(wallet_from));
wallet_from.to_string());
wallets.modify(it, payer, [&](auto& w) {
w.available -= amount;
w.blocked += amount;
@@ -113,12 +113,12 @@ void ledger2::walletop(eosio::name coopname,
break;
}
case WalletOp::UNBLOCK: {
eosio::check(wallet_from != 0, "walletop UNBLOCK: требуется wallet_from");
eosio::check(wallet_to == 0, "walletop UNBLOCK: wallet_to должен быть 0");
auto it = wallets.find(wallet_from);
eosio::check(wallet_from.value != 0, "walletop UNBLOCK: требуется wallet_from");
eosio::check(wallet_to.value == 0, "walletop UNBLOCK: wallet_to должен быть пустым");
auto it = wallets.find(wallet_from.value);
eosio::check(it != wallets.end() && it->blocked >= amount,
std::string{"walletop UNBLOCK: недостаточно blocked на кошельке "} +
std::to_string(wallet_from));
wallet_from.to_string());
wallets.modify(it, payer, [&](auto& w) {
w.blocked -= amount;
w.available += amount;
@@ -128,12 +128,12 @@ void ledger2::walletop(eosio::name coopname,
case WalletOp::REVOKE: {
// Зеркало ISSUE: убираем amount с wallet_from без увеличения куда-либо.
// Используется только из ledger2::revert при откате ISSUE-операций.
eosio::check(wallet_from != 0, "walletop REVOKE: требуется wallet_from");
eosio::check(wallet_to == 0, "walletop REVOKE: wallet_to должен быть 0");
auto it = wallets.find(wallet_from);
eosio::check(wallet_from.value != 0, "walletop REVOKE: требуется wallet_from");
eosio::check(wallet_to.value == 0, "walletop REVOKE: wallet_to должен быть пустым");
auto it = wallets.find(wallet_from.value);
eosio::check(it != wallets.end() && it->available >= amount,
std::string{"walletop REVOKE: недостаточно available на кошельке "} +
std::to_string(wallet_from));
wallet_from.to_string());
wallets.modify(it, payer, [&](auto& w) { w.available -= amount; });
cleanup_if_empty(wallet_from);
break;
@@ -13,7 +13,7 @@
* системы учёта, progwallet.blocked не проводится через 80-й счёт,
* поэтому любая попытка провести его через Dr 51 / Cr 80 вызовет
* двойной учёт на бухуровне. Вместо этого переносим только
* wallet-аналитику (9001 BLAGOROST_INVEST, 10001 GENERATOR_COMMIT).
* wallet-аналитику (w.cap.bginv BLAGOROST_INVEST, w.cap.gncom GENERATOR_COMMIT).
*
* Алгоритм на каждый кооператив:
*
@@ -38,14 +38,14 @@
* share_legacy >= share_money
*
* 4. Отправка 4 inline apply (ненулевые пропускаются):
* apply(migration::MIN_SHARE, min_total) → Dr 51 / Cr 80, MIN_SHARE_FUND 2002
* apply(migration::SHARE, share_remain) → Dr 51 / Cr 80, SHARE_FUND_PAY 2001
* apply(migration::ENTRY, entry_legacy) → Dr 51 / Cr 86, ENTRANCE_FEES 3001
* apply(migration::RID, rid_share) → Dr 04 / Cr 80, SHARE_FUND_RID 2003
* apply(migration::MIN_SHARE, min_total) → Dr 51 / Cr 80, MIN_SHARE_FUND (w.reg.minshr)
* apply(migration::SHARE, share_remain) → Dr 51 / Cr 80, SHARE_FUND_PAY (w.wal.share)
* apply(migration::ENTRY, entry_legacy) → Dr 51 / Cr 86, ENTRANCE_FEES (w.reg.entry)
* apply(migration::RID, rid_share) → Dr 04 / Cr 80, SHARE_FUND_RID (w.wal.sharid)
*
* 5. Прямой emplace в wallets2 для progwallets (БЕЗ бух-проводок):
* Σ progwallet[blagorost].blocked → wallets2[BLAGOROST_INVEST 9001]
* Σ progwallet[generator].blocked → wallets2[GENERATOR_COMMIT 10001]
* Σ progwallet[blagorost].blocked → wallets2[BLAGOROST_INVEST w.cap.bginv]
* Σ progwallet[generator].blocked → wallets2[GENERATOR_COMMIT w.cap.gncom]
*
* Если запись wallets2 уже есть (после inline apply сработали) —
* aggregate через wallets.modify(available += sum). Иначе — emplace.
@@ -168,27 +168,28 @@ inline void send_transit(eosio::name self_name,
*/
inline void emplace_wallet_only(eosio::name self_name,
eosio::name coopname,
uint64_t wallet_id,
eosio::name wallet_id,
const eosio::asset& amt) {
if (amt.amount == 0) return;
wallets2_index wallets(self_name, coopname.value);
const auto name_view = ledger2_get_wallet_name_by_id(wallet_id);
eosio::check(!name_view.empty(),
std::string{"migrate: unknown wallet id "} + std::to_string(wallet_id));
const auto human_view = ledger2_get_wallet_human_name(wallet_id);
eosio::check(!human_view.empty(),
std::string{"migrate: unknown wallet "} + wallet_id.to_string());
auto it = wallets.find(wallet_id);
auto it = wallets.find(wallet_id.value);
if (it == wallets.end()) {
wallets.emplace(self_name, [&](auto& w) {
w.id = wallet_id;
w.name = std::string(name_view);
w.name = std::string(human_view);
w.available = amt;
w.blocked = eosio::asset(0, amt.symbol);
});
} else {
// Если кошелёк уже создан (например, предыдущим inline apply TRANSIT_*) —
// просто доливаем сумму в available. Не должно случаться при чистой
// миграции (9001/10001 не используются базовыми TRANSIT_*), но безопасно.
// миграции (w.cap.bginv/w.cap.gncom не используются базовыми TRANSIT_*),
// но безопасно.
wallets.modify(it, self_name, [&](auto& w) { w.available += amt; });
}
}
@@ -130,14 +130,15 @@ enum class WalletOp : uint8_t {
* @brief Описание одной именованной операции.
*
* Для WALLET_ONLY поля `debit_account_id` / `credit_account_id` равны 0
* (compile-time валидацией ниже).
* (compile-time валидацией ниже). Для ISSUE `wallet_from = eosio::name{}`,
* для BLOCK/UNBLOCK/REVOKE `wallet_to = eosio::name{}`.
*/
struct OperationRegistryEntry {
eosio::name code; ///< operation_code с префиксом `o.<contract>.<verb>`
eosio::name process_type; ///< тип процесса с префиксом `p.<contract>.<noun>`
WalletOp wallet_op;
uint64_t wallet_from; ///< 0 для ISSUE
uint64_t wallet_to; ///< 0 для BLOCK/UNBLOCK
eosio::name wallet_from; ///< пустое имя для ISSUE
eosio::name wallet_to; ///< пустое имя для BLOCK/UNBLOCK/REVOKE
uint64_t debit_account_id; ///< 0 для WALLET_ONLY
uint64_t credit_account_id; ///< 0 для WALLET_ONLY
const char* human_name;
@@ -150,17 +151,17 @@ struct OperationRegistryEntry {
*/
static constexpr OperationRegistryEntry OPERATION_REGISTRY[] = {
// 1. Вступительный взнос: Dr 51 / Cr 86, ISSUE ENTRANCE_FEES
{ operations::registrator::PAY_ENTRANCE, processes::registrator::ACCEPT, WalletOp::ISSUE, 0, ledger2_wallets::ENTRANCE_FEES,
{ operations::registrator::PAY_ENTRANCE, processes::registrator::ACCEPT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::ENTRANCE_FEES,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::TARGET_RECEIPTS,
"Вступительный взнос пайщика" },
// 2. Минимальный паевой взнос (при регистрации): Dr 51 / Cr 80, ISSUE MIN_SHARE_FUND
{ operations::registrator::PUT_MINSHARE, processes::registrator::ACCEPT, WalletOp::ISSUE, 0, ledger2_wallets::MIN_SHARE_FUND,
{ operations::registrator::PUT_MINSHARE, processes::registrator::ACCEPT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::MIN_SHARE_FUND,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Минимальный паевой взнос пайщика при регистрации" },
// 3. Внесение паевого взноса: Dr 51 / Cr 80, ISSUE SHARE_FUND_PAY
{ operations::wallet::COMPLETE_DEPOSIT, processes::wallet::DEPOSIT, WalletOp::ISSUE, 0, ledger2_wallets::SHARE_FUND_PAY,
{ operations::wallet::COMPLETE_DEPOSIT, processes::wallet::DEPOSIT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::SHARE_FUND_PAY,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Внесение пайщиком паевого взноса" },
@@ -171,7 +172,7 @@ static constexpr OperationRegistryEntry OPERATION_REGISTRY[] = {
"Возврат паевого взноса пайщику" },
// 5. Импорт пайщика Благорост (offline): Dr 51 / Cr 80, ISSUE BLAGOROST_INVEST
{ operations::capital::IMPORT, processes::capital::IMPORT, WalletOp::ISSUE, 0, ledger2_wallets::BLAGOROST_INVEST,
{ operations::capital::IMPORT, processes::capital::IMPORT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::BLAGOROST_INVEST,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Паевой взнос по целевой потребительской программе «Благорост» (офлайн-импорт)" },
@@ -182,7 +183,7 @@ static constexpr OperationRegistryEntry OPERATION_REGISTRY[] = {
"Инвестиция в ЦПП «Благорост» (перенос между кошельками паевого фонда)" },
// 7. Коммит РИД: Dr 08 / Cr 80, ISSUE GENERATOR_COMMIT
{ operations::capital::COMMIT_RID, processes::capital::RID, WalletOp::ISSUE, 0, ledger2_wallets::GENERATOR_COMMIT,
{ operations::capital::COMMIT_RID, processes::capital::RID, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::GENERATOR_COMMIT,
ledger2_accounts::NON_CURRENT_INVESTMENTS, ledger2_accounts::SHARE_FUND,
"Коммит результата интеллектуальной деятельности по программе «Благорост»" },
@@ -193,12 +194,12 @@ static constexpr OperationRegistryEntry OPERATION_REGISTRY[] = {
"Приём результата интеллектуальной деятельности в паевой фонд" },
// 9. Акт-2 имущественный паевой взнос: Dr 51 / Cr 80, ISSUE BLAGOROST_PROPERTY
{ operations::capital::ACCEPT_PROPERTY, processes::capital::PROPERTY, WalletOp::ISSUE, 0, ledger2_wallets::BLAGOROST_PROPERTY,
{ operations::capital::ACCEPT_PROPERTY, processes::capital::PROPERTY, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::BLAGOROST_PROPERTY,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Паевой взнос (не денежный) по программе «Благорост»" },
// 10. Выдача беспроцентного займа пайщику: Dr 58 / Cr 51, ISSUE LOAN_ISSUED
{ operations::capital::LEND, processes::capital::DEBT, WalletOp::ISSUE, 0, ledger2_wallets::LOAN_ISSUED,
{ operations::capital::LEND, processes::capital::DEBT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::LOAN_ISSUED,
ledger2_accounts::FINANCIAL_INVESTMENTS, ledger2_accounts::BANK_ACCOUNT,
"Выдача пайщику беспроцентного займа" },
@@ -209,7 +210,7 @@ static constexpr OperationRegistryEntry OPERATION_REGISTRY[] = {
"Возврат беспроцентного займа пайщика по акту-2" },
// 12. Подтверждение поставки: Dr 51 / Cr 80, ISSUE SHARE_FUND_PAY
{ operations::marketplace::CONFIRM_SUPPLY, processes::marketplace::REQUEST, WalletOp::ISSUE, 0, ledger2_wallets::SHARE_FUND_PAY,
{ operations::marketplace::CONFIRM_SUPPLY, processes::marketplace::REQUEST, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::SHARE_FUND_PAY,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Подтверждение поставки товара/услуги" },
@@ -228,22 +229,22 @@ static constexpr OperationRegistryEntry OPERATION_REGISTRY[] = {
// ----- Миграционные (o.mig.*) — вызываются только из migrate.cpp -----
// 15. Миграция: минимальный паевой: Dr 51 / Cr 80, ISSUE MIN_SHARE_FUND
{ operations::migration::MIN_SHARE, processes::migration::TRANSIT, WalletOp::ISSUE, 0, ledger2_wallets::MIN_SHARE_FUND,
{ operations::migration::MIN_SHARE, processes::migration::TRANSIT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::MIN_SHARE_FUND,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Транзитный перенос: минимальные паевые взносы при миграции" },
// 16. Миграция: остаток паевых деньгами: Dr 51 / Cr 80, ISSUE SHARE_FUND_PAY
{ operations::migration::SHARE, processes::migration::TRANSIT, WalletOp::ISSUE, 0, ledger2_wallets::SHARE_FUND_PAY,
{ operations::migration::SHARE, processes::migration::TRANSIT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::SHARE_FUND_PAY,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Транзитный перенос: остаток паевых взносов деньгами при миграции" },
// 17. Миграция: вступительные: Dr 51 / Cr 86, ISSUE ENTRANCE_FEES
{ operations::migration::ENTRY, processes::migration::TRANSIT, WalletOp::ISSUE, 0, ledger2_wallets::ENTRANCE_FEES,
{ operations::migration::ENTRY, processes::migration::TRANSIT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::ENTRANCE_FEES,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::TARGET_RECEIPTS,
"Транзитный перенос: вступительные взносы при миграции" },
// 18. Миграция: РИД в НМА: Dr 04 / Cr 80, ISSUE SHARE_FUND_RID
{ operations::migration::RID, processes::migration::TRANSIT, WalletOp::ISSUE, 0, ledger2_wallets::SHARE_FUND_RID,
{ operations::migration::RID, processes::migration::TRANSIT, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::SHARE_FUND_RID,
ledger2_accounts::INTANGIBLE_ASSETS, ledger2_accounts::SHARE_FUND,
"Транзитный перенос: принятые РИД в паевой фонд при миграции" },
};
@@ -294,6 +295,7 @@ namespace ledger2_registry_detail {
const auto& e = OPERATION_REGISTRY[i];
if (e.wallet_op == WalletOp::TRANSFER || e.wallet_op == WalletOp::WALLET_ONLY) {
if (e.wallet_from == e.wallet_to) return false;
if (e.wallet_from.value == 0 || e.wallet_to.value == 0) return false;
}
}
return true;
@@ -312,8 +314,8 @@ namespace ledger2_registry_detail {
constexpr bool wallets_exist_in_registry() {
for (size_t i = 0; i < OPERATION_REGISTRY_SIZE; ++i) {
const auto& e = OPERATION_REGISTRY[i];
if (e.wallet_from != 0 && ledger2_get_wallet_name_by_id(e.wallet_from).empty()) return false;
if (e.wallet_to != 0 && ledger2_get_wallet_name_by_id(e.wallet_to).empty()) return false;
if (e.wallet_from.value != 0 && !ledger2_is_known_wallet(e.wallet_from)) return false;
if (e.wallet_to.value != 0 && !ledger2_is_known_wallet(e.wallet_to)) return false;
}
return true;
}
@@ -4,8 +4,10 @@
#include <cstdint>
#include <string_view>
#include <eosio/eosio.hpp>
/**
* @brief Стандарт кошельков ledger2 (пересмотр 2026-04-20).
* @brief Стандарт кошельков ledger2 (пересмотр 2026-04-27).
*
* Кошельки ledger2 — это аналитические разрезы бухгалтерских счетов:
* «куда/откуда движутся средства» на уровне ЦПП и фондов. Учёт остатков
@@ -13,71 +15,65 @@
* отдельного «зеркального» wallet-а для 51 НЕТ — любая money-in операция
* сразу адресуется целевому кошельку-назначению.
*
* Нумерация — ×1000, группы:
* 2xxx — Паевой фонд (журнал Cr 80)
* 3xxx — Целевое финансирование (Cr 86)
* 4xxx — Выплаты / обязательства / финансовые вложения (sinks, loans)
* 5xxx — Служебные (ручные корректировки)
* 9xxx — ЦПП «Благорост»
* 10xxx — ЦПП «Генератор» (промежуточное состояние коммитов, Dr 08)
* 11xxx — ЦПП «Marketplace» (резерв)
* Идентификатор кошелька — `eosio::name` с префиксом `w.<contract>.<waltype>`
* (по аналогии с операциями `o.<contract>.<verb>` и процессами
* `p.<contract>.<noun>`). Длина ≤ 13 base32-символов.
*
* Пересмотр 2026-04-20:
* - Удалён 1001 CASH_MAIN: 51-й счёт ведётся только в accounts2.
* - Удалены 4050 LOAN_RECEIVED / 4052 DEBT_CLOSED_SINK: счёт 67 убран,
* займы идут через 58 (cap.loanissue: Dr 58 / Cr 51; cap.loanrepay:
* Dr 80 / Cr 58).
* - Добавлены 9001-9004 Благорост и 10001-10002 Генератор для разнесения
* инвестиций/имущества/коммитов по программам.
* - Добавлен 5001 MANUAL_ADJUST — резерв под ручные корректировки
* (action ledger2::adjust, не реализован в MVP).
* Группы:
* w.wal.* — Паевой фонд (журнал Cr 80) и возвраты пайщикам
* w.reg.* — Регистрация (минимальный паевой, вступительные)
* w.sov.* — Целевое финансирование (членские, делегатские)
* w.cap.* — Программы паевого фонда (Благорост, Генератор) и займы
* w.mkt.* — Маркетплейс (выплаты поставщикам, общий фонд «Стол Заказов»)
* w.led.* — Служебные (ручные корректировки)
*
* При первом ISSUE/TRANSFER кошелёк создаётся автоматически по имени
* Sentinel `eosio::name{}` (пустое имя, value=0) — «кошелёк вне системы»
* для ISSUE (нет wallet_from) и REVOKE (нет wallet_to).
*
* При первом ISSUE/TRANSFER кошелёк создаётся автоматически по записи
* из WALLET_REGISTRY. При обнулении available+blocked запись удаляется.
*
* @ingroup public_ledger2_consts
*/
struct ledger2_wallets {
// Группа 2xxx — паевой фонд (Cr 80)
static constexpr uint64_t SHARE_FUND_PAY = 2001; ///< ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
static constexpr uint64_t MIN_SHARE_FUND = 2002; ///< Минимальный паевой взнос (при регистрации)
static constexpr uint64_t SHARE_FUND_RID = 2003; ///< Паевой фонд — РИД, принятые в НМА (Dr 04 / Cr 80)
// wallet — паевой фонд + возвраты
static constexpr eosio::name SHARE_FUND_PAY = "w.wal.share"_n; ///< ЦПП «Цифровой Кошелёк» — паевые взносы деньгами (Cr 80)
static constexpr eosio::name SHARE_FUND_RID = "w.wal.sharid"_n; ///< Паевой фонд — РИД, принятые в НМА (Dr 04 / Cr 80)
static constexpr eosio::name WITHDRAWALS_SINK = "w.wal.wthdrw"_n; ///< Возвраты паевых взносов пайщикам (sink TRANSFER)
// Группа 3xxx — целевое финансирование (Cr 86)
static constexpr uint64_t ENTRANCE_FEES = 3001; ///< Вступительные взносы
static constexpr uint64_t MEMBERSHIP_FEES = 3002; ///< Членские взносы (платформенные)
static constexpr uint64_t DELEGATE_FEES = 3003; ///< Делегатские членские взносы (цель CONVERT_TO_AXN)
// registrator — минимальный паевой + вступительные
static constexpr eosio::name MIN_SHARE_FUND = "w.reg.minshr"_n; ///< Минимальный паевой взнос при регистрации (Cr 80)
static constexpr eosio::name ENTRANCE_FEES = "w.reg.entry"_n; ///< Вступительные взносы (Cr 86)
// Группа 4xxx — выплаты / обязательства / финансовые вложения
static constexpr uint64_t WITHDRAWALS_SINK = 4001; ///< Возвраты паевых взносов (цель WITHDRAW_COMPLETE)
static constexpr uint64_t SUPPLIER_PAYMENTS = 4002; ///< Выплаты поставщикам (цель RECEIVE_CONFIRM)
static constexpr uint64_t LOAN_ISSUED = 4051; ///< Выданные пайщикам беспроцентные займы (Dr 58 / Cr 51)
// soviet — членские взносы
static constexpr eosio::name MEMBERSHIP_FEES = "w.sov.member"_n; ///< Членские взносы (платформенные) (Cr 86)
static constexpr eosio::name DELEGATE_FEES = "w.sov.delgte"_n; ///< Делегатские членские взносы (цель CONVERT_TO_AXN) (Cr 86)
// Группа 5xxx — служебные
static constexpr uint64_t MANUAL_ADJUST = 5001; ///< Ручные корректировки (резерв под ledger2::adjust)
// capital — программы Благорост, Генератор + займы
static constexpr eosio::name LOAN_ISSUED = "w.cap.loan"_n; ///< Выданные пайщикам беспроцентные займы (Dr 58 / Cr 51)
static constexpr eosio::name BLAGOROST_INVEST = "w.cap.bginv"_n; ///< Благорост — инвестиции деньгами (Cr 80)
static constexpr eosio::name BLAGOROST_RID = "w.cap.bgrid"_n; ///< Благорост — принятые РИД (Dr 04 / Cr 08 после accept)
static constexpr eosio::name BLAGOROST_PROPERTY = "w.cap.bgprop"_n; ///< Благорост — имущественные паевые взносы
static constexpr eosio::name BLAGOROST_MEMBERSHIP = "w.cap.bgmem"_n; ///< Благорост — членские взносы по программе (Cr 86)
static constexpr eosio::name GENERATOR_COMMIT = "w.cap.gncom"_n; ///< Генератор — паевой взнос имуществом в статусе «принятый коммит» (Dr 08 / Cr 80)
static constexpr eosio::name GENERATOR_MEMBERSHIP = "w.cap.gnmem"_n; ///< Генератор — членские взносы по программе (Cr 86)
// Группа 9xxx — ЦПП «Благорост»
static constexpr uint64_t BLAGOROST_INVEST = 9001; ///< Благорост — инвестиции деньгами
static constexpr uint64_t BLAGOROST_RID = 9002; ///< Благорост — принятые РИД (Dr 04 / Cr 08 после accept)
static constexpr uint64_t BLAGOROST_PROPERTY = 9003; ///< Благорост — имущественные паевые взносы
static constexpr uint64_t BLAGOROST_MEMBERSHIP = 9004; ///< Благорост — членские взносы по программе (Cr 86)
// marketplace — выплаты + общий фонд
static constexpr eosio::name SUPPLIER_PAYMENTS = "w.mkt.payout"_n; ///< Выплаты поставщикам (sink RECEIVE_CONFIRM)
static constexpr eosio::name MARKETPLACE_FUND = "w.mkt.fund"_n; ///< ЦПП «Стол Заказов» — общий кошелёк программы (резерв)
// Группа 10xxx — ЦПП «Генератор» (промежуточное состояние Dr 08)
static constexpr uint64_t GENERATOR_COMMIT = 10001; ///< Генератор — паевой взнос имуществом в статусе «принятый коммит» (Dr 08 / Cr 80)
static constexpr uint64_t GENERATOR_MEMBERSHIP = 10002; ///< Генератор — членские взносы по программе (Cr 86)
// Группа 11xxx — ЦПП «Стол Заказов» (резерв)
static constexpr uint64_t MARKETPLACE_FUND = 11001; ///< ЦПП «Стол Заказов» — общий кошелёк программы (пустой в MVP)
// ledger2 — служебные
static constexpr eosio::name MANUAL_ADJUST = "w.led.adjust"_n; ///< Ручные корректировки (резерв под ledger2::adjust)
};
/**
* @brief Справочник кошелька: id → имя.
* @brief Справочник кошелька: machine name → human-readable name.
*
* `constexpr std::array` + `string_view` — без dynamic init в WASM.
*/
struct Ledger2WalletMeta {
uint64_t id;
std::string_view name;
eosio::name name;
std::string_view human_name;
};
inline constexpr std::array<Ledger2WalletMeta, 17> LEDGER2_WALLET_REGISTRY = {{
@@ -102,24 +98,49 @@ inline constexpr std::array<Ledger2WalletMeta, 17> LEDGER2_WALLET_REGISTRY = {{
static constexpr size_t LEDGER2_WALLET_REGISTRY_SIZE = LEDGER2_WALLET_REGISTRY.size();
// Compile-time проверка уникальности id кошельков.
// Compile-time проверка уникальности имён кошельков.
namespace ledger2_wallets_detail {
constexpr bool wallet_ids_unique() {
constexpr bool wallet_names_unique() {
for (size_t i = 0; i < LEDGER2_WALLET_REGISTRY_SIZE; ++i) {
for (size_t j = i + 1; j < LEDGER2_WALLET_REGISTRY_SIZE; ++j) {
if (LEDGER2_WALLET_REGISTRY[i].id == LEDGER2_WALLET_REGISTRY[j].id) return false;
if (LEDGER2_WALLET_REGISTRY[i].name == LEDGER2_WALLET_REGISTRY[j].name) return false;
}
}
return true;
}
constexpr bool wallet_names_nonempty() {
for (size_t i = 0; i < LEDGER2_WALLET_REGISTRY_SIZE; ++i) {
if (LEDGER2_WALLET_REGISTRY[i].name.value == 0) return false;
}
return true;
}
}
static_assert(ledger2_wallets_detail::wallet_ids_unique(),
"LEDGER2_WALLET_REGISTRY: duplicate wallet id detected");
static_assert(ledger2_wallets_detail::wallet_names_unique(),
"LEDGER2_WALLET_REGISTRY: duplicate wallet name detected");
static_assert(ledger2_wallets_detail::wallet_names_nonempty(),
"LEDGER2_WALLET_REGISTRY: empty eosio::name (sentinel) не должно быть в реестре");
inline constexpr std::string_view ledger2_get_wallet_name_by_id(uint64_t wallet_id) {
/**
* @brief Возвращает human-readable имя кошелька по его eosio::name.
*
* Возвращает пустой `string_view` для незарегистрированных имён и для пустого
* имени (sentinel — кошелёк вне системы при ISSUE/REVOKE).
*/
inline constexpr std::string_view ledger2_get_wallet_human_name(eosio::name wallet_name) {
for (size_t i = 0; i < LEDGER2_WALLET_REGISTRY_SIZE; ++i) {
if (LEDGER2_WALLET_REGISTRY[i].id == wallet_id) return LEDGER2_WALLET_REGISTRY[i].name;
if (LEDGER2_WALLET_REGISTRY[i].name == wallet_name) return LEDGER2_WALLET_REGISTRY[i].human_name;
}
return std::string_view{};
}
/**
* @brief Проверяет, что `wallet_name` присутствует в LEDGER2_WALLET_REGISTRY.
*/
inline constexpr bool ledger2_is_known_wallet(eosio::name wallet_name) {
for (size_t i = 0; i < LEDGER2_WALLET_REGISTRY_SIZE; ++i) {
if (LEDGER2_WALLET_REGISTRY[i].name == wallet_name) return true;
}
return false;
}
@@ -13,18 +13,20 @@
*
* @brief Кошелёк управленческого учёта ledger2.
*
* Простой порядковый id (без смещения). Запись создаётся upsert-ом
* Идентификатор — `eosio::name` с префиксом `w.<contract>.<waltype>`
* (см. `lib/core/ledger2/wallets.hpp::LEDGER2_WALLET_REGISTRY`). Поле `name` —
* человекочитаемое отображение из реестра. Запись создаётся upsert-ом
* при первом ISSUE/TRANSFER на кошелёк и удаляется, когда
* available == 0 && blocked == 0. Писем «writeoff» не существует —
* все выплаты моделируются как TRANSFER в кошельки-накопители.
*/
struct [[eosio::table, eosio::contract(LEDGER2)]] wallet2 {
uint64_t id;
eosio::name id;
std::string name;
eosio::asset available;
eosio::asset blocked;
uint64_t primary_key() const { return id; }
uint64_t primary_key() const { return id.value; }
bool is_empty() const {
return available.amount == 0 && blocked.amount == 0;
@@ -3,8 +3,8 @@
# кооператив с двойной конвертацией (деньги → имущество и обратно).
#
# Многоступенчатый процесс (10+ узлов состояний). Две ledger2-операции:
# • o.mkt.supply — подтверждение поставки на КУ (ISSUE → 2001, Дт 51 / Кт 80)
# • o.mkt.recv — выплата поставщику после получения (TRANSFER 2001 → 4002, Дт 80 / Кт 51)
# • o.mkt.supply — подтверждение поставки на КУ (ISSUE → w.wal.share, Дт 51 / Кт 80)
# • o.mkt.recv — выплата поставщику после получения (TRANSFER w.wal.share → w.mkt.payout, Дт 80 / Кт 51)
#
# Источники правды в коде:
# • cpp/marketplace/marketplace.hpp — actions
@@ -105,7 +105,7 @@ actions:
purpose: >
Заказчик подписывает второй акт — приём имущества подтверждён.
Применяется ledger2-операция o.mkt.recv — выплата поставщику
(TRANSFER 2001 → 4002, Дт 80 / Кт 51), устанавливается гарантийный период.
(TRANSFER w.wal.share → w.mkt.payout, Дт 80 / Кт 51), устанавливается гарантийный период.
- name: marketplace::complete
human: Завершить
actor: chairman
@@ -338,7 +338,7 @@ scenario:
action: marketplace::receivecnf
description: >
Заказчик подписывает RECEIVE_ACT_CONF. Применяется o.mkt.recv —
выплата поставщику (TRANSFER 2001 → 4002, Дт 80 / Кт 51), установлен
выплата поставщику (TRANSFER w.wal.share → w.mkt.payout, Дт 80 / Кт 51), установлен
гарантийный период.
- step: 8
title: Завершение по гарантии
@@ -431,8 +431,8 @@ operations:
- ledger_code: o.mkt.supply
human_name: Подтверждение поставки
wallet_op: ISSUE
wallet_from: null
wallet_to: 2001 # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_from: ''
wallet_to: w.wal.share # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
debit: 51 # Расчётный счёт
credit: 80 # Паевой фонд (складочный капитал)
amount_ref: request.total_cost
@@ -445,8 +445,8 @@ operations:
- ledger_code: o.mkt.recv
human_name: Подтверждение получения — выплата поставщику
wallet_op: TRANSFER
wallet_from: 2001 # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_to: 4002 # Выплаты поставщикам
wallet_from: w.wal.share # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_to: w.mkt.payout # Выплаты поставщикам
debit: 80 # Паевой фонд
credit: 51 # Расчётный счёт
amount_ref: request.base_cost
@@ -247,8 +247,8 @@ operations:
- ledger_code: o.reg.payent
human_name: Вступительный взнос пайщика
wallet_op: ISSUE
wallet_from: null
wallet_to: 3001 # Вступительные взносы
wallet_from: ''
wallet_to: w.reg.entry # Вступительные взносы
debit: 51 # Расчётный счёт
credit: 86 # Целевое финансирование
amount_ref: candidate.initial
@@ -261,8 +261,8 @@ operations:
- ledger_code: o.reg.putmin
human_name: Минимальный паевой взнос при регистрации
wallet_op: ISSUE
wallet_from: null
wallet_to: 2002 # Минимальный паевой взнос
wallet_from: ''
wallet_to: w.reg.minshr # Минимальный паевой взнос
debit: 51 # Расчётный счёт
credit: 80 # Паевой фонд (складочный капитал)
amount_ref: candidate.minimum
@@ -173,8 +173,8 @@ operations:
- ledger_code: o.wal.depcpl
human_name: Внесение пайщиком паевого взноса
wallet_op: ISSUE
wallet_from: null
wallet_to: 2001 # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_from: ''
wallet_to: w.wal.share # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
debit: 51 # Расчётный счёт
credit: 80 # Паевой фонд (складочный капитал)
amount_ref: deposit.quantity
@@ -4,7 +4,7 @@
#
# Пятиактовый процесс с авторизацией советом. Запись заявки живёт в таблице
# `withdraws` от создания до выплаты или отказа. На закрывающем действии
# completewthd срабатывает одна операция o.wal.wthcpl (TRANSFER 2001 → 4001).
# completewthd срабатывает одна операция o.wal.wthcpl (TRANSFER w.wal.share → w.wal.wthdrw).
#
# Источники правды в коде:
# • cpp/wallet/wallet.hpp — actions
@@ -30,7 +30,7 @@ purpose: >
совет рассматривает заявку и принимает решение, после авторизации платёж
отправляется в Gateway. На подтверждении выплаты в ledger2 применяется
одна операция o.wal.wthcpl: средства переводятся с кошелька пайщика на
системный кошелёк выплат (4001) с обратной проводкой Дт 80 / Кт 51.
системный кошелёк выплат (w.wal.wthdrw) с обратной проводкой Дт 80 / Кт 51.
roles:
- contributor
- soviet
@@ -67,7 +67,7 @@ actions:
role: closer
purpose: >
Gateway подтвердил списание средств в пользу пайщика. Это закрывающее
действие: применяется операция o.wal.wthcpl (TRANSFER 2001 → 4001),
действие: применяется операция o.wal.wthcpl (TRANSFER w.wal.share → w.wal.wthdrw),
разблокированная сумма уходит со счёта 80 на счёт 51, запись заявки
удаляется.
- name: wallet::declinewthd
@@ -106,7 +106,7 @@ states:
human: Возврат выплачен
description: >
Gateway подтвердил списание, средства переведены с кошелька пайщика
на системный кошелёк выплат (2001 → 4001), в бухгалтерии прошла
на системный кошелёк выплат (w.wal.share → w.wal.wthdrw), в бухгалтерии прошла
обратная проводка Дт 80 / Кт 51. Запись заявки удалена.
kind: final
- name: removed
@@ -124,7 +124,7 @@ transitions:
actor: contributor
guards:
- Пайщик имеет статус active.
- Сумма возврата ≤ доступного остатка на SHARE_FUND_PAY (2001).
- Сумма возврата ≤ доступного остатка на SHARE_FUND_PAY (w.wal.share).
- withdraw_hash уникален.
- Заявление подписано ЭЦП пайщика.
@@ -227,13 +227,13 @@ scenario:
action: wallet::completewthd
description: >
Gateway подтверждает списание средств в пользу пайщика. Контракт
применяет ledger2-операцию o.wal.wthcpl (TRANSFER 2001 → 4001 с
применяет ledger2-операцию o.wal.wthcpl (TRANSFER w.wal.share → w.wal.wthdrw с
обратной проводкой Дт 80 / Кт 51) и удаляет запись заявки.
pre:
- Заявка в статусе `authorized`.
- Gateway подтвердил списание.
post:
- Сумма переведена с SHARE_FUND_PAY (2001) на WITHDRAWALS (4001).
- Сумма переведена с SHARE_FUND_PAY (w.wal.share) на WITHDRAWALS (w.wal.wthdrw).
- В ledger2 применена операция o.wal.wthcpl.
- Запись заявки удалена.
@@ -278,15 +278,15 @@ operations:
- ledger_code: o.wal.wthcpl
human_name: Возврат паевого взноса пайщику
wallet_op: TRANSFER
wallet_from: 2001 # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_to: 4001 # Возвраты паевых взносов пайщикам
wallet_from: w.wal.share # ЦПП «Цифровой Кошелёк» — паевые взносы деньгами
wallet_to: w.wal.wthdrw # Возвраты паевых взносов пайщикам
debit: 80 # Паевой фонд (складочный капитал)
credit: 51 # Расчётный счёт
amount_ref: withdraw.quantity
triggered_by: wallet::completewthd
description: >
Обратная операция к o.wal.depcpl: средства уходят с кошелька пайщика
«ЦПП Цифровой Кошелёк» (2001) на системный кошелёк выплат (4001).
«ЦПП Цифровой Кошелёк» (w.wal.share) на системный кошелёк выплат (w.wal.wthdrw).
В бухгалтерии прошла проводка Дт 80 / Кт 51 — паевой фонд уменьшился,
деньги ушли с расчётного счёта пайщику.
+17 -12
View File
@@ -6319,7 +6319,7 @@ input GetInvestInput {
}
input GetLedger2HistoryInput {
"""ID счёта/кошелька (×1000)"""
"""Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий."""
accountId: Int
"""Имена blockchain-действий: apply | walletop | debit | credit"""
@@ -6342,6 +6342,11 @@ input GetLedger2HistoryInput {
processHash: String
sortOrder: String = "DESC"
username: String
"""
eosio::name кошелька (`w.<contract>.<waltype>`) — для walletop действий.
"""
walletName: String
}
input GetLedgerHistoryInput {
@@ -6678,11 +6683,11 @@ type Ledger2Operation {
quantity: String
username: String
"""walletop: wallet_from (исходящий)"""
walletFrom: Int
"""walletop: wallet_from (eosio::name w.<contract>.<waltype>)"""
walletFrom: String
"""walletop: wallet_to (входящий)"""
walletTo: Int
"""walletop: wallet_to (eosio::name w.<contract>.<waltype>)"""
walletTo: String
}
type Ledger2Wallet {
@@ -6692,8 +6697,8 @@ type Ledger2Wallet {
"""Заблокированный баланс"""
blocked: String!
"""ID кошелька (×1000 offset): 1001/2001/3001/4001"""
id: Int!
"""eosio::name-идентификатор кошелька (w.<contract>.<waltype>)"""
id: String!
"""Название кошелька"""
name: String!
@@ -9518,7 +9523,7 @@ type Query {
getLedger2History(input: GetLedger2HistoryInput!): Ledger2HistoryResponse!
"""
Общекооперативные кошельки из ledger2::wallets (1001/2001/3001/4001). Кошельки пайщиков живут в контракте soviet — сюда не попадают.
Общекооперативные кошельки из ledger2::wallets (eosio::name w.<contract>.<waltype>). Кошельки пайщиков живут в контракте soviet — сюда не попадают.
"""
getLedger2Wallets(coopname: String!): [Ledger2Wallet!]!
@@ -11746,8 +11751,8 @@ type WaitWeight {
input WalmoveInput {
coopname: String!
"""id кошелька-источника"""
fromWallet: Int!
"""eosio::name кошелька-источника (w.<contract>.<waltype>)"""
fromWallet: String!
"""Обязательное обоснование корректировки"""
memo: String!
@@ -11757,8 +11762,8 @@ input WalmoveInput {
"""
quantity: String!
"""id кошелька-приёмника"""
toWallet: Int!
"""eosio::name кошелька-приёмника (w.<contract>.<waltype>)"""
toWallet: String!
"""Владелец кошельков (для коллективных — coopname)"""
username: String!
@@ -22,11 +22,20 @@ export class GetLedger2HistoryInputDTO {
@IsString()
coopname!: string;
@Field(() => Int, { nullable: true, description: 'ID счёта/кошелька (×1000)' })
@Field(() => Int, { nullable: true, description: 'Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий.' })
@IsOptional()
@IsInt()
accountId?: number;
@Field(() => String, {
nullable: true,
description: 'eosio::name кошелька (`w.<contract>.<waltype>`) — для walletop действий.',
})
@IsOptional()
@IsString()
@MaxLength(13)
walletName?: string;
@Field(() => [String], {
nullable: true,
description: 'Имена blockchain-действий: apply | walletop | debit | credit',
@@ -38,11 +38,11 @@ export class Ledger2OperationDTO {
@Field(() => Int, { nullable: true, description: 'ID счёта/кошелька (×1000)' })
accountId?: number | null;
@Field(() => Int, { nullable: true, description: 'walletop: wallet_from (исходящий)' })
walletFrom?: number | null;
@Field(() => String, { nullable: true, description: 'walletop: wallet_from (eosio::name w.<contract>.<waltype>)' })
walletFrom?: string | null;
@Field(() => Int, { nullable: true, description: 'walletop: wallet_to (входящий)' })
walletTo?: number | null;
@Field(() => String, { nullable: true, description: 'walletop: wallet_to (eosio::name w.<contract>.<waltype>)' })
walletTo?: string | null;
@Field(() => String, { nullable: true, description: 'Asset "100.0000 RUB"' })
quantity?: string | null;
@@ -1,13 +1,16 @@
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Field, ObjectType } from '@nestjs/graphql';
/**
* Общекооперативный кошелёк ledger2 (1001/2001/3001/4001...).
* Общекооперативный кошелёк ledger2.
* Кошельки пайщиков живут в контракте soviet — они сюда не попадают.
*
* `id` — eosio::name-идентификатор `w.<contract>.<waltype>` (например
* `w.wal.share`, `w.cap.bginv`). См. `cooptypes/ledger2/wallets.ts`.
*/
@ObjectType('Ledger2Wallet')
export class Ledger2WalletDTO {
@Field(() => Int, { description: 'ID кошелька (×1000 offset): 1001/2001/3001/4001' })
id!: number;
@Field(() => String, { description: 'eosio::name-идентификатор кошелька (w.<contract>.<waltype>)' })
id!: string;
@Field(() => String, { description: 'Название кошелька' })
name!: string;
@@ -1,10 +1,13 @@
import { Field, Int, InputType } from '@nestjs/graphql';
import { IsInt, IsString, MaxLength, MinLength, Matches, Min } from 'class-validator';
import { Field, InputType } from '@nestjs/graphql';
import { IsString, MaxLength, MinLength, Matches, Length } from 'class-validator';
/**
* Input для мутации `walmoveWallets` (operation `o.adj.walmove`).
*
* `quantity` — строка с символом, как в действии apply: `"100.0000 RUB"`.
* `fromWallet`/`toWallet` — eosio::name-идентификаторы кошельков
* (`w.<contract>.<waltype>`), см. `cooptypes/ledger2/wallets.ts`.
*
* Backend валидирует совместимость from_wallet/to_wallet с одним account_id
* (через `Ledger2.LEDGER2_OPERATION_REGISTRY`) ДО подписания.
*/
@@ -18,15 +21,15 @@ export class WalmoveInputDTO {
@IsString()
username!: string;
@Field(() => Int, { description: 'id кошелька-источника' })
@IsInt()
@Min(1)
fromWallet!: number;
@Field(() => String, { description: 'eosio::name кошелька-источника (w.<contract>.<waltype>)' })
@IsString()
@Length(1, 13)
fromWallet!: string;
@Field(() => Int, { description: 'id кошелька-приёмника' })
@IsInt()
@Min(1)
toWallet!: number;
@Field(() => String, { description: 'eosio::name кошелька-приёмника (w.<contract>.<waltype>)' })
@IsString()
@Length(1, 13)
toWallet!: string;
@Field(() => String, { description: 'Сумма с символом, например "100.0000 RUB"' })
@IsString()
@@ -39,7 +39,7 @@ export class Ledger2Resolver {
@Query(() => [Ledger2WalletDTO], {
name: 'getLedger2Wallets',
description:
'Общекооперативные кошельки из ledger2::wallets (1001/2001/3001/4001). ' +
'Общекооперативные кошельки из ledger2::wallets (eosio::name w.<contract>.<waltype>). ' +
'Кошельки пайщиков живут в контракте soviet — сюда не попадают.',
})
@UseGuards(GqlJwtAuthGuard, RolesGuard)
@@ -116,26 +116,28 @@ export class Ledger2Service {
* wallet_from, debit для wallet_to — для standard-flow). Все стандартные
* кошельки имеют **один** account_id во всех операциях, поэтому первая
* найденная привязка достаточна.
*
* `walletName` — eosio::name кошелька (`w.<contract>.<waltype>`).
*/
private resolveWalletAccountId(walletId: number): number | null {
private resolveWalletAccountId(walletName: string): number | null {
for (const op of Ledger2.LEDGER2_OPERATION_REGISTRY) {
if (op.kind === 'adjustment') continue;
if (op.wallet_op === 'WALLET_ONLY') continue; // у WALLET_ONLY нет debit/credit
if (op.wallet_from === walletId && op.credit !== null) return op.credit;
if (op.wallet_to === walletId && op.credit !== null && op.wallet_op === 'ISSUE') return op.credit;
if (op.wallet_to === walletId && op.debit !== null) return op.debit;
if (op.wallet_from === walletId && op.debit !== null) return op.debit;
if (op.wallet_from === walletName && op.credit !== null) return op.credit;
if (op.wallet_to === walletName && op.credit !== null && op.wallet_op === 'ISSUE') return op.credit;
if (op.wallet_to === walletName && op.debit !== null) return op.debit;
if (op.wallet_from === walletName && op.debit !== null) return op.debit;
}
// Кошельки, которые встречаются только в WALLET_ONLY (например 9001 в o.cap.invest)
// Кошельки, которые встречаются только в WALLET_ONLY (например w.cap.bginv в o.cap.invest)
// — выводим из источника: WALLET_ONLY переносит между кошельками одного счёта,
// значит партнёрский кошелёк по WALLET_ONLY имеет тот же account_id, что и
// его пара в стандартной операции.
for (const op of Ledger2.LEDGER2_OPERATION_REGISTRY) {
if (op.wallet_op !== 'WALLET_ONLY' || op.kind === 'adjustment') continue;
if (op.wallet_from === walletId && op.wallet_to !== null) {
if (op.wallet_from === walletName && op.wallet_to !== null) {
return this.resolveWalletAccountId(op.wallet_to);
}
if (op.wallet_to === walletId && op.wallet_from !== null) {
if (op.wallet_to === walletName && op.wallet_from !== null) {
return this.resolveWalletAccountId(op.wallet_from);
}
}
@@ -17,12 +17,12 @@ export interface Ledger2OperationDomainInterface {
processHash: string | null;
/** Кто инициировал действие; для миграции может быть null. */
username: string | null;
/** Для walletop/debit/credit — id счёта/кошелька (×1000 offset). */
/** Для debit/credit — id бух.счёта (×1000 offset, например 51000/80000). null для walletop/apply. */
accountId: number | null;
/** Для walletop — wallet_from (исходящий кошелёк). */
walletFrom: number | null;
/** Для walletop — wallet_to (входящий кошелёк). */
walletTo: number | null;
/** Для walletop — wallet_from (eosio::name `w.<contract>.<waltype>`). */
walletFrom: string | null;
/** Для walletop — wallet_to (eosio::name `w.<contract>.<waltype>`). */
walletTo: string | null;
/** Сумма в формате asset (`"100.0000 RUB"`) — применимо к walletop/debit/credit. */
quantity: string | null;
memo: string | null;
@@ -31,8 +31,10 @@ export interface Ledger2OperationDomainInterface {
export interface Ledger2HistoryFilterDomainInterface {
coopname: string;
/** Фильтр по конкретному счёту/кошельку (×1000 offset). */
/** Фильтр по бух.счёту (×1000 offset, например 51000/80000) — для debit/credit. */
accountId?: number;
/** Фильтр по eosio::name кошелька (`w.<contract>.<waltype>`) — для walletop. */
walletName?: string;
/** Одно из имён действий `apply|walletop|debit|credit`. Пусто = все. */
actionNames?: string[];
/** Код операции из OPERATION_REGISTRY (только для `apply`). */
@@ -2,22 +2,14 @@
* Общекооперативный кошелёк ledger2 (таблица `ledger2::wallets`).
*
* Это **не** кошельки пайщиков — те живут в контракте `soviet`. Здесь —
* аналитические разрезы бухгалтерских счетов уровня кооператива:
* 2001 — паевой фонд: взносы деньгами;
* 2002 — минимальный паевой взнос;
* 2003 — паевой фонд: принятые РИД;
* 3001–3003 — вступительные / членские / делегатские взносы;
* 4001 — возвраты паевых взносов пайщикам;
* 4002 — выплаты поставщикам;
* 4051 — выданные беспроцентные займы;
* 9xxx / 10xxx / 11xxx — кошельки ЦПП «Благорост» / «Генератор» / «Стол Заказов».
* аналитические разрезы бухгалтерских счетов уровня кооператива.
*
* Полный реестр — `components/context/notes/LEDGER2_CHART_OF_ACCOUNTS.md` и
* `contracts/cpp/lib/core/ledger2/wallets.hpp`. ID идут блоками ×1000 по
* родительскому бухсчёту (Epic 1 Story 1.13).
* `id` — eosio::name-идентификатор `w.<contract>.<waltype>` (например
* `w.wal.share`, `w.cap.bginv`, `w.reg.entry`). Полный реестр — в
* `cooptypes/ledger2/wallets.ts` и `contracts/cpp/lib/core/ledger2/wallets.hpp`.
*/
export interface Ledger2WalletDomainInterface {
id: number;
id: string;
name: string;
available: string;
blocked: string;
@@ -9,8 +9,10 @@ export interface WalmoveBlockchainDomainInterface {
initiator: string;
/** Владелец кошельков (для аналитики; для коллективных — обычно coopname). */
username: string;
fromWallet: number;
toWallet: number;
/** eosio::name кошелька-источника (`w.<contract>.<waltype>`). */
fromWallet: string;
/** eosio::name кошелька-приёмника (`w.<contract>.<waltype>`). */
toWallet: string;
/** Сумма с символом, например `"100.00 RUB"`. */
quantity: string;
/** SHA-256 хэш корректировки (генерирует backend). */
@@ -88,7 +88,7 @@ export const PROCESS_HASH_LOCATOR: Readonly<Record<string, HashLocation[]>> = Ob
'p.cap.import': [{ code: 'capital', table: 'contributors', field: 'contributor_hash' }],
// p.cap.invest — wallet-only перенос 2001 → 9001 без Dr/Cr.
// p.cap.invest — wallet-only перенос w.wal.share → w.cap.bginv без Dr/Cr.
'p.cap.invest': [{ code: 'capital', table: 'contributors', field: 'contributor_hash' }],
// capital::pgproperties.property_hash — приём имущества в паевой фонд.
@@ -66,7 +66,7 @@ export class TypeOrmLedger2StateRepository implements Ledger2StatePort {
return (rows as Array<{ value: Record<string, unknown> }>).map((row) => {
const v = row.value ?? {};
return {
id: parseInt(String(v.id ?? 0), 10),
id: String(v.id ?? ''),
name: String(v.name ?? ''),
available: String(v.available ?? '0.0000 RUB'),
blocked: String(v.blocked ?? '0.0000 RUB'),
@@ -87,8 +87,9 @@ export class TypeOrmLedger2StateRepository implements Ledger2StatePort {
const clauses: string[] = [];
if (filter.accountId !== undefined) {
// apply-события не имеют accountId/wallet_id в data — связь идёт через process_hash
// с siblings (walletop/debit/credit). Для raw-siblings достаточно прямого совпадения.
// Бух.счёт (debit/credit действий) — числовой. apply-события не имеют account_id
// в data, связь идёт через process_hash с siblings (debit/credit). Для raw-siblings
// достаточно прямого совпадения.
const siblingOnly =
!!filter.actionNames &&
filter.actionNames.length > 0 &&
@@ -98,9 +99,6 @@ export class TypeOrmLedger2StateRepository implements Ledger2StatePort {
`(
(a.data ->> 'id')::text = $${pIdx}
OR (a.data ->> 'account_id')::text = $${pIdx}
OR (a.data ->> 'wallet_id')::text = $${pIdx}
OR (a.data ->> 'wallet_from')::text = $${pIdx}
OR (a.data ->> 'wallet_to')::text = $${pIdx}
)`,
);
} else {
@@ -114,9 +112,6 @@ export class TypeOrmLedger2StateRepository implements Ledger2StatePort {
AND (
(b.data ->> 'id')::text = $${pIdx}
OR (b.data ->> 'account_id')::text = $${pIdx}
OR (b.data ->> 'wallet_id')::text = $${pIdx}
OR (b.data ->> 'wallet_from')::text = $${pIdx}
OR (b.data ->> 'wallet_to')::text = $${pIdx}
)
)`,
);
@@ -124,6 +119,41 @@ export class TypeOrmLedger2StateRepository implements Ledger2StatePort {
params.push(String(filter.accountId));
pIdx += 1;
}
if (filter.walletName) {
// Кошелёк (walletop действий) — eosio::name. Для apply-событий wallet_from/to нет
// в data, поэтому фильтр через process_hash подтягивает все 3 действия (apply +
// walletop + debit/credit) одного процесса, в котором участвует этот кошелёк.
const siblingOnly =
!!filter.actionNames &&
filter.actionNames.length > 0 &&
!filter.actionNames.includes('apply');
if (siblingOnly) {
clauses.push(
`(
(a.data ->> 'wallet_from') = $${pIdx}
OR (a.data ->> 'wallet_to') = $${pIdx}
OR (a.data ->> 'id') = $${pIdx}
)`,
);
} else {
clauses.push(
`LOWER(a.data ->> 'process_hash') IN (
SELECT LOWER(b.data ->> 'process_hash')
FROM blockchain_actions b
WHERE b.account = $1
AND b.data ->> 'coopname' = $2
AND b.data ->> 'process_hash' IS NOT NULL
AND (
(b.data ->> 'wallet_from') = $${pIdx}
OR (b.data ->> 'wallet_to') = $${pIdx}
OR (b.data ->> 'id') = $${pIdx}
)
)`,
);
}
params.push(filter.walletName);
pIdx += 1;
}
if (filter.processHash) {
clauses.push(`LOWER(a.data ->> 'process_hash') = $${pIdx}`);
params.push(filter.processHash.toLowerCase());
@@ -201,15 +231,12 @@ export class TypeOrmLedger2StateRepository implements Ledger2StatePort {
(a.data ->> 'operation_code') AS "operationCode",
LOWER(a.data ->> 'process_hash') AS "processHash",
(a.data ->> 'username') AS "username",
COALESCE(
NULLIF(a.data ->> 'id', '')::bigint,
NULLIF(a.data ->> 'account_id', '')::bigint,
NULLIF(a.data ->> 'wallet_id', '')::bigint,
NULLIF(a.data ->> 'wallet_to', '')::bigint,
NULLIF(a.data ->> 'wallet_from', '')::bigint
) AS "accountId",
NULLIF(a.data ->> 'wallet_from', '')::bigint AS "walletFrom",
NULLIF(a.data ->> 'wallet_to', '')::bigint AS "walletTo",
CASE WHEN a.name IN ('debit', 'credit')
THEN NULLIF(a.data ->> 'account_id', '')::bigint
ELSE NULL
END AS "accountId",
NULLIF(a.data ->> 'wallet_from', '') AS "walletFrom",
NULLIF(a.data ->> 'wallet_to', '') AS "walletTo",
COALESCE(
NULLIF(a.data ->> 'quantity', ''),
NULLIF(a.data ->> 'amount', '')
@@ -234,8 +261,8 @@ export class TypeOrmLedger2StateRepository implements Ledger2StatePort {
processHash: (r.processHash as string | null) ?? null,
username: (r.username as string | null) ?? null,
accountId: r.accountId !== null && r.accountId !== undefined ? Number(r.accountId) : null,
walletFrom: r.walletFrom !== null && r.walletFrom !== undefined ? Number(r.walletFrom) : null,
walletTo: r.walletTo !== null && r.walletTo !== undefined ? Number(r.walletTo) : null,
walletFrom: r.walletFrom != null ? String(r.walletFrom) : null,
walletTo: r.walletTo != null ? String(r.walletTo) : null,
quantity: (r.quantity as string | null) ?? null,
memo: (r.memo as string | null) ?? null,
createdAt: r.createdAt instanceof Date ? r.createdAt : new Date(String(r.createdAt)),
@@ -25,9 +25,12 @@ export interface IAccount2 {
/**
* Строка таблицы `wallets` контракта ledger2 (аналитический разрез бухсчёта).
* Source: `table_ledger2_wallet.hpp`.
*
* `id` — eosio::name-идентификатор кошелька (`w.<contract>.<waltype>`),
* см. cooptypes/ledger2/wallets.ts. Поле `name` — человекочитаемое название.
*/
export interface IWallet2 {
id: number
id: IName
name: string
available: IAsset
blocked: IAsset
@@ -66,8 +69,8 @@ export interface IWalmove {
coopname: IName
initiator: IName
username: IName
from_wallet: number
to_wallet: number
from_wallet: IName
to_wallet: IName
amount: IAsset
process_hash: IChecksum256
memo: string
@@ -92,8 +95,8 @@ export interface IRevert {
* 2=BLOCK / 3=UNBLOCK не разрешены — они симметричны сами себе.
*/
mirror_wallet_op: number
mirror_wallet_from: number
mirror_wallet_to: number
mirror_wallet_from: IName
mirror_wallet_to: IName
mirror_debit_account_id: number
mirror_credit_account_id: number
process_hash: IChecksum256
+27 -22
View File
@@ -8,7 +8,12 @@
* Нейминг: `o.<contract>.<verb>`, где префикс `o.` — operation, далее имя
* контракта-источника (reg/wal/cap/mkt/sov/mig), затем короткий глагол.
* Длина eosio::name ≤ 12 символов (13-й символ имеет ограничение по алфавиту).
*
* Идентификаторы кошельков (`wallet_from`/`wallet_to`) — eosio::name с
* префиксом `w.<contract>.<waltype>` (см. `./wallets.ts`). Пустая строка
* (`""`) — sentinel «кошелёк вне системы» для ISSUE и REVOKE.
*/
import type { IName } from '../interfaces/ledger2'
export type WalletOp = 'ISSUE' | 'TRANSFER' | 'BLOCK' | 'UNBLOCK' | 'WALLET_ONLY' | 'REVOKE'
@@ -27,10 +32,10 @@ export interface OperationMeta {
* REVERSAL = REVOKE/TRANSFER/WALLET_ONLY в зависимости от зеркала).
*/
wallet_op: WalletOp | null
/** id кошелька-источника (null для ISSUE и для adjustment-операций). */
wallet_from: number | null
/** id кошелька-приёмника (null для BLOCK/UNBLOCK и для adjustment-операций). */
wallet_to: number | null
/** Кошелёк-источник (null для ISSUE и для adjustment-операций). */
wallet_from: IName | null
/** Кошелёк-приёмник (null для BLOCK/UNBLOCK и для adjustment-операций). */
wallet_to: IName | null
/** Код счёта Дт (null для WALLET_ONLY и для adjustment-операций). */
debit: number | null
/** Код счёта Кт (null для WALLET_ONLY и для adjustment-операций). */
@@ -53,97 +58,97 @@ export interface OperationMeta {
export const LEDGER2_OPERATION_REGISTRY: readonly OperationMeta[] = [
// registrator
{ code: 'o.reg.payent', process_type: 'p.reg.accept', contract: 'registrator',
name: 'PAY_ENTRANCE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 3001,
name: 'PAY_ENTRANCE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.reg.entry',
debit: 51, credit: 86,
human_name: 'Вступительный взнос пайщика' },
{ code: 'o.reg.putmin', process_type: 'p.reg.accept', contract: 'registrator',
name: 'PUT_MINSHARE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 2002,
name: 'PUT_MINSHARE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.reg.minshr',
debit: 51, credit: 80,
human_name: 'Минимальный паевой взнос пайщика при регистрации' },
// wallet
{ code: 'o.wal.depcpl', process_type: 'p.wal.depo', contract: 'wallet',
name: 'COMPLETE_DEPOSIT', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 2001,
name: 'COMPLETE_DEPOSIT', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.wal.share',
debit: 51, credit: 80,
human_name: 'Внесение пайщиком паевого взноса' },
{ code: 'o.wal.wthcpl', process_type: 'p.wal.wthdrw', contract: 'wallet',
name: 'COMPLETE_WITHDRAW', wallet_op: 'TRANSFER', wallet_from: 2001, wallet_to: 4001,
name: 'COMPLETE_WITHDRAW', wallet_op: 'TRANSFER', wallet_from: 'w.wal.share', wallet_to: 'w.wal.wthdrw',
debit: 80, credit: 51,
human_name: 'Возврат паевого взноса пайщику' },
// capital
{ code: 'o.cap.import', process_type: 'p.cap.import', contract: 'capital',
name: 'IMPORT', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 9001,
name: 'IMPORT', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.cap.bginv',
debit: 51, credit: 80,
human_name: 'Паевой взнос по ЦПП «Благорост» (офлайн-импорт)' },
{ code: 'o.cap.invest', process_type: 'p.cap.invest', contract: 'capital',
name: 'INVEST', wallet_op: 'WALLET_ONLY', wallet_from: 2001, wallet_to: 9001,
name: 'INVEST', wallet_op: 'WALLET_ONLY', wallet_from: 'w.wal.share', wallet_to: 'w.cap.bginv',
debit: null, credit: null,
human_name: 'Инвестиция в ЦПП «Благорост» (перенос между кошельками)' },
{ code: 'o.cap.commit', process_type: 'p.cap.rid', contract: 'capital',
name: 'COMMIT_RID', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 10001,
name: 'COMMIT_RID', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.cap.gncom',
debit: 8, credit: 80,
human_name: 'Коммит РИД по программе «Благорост»' },
{ code: 'o.cap.accept', process_type: 'p.cap.rid', contract: 'capital',
name: 'ACCEPT_RID', wallet_op: 'TRANSFER', wallet_from: 10001, wallet_to: 9002,
name: 'ACCEPT_RID', wallet_op: 'TRANSFER', wallet_from: 'w.cap.gncom', wallet_to: 'w.cap.bgrid',
debit: 4, credit: 8,
human_name: 'Приём РИД в паевой фонд' },
{ code: 'o.cap.actprp', process_type: 'p.cap.prop', contract: 'capital',
name: 'ACCEPT_PROPERTY', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 9003,
name: 'ACCEPT_PROPERTY', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.cap.bgprop',
debit: 51, credit: 80,
human_name: 'Паевой взнос (имущественный) по программе «Благорост»' },
{ code: 'o.cap.lend', process_type: 'p.cap.debt', contract: 'capital',
name: 'LEND', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 4051,
name: 'LEND', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.cap.loan',
debit: 58, credit: 51,
human_name: 'Выдача пайщику беспроцентного займа' },
{ code: 'o.cap.repay', process_type: 'p.cap.rid', contract: 'capital',
name: 'REPAY', wallet_op: 'TRANSFER', wallet_from: 4051, wallet_to: 2001,
name: 'REPAY', wallet_op: 'TRANSFER', wallet_from: 'w.cap.loan', wallet_to: 'w.wal.share',
debit: 80, credit: 58,
human_name: 'Возврат беспроцентного займа пайщика по акту-2' },
// marketplace
{ code: 'o.mkt.supply', process_type: 'p.mkt.reqst', contract: 'marketplace',
name: 'CONFIRM_SUPPLY', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 2001,
name: 'CONFIRM_SUPPLY', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.wal.share',
debit: 51, credit: 80,
human_name: 'Подтверждение поставки' },
{ code: 'o.mkt.recv', process_type: 'p.mkt.reqst', contract: 'marketplace',
name: 'CONFIRM_RECEIPT', wallet_op: 'TRANSFER', wallet_from: 2001, wallet_to: 4002,
name: 'CONFIRM_RECEIPT', wallet_op: 'TRANSFER', wallet_from: 'w.wal.share', wallet_to: 'w.mkt.payout',
debit: 80, credit: 51,
human_name: 'Подтверждение получения — выплата поставщику' },
// soviet
{ code: 'o.sov.axncnv', process_type: 'p.sov.axncnv', contract: 'soviet',
name: 'CONVERT_AXN', wallet_op: 'TRANSFER', wallet_from: 2001, wallet_to: 3003,
name: 'CONVERT_AXN', wallet_op: 'TRANSFER', wallet_from: 'w.wal.share', wallet_to: 'w.sov.delgte',
debit: 80, credit: 86,
human_name: 'Трансляция паевого в членский взнос инфраструктуры' },
// migration
{ code: 'o.mig.minshr', process_type: 'p.mig.trans', contract: 'migration',
name: 'MIN_SHARE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 2002,
name: 'MIN_SHARE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.reg.minshr',
debit: 51, credit: 80,
human_name: 'Транзит: минимальные паевые взносы' },
{ code: 'o.mig.share', process_type: 'p.mig.trans', contract: 'migration',
name: 'SHARE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 2001,
name: 'SHARE', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.wal.share',
debit: 51, credit: 80,
human_name: 'Транзит: остаток паевых взносов деньгами' },
{ code: 'o.mig.entry', process_type: 'p.mig.trans', contract: 'migration',
name: 'ENTRY', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 3001,
name: 'ENTRY', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.reg.entry',
debit: 51, credit: 86,
human_name: 'Транзит: вступительные взносы' },
{ code: 'o.mig.rid', process_type: 'p.mig.trans', contract: 'migration',
name: 'RID', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 2003,
name: 'RID', wallet_op: 'ISSUE', wallet_from: null, wallet_to: 'w.wal.sharid',
debit: 4, credit: 80,
human_name: 'Транзит: принятые РИД в паевой фонд' },
+51 -28
View File
@@ -4,41 +4,64 @@
* (`LEDGER2_WALLET_REGISTRY`). При добавлении/переименовании кошелька
* синхронизировать обе стороны.
*
* Используется для tooltip-подсказок в UI (standards-site, desktop).
* Идентификатор кошелька — `eosio::name` с префиксом `w.<contract>.<waltype>`
* по аналогии с операциями (`o.<contract>.<verb>`) и процессами
* (`p.<contract>.<noun>`). Длина ≤ 13 base32-символов.
*
* `human_name` — отображаемое имя в UI (standards-site, desktop).
*/
import type { IName } from '../interfaces/ledger2'
export interface WalletMeta {
id: number
name: string
/** Машинный идентификатор — eosio::name в контракте. */
name: IName
/** Человекочитаемое название для UI. */
human_name: string
}
export const LEDGER2_WALLET_REGISTRY: readonly WalletMeta[] = [
// id=0 — frontend-only: в контракте нет, но walletop issue/consume использует
// 0 как «кошелёк-источник/сток извне системы» (эмиссия / сжигание).
{ id: 0, name: 'Выпуск' },
{ id: 2001, name: 'ЦПП «Цифровой Кошелёк» — паевые взносы деньгами' },
{ id: 2002, name: 'Минимальный паевой взнос' },
{ id: 2003, name: 'Паевой фонд — принятые РИД' },
{ id: 3001, name: 'Вступительные взносы' },
{ id: 3002, name: 'Членские взносы (платформенные)' },
{ id: 3003, name: 'Делегатские членские взносы' },
{ id: 4001, name: 'Возвраты паевых взносов пайщикам' },
{ id: 4002, name: 'Выплаты поставщикам' },
{ id: 4051, name: 'Выданные пайщикам беспроцентные займы' },
{ id: 5001, name: 'Ручные корректировки' },
{ id: 9001, name: 'ЦПП «Благорост» — инвестиции деньгами' },
{ id: 9002, name: 'ЦПП «Благорост» — принятые РИД' },
{ id: 9003, name: 'ЦПП «Благорост» — имущественные паевые взносы' },
{ id: 9004, name: 'ЦПП «Благорост» — членские взносы' },
{ id: 10001, name: 'ЦПП «Генератор» — принятый коммит (имущество)' },
{ id: 10002, name: 'ЦПП «Генератор» — членские взносы' },
{ id: 11001, name: 'ЦПП «Стол Заказов» — общий кошелёк' },
// Группа паевого фонда (Cr 80)
{ name: 'w.wal.share', human_name: 'ЦПП «Цифровой Кошелёк» — паевые взносы деньгами' },
{ name: 'w.reg.minshr', human_name: 'Минимальный паевой взнос' },
{ name: 'w.wal.sharid', human_name: 'Паевой фонд — принятые РИД' },
// Целевое финансирование (Cr 86)
{ name: 'w.reg.entry', human_name: 'Вступительные взносы' },
{ name: 'w.sov.member', human_name: 'Членские взносы (платформенные)' },
{ name: 'w.sov.delgte', human_name: 'Делегатские членские взносы' },
// Выплаты / обязательства / финансовые вложения
{ name: 'w.wal.wthdrw', human_name: 'Возвраты паевых взносов пайщикам' },
{ name: 'w.mkt.payout', human_name: 'Выплаты поставщикам' },
{ name: 'w.cap.loan', human_name: 'Выданные пайщикам беспроцентные займы' },
// Служебные
{ name: 'w.led.adjust', human_name: 'Ручные корректировки' },
// ЦПП «Благорост»
{ name: 'w.cap.bginv', human_name: 'ЦПП «Благорост» — инвестиции деньгами' },
{ name: 'w.cap.bgrid', human_name: 'ЦПП «Благорост» — принятые РИД' },
{ name: 'w.cap.bgprop', human_name: 'ЦПП «Благорост» — имущественные паевые взносы' },
{ name: 'w.cap.bgmem', human_name: 'ЦПП «Благорост» — членские взносы' },
// ЦПП «Генератор»
{ name: 'w.cap.gncom', human_name: 'ЦПП «Генератор» — принятый коммит (имущество)' },
{ name: 'w.cap.gnmem', human_name: 'ЦПП «Генератор» — членские взносы' },
// ЦПП «Стол Заказов»
{ name: 'w.mkt.fund', human_name: 'ЦПП «Стол Заказов» — общий кошелёк' },
] as const
const walletNameById = new Map<number, string>(
LEDGER2_WALLET_REGISTRY.map((w) => [w.id, w.name]),
const walletHumanByName = new Map<string, string>(
LEDGER2_WALLET_REGISTRY.map((w) => [w.name, w.human_name]),
)
export function getWalletName(id: number | null | undefined): string | undefined {
if (id == null) return undefined
return walletNameById.get(id)
/**
* Возвращает человекочитаемое имя кошелька по его eosio::name-идентификатору.
* Возвращает undefined для незарегистрированных имён и для пустого имени
* (sentinel "" — кошелёк-источник/сток вне системы при ISSUE/REVOKE).
*/
export function getWalletHumanName(name: IName | null | undefined): string | undefined {
if (!name) return undefined
return walletHumanByName.get(name)
}
@@ -4,10 +4,10 @@ div.page-shell
q-card.q-mt-md(flat)
q-card-section
.row.q-gutter-sm.items-center.q-mb-sm(
v-if='filters.accountId !== null || filters.processHash || filters.username'
v-if='filters.accountId !== null || filters.walletName || filters.processHash || filters.username'
)
q-chip(
v-if='filters.accountId !== null'
v-if='filters.accountId !== null || filters.walletName'
removable
color='primary'
text-color='white'
@@ -198,10 +198,10 @@ div.page-shell
DirectionCell(:direction='cp.row.direction')
template(#body-cell-walletFrom='cp')
q-td(:props='cp')
WalletIdCell(:wallet-id='cp.row.walletFrom')
WalletIdCell(:wallet-name='cp.row.walletFrom')
template(#body-cell-walletTo='cp')
q-td(:props='cp')
WalletIdCell(:wallet-id='cp.row.walletTo')
WalletIdCell(:wallet-name='cp.row.walletTo')
template(#body-cell-quantity='cp')
q-td.text-right(:props='cp') {{ formatAmount(cp.row.quantity) }}
.text-caption.text-grey-6(v-else) Движений по кошелькам нет
@@ -381,7 +381,10 @@ const pagination = ref({ page: 1, rowsPerPage: 50, rowsNumber: 0 })
const filters = reactive<{
dateFrom: string
dateTo: string
/** Бух.счёт (×1000): 51000/80000/86000 — для фильтра по debit/credit. */
accountId: number | null
/** eosio::name кошелька (`w.<contract>.<waltype>`) — для фильтра по walletop. */
walletName: string | null
accountKind: 'wallet' | 'account' | null
accountName: string
processHash: string | null
@@ -393,6 +396,7 @@ const filters = reactive<{
dateFrom: '',
dateTo: '',
accountId: null,
walletName: null,
accountKind: null,
accountName: '',
processHash: null,
@@ -436,19 +440,23 @@ async function applyProcessHashSearch(): Promise<void> {
const accountFilterLabel = computed(() => {
if (filters.accountId === null) return ''
if (filters.accountKind === 'wallet') {
return `Кошелёк ${filters.accountId}${filters.accountName ? `${filters.accountName}` : ''}`
if (filters.accountKind === 'wallet' && filters.walletName) {
return `Кошелёк ${filters.walletName}${filters.accountName ? `${filters.accountName}` : ''}`
}
const displayCode = Math.round(filters.accountId / 1000)
return `Счёт ${displayCode}${filters.accountName ? `${filters.accountName}` : ''}`
if (filters.accountId !== null) {
const displayCode = Math.round(filters.accountId / 1000)
return `Счёт ${displayCode}${filters.accountName ? `${filters.accountName}` : ''}`
}
return ''
})
async function clearAccountFilter() {
filters.accountId = null
filters.walletName = null
filters.accountKind = null
filters.accountName = ''
const q = { ...route.query }
delete q.wallet_name
delete q.wallet_id
delete q.account_id
await router.replace({ query: q })
@@ -472,17 +480,21 @@ async function clearUsernameFilter() {
reload()
}
async function resolveAccountName(id: number, kind: 'wallet' | 'account') {
async function resolveWalletName(walletName: string) {
try {
if (kind === 'wallet') {
const wallets = await ledger2Store.loadWallets(info.coopname)
const w = wallets.find((x) => x.id === id)
if (w) filters.accountName = w.name
} else {
const accounts = await ledger2Store.loadAccounts(info.coopname)
const a = accounts.find((x) => x.id === id)
if (a) filters.accountName = a.name
}
const wallets = await ledger2Store.loadWallets(info.coopname)
const w = wallets.find((x) => x.id === walletName)
if (w) filters.accountName = w.name
} catch {
// молча — chip покажется без названия
}
}
async function resolveAccountName(id: number) {
try {
const accounts = await ledger2Store.loadAccounts(info.coopname)
const a = accounts.find((x) => x.id === id)
if (a) filters.accountName = a.name
} catch {
// молча — chip покажется без названия
}
@@ -514,8 +526,9 @@ const accountColumns = [
interface WalletRow {
globalSequence: string
direction: 'in' | 'out' | 'move'
walletFrom: number | null
walletTo: number | null
/** eosio::name кошелька (`w.<contract>.<waltype>`) или null. */
walletFrom: string | null
walletTo: string | null
quantity: string | null
}
interface AccountRow {
@@ -565,6 +578,7 @@ const hasAnyFilter = computed(
!!filters.dateFrom ||
!!filters.dateTo ||
filters.accountId !== null ||
!!filters.walletName ||
!!filters.processHash ||
!!filters.username ||
filters.adjustmentsOnly,
@@ -574,6 +588,7 @@ async function resetFilters() {
filters.dateFrom = ''
filters.dateTo = ''
filters.accountId = null
filters.walletName = null
filters.accountKind = null
filters.accountName = ''
filters.processHash = null
@@ -581,6 +596,7 @@ async function resetFilters() {
filters.username = null
filters.adjustmentsOnly = false
const q = { ...route.query }
delete q.wallet_name
delete q.wallet_id
delete q.account_id
delete q.process_hash
@@ -647,6 +663,7 @@ async function load() {
// process_hash, но не operation_code как payload-поле). Поэтому фильтр
// «Только корректировки» работает через actionNames=[walmove,revert].
if (filters.accountId !== null) input.accountId = filters.accountId
if (filters.walletName) input.walletName = filters.walletName
if (filters.processHash) input.processHash = filters.processHash
if (filters.username) input.username = filters.username
if (filters.dateFrom) input.dateFrom = new Date(filters.dateFrom)
@@ -711,11 +728,11 @@ onMounted(async () => {
if (route.query.account_id) {
filters.accountId = Number(route.query.account_id)
filters.accountKind = 'account'
resolveAccountName(filters.accountId, 'account')
} else if (route.query.wallet_id) {
filters.accountId = Number(route.query.wallet_id)
resolveAccountName(filters.accountId)
} else if (route.query.wallet_name) {
filters.walletName = String(route.query.wallet_name)
filters.accountKind = 'wallet'
resolveAccountName(filters.accountId, 'wallet')
resolveWalletName(filters.walletName)
}
if (route.query.process_hash) {
filters.processHash = String(route.query.process_hash).toLowerCase()
@@ -51,7 +51,7 @@ div.page-shell
flat dense size='sm' color='primary'
icon='fa-solid fa-arrow-right'
label='К операциям'
:to='{ name: "reports-operations", query: { wallet_id: props.row.id } }'
:to='{ name: "reports-operations", query: { wallet_name: props.row.id } }'
)
q-table(
flat dense
@@ -65,13 +65,13 @@ div.page-shell
)
template(#body-cell-direction='cp')
q-td(:props='cp')
DirectionCell(:direction='directionFor(cp.row, props.row.id)')
DirectionCell(:direction='directionFor(cp.row, String(props.row.id))')
template(#body-cell-walletFrom='cp')
q-td(:props='cp')
WalletIdCell(:wallet-id='cp.row.walletFrom')
WalletIdCell(:wallet-name='cp.row.walletFrom')
template(#body-cell-walletTo='cp')
q-td(:props='cp')
WalletIdCell(:wallet-id='cp.row.walletTo')
WalletIdCell(:wallet-name='cp.row.walletTo')
template(#body-cell-quantity='cp')
q-td.text-right(:props='cp') {{ cp.row.quantity ? formatAsset2Digits(cp.row.quantity) : '—' }}
template(#body-cell-memo='cp')
@@ -144,11 +144,12 @@ const loading = ref(false)
const pagination = ref({ rowsPerPage: 0 })
const wallets = ref<ILedger2Wallet[]>([])
const expanded = ref(new Set<number>())
const childOps = ref(new Map<number, ILedger2Operation[]>())
const childLoading = ref(new Set<number>())
// Идентификатор кошелька — eosio::name (строка `w.<contract>.<waltype>`).
const expanded = ref(new Set<string>())
const childOps = ref(new Map<string, ILedger2Operation[]>())
const childLoading = ref(new Set<string>())
const transferDialog = reactive<{ open: boolean; fromWallet: number | null }>({
const transferDialog = reactive<{ open: boolean; fromWallet: string | null }>({
open: false,
fromWallet: null,
})
@@ -158,8 +159,8 @@ function openTransferEmpty(): void {
transferDialog.open = true
}
function openTransferFor(walletId: number): void {
transferDialog.fromWallet = walletId
function openTransferFor(walletName: string): void {
transferDialog.fromWallet = walletName
transferDialog.open = true
}
@@ -204,17 +205,17 @@ const childColumns: any[] = [
{ name: 'open', align: 'right', label: '', field: 'processHash' },
]
function directionFor(op: ILedger2Operation, walletId: number): 'in' | 'out' | 'move' {
function directionFor(op: ILedger2Operation, walletName: string): 'in' | 'out' | 'move' {
const from = op.walletFrom ?? null
const to = op.walletTo ?? null
if (from === walletId && to && to !== walletId) return 'out'
if (to === walletId && from && from !== walletId) return 'in'
if (to === walletId && !from) return 'in'
if (from === walletId && !to) return 'out'
if (from === walletName && to && to !== walletName) return 'out'
if (to === walletName && from && from !== walletName) return 'in'
if (to === walletName && !from) return 'in'
if (from === walletName && !to) return 'out'
return 'move'
}
async function toggleExpand(id: number) {
async function toggleExpand(id: string) {
if (expanded.value.has(id)) {
expanded.value.delete(id)
return
@@ -225,7 +226,7 @@ async function toggleExpand(id: number) {
try {
const resp = await ledger2Store.loadHistory({
coopname: info.coopname,
accountId: id,
walletName: id,
actionNames: ['walletop'],
limit: 20,
sortOrder: 'DESC',
@@ -92,8 +92,9 @@ import { useLedger2Store, type ILedger2Wallet } from 'src/entities/Ledger2'
interface Props {
modelValue: boolean
/** Если задано — диалог открыт «из строки кошелька»: from_wallet залочен. */
fixedFromWallet?: number | null
/** Если задано — диалог открыт «из строки кошелька»: from_wallet залочен.
* Значение — eosio::name кошелька (`w.<contract>.<waltype>`). */
fixedFromWallet?: string | null
/** Список кошельков для выбора (общекооперативные ledger2 wallets). */
wallets: ILedger2Wallet[]
}
@@ -111,8 +112,8 @@ const formRef = ref<{ validate: () => Promise<boolean> } | null>(null)
const loading = ref(false)
const form = reactive({
fromWallet: null as number | null,
toWallet: null as number | null,
fromWallet: null as string | null,
toWallet: null as string | null,
amountStr: '',
memo: '',
})
@@ -121,20 +122,22 @@ const form = reactive({
* Привязка wallet→account по cooptypes (LEDGER2_OPERATION_REGISTRY).
* Та же логика, что в backend: первая стандартная операция, где встречается
* этот кошелёк, даёт его account_id (debit/credit зависит от роли).
*
* `walletName` — eosio::name кошелька (`w.<contract>.<waltype>`).
*/
function resolveAccountId(walletId: number): number | null {
function resolveAccountId(walletName: string): number | null {
for (const op of Ledger2.LEDGER2_OPERATION_REGISTRY) {
if (op.kind === 'adjustment') continue
if (op.wallet_op === 'WALLET_ONLY') continue
if (op.wallet_from === walletId && op.credit !== null) return op.credit
if (op.wallet_to === walletId && op.wallet_op === 'ISSUE' && op.credit !== null) return op.credit
if (op.wallet_to === walletId && op.debit !== null) return op.debit
if (op.wallet_from === walletId && op.debit !== null) return op.debit
if (op.wallet_from === walletName && op.credit !== null) return op.credit
if (op.wallet_to === walletName && op.wallet_op === 'ISSUE' && op.credit !== null) return op.credit
if (op.wallet_to === walletName && op.debit !== null) return op.debit
if (op.wallet_from === walletName && op.debit !== null) return op.debit
}
for (const op of Ledger2.LEDGER2_OPERATION_REGISTRY) {
if (op.wallet_op !== 'WALLET_ONLY' || op.kind === 'adjustment') continue
if (op.wallet_from === walletId && op.wallet_to !== null) return resolveAccountId(op.wallet_to)
if (op.wallet_to === walletId && op.wallet_from !== null) return resolveAccountId(op.wallet_from)
if (op.wallet_from === walletName && op.wallet_to !== null) return resolveAccountId(op.wallet_to)
if (op.wallet_to === walletName && op.wallet_from !== null) return resolveAccountId(op.wallet_from)
}
return null
}
@@ -1,8 +1,8 @@
<template lang="pug">
span(v-if='walletId === null || walletId === undefined').text-grey-6
span(v-if='!walletName').text-grey-6
span.wallet-id-cell(v-else)
EntityIdBadge(
:rawId='walletId'
:rawId='walletName'
copy-on-click
)
q-tooltip(v-if='tooltipText' anchor='top middle' self='bottom middle') {{ tooltipText }}
@@ -11,15 +11,15 @@ span.wallet-id-cell(v-else)
<script setup lang="ts">
import { computed } from 'vue'
import { EntityIdBadge } from 'src/shared/ui'
import { getWalletName } from 'src/shared/lib/ledger2'
import { getWalletHumanName } from 'src/shared/lib/ledger2'
const props = defineProps<{
walletId: number | null | undefined
walletName: string | null | undefined
}>()
const tooltipText = computed(() => {
if (props.walletId === null || props.walletId === undefined) return ''
const name = getWalletName(props.walletId)
return name ? `${name} (клик — копировать)` : 'Клик — копировать'
if (!props.walletName) return ''
const human = getWalletHumanName(props.walletName)
return human ? `${human} (клик — копировать)` : 'Клик — копировать'
})
</script>
@@ -21,7 +21,8 @@ interface ILedger2Store {
loadWallets: (coopname: string) => Promise<ILedger2Wallet[]>
loadHistory: (input: ILedger2HistoryFilterInput) => Promise<ILedger2HistoryResponse | undefined>
getAccountById: (id: number) => ILedger2Account | undefined
getWalletById: (id: number) => ILedger2Wallet | undefined
/** `name` — eosio::name кошелька (`w.<contract>.<waltype>`). */
getWalletByName: (name: string) => ILedger2Wallet | undefined
walmoveWallets: (input: IWalmoveInput) => Promise<ILedger2AdjustmentResult>
}
@@ -60,8 +61,8 @@ export const useLedger2Store = defineStore(namespace, (): ILedger2Store => {
return accounts.value.find((a) => a.id === id)
}
function getWalletById(id: number): ILedger2Wallet | undefined {
return wallets.value.find((w) => w.id === id)
function getWalletByName(name: string): ILedger2Wallet | undefined {
return wallets.value.find((w) => w.id === name)
}
async function walmoveWallets(input: IWalmoveInput): Promise<ILedger2AdjustmentResult> {
@@ -76,7 +77,7 @@ export const useLedger2Store = defineStore(namespace, (): ILedger2Store => {
loadWallets,
loadHistory,
getAccountById,
getWalletById,
getWalletByName,
walmoveWallets,
}
})
@@ -12,4 +12,4 @@ import { Ledger2 } from 'cooptypes'
export type WalletMeta = Ledger2.WalletMeta
export const LEDGER2_WALLET_REGISTRY = Ledger2.LEDGER2_WALLET_REGISTRY
export const getWalletName = Ledger2.getWalletName
export const getWalletHumanName = Ledger2.getWalletHumanName
+56 -48
View File
@@ -5641,7 +5641,7 @@ export type ValueTypes = {
_id: string | Variable<any, string>
};
["GetLedger2HistoryInput"]: {
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?: number | undefined | null | Variable<any, string>,
/** Имена blockchain-действий: apply | walletop | debit | credit */
actionNames?: Array<string> | undefined | null | Variable<any, string>,
@@ -5657,7 +5657,9 @@ export type ValueTypes = {
/** process_hash для выборки всех действий одной операции */
processHash?: string | undefined | null | Variable<any, string>,
sortOrder?: string | undefined | null | Variable<any, string>,
username?: string | undefined | null | Variable<any, string>
username?: string | undefined | null | Variable<any, string>,
/** eosio::name кошелька (`w.<contract>.<waltype>`) — для walletop действий. */
walletName?: string | undefined | null | Variable<any, string>
};
["GetLedgerHistoryInput"]: {
/** ID счета для фильтрации. Если не указан, возвращаются операции по всем счетам */
@@ -5874,7 +5876,7 @@ export type ValueTypes = {
['...on Ledger2HistoryResponse']?: Omit<ValueTypes["Ledger2HistoryResponse"], "...on Ledger2HistoryResponse">
}>;
["Ledger2Operation"]: AliasType<{
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?:boolean | `@${string}`,
/** apply | walletop | debit | credit */
action?:boolean | `@${string}`,
@@ -5891,9 +5893,9 @@ export type ValueTypes = {
/** Asset "100.0000 RUB" */
quantity?:boolean | `@${string}`,
username?:boolean | `@${string}`,
/** walletop: wallet_from (исходящий) */
/** walletop: wallet_from (eosio::name w.<contract>.<waltype>) */
walletFrom?:boolean | `@${string}`,
/** walletop: wallet_to (входящий) */
/** walletop: wallet_to (eosio::name w.<contract>.<waltype>) */
walletTo?:boolean | `@${string}`,
__typename?: boolean | `@${string}`,
['...on Ledger2Operation']?: Omit<ValueTypes["Ledger2Operation"], "...on Ledger2Operation">
@@ -5903,7 +5905,7 @@ export type ValueTypes = {
available?:boolean | `@${string}`,
/** Заблокированный баланс */
blocked?:boolean | `@${string}`,
/** ID кошелька (×1000 offset): 1001/2001/3001/4001 */
/** eosio::name-идентификатор кошелька (w.<contract>.<waltype>) */
id?:boolean | `@${string}`,
/** Название кошелька */
name?:boolean | `@${string}`,
@@ -9234,14 +9236,14 @@ validateReportEdits?: [{ editsJson: string | Variable<any, string>, reportType:
}>;
["WalmoveInput"]: {
coopname: string | Variable<any, string>,
/** id кошелька-источника */
fromWallet: number | Variable<any, string>,
/** eosio::name кошелька-источника (w.<contract>.<waltype>) */
fromWallet: string | Variable<any, string>,
/** Обязательное обоснование корректировки */
memo: string | Variable<any, string>,
/** Сумма с символом, например "100.0000 RUB" */
quantity: string | Variable<any, string>,
/** id кошелька-приёмника */
toWallet: number | Variable<any, string>,
/** eosio::name кошелька-приёмника (w.<contract>.<waltype>) */
toWallet: string | Variable<any, string>,
/** Владелец кошельков (для коллективных — coopname) */
username: string | Variable<any, string>
};
@@ -13749,7 +13751,7 @@ export type ResolverInputTypes = {
_id: string
};
["GetLedger2HistoryInput"]: {
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?: number | undefined | null,
/** Имена blockchain-действий: apply | walletop | debit | credit */
actionNames?: Array<string> | undefined | null,
@@ -13765,7 +13767,9 @@ export type ResolverInputTypes = {
/** process_hash для выборки всех действий одной операции */
processHash?: string | undefined | null,
sortOrder?: string | undefined | null,
username?: string | undefined | null
username?: string | undefined | null,
/** eosio::name кошелька (`w.<contract>.<waltype>`) — для walletop действий. */
walletName?: string | undefined | null
};
["GetLedgerHistoryInput"]: {
/** ID счета для фильтрации. Если не указан, возвращаются операции по всем счетам */
@@ -13975,7 +13979,7 @@ export type ResolverInputTypes = {
__typename?: boolean | `@${string}`
}>;
["Ledger2Operation"]: AliasType<{
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?:boolean | `@${string}`,
/** apply | walletop | debit | credit */
action?:boolean | `@${string}`,
@@ -13992,9 +13996,9 @@ export type ResolverInputTypes = {
/** Asset "100.0000 RUB" */
quantity?:boolean | `@${string}`,
username?:boolean | `@${string}`,
/** walletop: wallet_from (исходящий) */
/** walletop: wallet_from (eosio::name w.<contract>.<waltype>) */
walletFrom?:boolean | `@${string}`,
/** walletop: wallet_to (входящий) */
/** walletop: wallet_to (eosio::name w.<contract>.<waltype>) */
walletTo?:boolean | `@${string}`,
__typename?: boolean | `@${string}`
}>;
@@ -14003,7 +14007,7 @@ export type ResolverInputTypes = {
available?:boolean | `@${string}`,
/** Заблокированный баланс */
blocked?:boolean | `@${string}`,
/** ID кошелька (×1000 offset): 1001/2001/3001/4001 */
/** eosio::name-идентификатор кошелька (w.<contract>.<waltype>) */
id?:boolean | `@${string}`,
/** Название кошелька */
name?:boolean | `@${string}`,
@@ -17227,14 +17231,14 @@ validateReportEdits?: [{ editsJson: string, reportType: ResolverInputTypes["Repo
}>;
["WalmoveInput"]: {
coopname: string,
/** id кошелька-источника */
fromWallet: number,
/** eosio::name кошелька-источника (w.<contract>.<waltype>) */
fromWallet: string,
/** Обязательное обоснование корректировки */
memo: string,
/** Сумма с символом, например "100.0000 RUB" */
quantity: string,
/** id кошелька-приёмника */
toWallet: number,
/** eosio::name кошелька-приёмника (w.<contract>.<waltype>) */
toWallet: string,
/** Владелец кошельков (для коллективных — coopname) */
username: string
};
@@ -21623,7 +21627,7 @@ export type ModelTypes = {
_id: string
};
["GetLedger2HistoryInput"]: {
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?: number | undefined | null,
/** Имена blockchain-действий: apply | walletop | debit | credit */
actionNames?: Array<string> | undefined | null,
@@ -21639,7 +21643,9 @@ export type ModelTypes = {
/** process_hash для выборки всех действий одной операции */
processHash?: string | undefined | null,
sortOrder?: string | undefined | null,
username?: string | undefined | null
username?: string | undefined | null,
/** eosio::name кошелька (`w.<contract>.<waltype>`) — для walletop действий. */
walletName?: string | undefined | null
};
["GetLedgerHistoryInput"]: {
/** ID счета для фильтрации. Если не указан, возвращаются операции по всем счетам */
@@ -21838,7 +21844,7 @@ export type ModelTypes = {
totalPages: number
};
["Ledger2Operation"]: {
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?: number | undefined | null,
/** apply | walletop | debit | credit */
action: string,
@@ -21855,18 +21861,18 @@ export type ModelTypes = {
/** Asset "100.0000 RUB" */
quantity?: string | undefined | null,
username?: string | undefined | null,
/** walletop: wallet_from (исходящий) */
walletFrom?: number | undefined | null,
/** walletop: wallet_to (входящий) */
walletTo?: number | undefined | null
/** walletop: wallet_from (eosio::name w.<contract>.<waltype>) */
walletFrom?: string | undefined | null,
/** walletop: wallet_to (eosio::name w.<contract>.<waltype>) */
walletTo?: string | undefined | null
};
["Ledger2Wallet"]: {
/** Доступный баланс */
available: string,
/** Заблокированный баланс */
blocked: string,
/** ID кошелька (×1000 offset): 1001/2001/3001/4001 */
id: number,
/** eosio::name-идентификатор кошелька (w.<contract>.<waltype>) */
id: string,
/** Название кошелька */
name: string
};
@@ -23694,7 +23700,7 @@ export type ModelTypes = {
getLedger2Accounts: Array<ModelTypes["Ledger2Account"]>,
/** История операций ledger2 с серверными фильтрами (action/accountId/username/date-range). */
getLedger2History: ModelTypes["Ledger2HistoryResponse"],
/** Общекооперативные кошельки из ledger2::wallets (1001/2001/3001/4001). Кошельки пайщиков живут в контракте soviet — сюда не попадают. */
/** Общекооперативные кошельки из ledger2::wallets (eosio::name w.<contract>.<waltype>). Кошельки пайщиков живут в контракте soviet — сюда не попадают. */
getLedger2Wallets: Array<ModelTypes["Ledger2Wallet"]>,
/** Получить историю операций по счетам кооператива. Возвращает список операций с возможностью фильтрации по account_id и пагинацией. Операции сортируются по дате создания (новые первыми). */
getLedgerHistory: ModelTypes["LedgerHistoryResponse"],
@@ -25212,14 +25218,14 @@ export type ModelTypes = {
};
["WalmoveInput"]: {
coopname: string,
/** id кошелька-источника */
fromWallet: number,
/** eosio::name кошелька-источника (w.<contract>.<waltype>) */
fromWallet: string,
/** Обязательное обоснование корректировки */
memo: string,
/** Сумма с символом, например "100.0000 RUB" */
quantity: string,
/** id кошелька-приёмника */
toWallet: number,
/** eosio::name кошелька-приёмника (w.<contract>.<waltype>) */
toWallet: string,
/** Владелец кошельков (для коллективных — coopname) */
username: string
};
@@ -29831,7 +29837,7 @@ export type GraphQLTypes = {
_id: string
};
["GetLedger2HistoryInput"]: {
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?: number | undefined | null,
/** Имена blockchain-действий: apply | walletop | debit | credit */
actionNames?: Array<string> | undefined | null,
@@ -29847,7 +29853,9 @@ export type GraphQLTypes = {
/** process_hash для выборки всех действий одной операции */
processHash?: string | undefined | null,
sortOrder?: string | undefined | null,
username?: string | undefined | null
username?: string | undefined | null,
/** eosio::name кошелька (`w.<contract>.<waltype>`) — для walletop действий. */
walletName?: string | undefined | null
};
["GetLedgerHistoryInput"]: {
/** ID счета для фильтрации. Если не указан, возвращаются операции по всем счетам */
@@ -30065,7 +30073,7 @@ export type GraphQLTypes = {
};
["Ledger2Operation"]: {
__typename: "Ledger2Operation",
/** ID счёта/кошелька (×1000) */
/** Бух.счёт (×1000): 51000/80000/86000 — для debit/credit действий. */
accountId?: number | undefined | null,
/** apply | walletop | debit | credit */
action: string,
@@ -30082,10 +30090,10 @@ export type GraphQLTypes = {
/** Asset "100.0000 RUB" */
quantity?: string | undefined | null,
username?: string | undefined | null,
/** walletop: wallet_from (исходящий) */
walletFrom?: number | undefined | null,
/** walletop: wallet_to (входящий) */
walletTo?: number | undefined | null,
/** walletop: wallet_from (eosio::name w.<contract>.<waltype>) */
walletFrom?: string | undefined | null,
/** walletop: wallet_to (eosio::name w.<contract>.<waltype>) */
walletTo?: string | undefined | null,
['...on Ledger2Operation']: Omit<GraphQLTypes["Ledger2Operation"], "...on Ledger2Operation">
};
["Ledger2Wallet"]: {
@@ -30094,8 +30102,8 @@ export type GraphQLTypes = {
available: string,
/** Заблокированный баланс */
blocked: string,
/** ID кошелька (×1000 offset): 1001/2001/3001/4001 */
id: number,
/** eosio::name-идентификатор кошелька (w.<contract>.<waltype>) */
id: string,
/** Название кошелька */
name: string,
['...on Ledger2Wallet']: Omit<GraphQLTypes["Ledger2Wallet"], "...on Ledger2Wallet">
@@ -32081,7 +32089,7 @@ export type GraphQLTypes = {
getLedger2Accounts: Array<GraphQLTypes["Ledger2Account"]>,
/** История операций ledger2 с серверными фильтрами (action/accountId/username/date-range). */
getLedger2History: GraphQLTypes["Ledger2HistoryResponse"],
/** Общекооперативные кошельки из ledger2::wallets (1001/2001/3001/4001). Кошельки пайщиков живут в контракте soviet — сюда не попадают. */
/** Общекооперативные кошельки из ledger2::wallets (eosio::name w.<contract>.<waltype>). Кошельки пайщиков живут в контракте soviet — сюда не попадают. */
getLedger2Wallets: Array<GraphQLTypes["Ledger2Wallet"]>,
/** Получить историю операций по счетам кооператива. Возвращает список операций с возможностью фильтрации по account_id и пагинацией. Операции сортируются по дате создания (новые первыми). */
getLedgerHistory: GraphQLTypes["LedgerHistoryResponse"],
@@ -33691,14 +33699,14 @@ export type GraphQLTypes = {
};
["WalmoveInput"]: {
coopname: string,
/** id кошелька-источника */
fromWallet: number,
/** eosio::name кошелька-источника (w.<contract>.<waltype>) */
fromWallet: string,
/** Обязательное обоснование корректировки */
memo: string,
/** Сумма с символом, например "100.0000 RUB" */
quantity: string,
/** id кошелька-приёмника */
toWallet: number,
/** eosio::name кошелька-приёмника (w.<contract>.<waltype>) */
toWallet: string,
/** Владелец кошельков (для коллективных — coopname) */
username: string
};
+330 -72
View File
@@ -1043,6 +1043,18 @@ importers:
specifier: ^6.1.1
version: 6.3.0
components/docs-harness:
devDependencies:
'@playwright/test':
specifier: ^1.59.1
version: 1.59.1
playwright:
specifier: ^1.59.1
version: 1.59.1
sharp:
specifier: ^0.34.5
version: 0.34.5
components/factory:
dependencies:
ajv:
@@ -4239,6 +4251,143 @@ packages:
'@iconify/utils@3.1.0':
resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==}
'@img/colour@1.1.0':
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
engines: {node: '>=18'}
'@img/sharp-darwin-arm64@0.34.5':
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-x64@0.34.5':
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-darwin-arm64@1.2.4':
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.2.4':
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-linux-arm64@1.2.4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
'@img/sharp-win32-arm64@0.34.5':
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [win32]
'@img/sharp-win32-ia32@0.34.5':
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-x64@0.34.5':
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
'@inquirer/ansi@1.0.2':
resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==}
engines: {node: '>=18'}
@@ -4743,14 +4892,12 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@livekit/rtc-node-linux-x64-gnu@0.13.24':
resolution: {integrity: sha512-vKOxzN/SsrtV8zIVwZCi31bZUhlb6RhJZ0NnY5MwKGSRFPi7Dwt8fmr0Vh0YmsY/p+4eZjKxvFmy7L3WVE54zw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@livekit/rtc-node-win32-x64-msvc@0.13.24':
resolution: {integrity: sha512-yTzqwndq2oKLUkXW2i/BkZMJC6kZOpRO/DKvkkKQvqc3Q+JuWz1m48GmyjIwTOKF28QjqEU3+IrnD65Uu+mFOg==}
@@ -5202,28 +5349,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@nx/nx-linux-arm64-musl@20.8.4':
resolution: {integrity: sha512-AlZZFolS/S0FahRKG7rJ0Z9CgmIkyzHgGaoy3qNEMDEjFhR3jt2ZZSLp90W7zjgrxojOo90ajNMrg2UmtcQRDA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@nx/nx-linux-x64-gnu@20.8.4':
resolution: {integrity: sha512-MSu+xVNdR95tuuO+eL/a/ZeMlhfrZ627On5xaCZXnJ+lFxNg/S4nlKZQk0Eq5hYALCd/GKgFGasRdlRdOtvGPg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@nx/nx-linux-x64-musl@20.8.4':
resolution: {integrity: sha512-KxpQpyLCgIIHWZ4iRSUN9ohCwn1ZSDASbuFCdG3mohryzCy8WrPkuPcb+68J3wuQhmA5w//Xpp/dL0hHoit9zQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@nx/nx-win32-arm64-msvc@20.8.4':
resolution: {integrity: sha512-ffLBrxM9ibk+eWSY995kiFFRTSRb9HkD5T1s/uZyxV6jfxYPaZDBAWAETDneyBXps7WtaOMu+kVZlXQ3X+TfIA==}
@@ -5720,42 +5863,36 @@ packages:
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.6':
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.6':
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.6':
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.6':
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.6':
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@parcel/watcher-win32-arm64@2.5.6':
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
@@ -5833,6 +5970,11 @@ packages:
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@playwright/test@1.59.1':
resolution: {integrity: sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==}
engines: {node: '>=18'}
hasBin: true
'@pm2/agent@2.0.4':
resolution: {integrity: sha512-n7WYvvTJhHLS2oBb1PjOtgLpMhgImOq8sXkPBw6smeg9LJBWZjiEgPKOpR8mn9UJZsB5P3W4V/MyvNnp31LKeA==}
@@ -5998,42 +6140,36 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.12':
resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12':
resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12':
resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.12':
resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.0.0-rc.12':
resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.0.0-rc.12':
resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==}
@@ -6180,79 +6316,66 @@ packages:
resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.60.0':
resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.60.0':
resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.60.0':
resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.60.0':
resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.60.0':
resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.60.0':
resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.60.0':
resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.60.0':
resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.60.0':
resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.60.0':
resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.60.0':
resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.60.0':
resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.60.0':
resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==}
@@ -7321,49 +7444,41 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -11135,6 +11250,11 @@ packages:
fs@0.0.1-security:
resolution: {integrity: sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==}
fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -12904,28 +13024,24 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
@@ -14908,6 +15024,16 @@ packages:
resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==}
engines: {node: '>=16.0.0'}
playwright-core@1.59.1:
resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==}
engines: {node: '>=18'}
hasBin: true
playwright@1.59.1:
resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==}
engines: {node: '>=18'}
hasBin: true
plur@4.0.0:
resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==}
engines: {node: '>=10'}
@@ -16113,56 +16239,48 @@ packages:
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [linux]
libc: glibc
sass-embedded-linux-arm@1.98.0:
resolution: {integrity: sha512-03baQZCxVyEp8v1NWBRlzGYrmVT/LK7ZrHlF1piscGiGxwfdxoLXVuxsylx3qn/dD/4i/rh7Bzk7reK1br9jvQ==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [linux]
libc: glibc
sass-embedded-linux-musl-arm64@1.98.0:
resolution: {integrity: sha512-LeqNxQA8y4opjhe68CcFvMzCSrBuJqYVFbwElEj9bagHXQHTp9xVPJRn6VcrC+0VLEDq13HVXMv7RslIuU0zmA==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [linux]
libc: musl
sass-embedded-linux-musl-arm@1.98.0:
resolution: {integrity: sha512-OBkjTDPYR4hSaueOGIM6FDpl9nt/VZwbSRpbNu9/eEJcxE8G/vynRugW8KRZmCFjPy8j/jkGBvvS+k9iOqKV3g==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [linux]
libc: musl
sass-embedded-linux-musl-riscv64@1.98.0:
resolution: {integrity: sha512-7w6hSuOHKt8FZsmjRb3iGSxEzM87fO9+M8nt5JIQYMhHTj5C+JY/vcske0v715HCVj5e1xyTnbGXf8FcASeAIw==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [linux]
libc: musl
sass-embedded-linux-musl-x64@1.98.0:
resolution: {integrity: sha512-QikNyDEJOVqPmxyCFkci8ZdCwEssdItfjQFJB+D+Uy5HFqcS5Lv3d3GxWNX/h1dSb23RPyQdQc267ok5SbEyJw==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [linux]
libc: musl
sass-embedded-linux-riscv64@1.98.0:
resolution: {integrity: sha512-E7fNytc/v4xFBQKzgzBddV/jretA4ULAPO6XmtBiQu4zZBdBozuSxsQLe2+XXeb0X4S2GIl72V7IPABdqke/vA==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [linux]
libc: glibc
sass-embedded-linux-x64@1.98.0:
resolution: {integrity: sha512-VsvP0t/uw00mMNPv3vwyYKUrFbqzxQHnRMO+bHdAMjvLw4NFf6mscpym9Bzf+NXwi1ZNKnB6DtXjmcpcvqFqYg==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [linux]
libc: glibc
sass-embedded-unknown-all@1.98.0:
resolution: {integrity: sha512-C4MMzcAo3oEDQnW7L8SBgB9F2Fq5qHPnaYTZRMOH3Mp/7kM4OooBInXpCiiFjLnjY95hzP4KyctVx0uYR6MYlQ==}
@@ -16335,6 +16453,10 @@ packages:
shallowequal@1.1.0:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
sharp@0.34.5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
shebang-command@1.2.0:
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
engines: {node: '>=0.10.0'}
@@ -18862,7 +18984,7 @@ snapshots:
'@stylistic/eslint-plugin': 2.13.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/parser': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
'@vitest/eslint-plugin': 1.6.13(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@7.18.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)(vitest@1.6.1(@types/node@20.19.37)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1))
'@vitest/eslint-plugin': 1.6.13(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)(vitest@1.6.1(@types/node@20.19.37)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1))
eslint: 9.39.4(jiti@2.6.1)
eslint-config-flat-gitignore: 0.1.8
eslint-flat-config-utils: 0.3.1
@@ -18879,7 +19001,7 @@ snapshots:
eslint-plugin-regexp: 2.10.0(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-toml: 0.11.1(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-unicorn: 55.0.0(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@7.18.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-vue: 9.33.0(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-yml: 1.19.1(eslint@9.39.4(jiti@2.6.1))
eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.5.31)(eslint@9.39.4(jiti@2.6.1))
@@ -18927,7 +19049,7 @@ snapshots:
eslint-plugin-regexp: 2.10.0(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-toml: 0.12.0(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-unicorn: 56.0.1(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@7.18.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-vue: 9.33.0(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-yml: 1.19.1(eslint@9.39.4(jiti@2.6.1))
eslint-processor-vue-blocks: 1.0.0(@vue/compiler-sfc@3.5.31)(eslint@9.39.4(jiti@2.6.1))
@@ -22140,6 +22262,102 @@ snapshots:
'@iconify/types': 2.0.0
mlly: 1.8.2
'@img/colour@1.1.0': {}
'@img/sharp-darwin-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.2.4
optional: true
'@img/sharp-darwin-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.2.4
optional: true
'@img/sharp-libvips-darwin-arm64@1.2.4':
optional: true
'@img/sharp-libvips-darwin-x64@1.2.4':
optional: true
'@img/sharp-libvips-linux-arm64@1.2.4':
optional: true
'@img/sharp-libvips-linux-arm@1.2.4':
optional: true
'@img/sharp-libvips-linux-ppc64@1.2.4':
optional: true
'@img/sharp-libvips-linux-riscv64@1.2.4':
optional: true
'@img/sharp-libvips-linux-s390x@1.2.4':
optional: true
'@img/sharp-libvips-linux-x64@1.2.4':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
optional: true
'@img/sharp-linux-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.2.4
optional: true
'@img/sharp-linux-arm@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.2.4
optional: true
'@img/sharp-linux-ppc64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.2.4
optional: true
'@img/sharp-linux-riscv64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-riscv64': 1.2.4
optional: true
'@img/sharp-linux-s390x@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.2.4
optional: true
'@img/sharp-linux-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.2.4
optional: true
'@img/sharp-linuxmusl-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
optional: true
'@img/sharp-linuxmusl-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
optional: true
'@img/sharp-wasm32@0.34.5':
dependencies:
'@emnapi/runtime': 1.9.1
optional: true
'@img/sharp-win32-arm64@0.34.5':
optional: true
'@img/sharp-win32-ia32@0.34.5':
optional: true
'@img/sharp-win32-x64@0.34.5':
optional: true
'@inquirer/ansi@1.0.2': {}
'@inquirer/ansi@2.0.4': {}
@@ -24359,6 +24577,10 @@ snapshots:
'@pkgr/core@0.2.9': {}
'@playwright/test@1.59.1':
dependencies:
playwright: 1.59.1
'@pm2/agent@2.0.4':
dependencies:
async: 3.2.6
@@ -26291,18 +26513,6 @@ snapshots:
vite: 8.0.3(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.31(typescript@5.9.3)
'@vitest/eslint-plugin@1.6.13(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@7.18.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)(vitest@1.6.1(@types/node@20.19.37)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1))':
dependencies:
'@typescript-eslint/scope-manager': 8.57.2
'@typescript-eslint/utils': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.4(jiti@2.6.1)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
typescript: 5.9.3
vitest: 1.6.1(@types/node@20.19.37)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1)
transitivePeerDependencies:
- supports-color
'@vitest/eslint-plugin@1.6.13(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@7.32.0)(typescript@5.9.3))(eslint@7.32.0)(typescript@5.9.3))(eslint@7.32.0)(typescript@5.9.3)(vitest@4.1.1(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(vite@8.0.3(@types/node@20.19.37)(esbuild@0.27.4)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))':
dependencies:
'@typescript-eslint/scope-manager': 8.57.2
@@ -26327,6 +26537,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vitest/eslint-plugin@1.6.13(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)(vitest@1.6.1(@types/node@20.19.37)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1))':
dependencies:
'@typescript-eslint/scope-manager': 8.57.2
'@typescript-eslint/utils': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.4(jiti@2.6.1)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
typescript: 5.9.3
vitest: 1.6.1(@types/node@20.19.37)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1)
transitivePeerDependencies:
- supports-color
'@vitest/eslint-plugin@1.6.13(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)(vitest@2.1.9(@types/node@22.19.15)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.1))':
dependencies:
'@typescript-eslint/scope-manager': 8.57.2
@@ -30600,12 +30822,6 @@ snapshots:
semver: 7.7.4
strip-indent: 3.0.0
eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@7.18.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)):
dependencies:
eslint: 9.39.4(jiti@2.6.1)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@7.32.0)(typescript@5.9.3))(eslint@7.32.0)(typescript@5.9.3))(eslint@7.32.0):
dependencies:
eslint: 7.32.0
@@ -31592,6 +31808,9 @@ snapshots:
fs@0.0.1-security: {}
fsevents@2.3.2:
optional: true
fsevents@2.3.3:
optional: true
@@ -36239,6 +36458,14 @@ snapshots:
pvutils: 1.1.5
tslib: 2.8.1
playwright-core@1.59.1: {}
playwright@1.59.1:
dependencies:
playwright-core: 1.59.1
optionalDependencies:
fsevents: 2.3.2
plur@4.0.0:
dependencies:
irregular-plurals: 3.5.0
@@ -37953,6 +38180,37 @@ snapshots:
shallowequal@1.1.0: {}
sharp@0.34.5:
dependencies:
'@img/colour': 1.1.0
detect-libc: 2.1.2
semver: 7.7.4
optionalDependencies:
'@img/sharp-darwin-arm64': 0.34.5
'@img/sharp-darwin-x64': 0.34.5
'@img/sharp-libvips-darwin-arm64': 1.2.4
'@img/sharp-libvips-darwin-x64': 1.2.4
'@img/sharp-libvips-linux-arm': 1.2.4
'@img/sharp-libvips-linux-arm64': 1.2.4
'@img/sharp-libvips-linux-ppc64': 1.2.4
'@img/sharp-libvips-linux-riscv64': 1.2.4
'@img/sharp-libvips-linux-s390x': 1.2.4
'@img/sharp-libvips-linux-x64': 1.2.4
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
'@img/sharp-linux-arm': 0.34.5
'@img/sharp-linux-arm64': 0.34.5
'@img/sharp-linux-ppc64': 0.34.5
'@img/sharp-linux-riscv64': 0.34.5
'@img/sharp-linux-s390x': 0.34.5
'@img/sharp-linux-x64': 0.34.5
'@img/sharp-linuxmusl-arm64': 0.34.5
'@img/sharp-linuxmusl-x64': 0.34.5
'@img/sharp-wasm32': 0.34.5
'@img/sharp-win32-arm64': 0.34.5
'@img/sharp-win32-ia32': 0.34.5
'@img/sharp-win32-x64': 0.34.5
shebang-command@1.2.0:
dependencies:
shebang-regex: 1.0.0