Применение принципов функционального программирования при проектировании ERP

    Привет, Хабр!

    В этой статье мы попробуем взглянуть на архитектуру учетных систем (ERP, CRM, WMS, MES, B2B, ...) с позиций функционального программирования. Существующие системы сложны. Они базируются на реляционной схеме данных, и имеют огромный мутабельный стейт в виде сотен связаных таблиц. При этом единственным «источником правды» в таких системах является хронологически-упорядоченный журнал первичных документов (отпечатков событий реального мира), которые, очевидно, должны быть иммутабельными (и это правило соблюдается в аудируемых системах, где корректировки «задним числом» запрещены). Журнал документов составляет от силы 20% объема БД, а все остальное — промежуточные абстракции и агрегаты, с которыми удобно работать на языке SQL, но которые требуют постоянной синхронизации с документами, и между собой.

    Если вернуться к истокам (устранить избыточность данных и отказаться от хранения агрегатов), а все бизнес-алгоритмы реализовать в виде функций, применяемых непосредственно к потоку первичных документов — мы получим функциональную СУБД, и построенную на ней функциональную ERP. Проблема производительности решается благодаря мемоизации, а объем функционального кода будет вполне соизмерим с объемом декларативного SQL, и не сложнее для понимания. В данной статье мы продемонстрируем подход, разработав простейшую файловую СУБД на языке TypeScript и рантайме Deno (аналог Node.js), а также протестируем производительность сверток на примере типичных бизнес-задач.

    Почему это актуально


    1) Мутабельный стейт + избыточность данных — это плохо, особенно когда необходимо обеспечивать его постоянную синхронизацию с потоком документов. Это источник потенциальных расхождений учетных данных (баланс не сходится) и трудно обнаруживаемых побочных эффектов.

    2) Жесткая реляционная схема хранения исходных и промежуточных данных дорого обходится в Big Data, гетерогенных системах, и в условиях быстрых изменений — то есть по сути везде. Мы предлагаем хранить документы в исходном виде, упорядочив по времени, разрешив связи «от новых к старым» и никогда наоборот. Это позволит рассчитывать большинство агрегатов однопроходными алгоритмами прямо из документов, а все остальные таблицы — не нужны.

    3) SQL устарел, так как предполагает доступность любых данных в любой момент, а в распределенных системах это очевидно не так — при разработке алгоритмов Big Data нужно быть готовым к тому, что часть необходимых данных появится позже, а часть уже появлялась раньше. Это требует небольшого переосмысления языка запросов, и сознательной заботы о кэшировании.

    4) Современные ЯП позволяют создать отзывчивую систему, оперирующую миллионами записей локально на ноутбуке, где РСУБД просто не установится. А если говорить о серверах — предлагаемая схема имеет больше возможностей для параллелизма, в том числе на кластерах типа SPARK.

    Предыстория вопроса


    Проработав достаточно долго с различным бизнес-ПО (системы учета, планирования, WMS), практически везде сталкивался с двумя проблемами — сложность внесения изменений в схему данных, и частое падение производительности, когда эти изменения таки вносились. Вообще, эти системы имеют сложную структуру, поскольку к ним предъявляются противоречивые требования:

    1) Аудируемость. Нужно хранить все первичные документы в неизменном виде. Разделение на справочники и операции весьма условно, во взрослых системах справочники огранизованы с версионированием, где каждое изменение оформляется специальным документом. Таким образом, исходные документы — это иммутабельная часть системы, и она является единственным «источником правды», а все остальные данные могут быть восстановлены из нее.

    2) Производительность запросов. Например, при создании строки заказа на продажу система должна рассчитать цену товара с учетом скидок, для чего необходимо извлечь статус клиента, его текущий баланс, историю покупок, текущие акции в регионе, и т.д. Естественно, вся необходимая информация не может быть вычислена «на лету», а должна быть доступной в полу-готовом виде. Поэтому существующие системы хранят удобные абстракции над строками документов (проводки), а также заранее рассчитанные агрегаты (регистры накопления, временные срезы, текущие остатки, сводные проводки, и т.д.). Их объем составляет до 80% размера БД, структура таблиц жестко фиксирована, при любых изменениях в алгоритмах — программист должен позаботиться о правильном обновлении агрегатов. По сути агрегаты это и есть мутабельное состояние системы.

    3) Транзакционная производительность. При проведении любого документа нужно пересчитать все агретаты, а это обычно блокирующая операция. Поэтому алгоритмы обновления агрегатов — самая болезненная точка системы, и при внесении большого количества изменений имеется существенный риск что-то сломать, и тогда данные «разъедутся», то есть агрегаты перестанут соответствовать документам. Эта ситуация — бич всех проектов внедрения и последующей поддержки.

    Устанавливаем основы новой архитектуры


    1) Хранение. Основа БД — хронологически упорядоченный журнал документов, отражающих свершившиеся факты реального мира. Справочники это тоже документы, просто длительного действия. И документ, и каждая версия записи справочника — иммутабельны. Никаких других данных в виде проводок / регистров / остатков в системе не хранится (сильное провокационное утверждение, в жизни бывает по разному, но нужно стремиться к совершенству). Документ имеет ряд системных атрибутов:

    {
        "sys": {
            "code": "partner.1",  // человеко-читаемый код, идентификатор группы
            "ts": 1578263624612,  // временная метка записи
            "id": "partner.1^1578263624612",  // составной уникальный глобальный ID
            "cache": 1  // признак необходимости принудительного кэширования
        },
        ...
    }

    Документы с одинаковым code и разными ts образуют историческую группу, где актуальной считается последняя запись, остальные — историческими. Если установлен атрибут cache, последняя запись из группы попадают в так называемый топ-кэш, и одновременно все записи попадают в фулл-кэш, таким образом мы можем быстро извлечь запись справочника как по id, так и по code.

    Документы могут дописываться в конец журнала, и никогда в середину. Изменение или удаление (отмена) старого документа — это всегда новый документ, записываемый в журнал с текущим ts. Таким образом, причинно-следственная связь определяется положением документа в журнале, скачки в прошлое или будущее запрещены (при этом даты принятия к учету, даты исполнения планов могут быть любыми, но с точки зрения ядра системы это просто атрибуты).

    2) Связи. Документы могут ссылаться друг на друга по id. В отличие от «sql foreign key» — указывать тип сущности, на которую ссылаемся, нет необходимости, так как сущности лежат вперемешку, а id уникален. Связи от ранних документов к более поздним запрещены. Это означает, что в любом пользовательском алгоритме при обработке текущего документа могут быть востребованы связанные документы, уже встречавшиеся в выборке ранее (и по идее они должны быть кэшированы — либо ядром, либо пользовательским алгоритмом).

    3) Горизонт иммутабельности. Часть документов, с которыми ведется активная работа (т.н. открытые документы) не может быть зафиксирована, поэтому вводится понятие горизонта иммутабельности, а база данных разделяется на 2 физических хранилища — иммутабельное хранилище и текущее хранилище. Все документы в первом хранилище имеют временную метку меньше горизонта, они неизменны, а результаты всех сверток кэшируются и переиспользуются. Все что позднее — называется текущим периодом, и при каждом запросе второе хранилище сканируется заново. Такая схема дает линейное время. Горизонт иммутабельности — термин, хорошо знакомый коллегам из 1С, и бухгалтерам. Производительность системы зависит исключительно от размера бардака текущего периода, и в этом вопросе мировая бизнес-практика беспощадна — чем он меньше, тем лучше.

    4) Алгоритмы. Журнал документов может храниться в любом виде — последовательный файл, документная БД, таблица РСУБД, поступать из внешнего стрима — главное чтобы они были извлекаемы в прямом либо обратном хронологическом порядке. Любой бизнес-алгоритм — это композиция функций filter(), reduce(), get(), gettop(), примененная к потоку документов. Ввиду отсутствия семантики JOIN, у пользователя остается возможность использовать вложенные подзапросы к БД, либо пытаться ограничиться одним проходом, помещая в пользовательский кэш все, что может потребоваться в будущем. Естественно, помогает системный кэш, хранящий как отдельные документы, к которым уже были запросы по id / code, так и результаты расчетов, выполненных ранее (при полном совпадении входных параметров этих расчетов).

    5) Мемоизация, или кэширование. Результаты запросов и расчетов попадают в кэш в случаях:

    • документ имеет атрибут cache, при первом reduce() он записывается в фулл-кэш, и обновляет запись в топ-кэше;
    • документ извлечен запросом по id / code, и он находится в иммутабельном хранилище;
    • reduce() завершил расчет по иммутабельному хранилищу, промежуточный результат клонируется и записывается в кэш, а расчет продолжается по текущему хранилищу.

    Мы видим, что в отличие от жестко-структурированного «кэша» в системах, основанных на РСУБД, мы имеем адаптивный кэш, наполняемый по мере работы системы. Необходимость экономить память заставляет ограничивать объем кэшируемой информации, поэтому, например, результат функции filter() не кэшируется, а результат reduce() — обязательно. Пользователю даются ограниченные инструменты управления кэшем.

    6) Поиск бывает 3-х видов. Первый — когда при обработке текущего документа нам нужно найти несколько связанных документов по неточным критериям. В этом случае либо подзапрос, либо в своем reduce() заранее сохраням все что может потенциально потребоваться, и когда потребовалось — ищем в этой выборке. Второй вид поиска — когда нам нужно получить актуальные элементы справочника, без исторических данных (т.н. текущий срез). В этом случае используется топ-кэш, который как раз хранит такие элементы. В третьем случае это fullscan по базе в обратной хронологической последовательности. Насколько целесообразно кэшировать результаты пользовательских поисков — вопрос дискуссионный, в какой-то мере очевидно необходимо, например с ограничением объема выборки.

    7) Добавление документов и перемещение горизонта. При добавлении нового документа актуализируется только топ-кэш. При перемещении документа из текущего хранилища в иммутабельное — по идее нужно до-обновить все кэшированные агрегаты, эта операция тяжелая (зависит от количества переносимых документов и количества результатов в кэше), и должна выполняться по аналогии с закрытием учетного периода — в часы наименьшей нагрузки.

    Простая функциональная СУБД


    Итак, попробуем что-нибудь написать. Язык TypeScript выбран за идеальное сочетание скриптового динамизма и типизации, рантайм Deno — за удобную поддержку TypeScript и WASM, а также наличия Rust API, что теоретически дает нам шанс ускорить некоторые алгоритмы (хотя это неточно).
    Документы в нашей СУБД будут храниться в виде 2-х последовательных файлов, содержащих объекты JSON, разделенные символом "\x01", так как это позволяет написать быстрый потоковый парсер. API чтения состоит пока всего из 3-х функций:

    type Document = any
    type Result = any
    
    public async get(id: string): Promise<Document | undefined>
    
    public async gettop(code: string): Promise<Document | undefined>
    
    public async reduce(
        filter: (result: Result, doc: Document) => Promise<boolean>, 
        reducer: (result: Result, doc: Document) => Promise<void>,
        result: Result
    ): Promise<Result>

    Первая функция возвращает документ по id, вторая возвращает последний документ с заданным code, третья осуществляет фильтрацию и свертку, принимая на вход функцию фильтрации, функцию свертки и начальное значение аккумулятора. Мы сознательно не использовуем цепочку filter().reduce(), так как хотим кэшировать итоговый результат, а в случае цепочки — кэшировать отдельно результат фильтрации расточительно, а кэшировать результат свертки без знания условий фильтрации — бессмысленно. Поэтому reduce() получает на вход сразу все необходимое для расчета, и использует составной хэш от трех параметров в качестве ключа мемоизации.

    Собственно, весь пользовательский алгортм представляет собой реализацию колбэков filter и reducer, а аккумулятор-результат может быть любым сериализуемым объектом. Обратите внимание, что оба колбэка возвращают промис, то есть внутри них разрешены вложенные запросы get() и reduce(). Благодаря промисам вложенный цикл (например по строкам текущего документ) можно параллелить (см. второй тест).

    Исходные данные


    Рассмотрим простейшую систему учета покупок и продаж. Нам нужны справочники контрагентов и номенклатур, а также документы покупки и продажи. Если мы хотим считать себестоимость расходов и маржу, нужен еще один документ — сопоставление приходов с расходами, но это уже тема отдельной статьи.

    Партнеры и номенклатуры

    {
        "sys": {
            "code": "partner.1",
            "ts": 1578263624612,
            "id": "partner.1^1578263624612",
            "cache": 1     
        },
        "type": "partner.retail",
        "name": "Рога и копыта ООО"
    }
    {
        "sys": {
            "code": "invent.1",
            "ts": 1578263624612,
            "id": "invent.1^1578263624612",
            "cache": 1     
        },
        "type": "invent.material",
        "name": "Гвоздь строительный 20мм"
    }

    Атрибут type — пользовательский, его иерархия никак не используется ядром, а лишь в пользовательских алгоритмах. Также не имеет значение семантика атрибута code — для ядра это просто строка.

    Покупки и продажи

    {
        "sys": {
            "code": "purch.1",
            "ts": 1578263624613,
            "id": "purch.1^1578263624613"  
        },
        "type": "purch",
        "date": "2020-01-07",
        "partner": "partner.3^1578263624612",
        "lines": [
            {
                "invent": "invent.0^1578263624612",
                "qty": 2,
                "price": 232.62838134273366
            },
            {
                "invent": "invent.1^1578263624917",
                "qty": 24,
                "price": 174.0459600393788
            }
        ]
    }

    Документы отличаются только типом (purch | sale), cтроки хранятся прямо в документе (в реляционной схеме они лежали бы в отдельной таблице).

    Реализация алгоритмов


    Анализ продаж
    Считаем общую сумму продаж, средний чек, и среднее количество строк на документ.

    import { FuncDB } from './FuncDB.ts'
    const db = FuncDB.open('./sample_database/')
    
    let res = await db.reduce(
        async (_, doc) => doc.type == 'sale',  // фильтруем только продажи
        async (result, doc) => {
            result.doccou++
            doc.lines.forEach(line => {  // цикл по строкам документа
                result.linecou++
                result.amount += line.price * line.qty
            })
        },
        {amount: 0, doccou: 0, linecou: 0}  // инициализируем аккумулятор
    )
    
    console.log(`
        amount total = ${res.amount}
        amount per document = ${res.amount / res.doccou}
        lines per document = ${res.linecou / res.doccou}`
    )

    Обороты в разрезе номенклатур и партнеров
    По сути это сводная таблица, поэтому в качестве аккумулятора используем Map.

    class ResultRow { // строка результирующей таблицы
        invent_name = ''
        partner_name = ''
        debit_qty = 0
        debit_amount = 0
        credit_qty = 0
        credit_amount = 0
    }
    
    let res = await db.reduce(
        async (_, doc) => doc.type == 'purch' || doc.type == 'sale',
        async (result, doc) => {
            // поскольку внутри цикла у нас await - параллелим обработку строк
            const promises = doc.lines.map(async (line) => {
                const key = line.invent + doc.partner
                let row = result.get(key)
                if (row === undefined) {
                    row = new ResultRow()
                    // наименования получаем подзапросами к базе (они кэшируются)
                    row.invent_name = (await db.get(line.invent))?.name ?? ' not found'
                    row.partner_name = (await db.get(doc.partner))?.name ?? ' not found'
                    result.set(key, row)
                }
                if (doc.type == 'purch') {
                    row.debit_qty += line.qty
                    row.debit_amount += line.qty * line.price
                } else if (doc.type == 'sale') {
                    row.credit_qty += line.qty
                    row.credit_amount += line.qty * line.price
                }
            })
            await Promise.all(promises)
        },
        new Map<string, ResultRow>() // результирующая таблица (аккумулятор)
    )

    Мы видим, что почти половину кода составляет извлечение наименований подзапросами. Это легко исправить, написав сервисную функцию, но для общего понимания оставлю так. Обратите внимание, что мы параллелим обработку строк — в случае если номенклатуры нет в кэше — запускается fullscan, результата которого в нашем случае ждать необязательно.

    Бенчмаркинг


    Тестируем на сгенерированных данных:
    иммутабельное хранилище: 100 номенклатур + 100 контрагентов + 100 тыс. документов
    текущее хранилище: 10 номенклатур + 10 контрагентов + 10 тыс. документов
    Использую доисторический ноутбук с процессором Intel Celeron CPU N2830 @ 2.16 GHz

    В первом тесте демонстрируем кэширование — сначала запускаем анализ продаж, затем добавляем новый документ продажи, и снова запускаем расчет. Видно что второй раз иммутабельное хранилище не обрабатывается, и расчет происходит в 10 раз быстрее.

    Результаты - 100 тыс. документов за 11.1 секунды:
    file: database_immutable.json:
     100200 docs parsed (0 errors)
     50018 docs processed (0 errors)
    11.098s elapsed
    file: database_current.json:
     10020 docs parsed (0 errors)
     4987 docs processed (0 errors)
    1.036s elapsed
    amount total = 623422871.2641689
    amount per document = 11389.839613851627
    lines per document = 3.6682561432355896

    file: database_current.json:
     10021 docs parsed (0 errors)
     4988 docs processed (0 errors)
    1.034s elapsed
    amount total = 623433860.2641689
    amount per document = 11389.832290707558
    lines per document = 3.6682073954983925

    Если честно, я рассчитывал минимум на миллион документов в секунду. Разберемся, где у нас основная задержка на примере обработки первого файла:
    8.8s — чтение файла и извлечение строковых JSON, разделенных символом "\x01"
    1.9s — парсинг JSON в объекты
    0.4s — кэширование + пользовательский алгоритм
    Заглянув в исходники Deno, я понял, основная задержка возникает при декодировании unicode, ведь V8 в качестве байто-дробилки подходит плохо. Это значит, что переписать критические куски на WASM/Rust будет очень просто, а если в качестве хранилища использовать нормальную объектную БД, то и парсинга JSON можно избежать, и тогда достичь миллиона записей в секунду — более чем реально. И это я не говорю про нормальное железо.

    Второй тест мы запускаем сначала на свеже-сгенерированной базе, затем после прогона первого теста. Второй раз производительность упала в 3 раза, потому что в первом тесте мы добавили документ продажи, ссылающийся на несуществующего партнера и номенклатуру, и, не найдя их в кэше, система вынуждена дважды запускать fullscan. Но эта ситуация по сути аварийная, а нормальная выглядит так:

    Результаты - 100 тыс. документов за 13.3 секунды:
    file: database_immutable.json:
     100200 docs parsed (0 errors)
     100000 docs processed (0 errors)
    13.307s elapsed
    file: database_current.json:
     10020 docs parsed (0 errors)
     10000 docs processed (0 errors)
    1.296s elapsed

    invent name | partner name | debet qty | debet amount | credit qty | credit amount | balance amount
    ===========================================================================
    invent 92 | partner 50 | 164 | 34795.53690513125 | 338 | 64322.24591475369 | -29526.709009622435
    invent 44 | partner 32 | 285 | 57382.115033253926 | 209 | 43572.164405352596 | 13809.95062790133
    invent 95 | partner 32 | 340 | 73377.08274368728 | 205 | 42007.69685305944 | 31369.38589062784
    invent 73 | partner 32 | 325 | 57874.269249290744 | 300 | 58047.56414301135 | -173.29489372060198
    invent 39 | partner 32 | 333 | 69749.88883753444 | 415 | 86369.07805766111 | -16619.189220126675
    invent 80 | partner 49 | 388 | 74965.03809449819 | 279 | 51438.03787960939 | 23527.0002148888
    invent 99 | partner 49 | 337 | 69360.84770099446 | 292 | 58521.2605634746 | 10839.587137519862
    invent 38 | partner 45 | 302 | 63933.21291162898 | 217 | 44866.95192796074 | 19066.26098366824
    invent 34 | partner 45 | 343 | 69539.75051653324 | 205 | 41155.65340219566 | 28384.09711433758
    invent 41 | partner 45 | 278 | 63474.209440233106 | 258 | 45246.446456763035 | 18227.76298347007
    < tail skipped >

    Наконец-то пошла рабочая нагрузка, мы делаем 2 вложенных асинхронных запроса, и получаем затраты на пользовательский алгоритм — 2.6s. Допускаю, что оборачивание любой функции в промис накладно, а в нашем случае запрос к кэшу выполняется синхронно, и возможно это место стоит оптимизировать.

    Резюме


    В целом я доволен результатом, схема получилась вполне рабочая, проект можно развивать, если у кого есть мысли — пишите. Буду благодарен за ссылку на публичные обфусцированные данные, приближенные к реальности (счета-фактуры, EDI, или что-то подобное), необходимые для полноценного тестирования.

    Полный код на гитхабе

    UPD
    1) Статья в тему, благодарность VolCh за наводку.
    2) Аналогичный подход реализован в CouchDB, благодарность apapacy за наводку.

    PS
    Продолжение тут
    Поддержать автора
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 177

      +6
      Делать фулл скан всего и вся для любой простейшей аналитики такое себе решение…
      Да что там аналитика, простейший поиск не по ключу это фулл скан всего.
      Про join я даже не вспоминаю. Там пачка фуллсканов вылезет. Ну или надо гиганские кеши в памяти держать.
      Система умрет при любой минимальной нагрузке.

      На что только не идут люди лишь бы нормальную sql БД не проектировать.

      И в конце-концов есть Монга. Если уж очень хранилище разных документов хочется. Но метаданные справочники и прочее подобное все равно в sql лучше.

      PS: Все, или почти все sql бд поддерживают mater-slave из коробки и переживают выпадение мастера. При падении мастера база ненадолго в ридонли падает. А потом сама поднимается на запись и работает дальше.
        –2
        У меня на проекте была система (DAX) которая в месяц набирала от силы 10 миллионов транзакций, закрытие периода — от 8 часов на кластере MSSQL. Основные причины — блокировки в базе при разносках коррекций + обновление кучи индексов. У меня фулсканом будет быстрее, и главное — блокировок нет, распараллелить возможно руками. Не от хорошей жизни придумали map/reduce на всяких хадупах, просто SQL не тянет бигдату, хотя его иногда сверху наворачивают на тот же SPARK.
          +9
          Мапредьюсы и подобное нужно когда у нас петабайт данных.

          10 миллионов в месяц это даже смешно. Оно должно считаться на чем угодно без проблем. Данные даже в память влезут. Проверяйте свои алгоритмы рассчета. Там явно какие-то проблемы с ними.
            0
            Я уже там не работаю, да и не в этом дело. Идея — отказаться от мутабельного стейта в принципе. Пока функциональные языки слабо используются в проде, но тот же хаскель показывает какую-то сказочную производительность на иммутабельных коллекциях (тоже фулскан кстати), а если так посмотреть — чем обработка коллекций в памяти отличается от обработки первичных документов на диске? Только их количеством. Я бы посоревновался с тем же MSSQL, только мне нужна реальная бигдата, хотя-бы десятки миллиардов объектов, не знаю уж сколько это будет в байтах. Я как-то считал — одна сделка в ERP разносится по 60 таблицам где-то, а в источнике — просто документ со строками.
              +1
              Тем что диск это дорого. Его медленно читать и еще медленее писать. Если у нас МНОГО данных и соотвенно hdd еще и random read очень дорогим становится.
              С памятью никакого сравнения. Память, с учетом кеша проца, линейно читается просто замечательно.

              Так в чем проблема? Проверить идею легко же. Основные сценарии чтения: поиск, group by с простенькими аггрегатами, join, order by. И пейджинг какой-нибуд сверху. Можно просто взять и написать. Ноута хватит. Нагенерить гигов 300 данных вообще не проблема, так чтобы в память точно не влазило.
                0
                Уже все проверено. Интернет-биллинг на оракле писал, 100 миллиардов записей, составные индексы вешали вставку, методом проб и ошибок пришли к той же схеме — большая таблица, организованная по 1 индексу, однопроходный скан с хинтом index_asc, и собственным кэшем в виде временных таблиц. Проблем с большими реляционными системами две:
                — необходимость обновлять все индексы во всех связанных таблицах при каждой транзакции
                — необходимость предоставления многопользовательского доступа к данным
                В системах map/reduce вторая проблема не стоит в принципе, так как данные «только для чтения». В результате на SQL вы ничего не распараллелите, надежда лишь на движок, а при потоковой обработке — вы сами пишете аддитивные алгоритмы, хотя это непривычно.
                PS
                random read это дорого, не спорю, поэтому и фуллскан.
                  +6
                  Для одной большой таблицы с быстрыми вставками есть Кликхаус. Отлично работает в таком сценарии.

                  Не надо Оракл (да и sql базы вообще) грузить на запись. Они для чтения. В sql кладем все что условно постоянно. В том же биллинге всех юзеров со всеми миллионами их свойств и связей кладем в sql и обвешиваем со всех сторон индексами. А транзакции пишем в Кликхаус.

                  Не нужны вам паралельность и мапредьюс. Они не для этого. Они для данных не влазящих на один компьютер. И если там данных на грани влезания, то лучше уместить. Так проще и быстрее в среднем будет. Ключевое слово — Петабайт.
                    +1
                    Столбцовая база с быстрой вставкой? Хм, спасибо, не знал, посмотрю. С OLAP работал конечно, но обновление кубов у нас обычно по ночам. Хотя это не отменяет идеи функционального программирования, которое постепенно становится хайповой темой :)
                      0
                      Если мне память не изменяет, в Кликхаусе отсутствуют транзакции, что ставит на ней крест для использования в ERP для регистрации потока документов.
                        +2
                        Как будто в подходе автора они есть
                        0
                        Яндекс в качестве oltp базы не так давно начала предлагать вот это: ru.bmstu.wiki/Yandex_Database
                        но:
                        0) она, ИМХО, сырая ещё
                        1) там весьма суровые ограничения
                          0
                          Похоже, их newSQL — это ровно обратная концепция — все in memory, упор на распределенные транзакции, и т.д. Но вообще они молодцы, непонятно что теперь со всем этим будет, выйдет ли в жизнь. Наверно, сейчас чтобы выжить, нужно все в опенсорс выкладывать.
                          +3
                          SQL смело выдержит биллинг, если грамотно продумать схему таблиц и использовать комбинацию partitioned таблиц, кластерных индексов, in-memory таблиц и не использовать долгие и сложные транзакции на SQL.
                          все банковские АБСки есть суть ERP и написаны на SQL — например ЦФТ из Новосиба
                          +1
                          "— необходимость обновлять все индексы во всех связанных таблицах при каждой транзакции
                          — необходимость предоставления многопользовательского доступа к данным"
                          Не все, а только индексы для полей, которые участвуют в изменений обычно, (хотя если в вашей ERP, к примеру, тупо без разбора обновляются все поля документа при изменении статуса его, то это ваша «прикладная» печаль).
                          — Раз тут Вы упомянули о MS SQL, то там есть optimistic locking и inmemory database

                          «random read это дорого, не спорю, поэтому и фуллскан.»
                          Фулл-скан всегда это очень дорого, даже если табличка помещается в памяти — ядра ЦПу будут только и заниматься тем, что перелопачивать десятки миллионов строк (больше не видел на OLTP базах, мы ж о ERP говорим использования «такой схемы», обычно колом становится раньше )

                            +1
                            Если реляционная схема простая, то фулскан дороже. Если 30 таблиц для регистрации одной сделки — уже не факт. 10 миллионов JSON — это 10 секунд на мобильнике фулсканом, но в ERP это действительно долго получается. Тут нужен реальный проект чтобы нас рассудить.
                              0
                              10 секунд на выборку по списку документов для любой выборки по списку документов это не просто долго, это безумно долго. Вот недавний случай «из жизни»: есть 250 «точек» которые грузят клиентам «что-то» (предположим что таблица у нас всего одна «Заказы на отгрузку»). До недавнего времени «точка» список своих документов на отгрузку обновляла примерно раз в минуту (взяли товар, отдали клиенту, показали, выбили чек). Положим у нас 40 ядерный процессор на СУБД выделен (по факту на месте стоял более слабый) и памяти хватает, чтобы табличка целиком лежала в памяти. Таким образом имеем (грубый подсчёт), получается что 60% процессорного времени (10 расчётов в минуту, укладывались сканом в 5-6 секунд на ядро * 40 ядер) СУБД будет поддерживать только эту операцию — выборки списка документов на «отгрузку» для каждой точки (в реальности больше, т.к. тут задачи на расчёт для простоты не мешают друг другу и не ждут ничего, а культурно раскладываются по ядрам последовательно). «Примерно так» и было в действительности «на месте» до ухода «в индекс».
                                0
                                С поправкой на мое железо, неоптимизированный JavaScript все не так плохо. В реальности на одном приличном ядре и том же Rust я могу сделать примерно 10 миллионов объектов в секунду. Для бигдаты это ничто, но для ERP уже близко. Но, вообще-то нужен реальный проект.
                    +1
                    SQL вполне тянет бигдату. Но для этого нужна простая вещь — дать оптимизатору спарка статистику, которой у него обычно нет. При этом условии спарк генерирует вполне вменяемые джойны, например.
                      0
                      Дать статистику, то есть сделать один полный проход по данным, правильно я понимаю?
                        +1
                        Ну, не обязательно сделать один (дополнительный) проход — можно собрать ее в процессе предыдущего прохода, например. Кто-то же эти данные записал — вот он в принципе может эту статистику знать. А иначе откуда спарк допустим узнает, сколько строк у нас в таблице? Там же внутри паркет, или вообще CSV, а у этих форматов нет таких метаданных (и потом, это может быть много файлов паркет).

                        В общем-то, это и для обычных СУБД все ровно так же — если вы попросите Оракла собрать статистику по таблице, он точно также сделает фуллскан, скорее всего.
                  +2

                  Идёт вцелом правильные. Надо сказать что такая реализация с иммутабельностью и реализацией map/reduce уже существует это couchdb. Правда она явно не для highload. Как сказал ее автор, вы должны думать что couchdb делает всё очень плохо кроме быть может репликации.

                    0
                    Спасибо за couchdb, очень близко! У них мутабельные деревья, поэтому им сложно, но зато универсально. В моем случае достаточно узкая задача — учет. Надо поковырять этот коуч, если там есть возможность разделить базу на иммутабельную часть и обычную — тогда проблемы единственного пишущего треда можно и избежать, точнее минимизировать.
                      +1

                      В couchdb версии документа хранятся вечно. В этом смысле они иммутабельны. То есть нельзя изменить что-то и не оставитььследов.

                        0
                        Читаю про couchbase, собственно их view это и есть кэш, определяемый пользователями. Непонятно что со связями, если разрешить связь от любого к любому — рано или поздно это кончится хаосом. Надо изучать.
                          +1

                          Couchbase это синтаксический сахар вокруг couchdb и при этом с какой-то сложной лицензией. В couchdb однажды созданный map/reduce создаёт что-то вроде индекса который пересччитывается только по изменившимся документам.
                          Проблема в том что она не поддерживается в актуальном состоянии при записи как например регистры в 1с а пересчитывается только по мере необходимости при доступе. Поэтому иногда создают кроны которые эти Вью периодически вызывают

                            0
                            Понятно, ход мысли аналогичный, правда с разницей в 15 лет )
                              0
                              Проблема в том что она не поддерживается в актуальном состоянии при записи как например регистры в 1с а пересчитывается только по мере необходимости при доступе.
                              Это ж не проблема, а наоборот тренд — ленивые вычисления. Проблема понятна — мы не контролируем момент запуска тяжелой операции. С другой стороны жизненно — кому данные нужны, тот и подождет )
                      +3
                      Блокировки при обновлении агрегатов не просто так.
                      В схеме без блокировок, как я понимаю, нельзя сделать правило «запрещены отрицательные складские остатки», или «этот документ только что изменён юзером А, откройте его заново и вносите свои изменения».
                        0
                        Я согласен. В данном случае проблемы блокировок коснутся не всей базы, а только ее мутабельной части (текущего периода), потому что остатки по иммутабельной части лежат в кэше в готовом виде, если их в таком разрезе хоть кто-то когда-то запросил.
                        Понимаю, что в современных СУБД есть партиционирование таблиц, текущий период в отдельном файле, и т.д. Проблема в том, что эту логику реализует дорогое ядро, а на уровне семантики прикладного программирования — пиши в любое место, блокируй что хочешь. Идея заключается в том, что если мы хотим настоящую бигдату, прикладных программистов придется ограничивать более строгой семантикой, но тогда радикально упростится ядро самой СУБД, и можно будет параллелить расчеты.
                          +1

                          :)))
                          Где вы видели в ERP бигдату?
                          Биллинг сотовых — это не ERP, на всякий случай уточняю. )))


                          То, что Аксапта долго период закрывает — это не проблема SQL. Это проблема не оптимальных алгоритмов.

                            0
                            Согласен. Я ж не покушаюсь вообще на SQL, а только на ERP )) В той же аксапте — сначала мы исходные документы преобразовываем в проводки модуля / проводки ГК / регистры, а потом в сводных отчетах руководство хочет расшифровать любую сумму до строки первичного документа — и начинаем заново джойнить — от регистров к проводкам, от проводок к документам и т.д. А если на входе и на выходе по сути нужны только строки документов — зачем тогда все остальное промежуточное хранить?
                              +1

                              Во первых, OLTP и отчёты — это две сильно разные задачи.


                              Во вторых, без ледгеров (проводки модуля / проводки ГК) создать гибкую тиражируемую систему не получится.
                              Простой пример. Вчера обсуждали с репортерами (которые консолидированную отчётность готовят), что за 2019 год будем делать отчётность по сегментам (по странам продаж). Причем эти страны продаж не сильно пересекаются с нашими юр. лицами.
                              При наличии ГК просто сгенерируем кучу проводок для каждой компании и перераспределим по сегментам текущие операции на основе неких баз распределения. Например амортизация ноутбука за январь была одна сумма, а станет 6. Причем на момент начисления амортизации или учёта расходов эти базы распределения неизвестны. Собственно говоря, что будем делить по сегментам — тоже не было известно. :)))
                              И, главное, мы вообще никак код ЕРП трогать не будем (у нас Навик).
                              А теперь представьте, как решение подобной задачи будет выглядеть в вашей системе.

                            +2
                            Если хочется чего-то странного — идём к классическим data lake /data vault: есть быстрая реляционная БД транзакций, есть бигдата с архивом транзакций за хх лет, с блекджеком, ML и агрегатами, есть ETL, которые грузят каждые n часов данные из реляционной Бд в кластер бигдаты — и все довольны.
                            Если не хочется реляционной БД и допускается отставание на пару минут от реального времени — льём JSON пакетами, а дальше — impala умеет и с партициями работать, и с корзинами, и быстро сканить документы.
                            PS тестил второй сценарий для мобильного биллинга — запросы к 1.5Tb сырых csv типа «кто в центре города за последние 15 дней чаще всего ходил на сайты типа порносайты» выполнялись ~100 секунд. Если хранить эти же данные в avro / parquet — ~30 секунд.
                            PPS А всё же, зачем в ERP bigdata?
                              0
                              Первый вариант с блэкджеком — сложновато будет, и дорого, и специалисты нужны разнопрофильные. Второй сценарий — цифры ваши подтверждаю, все действительно летает, но это отчеты, а хочется полноценную БД.
                              PS
                              Зачем в ERP бигдата. Мне ставили как-то задачу — рассчитать полную себестоимость каждого поддона товара с учетом всех логистических затрат на перевозку и хранение (много транспортных плеч, на каждом складе товар сколько-то лежит, а это тоже затраты). Получился справочник только на 10 миллионов записей, а операций (по сути обычных распределений) уже ближе к миллиарду. И это всего-лишь опт, у ритейла данных поболее будет. Поэтому сейчас все ERP считают приблизительно, по номенклатурам, максимум по партиям, а вдруг менеджеры начитаются статей на хабре про интернет вещей, прицепят радиометку на каждый поддон, и захотят писать историю его жизни — вот вам и бигдата )
                                0
                                Все хранилища данных под IoT, которые видел — или облака с bigdata бэкэндом, или локальная bigdata с kafka, поэтому использование IoT сводится или к варианту с блэкджеком и разнопрофильными спецами, или к чугуневому велосипедостроению (чугуневому — чтоб не падало).
                                PS для бизнеса действительно будет различаться 40 поддонов в 1 партии, которые будут ехать одним контейнером по одинаковым складам?
                                  0
                                  Статья — как раз про чугуниевый )
                                  для бизнеса действительно будет различаться 40 поддонов в 1 партии, которые будут ехать одним контейнером по одинаковым складам?
                                  Партии разделяются, потом могут перебрасываться из филиала в филиал для пополнения страховых или под заказ, и сколько раз их туда-сюда гоняли — вообще-то интересная статистика. А если прицепить сюда себестоимость — можно поставить KPI менеджеру, который компонует маршруты, то есть задача вполне себе востребованная.
                                    0
                                    Делал я именно такую задачу на прошлом проекте. Вот один в один. :)
                                    Для каждой партии (проданной или на складе) расшифровка себестоимости до строки документа, сформировавшего себестоимость. В том числе — транспортные расходы и сколько раз туда — сюда гоняли.
                                    Нет там никакой бигдаты. :)
                                      0
                                      А мы отказались, и так производительность хромала.
                                        0
                                        Вы что, во время проведения это делали? :)
                                        Мы сделали отдельные кубики и смотрели. Причем не в той же базе, где навик работал — данные копировались в отдельную базу, там считалась детальная себестоимость и строились отчеты и куб на тех же данных формировался.
                                        Зачем в принципе такие данные онлайн могут быть нужны?
                                          0
                                          данные копировались в отдельную базу, там считалась детальная себестоимость
                                          Ну вот в том и вопрос, зачем нужны базовые механизмы ERP, если такую очевидную работу нужно выполнять за бортом. В 1С-ERP в итоге систему разделили на 2 части — оперативный учет и регламентный учет, и обмены между ними. Но тоже не выход, так как в оперативном учете тоже попадаем в ловушку производительности.
                                            0
                                            А зачем это всё в одну кучу-то тащить?
                                            Разные назначения — разные базы, на мой взгляд, логично, что OLAP-кубы будут отдельно от горячих данных и отдельно от архивов, а витрины данных для разных отделов и надобностей могут и вовсе на других движках находиться — и с производительностью проблемы решаются автоматически, и с кэшем, и с доступом.
                                              0
                                              Ну, потому что себестоимость это то ради чего покупают ERP. На граф сопоставлений потом натягиваются накладные расходы на исходное сырье, которое вы потратили месяц назад, и все автоматически. А в вашем случае как дооценить поддон, если вы его обсчитали на стороне?
                                                0
                                                Себестоимость поддона рассчитала ERP. А детализацию этой себестоимости, которая нужна для отдельных отчетов — рассчитали на стороне.
                                                Когда ERP досчитывает себестоимость — она делает записи в базе данных (в книге модуля, от которых вы хотите избавится). Ничто нам не мешает эти записи о себестоимости регулярно копировать и рассчитывать для них расшифровку себестоимости (что мы собственно говоря и делали).
                                                  0
                                                  Мне это эстетически не нравится, хотя так в основном и делают.
                                                    +1
                                                    А что не нравится?
                                                    Есть экскаваторы, которые хорошо умеют копать. Есть самосвалы, которые хорошо умеют возить.
                                                    Пытаться сделать одно универсальное решение… Так себе идея, прямо скажем. Требования то РАЗНЫЕ, следовательно оптимальная реализация в каждом случае тоже будет разной.
                                                  0
                                                  Ок, допустим, у нас горячая пора: близок конец квартала, текущее железо забито отчётами, кубами, планированием и сверками — встала задача посчитать, к примеру, историю поддонов — забрали срез данных, положили в облако (если все локальные ресурсы заняты), посчитали сутки, загрузили результат обратно в куб — все довольны.
                                                  А натягивать сову ERP на все возможные случаи применения: от транзакций и OLAP до отчётов, ML и архивов — немного странная идея.
                                                  PS помнится, кто-то вообще купил пару стоек RAM-дисков и перенёс БД на эти RAM-диски для скорости — тоже неплохой вариант, но не для всех случаев.
                                                    0
                                                    Ну кстати 1С к примеру так со своим СКД по сути и предлагает делать. И многие 1С разработчики считают что так и надо. Впрочем если объемы не огромные, то что-то в этом есть.
                                                      +1
                                                      Если у тебя есть только молоток, то во всем видишь гвозди. :)
                                                      Когда 1Сникам говорю, что я успешно РЕШИЛ задачу с детальной себестоимостью (с разделяющимися партиями, транспортными расходами на перевозки туда-сюда и т.п.) — у них подгорать обычно начинает. :)))
                                                        0
                                                        1С в своей нише малого и среднего бизнеса хороша, но всегда удивлялся, чем руководствуются люди, которые на платформе 1С реализуют работу 3500 розничных магазинов или работу 5000 пользователей онлайн.
                                                          0
                                                          Так Сложилось Исторически, а миграцию на подходящие инструменты никто даже согласовывать не станет — дорого, долго и непонятно.
                                                          Увы.
                                                    0
                                                    "… А в вашем случае как дооценить поддон, если вы его обсчитали на стороне?..."
                                                    Более того если склад у вас большой (или вообще на аутсорсе) и им рулит WMS, в документе ЕРП будут позиции сгруппированные по номенклатуре, далее вы пошлёте их в WMS и она даст вам реальное товародвижение (какие партии пошли по вашему документу).
                                                      0
                                                      Обычно WMS это в пределах группы зданий, то есть внутри нее логистических затрат не возникает, а значит себестоимость не считается. А если нужно перевести подальше, это уже перевозчик, затраты, себестоимость, то есть ERP.
                                                        0
                                                        0) Внутри нет, но партии разные с разной себестоимостью и какие партии пойдут по накладной перемещения согласно бизнес-логике WMS
                                                        1) Во вторых то что склад это отдельная группа зданий это ваша гипотеза, которая в реальной жизни может и не реализоваться. Я вот к примеру наблюдал реализацию, когда ЕРП система видя (WMS подсказала), что на основном складе не хватает части товара автоматически сформировать связанные документы на подвоз из других складов и документ отгрузки клиенту по факту обрабатывали 3 разных склада (разные партии, разные себестоимости транспортировки/ хранения по понятным причинам) с 2мя типами WMS один из которых склад-аутсорс.
                                                          0
                                                          Накладные перемещения — если они наружу, то инициировать их может и WMS, а обсчитывать-то придется в ERP, которая должна знать про партии. Нет, ну я допускаю, что к WMS тоже можно прикрутить расчет себестоимости, но по моему так не делают.
                                                          0
                                                          Предположим, есть свой автопарк и свой склад.
                                                          В чем принципиальная разница в расходах на зарплату водителю, амортизацию грузовика и бензин при перевозке со склада на склад и зарплатой кладовщика, амортизацией погрузчика и электричество для погрузчика при перемещении из ячейки в ячейку?
                                                          И там и там себестоимость. И структура расходов очень похожи. :)
                                                            0
                                                            Если вы учитываете расходы электрической энергии погрузчика на доставку каждого конкретного заказа, из пункта хранения склада А в пункт Б — С — Д — (там может быть сформирована цепочка), учитываются трудовые ресурсы «Грузчик Иванов тащил 15 минут двигатель ЯМЗ сер. номер ...» и т.д. равно как вы выписываете путевой лист на конкретную доставку товара со склада А в склад Б (пробег, расход топлива), командировочных расходов (склады в разных городах), то наверное разницы нет.
                                                              0
                                                              Стоп. Затраты внутри склада рассчитываются помесячно, и потом распределяются на все что лежало. Конечно, можно считать пробег электрокара и привязывать его к поддону, но это уже сюрр. Поэтому затраты на хранение — это ERP, там зарплата, амортизация и прочий газ. А перевозка дальше километра — это отдельная накладная, сумма известна сразу, и складывать ее в котел чтобы потом распределять — не всегда гуд.
                                                                0
                                                                Так я про то же. разный принцип расчёта себестоимости, в одном месте распределение по всему складу, а в другом, по тому что ехало в машине в конкретное время из пункта А в пункт Б. И такого рода затраты по факту учитываются обычно, т.к. до того как оно приехало это всё «прогноз» (а там машина сломалась, встали в пробку, метель — вот и поплыли командировочные расходы на ГСМ и т.д.)
                                                                  0
                                                                  На перевозки тоже в конце месяца считают. :)
                                                                  Я ведь и говорю — свой автопарк и свой склад. Зарплаты, амортизация и т.п. — это в конце месяца.

                                                                  А распределять по всему что лежало иногда не очень правильно. Если заняться оптимизацией складских операций — очень даже полезными будут детальные данные.
                                                                  Когда размазываем затраты поровну — не заметно, что один и тот же поддон десять раз за месяц с места на место перетащили.

                                                                  Мы не считали внутрискладские (все склады чужие), но если бы было надо — вполне можно.
                                                                  Более того, такой расчет явно бы делался за пределами ЕРП (распределение по складским операциям), а в ЕРП уже бы учитывались результаты расчетов — документы расходов, распределенные по партиям.

                                                                  То есть это пример того, что есть ТРИ разные системы (WMS, ERP, и процедуры расчета распределения затрат на складские операции), которые работают совместно — и это в некоторых случаях будет оптимальным решением.
                                                                  Пытаться все сделать в одной системе (ERP) очень часто не оптимально.
                                                        +1
                                                        Я выше написал — отчеты и OLTP — разные задачи. Нет смысла их смешивать.
                                                        И на самом деле нет никакой ловушки производительности для ERP систем. Ну просто напросто нет такого объема операций в принципе ни у одной фирмы, чтобы их не могла обработать стандартная SQL база данных.
                                                        Любая проблема c ERP системами, о которой начинают говорить — при ближайшем рассмотрении оказывается проблемой не технологии как таковой, а ошибок проектирования.
                                                        Например, начинают в одну кучу сваливать задачи OLTP системы и формирования отчетов. :)))

                                                        ERP — это OLTP система. Не надо пытаться повесить на неё «чужие» задачи — и сразу будет вам счастье.
                                          +1
                                          есть партиционирование таблиц, текущий период в отдельном файле, и т.д. Проблема в том, что эту логику реализует дорогое ядро, а на уровне семантики прикладного программирования — пиши в любое место, блокируй что хочешь
                                          Изменения архивных периодов для надёжности можно закрыть триггерами.

                                          Дорогое ядро — это достоинство. Потому как пишется и тестируется 1 раз квалифицированными людьми. А писать сложные функции с учётом архивного хвоста на каждую хотелку пользователя, которых возникает по 10 штук в день, слишком дорого.

                                          Особенная боль всё это отлаживать. А потом выбрасывать в помойку, как 95% ф-ций, которые при постановке нужны ещё вчера, а после сдачи вообще никому не нужны.
                                            0
                                            Функции писать не сложнее SQL при наличии удобного API, с остальным согласен.
                                              0
                                              Проблема не в переходе с SQL на сервер приложений, а в том, что данные предлагается искусственно порвать на 2 части (иммутабельная и оперативная), и прям вообще во всех операциях держать это в уме и склеивать эти части.
                                                0
                                                Нет, вы пишете один редьюссор в расчете на один поток документов. Рвет на части уже реализация, и склеивает она же, и кэш автоматически до-обновляет при фиксации очередной пачки документов. Проблемы начинаются, если мы хотим распараллелить алгоритм. В этом случае, он (алгоритм) должен быть аддитивным, либо вам придется писать 2 редьюссора — один агрегирует документы, второй агрегирует редьюссоры. В аддитивном алгоритме типа сводной таблицы с суммированием и мин/максом — редьюссоры склеиваются той же функцией, что и документы, но в общем случае это не так, например если мы считаем count distinct.
                                                  0
                                                  То есть, когда считаем оборот за январь 2020 и за январь 2014, можно написать код, в котором явно не указано, где проходит граница между активным и архивным периодами?
                                                    0
                                                    Да, дата это пользовательский атрибут, а таймштамп системный. Учетных периодов может быть несколько, вложенных один в другой, например по НДС квартал, про РСБУ месяц, по УУ — неделя. С российской практикой править все задним числом это мало согласуется, но всеж сейчас и у нас многие стали работать по белому, жизнь заставила.
                                        +1
                                        концепт — хорош. «справочник — это документ» — блестяще.
                                        .
                                        что хотелось бы уточнить:
                                        gettop должен получать top на момент документа, которому нужна выборка. Чтобы выборка всегда давала одинаковый результат при разных запросах.
                                        .
                                        но так становится уж слишком похоже на пресловую Точку Актуальности (ТА) в 1С.
                                          0
                                          Нет, топ — это всегда последняя запись. «Версию на дату» получать в большинстве случае не нужно, так как в документе есть прямая ссылка на нужную версию справочника (которая была топовой на момент разноски). Если для каких-то алгоритмов нужна версия на произвольную дату — значит придется запускать поиск и кэшировать. Скорее всего, придется добавить функцию search() и кэшировать к примеру первых 100 найденных записей. Точка актуальности — да, конечно, это по сути оно.
                                          PS
                                          Вы тот самый легендарный mazzy? Респект, читал Вас с удовольствием пока с AX работал )
                                            +2
                                            в этом случае придется делать два набора запросов — один в момент разноски документа, другой для повторного запроса в уже созданном документе. например, вам нужно распечатать документ (для определенности, ПКО) в момент разноски, а также потом в любой момент. Какая фамилия кассира должна появится в распечатке?
                                            .
                                            Спасибо. Спасибо за классную статью. Прежде всего за идею «справочник — это тоже документ». Похоже идею можно развить «в учетной системе все — это документ».
                                              0
                                              Да, запросы будут разные, первый условно по code (+ доп. критерии поиска), а второй — строго по ID.
                                                0
                                                разные запросы — это плохо. это снова ошибки при разработке.

                                                С другой стороны, в статье есть запрос «Обороты в разрезе номенклатур и партнеров»
                                                где идет запрос наименований номенклатуры и партнера
                                                const invent = await db.get(line.invent)
                                                const partner = await db.get(doc.partner)

                                                если аналогичный код находится не в отчете, а в методе печати документа (предположим, Расходая накладная)

                                                что делать, если напечатали документ и выяснили, что в наименовании ошибка?
                                                другим документом наименование исправили.
                                                как перепечатать документ с новым наименованием?
                                                отменять-переразносить? с перерасчетом и перерезервированием? но новая разноска может и не выполниться из-за проверок.

                                                кроме того, переразноска все равно должна взять старые реквизиты в некоторых местах (название компании, директор, кассир и прочее)
                                                А если в этих реквизитах тоже были валидные исправления?

                                                в общем, приходим к извечному вопросу учетных систем: как отличить исправления и измененения :)

                                                  0
                                                  К сожалению, все так, и полной иммутабельности не получится. Мы же документы не в файле будем хранить, а в какой-нибудь NoSQL, а там можно править конкретный объект. Конечно, возникнет геморрой по описанию правил доступа к атрибутам — какие, в каких случаях и кому можно править. Но в случае с наименованием — правим непосредственно в той версии, где ошибка, или во всех версиях. Тут мне подсказали, что 10 лет назад все это уже было изобретено )
                                          +3
                                          Вот в чём-то похожий подход у ребят habr.com/ru/company/lsfusion/blog/458376
                                            0
                                            Спасибо, я как-раз хотел их еще раз подробно перечитать, может и возникнет повод позвонить.
                                            +2

                                            Как-то очень похоже на event sourcing с измененными терминами типа event у нас документ, а snapshot — кэш

                                              0
                                              Ну да, журналы логов так парсят. Кстати, спасибо за наводку, нашел несколько приличных статей.
                                                +1

                                                Event Sourcing — это не журналы логов, совсем нет.

                                                  0
                                                  Интересно, что именно в 2005 это было популярной темой, вот и Фаулер писал, а до нас до сих пор в виде готовых продуктов не докатилось, мы до сих пор пишем процедуры и запросы на 1С-бейсике. Варианта два — либо мы отсталые, либо что-то пошло не так с этой темой.
                                                    0

                                                    До кого "до нас"?


                                                    И нет, это было "популярной темой" не только в 2005.

                                                      0
                                                      До российских компаний, не входящих в топ-10, я пока тут живу )
                                                      +1
                                                      Интересно, что именно в 2005 это было популярной темой, вот и Фаулер писал

                                                      Не только писал, но и говорил, и не только в 2005, а ещё и 2017, например.
                                                      Да и вообще, не только Фаулер.

                                                      а до нас до сих пор в виде готовых продуктов не докатилось, мы до сих пор пишем процедуры и запросы на 1С-бейсике. Варианта два — либо мы отсталые, либо что-то пошло не так с этой темой.

                                                      Смелое заявление. Предлагаю вариант 3 — «Я пишу процедуры и запросы на 1С-бейсике, поэтому нахожусь в схожем по интересам комьюнити».

                                                      А оглядываться по сторонам вообще полезно. Хотя бы чтобы не переизобретать давно известное заново.
                                                      Продукты где ES под капотом существуют и в России.
                                                        0
                                                        Предлагаю четвертый вариант — вы рекомендуете мне работодателя, кому интересен event-sourcing подход в проде, я попрошу комьюнити рекомендовать ему меня — как аналитика, программиста и технического писателя, и все будут удовлетворены :)
                                                          +1
                                                          Предлагаю четвертый вариант — вы рекомендуете мне работодателя, кому интересен event-sourcing подход в проде, я попрошу комьюнити рекомендовать ему меня — как аналитика, программиста и технического писателя, и все будут удовлетворены :)

                                                          Я к комьюнити близком к использованию ES в проде тоже не отношусь(увы), и поиском таковых компаний целенаправленно не занимался.
                                                          Людей с таким опытом, приобретённым в Российских компаниях видно периодически. Может быть кто-то ещё тут отпишется, или у lair есть примеры.
                                                          Так сходу помню только что www.aeon.world искали людей на Elixir/ES/CQRS.
                                                0
                                                Не совсем понятно, как будет работать механизм редактирования документов, например того же перемещения товаров со склада на склад.
                                                Сначала неподтвержденного документа, когда оператор в час, натурально, по чайной ложке добавляет позиции и редактирует их количество.
                                                Если каждая редакция будет отдельной записью, то база будет примерно на 90% занята по факту мусором. Хотя иногда приходится выяснять и такое, когда же именно оператор добавил очередную чайную ложку. Но этот отдельный лог всегда чистится, хотя бы через месяц-полгода.
                                                И потом, когда в нашей реальности нужно срочно отредактировать очередной документ, забитый неделю назад, и нужно снова распечатать правильную накладную.
                                                  0
                                                  Строки документа являются частью документа, а не лежат в отдельной таблице, поэтому пусть редактирует хоть день — запись в базу пойдет по кнопке сохранить, со всеми проверками заново — для каждой строки. Совместное редактирование одного документа — согласен, проблема, но такого известные мне системы и сейчас не позволяют.
                                                  Документ недельной давности отредактировать невозможно — создаем отменяющую копию, и далее новый документ, то есть каждая коррекция это +2 документа. Это если документ учетно-значимый (накладная таковой является). Вы же не можете отредактировать емейл после отправки, а накладную вы уже клиенту через электронный документооборот передали, клиент ее принял к учету, и так далее.
                                                    –1
                                                    Пользователи ERP они хитрые люди: они знают, что их комп может повиснуть, может лагнуть сеть, могут удалить элемент номенклатуры который они хотят использовать в документе или ликвидировать склад, с которой они пытались отгружать товар. Поэтому если документ относительно большой: ну так к примеру заявка поставщику на несколько сот позиций, которые менеджер может не торопясь пару недель (как нельзя изменить документ недельной давности????!!!) согласовывать и с закупками, и с продажами, и с «финиками», и с… — старается сохранять частями.
                                                      0
                                                      Мне кажется тут речь идет о документе уже закомиченном в систему, а вы говорите о временном документе, который еще на стадии формирования.
                                                        0
                                                        Что значит «закоммиченом»? Документ лежит в базе, его может посмотреть начальник менеджера («Чем этот балбес занимается весь день?»), по нему строятся отчёты на предмет как идёт процесс работы с закупками (менеджер ведь не одной этой поставкой занимается), по нему можно посмотреть историю изменений и т.д. и т.п.
                                                          +1
                                                          Такой документ совсем не обязательно сохранять в ту же базу, в которой проводят все расчеты, просто потому что этот документ еще не завершен.
                                                        0
                                                        Это понятно, мы же не евангелисты, в реальной системе сохраняемая песочница вполне себе нужна, в любой NoSQL она таки есть, все документы мутабельные. Да и, как тут уже обсудили — иногда реквизиты разнесенных документов придется править. Но на общий концепт это не влияет. В хороших компаниях закрытие периода — каждый день, максимум неделя, все что раньше — прибито гвоздями.
                                                        0
                                                        «Строки документа являются частью документа» — почему? В WMS, например, в общем случае операции атомарны и нет смысла менять весь документ при изменении конкретной строки.
                                                        Кстати, в упомянутой мной lsfusion именно так всё и устроено.
                                                          0
                                                          Ну, если их ничего не связывает значит это просто разные документы. Но зачастую связывает — у продажи есть накопительная скидка на заказ, у покупки — распределяемые накладные расходы, у перемещения — вагон в котором едет 1000 номенклатур, так что пожалуй чаще всего это связь типа владение, а не типа ссылка.
                                                            0
                                                            Связывает, например, вид операции, исполнитель и т.п. А в общем случае, количественные остатки на складе, например, изменяет конкретная строка документа, а сумму взаиморасчетов с контрагентами — документ в целом. И ничего не мешает разделить эти изменения на 2 потока, например.
                                                              0
                                                              Согласен, можно и разделить.
                                                          0
                                                          >Совместное редактирование одного документа — согласен, проблема, но такого известные мне системы и сейчас не позволяют.
                                                          В смысле не позволяют? А где же хранится текущая версия, в кеше клиента?
                                                          Обычно не требуется одновременная работа, но текущая версия из базы доступна для чтения остальных.
                                                          Вот на одном компьютере человек создал документ в базе, забил его позициями, сохранил, но не подтвердил. Потом пошел к начальнику/работнику отгрузки, спросил что-нибудь, и сразу там же добили что надо (Если у другого пользователя есть права на редактирование чужого неподписанного документа). Потом с любого рабочего места авторизовался под собой и подписал документ. Именно после подписи начинаются движения всяких счетчиков по складам и лицевым счетам.

                                                          В принципе решение простое, редактируемые версии документов хранить в отдельной таблице-лог, на которой можно повесить различные политики очистки истории. А подписанные документы уже в основной таблице.
                                                            0
                                                            Я с вами согласен, песочница нужна. И регистр мгновенных остатков придется делать, вполне себе мутабельный. Но это точечные отклонения, и общей концепции, на мой взгляд, не меняют.
                                                        0
                                                        про «признак необходимости принудительного кэширования»
                                                        мне кажется, что для упрощения концепта можно выкинуть этот признак из обсуждения. Просто предполагать, что где-то рядом с «кэшем планов запросов», «кэшем статистики» и прочими есть еще и интеллектуальный-кэш-данных. И можно отдельно обсуждать как его настраивать, какие свойства должен иметь этот кэш и прочее.
                                                          0
                                                          Согласен, мне он больше для тестов был нужен.
                                                          0

                                                          Я наверное что-то пропустил, но почему итоги считаются не по топам, а по всем документам?

                                                            0
                                                            В данном случае топы относились к справочникам, с документами так сложно будет поступать — все исторически записи меняли остатки, просто так их проигнорировать значит порвать учет на какую-нибудть историческую дату, поэтому обычно суммируют их все — включая ошибочные, сторнированные, сторнирующие и т.д.
                                                              0

                                                              Т.е. ввод новой версии документа, это ввод не полной информации, а только изменений?


                                                              1. 10 шт. Товар 1 Клиент 1
                                                              2. -1 шт. Товар 1 Клиент 1
                                                                — удаление — т.к. клиент был указан не верно
                                                              3. -9 шт. Товар 1 Клиент 1
                                                              4. 9 шт. Товар 1 Клиент 2

                                                              Так получается?

                                                                0
                                                                Да, конечно. Но обычно в ERP делают полное сторно, и следом вводят правильный (получается всего 3 документа вместо одного). Есть функционал частичного сторнирования строки, которым не любят пользоваться из-за худшей аудируемости.
                                                            +1
                                                            Интересная тема. Когда-то мне приходилось решать MES-подобные задачи, я тоже «изобрел» метод имутабельных документов. При этом удивлялся, почему я здесь один и где вообще теория, обсуждения, фреймворки и прочее.
                                                            ИМХО, за имутабельными документами будущее. Потому что это единственная абсолютно строгая форма описания чего-либо. Даже если у нас есть какое-то полностью автоматизированное предприятие с роботами и дронами, все равно нужно: 1. Собирать информацию о состоянии производства, неисправностях и др. (документы о событиях) 2. Получать по истории, отраженной в документах состояние на анализируемый момент времени (на текущий и иногда на другой). 3. Корректировать ошибки в истории или дополнять информацию, информацией, полученной позже (документы об исправлении ошибочных документов или уточнении информации).
                                                            Традиционный SQL подход здесь не работает, потому что информация по которой нужно делать запросы (состояние) виртуальна и вычисляется по истории документов.
                                                            Каждый раз когда я пытался запустить систему на принципах имутабельных документов основной проблемой была реакция программистов и пользователей.
                                                            Программист: «как теперь быть с привычными приемами типа SQL запросов, фильтров, постраничного показа в UI… Зачем все так сложно?»
                                                            Пользователь: «Значит, если я вижу на экране неверно внесенный параметр, то я не могу его просто поправить. Вместо этого я должен добавить документ о том, что я хочу исправить информацию, содержащуюся в ранее внесенном документе о том, что параметр имел некое значение в некий момент… хм, в старой программе, которую делал наш вася все было проще, вы не пробовали сделать как у него?»
                                                              0
                                                              Вот тут товарищи подсказали статью, гуглем можно читать.
                                                                0

                                                                А ведь пользователь прав. Все ошибаются. Если ошибка не критична для бизнес процесса, то почему ее нельзя просто исправить? Зачем вся эта бюрократия на пустом месте?


                                                                Строить любой документ по всей истории медленно. Актуальные версии смотрят минимум в 95% случаев. И они должны работать быстро.

                                                                  +1
                                                                  Всякая информация по своей природе имеет источник во внешнем мире, который определяет достоверность информации и ответственность за нее. Если просто исправить параметр в атрибутах доменного объекта или в документе, то источника не будет. Позже, если окажется что эта информация не верна или противоречит другой, установить суть проблемы уже будет нельзя.
                                                                  В качестве полу-меры для решения проблемы обычно логируют изменения в атрибутах доменных объектов. Но тут возникают сложности. Например, если пользователь меняет атрибут некого доменного объекта, это что это означает? Что соответствующий параметр действительно изменился где-то в реальном мире примерно в момент внесения изменений, или это означает что пользователь исправил ошибочно внесенное значение? В итоге начинают плодиться дополнительные поля, для внесения исправленных значений, дополнительные журналы для логирования изменений.
                                                                  В принципе, можно разрешить непосредственные изменения только в документах (не в д. объектах) и логировать эти изменения. При этом доменные объекты формируются и корректируются по истории документов с хранением их в БД. Я примерно так и делал. Здесь запись в логе — по сути документ о корректировке другого документа.
                                                                  Окончательное и строгое решение — опираться только на имутабельные документы. В реализации сложно, но задача становится чисто математической и если она решена то получается заманчиво строгая платформа для прикладных задач.
                                                                    0
                                                                    На словах и даже в проекте оно красиво. А потом приходят пользователи которые не понимают почему для изменения контактного телефона (просто пример незначащего поля) надо заниматься всей этой бюкратией. Почему нельзя его просто поменять? Да старый номер не нужен. В итоге незаметные для пользователя логи изменений таких вещей устраивают всех. Концы найти можно и работе не мешает.

                                                                    С иммутабельными документами все хорошо до тех пор пока не выяснится что последняя версия каждого документа собирается из пары сотен предыдущих версий. А какая-нибудь парочка особо важных и нужных документов собирается из 10к предыдущих. И когда приходит хотя бы сотня пользователей с простейшим запросом «Покажи мне актуальную версию документа» все начинает тормозить. Очень тормозить. А они еще f5 любят жать.
                                                                    Объяснить мол пусть тормозит, зато тут математически все прекрасно не выйдет.
                                                                      0
                                                                      Большим ERP часто делают периодическое обрезание, то есть удаление истории с определенным горизонтом, а вместо нее запись аналога «входящего сальдо» на момент обрезания. По-моему даже штатную процедуру такую видел. Скорее всего также и тут придется делать, вопрос лишь выбора этого периода. Только в нашем случае можно не обрезать, а просто партиционировать иммутабельное хранилище по периодам, записывая в начало следующего последний срез предыдущего.
                                                                      PS
                                                                      В моем случае актуальная версия документа — это последняя версия, ее не нужно ниоткуда собирать, это самодостаточный документ, со строчной частью если надо. Собирать нужно только агрегаты, которые, согласен, пользователи тоже иногда захотят получать быстро. В общем, выхода нет, все лучшее уже изобретено до нас.
                                                                        +1

                                                                        Бывают разные задачи. У меня была задача учета электрических счетчиков. На балансе имеется с пол миллиона счетчиков, по каждому ведется история — снятие показаний, кому поставили (в аренду), ремонты, поверки. Основная задача — выявлять хищения электроэнергии за счет манипуляций со счетчиками. Здесь кррме имутабельных документов, описывающих события со счетчиками наверно вообще другого решения нет.

                                                                          0
                                                                          Сама по себе идея с неизменными документами — полезная.
                                                                          Но пытаться для ЕРП прикрутить другую среду хранения вместо стандартных РСУБД… С моей точки зрения — очень сомнительно.
                                                                          Что нам мешает для «неизменных» документов использовать для табличек только инсерты и селекты, без апдейтов и делитов?
                                                                          Если правильно спроектировать систему и базу данных — все прекрасно будет работать. И я уверен, что работать будет лучше, чем если мы будем пытаться прикрутить инструменты от БигДата к ЕРП. Ну нет в ЕРП больших данных, нет.
                                                                            0
                                                                            Можно и в табличках хранить. Только для нестрогого JSON документные БД будут быстрее и дешевле. По поводу бигдаты и ERP — например в масштабах РФ действует перекрестная проверка сделок, (НДС, аффилированность, обоснованность цен и т.д.) База ЭСФ по всей стране положим за год — это уже бигдата. А учетные задачи все те же — обороты, сальдо, себестоимость. Я эту задачу решу минимальными средствами, а грузить все это добро в РСУБД — полезно только для бюджета проекта.
                                                                              0
                                                                              Да, для документов без жесткой структуры объектные базы лучше подойдут.

                                                                              И проверки сделок в рамках страны — это уже действительно ближе к биг дате. Хотя и не уверен — все зависит от структуры данных. Если там просто текстовые документы — это скорее биг дата (но их и обрабатывать сложно). А если там есть жесткая структура данных — вопрос…

                                                                              Но вот задачи в таком проекте сильно отличаются от ЕРП. Скидки не надо считать, актуальные остатки товаров на складе или актуальное состояние взаиморасчетов никому не нужны и т.п.
                                                                              Нужны относительно простые отчеты, но по огромному объему данных.
                                                                              И вот для таких применений как раз и можно (нужно) использовать инструменты из БигДаты.
                                                                                0
                                                                                По идее там стандартная XML, определенная в ФЗ, но на практике бывают ньюансы, закон ведь тоже меняется, поэтому желательно не отвергать документ формально не прошедший валидацию, а попытаться что-то полезное из него извлечь. Пока эта проверка проводится пост-фактум (тьфу-тьфу), согласен — ERP отдельно, а бигдата отдельно. Но если у меня будет свободное время, я все-таки попытаюсь выжать максимум из мап/редюс, хотя бы в самообразовательных целях. Если считать что датафайл влезет на мой диск целиком — заморачиваться на хадуп/спарк смысла нет — потоковый парсер пишется пару дней, распараллеливание расчета положим неделя, все остальное пользовательские алгоритмы.
                                                                            0
                                                                            У вас пользователи вообще не ошибаются? Поделитесь техниками обучения пользователей?
                                                                            Логировать, конечно, надо все и вся. Кто когда и что вносит-меняет. Я бы даже алертов понаделал на странные изменения. Ну не должен пользователь менять массово и много. А с другой стороны почему бы и нет, обычная системная ошибка.

                                                                            Обычная sql база, возможно рядом не менее обычная Монга с джейсонами.
                                                                            Нагрузка смешная. Пусть даже Москва: 15кк счетчиков и 15кк событий в месяц. Работать на калькуляторе будет.

                                                                            Сложную аналитику за большие периоды считать отдельно. Как и на чем зависит от… Но в общем чего-то с большим количеством памяти и без диска хватит.
                                                                              +2
                                                                              Там весь бюджет разработки был несколько тыщ. $, срок разработки 3 месяца а пользователи — женщины в халатах, которые протирают счетчики, сканируют штрих-коды и электрики в поле. Поэтому конечно пользователям разрешалось править «имутабельные» документы с логированием изменений и красной галочкой на каждом исправленном документе. Но итоговое текущее состояние счетчика таки строилось и перестраивалось по документам а не велось непосредственно.
                                                                              Работал сервер действительно на обычной SQL базе и обычной персоналке. Документов набиралось несколько миллионов в год вроде (Минская область). Отдал с исходниками, согласно договору. Честно, не знаю выжила ли эта система в итоге.
                                                                      0
                                                                      Все детали теоретически от пользователя можно инкапсулировать, и даже интерпретатор SQL сверху навернуть, но всеж лучше начинать переучиваться на функциональщину, императивный язык беден, и некоторые запросы SQL с оконными функциями и CTE выглядят еще хуже чем функциональный код.
                                                                        0
                                                                        Есть и обратная сторона: весь страх и ужас можно спрятать, и тогда появляются вопросы «чегойто наш отчёт тормозит?».
                                                                        Смотрим: ага, тормозит, потому что повис на 1 ядре, а оставшиеся 63 курят в сторонке, pushdown на storage cell тоже отдыхает. А почему?
                                                                        А потому что в where функция, которая тоже подзапрос с функцией, который тоже вызывает 2 фукнции, каждая из которых… Ну вы поняли.
                                                                        И в итоге оптимизатор смотрит на это горячее бразильское портанго и вешает всё на 1 ядро без распараллеливания — когда-нибудь да отработает.
                                                                          0
                                                                          Чтение иммутабельных документов ничего не может блокировать. Если нужны тяжелые вычисления в триггерах, значит нужно позаботиться о кэшировании результатов. В предлагаемой мной схеме (она же в couchdb) кэширование простое как валенок. А в пользовательских реляционных схемах оно становится неочевидным и запутанным уже после сотни таблиц. Это же обычные весы — сложность разработки vs сложность поддержки.
                                                                            0
                                                                            Я соглашусь лишь с тем, что при высокой транзакционной плотности вся эта схема непригодна, там нужна in memory db, а не какие-то там чистые фукнции. Как и драйвера на хаскеле никто не пишет.
                                                                        +1

                                                                        Интересно как с этой концепцией иммутабельной бд вы собираетесь поддерживать консистентность для различной бизнес-логики? Допустим юзер хочет перевести деньги другому юзеру. При параллельной обработке таких запросов может возникнуть ситуация гонки (race-condition) когда параллельный запрос увидит только только часть изменений другого запроса и таким образом перезапишет значения тем самым нарушив консистентность (не сойдутся остатки по счетам)
                                                                        Ок, с предлагаемым вами иммутабельным подходом можно не изменять данные в разных местах а просто запушить в лог некую запись об операции перевода а потом при чтении выполнить reduce и получить остатки по счету.
                                                                        Но стоит только добавить условие что юзер может выполнить перевод только при положительном остатке так вся эта идея с иммутабельностью проваливается потому что уже не получится запушить в лог факт операции так как клиенту нужно вернуть либо успех либо ошибку а значит проверку положительного остатка при переводе (редюс по всему списку переводов) нужно выполнить в самом запросе
                                                                        И таким образом получаем на порядок худший вариант чем изначально "мутабельная" версия так как редюс списка истории переводов (чтобы проверить остаток) будет занимать больше времени что увеличивает время конфликта с другими параллельными запросами (не говоря уже про саму неэффективность редюса при каждом запросе)
                                                                        И в конечном счете получается что мутабельность (точечные изменения в разных местах) и поддержка нужных индексов вместо пуша в лог и свертки (или всяких там подходов разделения чтения и записи а-ля cqrs) это самый эффективный способ реализации serializable-транзакций в базах данных (если конечно вам нужна консистентность чтобы сошлись остатки по счетам). Кстати советую посмотреть хороший доклад про уровни изоляции транзакций https://www.youtube.com/watch?v=5ZjhNTM8XU8
                                                                        Ну а хранение данных полнотью в оперативке (с консистентным сохранением на диск в режиме append-log) позволит выполнять несколько сот тысяч таких serializable-транзакций в секунду.
                                                                        Правда sql-базы под это не заточены но яркий пример базы данных которая умеет в больше 100к serializable-транзакций в секунду это Tarantool (https://habr.com/ru/company/oleg-bunin/blog/340062/, https://habr.com/ru/company/oleg-bunin/blog/310690/).
                                                                        Если вкратце то отличия подобных тарантулу in-memory баз от sql-баз (когда просто увеличиваем кеш) заключаются в следующем:
                                                                        а) не нужно поддерживать индексы на диске — так как используется только последовательная запись в конец файла то получаем скорость больше 100мб в секунду даже на крутящихся дисках а это позволяет записать больше сотни тысяч транзакций в секунду в бинарном формате
                                                                        б) изначально заточена под хранение в оперативке архитектура которая намного эффективнее кеша sql-баз — подробности тут — https://habr.com/ru/company/oleg-bunin/blog/310560/)
                                                                        в) при переходе с клиентских транзакций на серверные уменьшается время конфликта параллельных запросов и вообще необходимость в mvcc так как можно тупо выполнять транзакции в одном потоке что гарантирует serializable. Серверные транзакции отличаются от клиентских тем что вместо того чтобы стартовать транзакцию на клиенте и посылать отдельные запросы а потом посылать коммит (как это традиционно делают c sql-базами) — в базу одним запросом сразу передается вся логика обработки данных включая if-ы и разные циклы и вся эта логика уже выполняется на самой бд максимально близко к данным

                                                                          0
                                                                          Комментарий тянет на статью, спасибо!
                                                                          клиенту нужно вернуть либо успех либо ошибку а значит проверку положительного остатка при переводе (редюс по всему списку переводов) нужно выполнить в самом запросе
                                                                          Редьюс действительно придется выполнить, но только по текущему (незакрытому) периоду, в ERP это обычно неделя или месяц, для кассовых операций — день, в банке не знаю, похоже не более часа. В целом, проблему быстрого получения остатка признаю, видимо это будет отдельный мутабельный регистр.
                                                                          PS
                                                                          Все остальное пока не готов комментировать, нужно сначала прочесть.
                                                                            0
                                                                            Перечитал. Вы конечно правы, но тут задачи разные, у тарантула фокус на транзакционность, у CouchDB на извлечение, а я всего-лишь говорил о ERP, где из действительно важного — только регистр мгновенных остатков (резервы это тоже остатки, только с другим статусом наличия), а все остальные данные не являются мгновенно-критичными. Раньше была привычка на любую потребность создавать таблицу, а сейчас можно создать вместо таблицы — функцию. Этот подход имеет будущее в определенных кейсах, потому что функция чистая, она не изменяет хранимые данные, и другие функции не изменяют, таким образом все функции всегда ведут себя предсказуемо. Как только у вас появляется мутабельный стейт — поддерживать его консистентность самим с собой бывает труднее, чем выучить вместо SQL какую-нибудь Scala или эликсир. Но за повышение надежности и уменьшение количества таблиц тоже нужно платить, например снижением скорости отклика.
                                                                              0
                                                                              "… потому что функция чистая, она не изменяет хранимые данные..."
                                                                              Как это не изменяет? В вашем случае вы инсертите в базу — это уже «побочный эффект», а значит функциональной чистоты вы так не добьётесь.
                                                                                0
                                                                                Возможно (даже скорее всего) я просто видел плохие реализации ERP, где часть агрегатов лучше было бы выкинуть. Например в 1С остатки хранят срезами, а в DAX — только мгновенные, а срезы считают на лету. То есть на оси «чистые функции — грязный стейт» где-то есть оптимум, и он точно не справа.
                                                                            0
                                                                            "… Поэтому существующие системы хранят удобные абстракции над строками документов (проводки)..."
                                                                            Кстати, существуют системы, работающие «от проводки», а не «от документа», например ноне практически покойный «Инфин».
                                                                              +1

                                                                              Описанное в статье жутко похоже на Kappa Architecture. И идеи да, крайне правильные, но не до конца.
                                                                              В каноничной Каппе ещё используются семи-персистентные БД (SQL, не SQL, главное чтобы читать было удобно) в качестве кешей, автору настоятельно советую ознакомиться

                                                                                0
                                                                                А о графовых БД не задумывались?
                                                                                И о расчете себестоимости в функциональной БД не задумывались (плюсы/минусы)?
                                                                                Если да — пишите, будет что обсудить.
                                                                                  0
                                                                                  Про графовые — пока не понимаю каким боком их к моей задаче приспособить. Идея все-таки создать иммутабельное хранилище. Сейчас я думаю над написанием 3-х примеров:
                                                                                  1) Правка документа задним числом. Поскольку в данной схеме правка иммутабельных документов — это достаточно редкое (но необходимое) исключение — хранилище должно ее поддерживать. Понятно, что придется перезаписать кусок журнала, обеспечивая вставку, удаление, изменение. Понятно, что это приведет к перемещению горизонта иммуабельности, а значит — пересчету всех агрегатов, то есть операция по сути блокирующая. И надо уменьшить ее цену, например партиционированием хранилища (агрегаты должны вычисляться по каждой партиции, и потом сводиться, но не для всех алгоритмов это возможно в принципе).
                                                                                  2) Расчет себестоимости. Это первый алгоритм, реализацию которого нужно показать. Принципиальных проблем я пока тут не вижу, но они могут появиться в процессе.
                                                                                  3) Распараллеливание. Не очень представляю, как это сделать с учетом сквозных связей. То есть без определения кэшированных объектов ДО расчета параллелизм невозможен, а это уже когнитивная нагрузка на прикладного разработчика, не уверен что в итоге получится легко понимаемый код.
                                                                                  PS
                                                                                  Появится материал — буду выкладывать.
                                                                                    0
                                                                                    Про графовые — пока не понимаю каким боком их к моей задаче приспособить.

                                                                                    К текущей никак. Вернее, почти никак:) Но для разработки ERP они нужны.

                                                                                    2) Расчет себестоимости. Принципиальных проблем я пока тут не вижу, но они могут появиться в процессе.

                                                                                    Проблемы есть у текущих алгоритмов (в основном они связаны с быстродействием). Функциональная модель должна обеспечивать скорость расчета не ниже, чем существующие системы. От этого будет зависеть, подходит ли функциональная логика для разработки ERP-систем.

                                                                                    Появится материал — буду выкладывать.

                                                                                    Буду ждать. И просьба по поводу материала, который будет появляться, и по поводу распараллеливания — если можете, пишите как можно понятнее «для чайников».
                                                                                      0
                                                                                      2) Расчет себестоимости. Принципиальных проблем я пока тут не вижу, но они могут появиться в процессе.


                                                                                      Проблемы есть у текущих алгоритмов (в основном они связаны с быстродействием). Функциональная модель должна обеспечивать скорость расчета не ниже, чем существующие системы. От этого будет зависеть, подходит ли функциональная логика для разработки ERP-систем.

                                                                                      А можно примеры проблем с быстродействием?
                                                                                      Я этим вопросом интересуюсь, и все случаи, которые знаю, связаны с тем, что с базой работают с помощью медленных интерпретируемых императивных языков (циклы) и ORM, плюс в одной транзакции с проведением документа (естественно, с блокировками).

                                                                                      Стоит переписать на SQL, вынести в отдельные транзакции и оптимизировать — все расчеты получаются очень быстрыми. Очень большой запас по скорости.
                                                                                        0
                                                                                        Я этим вопросом интересуюсь, и все случаи, которые знаю, связаны с тем, что с базой работают с помощью медленных интерпретируемых императивных языков (циклы) и ORM, плюс в одной транзакции с проведением документа (естественно, с блокировками).
                                                                                        Стоит переписать на SQL, вынести в отдельные транзакции и оптимизировать — все расчеты получаются очень быстрыми.

                                                                                        Да.
                                                                                        На чистом SQL работает достаточно быстро. Быстрее пока не требуется.

                                                                                        Но перенос на чистый SQL это ведь самописная разработка, которую нужно выполнить и поддерживать. Т.е. это нельзя назвать универсальным инструментом ERP-системы (тем более, правила которой пользователь сможет менять). Я ведь правильно понимаю?
                                                                                          0
                                                                                          Да, правильно.

                                                                                          Но это ограничение просто — напросто означает, что в архитектуре современных распространенных ERP-систем есть косяк.
                                                                                          Оптимальное решение — переписать 5-10 модулей ERP-систем (самых тормозных, которые как раз выполняют массовую обработку данных, расчет себестоимости — самый яркий пример) на SQL. Так, чтобы пользователь мог менять правила и т.п. Внутри эти алгоритмы не настолько сложные, чтобы их нельзя было сделать достаточно гибкими.
                                                                                          И минимум на ближайшие лет 20 этого хватит с запасом. Большим запасом. :)

                                                                                          А пытаться переделать ERP-системы полностью — намного дороже.
                                                                                          Если просто поменять базу данных — это ведь само по себе ничего не изменит. На хабре много публикаций про SAP / HANA — очень хорошо про это рассказано.
                                                                                            +1
                                                                                            Оптимальное решение — переписать 5-10 модулей ERP-систем (самых тормозных, которые как раз выполняют массовую обработку данных, расчет себестоимости — самый яркий пример) на SQL. Так, чтобы пользователь мог менять правила и т.п. Внутри эти алгоритмы не настолько сложные, чтобы их нельзя было сделать достаточно гибкими.

                                                                                            И много таких модулей вы уже переписали? В массово (ну хотя бы тысяча клиентов) используемых ERP?

                                                                                              0
                                                                                              Ни одной. :)
                                                                                              На реальных проектах, в которых я участвовал, это ни разу не становилось настолько большой проблемой, чтобы надо было переписывать.

                                                                                              Я достаточно глубоко ковырял себестоимость в трех системах — NAV, Adempiere и 1C УПП.
                                                                                              И на одном проекте полностью воссоздал логику расчета себестоимости в NAV для одного из вариантов настроек (FIFO + учет по лотам). В самой системе все осталось как было, сделали отдельные алгоритмы в отдельной базе для расчета детализированной себестоимости.
                                                                                              Кстати, написать с нуля, чтобы считалось правильно, но немного не так, как NAV считает, было бы раза в три проще.
                                                                                                0
                                                                                                Ни одной.

                                                                                                Я так и подумал.

                                                                                                  0
                                                                                                  И на одном проекте полностью воссоздал логику расчета себестоимости в NAV для одного из вариантов настроек (FIFO + учет по лотам). В самой системе все осталось как было, сделали отдельные алгоритмы в отдельной базе для расчета детализированной себестоимости.

                                                                                                  Сколько заняло в часах/нормо-часах, сможете прикинуть?
                                                                                                  Нужно ведь не только воссоздать логику, но и сделать качественную интеграцию с существующей системой.

                                                                                                  Кстати, написать с нуля, чтобы считалось правильно, но немного не так, как NAV считает, было бы раза в три проще.

                                                                                                  Как и написать многие другие кастомизированные модули для ERP-системы:)
                                                                                                    +1
                                                                                                    Сколько заняло в часах/нормо-часах, сможете прикинуть?
                                                                                                    Нужно ведь не только воссоздать логику, но и сделать качественную интеграцию с существующей системой.


                                                                                                    Заняло примерно 800 часов.
                                                                                                    Интеграцию мы не делали. Просто «раскладывали» существующие записи себестоимости по элементам (инвойсам / кредит нотам закупок).

                                                                                                    Если полноценно переписывать модуль расчета себестоимости (с интеграцией, документацией и всеми вариантами настроек), то ориентировочно надо 2000 — 4000 часов и примерно пол года времени.
                                                                                                    Связь куска кода по расчету себестоимости с остальными модулями относительно слабая. Плюс навик работает только на MS SQL. Так что задача вполне решаемая. Но, разумеется, её имеет смысл делать только вендору (микрософт).
                                                                                                      0
                                                                                                      Спасибо за конкретный ответ.
                                                                                                    0
                                                                                                    А не кинете ссылку на какой-нибудь мануал с алгоритмом 1С УПП, с ней работать не приходилось, интересно.
                                                                                                      0

                                                                                                      Там, если мягко выражаться, фигня полная была.
                                                                                                      Нормального мануала не было (по крайней мере не нашел, хотя много искал), я код смотрел и результаты расчетов в регистрах.

                                                                                                  0
                                                                                                  Если просто поменять базу данных — это ведь само по себе ничего не изменит.

                                                                                                  Подождите. Так функциональные базы данных для меня загадочны. Тот же LsFusion в демо-версии работает на PostgreSQL, то есть «под» функциональной логикой все равно лежит реляционная СУБД.

                                                                                                  Но это ограничение просто — напросто означает, что в архитектуре современных распространенных ERP-систем есть косяк.

                                                                                                  В этом и есть вопрос. Поможет ли функциональное программирование избавиться от этого косяка и сделать быстрый расчет в ERP-системах / или хотя бы на уровне существующих ERP-систем / или ERP-системы, разработанные на функциональной логике, будут выполнять такие расчеты еще медленнее.
                                                                                                    0
                                                                                                    Какая метрика, и на какой методике расчета вас интересует? Например, фифо по лотам на миллионе документов — сколько времени оно должно считаться? Функциональщина сама по себе будет считать не медленней, но вопрос в методике — везде где я видел, было принято создавать приходы и дооценки задним числом, в результате при закрытии периода приходилось все массово рассопоставлять и заново сопоставлять. То есть мы можем придумать алгоритм как считать ее быстро и в онлайн, но практика заднего числа может все убить — получим просто толстую блокирующую операцию. Я сейчас простенький пример расчета пишу, чтобы не впустую — давайте согласуем методику.
                                                                                                      0
                                                                                                      Какая метрика, и на какой методике расчета вас интересует? Например, фифо по лотам на миллионе документов — сколько времени оно должно считаться?

                                                                                                      Хороший вопрос.
                                                                                                      Сама по себе скорость не так важна. Явно нужно сравнивать с другими системами (ERP или на «голой» РСУБД).
                                                                                                      К сожалению, сейчас для теста под рукой не найдется доступа к ERP-системе, кроме 1С.
                                                                                                      Возможно, коллеги подкинут описание кейса из Axapta/NAV?

                                                                                                      Методику для примера нужно брать самую распространенную и простую в реализации. Видимо, это будет ФИФО.
                                                                                                        0
                                                                                                        OK. Как чего-нибудь простое напишу сообщу.
                                                                                                        +1

                                                                                                        Инвойсы и кредит ноты легко могут прийти и через пол года после продажи партии. :)))
                                                                                                        Так что это нормально.
                                                                                                        Приходы задним числом… Ну такое бывает, но только в открытом периоде. Когда период закрывают — уже не лезут в товароведения.
                                                                                                        Опять же, большого смысла считать фактическую себестоимость на лету просто напросто нет. Она в любой момент может поменяться. А использовать стандартную себестоимость для грубой оценки можно без проблем в любом случае.


                                                                                                        Можно для теста использовать следующую модель:
                                                                                                        В течении месяца у нас никаких записей по себестоимости нет. И фифо тоже нет. Есть пары операций минус плюс, в которых количество одинаковое (перемещения), есть просто плюс (покупка, оприходования) и просто минус (продажа, списание). Предположим, для простоты, никакого производства нет. Для каждой строки товародвижения задан id строки, тип операции, id операции (для перемещения — один и тот же id для двух строк), дата, код товара, код склада, код ячейки, количество (плюс или минус, пять знаков после запятой). Выполняется ограничение — количество товара в ячейке не должно уходить в минус на конец любого дня (считаем, что все приходы в начале дня, все расходы в конце дня).
                                                                                                        И есть множество документов, генерирующих себестоимость — id строки документа (инвойс или кредит нота), дата, сумма (два знака после запятой, может быть положительной или отрицательной), id строки товародвижения — привязка может быть к любой операции товародвижения, положительной или отрицательной.


                                                                                                        На основе этих данных надо сформировать строки себестоимости: id строки себестоимости, id строки товародвижения, id строки документа себестоимости, дата, сумма (плюс или минус, много знаков после запятой). Для фифо упорядочиваем по дате и id строки товародвижения. Дата операции себестоимости должна быть большей из двух — дата документа или дата товарной операции. Если привязка себестоимости была к отрицательной строке товародвижения — создаётся две операции себестоимости для этой товарной — первая с той же суммой, что и в привязке документа, и ещё одна с суммой, умноженной на минус единицу. Для остальных ситуаций создаётся по одной строке себестоимости для каждой строки товародвижения и id строаи документа себестоимости, который к ней относится по фифо.
                                                                                                        Если сгенерить миллион строк товародвижения и тысяч 300 привязок себестоимости — можно оценить эффективность разных подходов. Реальный расчет будет сложнее (производство ещё есть), но в целом достаточно похоже.

                                                                                                          0
                                                                                                          Не получится, что такой просчет даже на миллионе записей пройдет за пару секунд, и сравнивать будет нечего?
                                                                                                            0
                                                                                                            :))))
                                                                                                            Пробовали?
                                                                                                            Еще раз почитайте условия. «Связок» (сопоставлений) товарных операций нет, их придется сделать.
                                                                                                            И сама по себе запись в базу нескольких миллионов записей занимает не несколько секунд.
                                                                                                            Миллион товарных операций в месяц — это не мелкая компания, а скорее средняя. И в таких пересчет себестоимости за месяц в ERP как раз и может занимать часы.

                                                                                                            Если на SQL написать, вполне можно уложиться в несколько десятков секунд или несколько минут (от данных и железа зависит). Но никак не пара секунд. Так что различия будут видны достаточно хорошо.
                                                                                                            Ну а если слишком быстро — сто миллионов товарных движений и тридцать миллионов разнесений стоимости. В итоге даст за сотню миллионов операций себестоимости минимум (а может и за миллиард перевалить, если будет много «длинных» цепочек). Тут уж точно даже на среднем железе подождать придется, не говоря о ноуте. :)))
                                                                                                            Но такие объемы — это уже очень крупные компании.
                                                                                                              0
                                                                                                              Пробовали?

                                                                                                              Нет, поэтому и задаю вопрос
                                                                                                              Если на SQL написать, вполне можно уложиться в несколько десятков секунд или несколько минут


                                                                                                              ОК
                                                                                                                0
                                                                                                                Миллион транзакций (строк документов) в месяц для оптовой торговли это уже много, для производства нормально, и только для ритейла ниочем. Граф сопоставлений — это связь многие-ко-многим, то есть сопоставлений вполне может получиться и 10 миллионов. Нужно обнаруживать и отсекать циклы (возвратные отходы в производстве). На последнем моем проекте такой объем Аксапта несколько часов закрывала, хотя с виду смешная задача. Потому что считала по каждой номенклатуре отдельно — граф + прогон коррекций. Как правильно тут говорили — ОРМ + циклы это уже давно моветон, но кто-то еще использует.
                                                                                                                  +1
                                                                                                                  Я и говорил про ОРМ + циклы. :)
                                                                                                                  К сожалению, их, насколько знаю, использует большинство (или почти все) вендоров популярных ERP.
                                                                                                                  В этом и кроется причина проблем. Даже для ритейла посчитать всё не является проблемой. Там десятки — сотни миллионов операций, которые прекрасно разбиваются по складам и считаются отдельно. Вернее большая часть расчетов отдельно, а часть потом совокупно.
                                                                                                                  Но все можно посчитать за разумные сроки с помощью существующих технологий (RDBMS).

                                                                                                                  Но ОРМ и циклы… :(
                                                                                                                    0
                                                                                                                    Ну, в Аксапте изначально был самопильный ОРМ, поскольку она декларировала поддержку любой ANSI SQL СУБД. Так и было, но после покупки MS сделала поддержку оракла недокументированной, и рекомендовала всем переход на MSSQL. Но кодобаза большая, никто не стал переписывать ни на .NET ни на TSQL. Сейчас когда я читаю статьи из мира Java про очередной ORM, мне странно, как будто в мир почивших EJB вернулся :) Я кстати давно не следил, но похоже диалекты SQL так и не стандартизовали, те же оконные функции, или CTE, поэтому пока все так.
                                                                                                                      0
                                                                                                                      В последних версиях стандарта SQL очень много прописано и последние версии СУБД также очень много поддерживают. И того, что есть — более чем достаточно для этих задач.

                                                                                                                      Да и, если на то пошло — смысл стараться сделать ERP систему всеядной по базам данных? Навик работает только с MS SQL и это ему не мешает — просто понимаешь, что к стоимости навика надо приплюсовать стоимость MS SQL.
                                                                                                                        0
                                                                                                                        Небольшая ремарка: На сколько знаю, MSSQL нескольких версий сертифицированны ФСБ и могут поставляться в госкомпании (предоставляли исходники на проверку), на счёт Oracle совсем не уверен, что совсем не мешает стоять шкафам с exadata в оных компаниях.
                                                                                                                      +1
                                                                                                                      Ну в 1с ERP 2 решается через СЛАУ (методом Гаусса, вроде)
                                                                                                              0
                                                                                                              Инвойсы и кредит ноты легко могут прийти и через пол года после продажи партии. :))) Так что это нормально.
                                                                                                              Давайте определимся с терминами. Инвойс это денежный приход? Он может не совпадать с количественным по времени? А как в этот промежуток сырье списывать, по какой себестоимости? Кредит-нота сторнирует только сумму, или и количество тоже? Что делаем если товар купили и продали, а в след. месяце пришел инвойс на растаможку и фрахт?
                                                                                                              Выполняется ограничение — количество товара в ячейке не должно уходить в минус на конец любого дня
                                                                                                              То есть ERP позволят в середине дня отгузить еще не полученное? Не верю. Тогда нужно разделять мухи (движение количества) и котлеты (движение денег) в разные контуры.
                                                                                                                +1
                                                                                                                Давайте определимся с терминами. Инвойс это денежный приход? Он может не совпадать с количественным по времени? А как в этот промежуток сырье списывать, по какой себестоимости? Кредит-нота сторнирует только сумму, или и количество тоже? Что делаем если товар купили и продали, а в след. месяце пришел инвойс на растаможку и фрахт?

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

                                                                                                                А сырье мы должны списывать по себестоимости, которая максимально (в рамках рвазумного) приближена к фактической. Это по стандартам фин отчетности.
                                                                                                                Если «товар купили и продали, а в след. месяце пришел инвойс на растаможку и фрахт» — в фин учете должен быть отражен как корректировка прошлых периодов, то есть датой, когда фактически получили инвойс.
                                                                                                                По большому счету, когда именно пришел инвойс, вообще не влияет на схему учета, если все настроено правильно. Хоть через несколько лет пришел. :)
                                                                                                                Другое дело, что очень часто и бухгалтера и консультанты не понимают, как это должно учитываться, и придумывают дикие схемы. :)

                                                                                                                То есть ERP позволят в середине дня отгузить еще не полученное? Не верю. Тогда нужно разделять мухи (движение количества) и котлеты (движение денег) в разные контуры.

                                                                                                                В ритейле на кассе обычно не проверяют остатки, а просто пробивают покупки.
                                                                                                                А разделять товародвижения и деньги в разные контуры — это правильный подход, и обычно так и сделано. :)
                                                                                                                  0
                                                                                                                  Нет, платежи — это отдельно
                                                                                                                  Это понятно, я о другом спрашивал — у нас в проектах количественный приход (без суммы) регистрировался одним документом (и с количеством можно было уже работать), а суммы приходили инвойсами, и в системе было 2 даты — дата количественного прихода и дата финансового прихода. И получалось странное — контроль количества был мгновенным, а контроль сумм помесячный.
                                                                                                                  Если «товар купили и продали, а в след. месяце пришел инвойс на растаможку и фрахт» — в фин учете должен быть отражен как корректировка прошлых периодов, то есть датой, когда фактически получили инвойс.
                                                                                                                  Это странно, отчетность по налогам квартальная, сейчас уже месячная, неужели у вас заморачивались с подачей уточненных деклараций на такую мелочь? У нас проще было — товар приходовали по экспертной себестоимости (без документов), а когда приходили документы — разницу вешали на финрезульат текущего периода, там немного получалось, ведь коррекции могли быть в разные стороны.
                                                                                                                  PS
                                                                                                                  В итоге приходим к необходимости вести 2 регистра остатков — количественные остатки и финансовые остатки. Хотя это противоречит РСБУ, по которому сырье без себестоимости мы не имеем права списывать. Но РСБУ возможно отменят, я давно не следил правда.
                                                                                                                    0
                                                                                                                    :)
                                                                                                                    Как раз и надо вести ДВА разных регистра остатков — количественный и стоимостной. И связь между ними будет весьма слабой. И РСБУ и МСФО не запрещает списывать сырье без себестоимости. :))))
                                                                                                                    Более того, вы в течении квартала можете ВООБЩЕ никак не считать себестоимость, рассчитать её только перед формированием квартальной отчетности — и это будет полностью соответствовать стандартам.

                                                                                                                    А вот то, что вы у себя делали:
                                                                                                                    товар приходовали по экспертной себестоимости (без документов), а когда приходили документы — разницу вешали на финрезульат текущего периода, там немного получалось, ведь коррекции могли быть в разные стороны

                                                                                                                    как раз нарушает и МСФО и РСБУ. И там и там — явный запрет использовать любые методики, кроме ФИФО и средневзвешенной. А то, что вы описали — вариация стандартной себестоимости (Standard costing).
                                                                                                                    Это хороший пример того, что я говорил — когда и бухгалтера и консультанты не разбираются в нюансах учета — получаются всякие дикие и обычно очень неоптимальные схемы.
                                                                                                                      0
                                                                                                                      Это просто говорит о том, что должен быть еще и разрез управленческого учета
                                                                                                                        0

                                                                                                                        Управленческий — это немного другое. В данном случае есть грубое нарушение стандартов бухучета.

                                                                                                                          0
                                                                                                                          Думали. Но двойной учет вести дорого, и кроме как в России мало где практикуется.
                                                                                                                            0
                                                                                                                            У меня другая информация
                                                                                                                          0
                                                                                                                          когда и бухгалтера и консультанты не разбираются в нюансах учета — получаются всякие дикие и обычно очень неоптимальные схемы.
                                                                                                                          Не будьте уж столь категоричны. Решение ставить сырье на финансовый приход по оценочной себестоимости принималось финансовым директором ) Мы не можем списать эту оценочную себестоимость в производство прямо с материального счета, но мы можем оформить эти затраты как резерв по сомнительным долгам, а по приходу инвойса — продебетовать счет сомнительных долгов, а сальдо (разницу) отнести на прибыли / убытки.

                                                                                                                          Если дооценивать в прошлых периодах — это значит весь финрезультат прошлых периодов надо пересчитывать, а кроме налогов есть еще отчеты акционерам с подробнейшей детализацией, где лишний миллион вполне могут заметить.

                                                                                                                          Принятое решение — оптимальное, штраф за нарушение РСБУ копеечный, зато учет получается иммутабельный, как раз как нужно в моей схеме. В вашей же схеме суммы без количества будут гулять задними числами постоянно, а плавающий финрез это такое себе.
                                                                                                                            0

                                                                                                                            "В вашей же схеме суммы без количества будут гулять задними числами постоянно, а плавающий финрез это такое себе."


                                                                                                                            Вот где я такое писал, можно цитату? :)

                                                                                                                              0
                                                                                                                              Если «товар купили и продали, а в след. месяце пришел инвойс на растаможку и фрахт» — в фин учете должен быть отражен как корректировка прошлых периодов, то есть датой, когда фактически получили инвойс.
                                                                                                                              Если это корректировка прошлых периодов — то плывет финрез, а его уже акционеру отдали, нехорошо. Если отражаем в текущем периоде — теряем связь между движением количества и движением суммы, например при дооценке прихода нужно дооценить также и затраты текущего периода, а там эта номенклатура уже не потреблялась, будет трудно расшифровать.
                                                                                                                                +1
                                                                                                                                www.minfin.ru/common/upload/library/2016/03/main/RU_BlueBook_GVT_2015_IAS_8.pdf

                                                                                                                                Почитайте стандарт. :)

                                                                                                                                Если отражаем в текущем периоде — теряем связь между движением количества и движением суммы, например при дооценке прихода нужно дооценить также и затраты текущего периода, а там эта номенклатура уже не потреблялась, будет трудно расшифровать.

                                                                                                                                А вот это — одно из следствий вашего «оптимального» решения. :)
                                                                                                                                У нас связь не терялась. То есть я в кубе легко показывал отдельно суммы, которые прошли в текущем периоде, но относились к продажам прошлого периода — со всей дополнительной информацией — количества, наименования товаров, покупатели, номера и даты документов, склады отгрузки и т.п.
                                                                                                                                  0
                                                                                                                                  Спасибо, понял.
                                                                                                                                  +1
                                                                                                                                  Если это корректировка прошлых периодов — то плывет финрез, а его уже акционеру отдали, нехорошо.


                                                                                                                                  Вот цитата из стандарта:
                                                                                                                                  Изменение в бухгалтерской оценке – корректировка балансовой стоимости актива или
                                                                                                                                  обязательства или величины, отражающей потребление актива в периоде, которая возникает в
                                                                                                                                  результате оценки текущего состояния активов и обязательств и ожидаемых будущих выгод и
                                                                                                                                  обязанностей, связанных с активами и обязательствами. Изменения в бухгалтерских оценках
                                                                                                                                  возникают в результате появления новой информации или развития событий и, следовательно, не
                                                                                                                                  являются исправлениями ошибок.


                                                                                                                                  О чем этот пункт.
                                                                                                                                  Мы оприходовали товар. Точную стоимость не знаем — еще не получили всех документов. Поэтому мы выполняем оценку на основе доступной нам информации (делаем начисления), и эту оценку используем как себестоимость товара.
                                                                                                                                  По мере получения инвойсов от поставщиков, заменяем оценку точными данными. То есть сторнируем суммы оценки и вместо оценки учитываем инвойсы. Делаем это датой, когда получили документы. То есть отчетность прошлых периодов НЕ КОРРЕКТИРУЕМ — это прямо указано в стандарте.
                                                                                                                                  ВАЖНО — расхождения между оценкой и фактом мы не просто тупо списываем на финрез, а отражаем по счету запасы на складах (для тех запасов, которые еще есть в наличии). Иначе будет нарушение стандартов.

                                                                                                                                  Для упрощения реализации в ERP системе мы не начинаем анализировать, что уже продано, а что еще нет и лежит на складах. Мы просто воспроизводим ВСЕ проводки по партиям товара, по которым пришли инвойсы, в текущем (открытом) периоде. Если товар уже продан — система «прогонит» суммы по складу и спишет на CoGS. Если еще на складе — оставит на складе.
                                                                                                                                  Всё. :)))
                                                                                                                                  Это — стандартная функциональность навика и, уверен, в аксапте тоже такое есть.
                                                                                                                                  То есть вообще ничего программировать не надо — просто правильно настроить. :)

                                                                                                                                  И не надо придумывать дикие схемы учета — достаточно просто грамотно реализовать требования стандартов с учетом возможностей ERP системы.
                                                                                                                                    0
                                                                                                                                    Если товар уже продан — система «прогонит» суммы по складу и спишет на CoGS. Если еще на складе — оставит на складе.
                                                                                                                                    Стандарт так и работает. После пары лет эксплуатации нам это стало неудобно, нам это не ИТ, а финдепартаменту собственника, они сказали что дооценок быть не должно, работайте по JIT, точка. Так что все вопросы туда.
                                                                                                    0

                                                                                                    "Если дооценивать в прошлых периодах — это значит весь финрезультат прошлых периодов надо пересчитывать"


                                                                                                    Вы ведь не читали стандарты, правильно? :)
                                                                                                    Корректировка проводится в текущем периоде, если она не настолько существенна, что надо перевыпускать отчётность.


                                                                                                    То, что вы описываете ("Мы не можем списать эту оценочную себестоимость в производство прямо с материального счета, но мы можем оформить эти затраты как резерв по сомнительным долгам, а по приходу инвойса — продебетовать счет сомнительных долгов, а сальдо (разницу) отнести на прибыли / убытки") некорректно с точки зрения стандартов. Более того, такой способ существенно усложняет схему учёта и делает невозможным нормальный анализ себестоимости.
                                                                                                    Вы вот упоминаете об оптимальности этого метода. А какие варианты сравнивали, можно узнать? Например, не рассматривали вариант, описанный в стандартах? :)
                                                                                                    По стандартам фин учёта по приходу товара надо выполнять начисление (аккруалс), а при прихода счета списывать начисление и учитывать счёт датой получения счета — чем этот вариант оказался хуже? :)

                                                                                                      0
                                                                                                      Вы вот все на стандарты ссылаетесь, а на конкретный вопрос так и не ответили. Придется мне. В прошлом месяце купили сырье, списали в производство, выпустили продукт, и сразу его продали. Движение по счетам примерно такое:

                                                                                                      60 => 10 => 20 => 43 => 90
                                                                                                      Дебет 20-го это стандартный управленческий отчет «детализация себестоимости», где подробно расшифровывается сумма — вплоть до количества потребленного сырья и его цены, чтобы отделить фактор цены от фактора норм расхода.

                                                                                                      В следующем месяце мы получаем накладные расходы, связанные с покупкой этого сырья. Куда их приходовать? На складе уже нет ни сырья ни продукции, дооценивать нечего. По стандартну нужно прогнать сумму коррекций по тем же счетам, вплоть до 90, но неприятность в том, что на счетах 10 и 20 появляются суммы без количества, которые выглядят странно в отчете детлизация себестоимости, и в складской оборотке.

                                                                                                      Поэтому и принято решение приходовать ТЗР по экспертной стоимости, без документов, по аналогии с начислениями резервов. Проводка будет XX => 10, где XX — некий клиринговый счет по данному типу резерва. По приходу инвойса делается проводка 60 => XX, а сальдо XX (разница между планом и фактом) списывается на финрез.
                                                                                                      PS
                                                                                                      Воможно мы об одном и том же говорим, извините, я не бухгалтер, могу каких-то терминов не знать, но общие принципы понимаю, так как приходилось это реализовывать.
                                                                                                        +1
                                                                                                        Движение по счетам примерно такое:

                                                                                                        60 => 10 => 20 => 43 => 90
                                                                                                        Дебет 20-го это стандартный управленческий отчет «детализация себестоимости», где подробно расшифровывается сумма — вплоть до количества потребленного сырья и его цены, чтобы отделить фактор цены от фактора норм расхода.

                                                                                                        В следующем месяце мы получаем накладные расходы, связанные с покупкой этого сырья. Куда их приходовать? На складе уже нет ни сырья ни продукции, дооценивать нечего. По стандартну нужно прогнать сумму коррекций по тем же счетам, вплоть до 90, но неприятность в том, что на счетах 10 и 20 появляются суммы без количества, которые выглядят странно в отчете детлизация себестоимости, и в складской оборотке.

                                                                                                        Все гениальное — просто. :)
                                                                                                        Делаем проводки в том месяце, в котором получили документы, по указанным вами счетам. То есть проводки те ж