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

Перенос остатков для аналитиков и разработчиков — удовольствие ниже среднего, потому что:

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

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

Итак, как же можно решить проблему переноса остатков?

Для начала давайте определимся с терминологией. Под сервисом балансового учёта я понимаю инструмент, который учитывает изменения состояний объектов таким образом, чтобы, просуммировав эти данные, можно было получить картину на любой момент времени.

Приведу пример из моей практики в Ozon.

Разработка производилась для Ozon 3PL — сервиса, с помощью которого мы доставляем товары внешних интернет-магазинов (принципалов) с использованием логистической инфраструктуры Ozon.

Функционал, о котором я расскажу: 
  1. Зачисляет на баланс стоимость товаров от внешних интернет-магазинов в тот момент, когда они передают их в Ozon 3PL.
  2. Списывает с баланса стоимость товаров от внешних интернет-магазинов, когда они: 
    • вручаются получателю;
    • возвращаются отправителю;
    • ломаются или теряются в процессе перевозки.


Путём суммирования сформированных сервисом данных можно быстро определить, сколько товаров от внешних интернет-магазинов было у нас в любой момент времени. Этот показатель и есть «баланс».

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

Запуская в промышленную эксплуатацию эту программу, мы понимали, что на момент запуска у нас будет какое-то количество товаров от внешних интернет-магазинов. Мы могли бы вычислить их общую стоимость аналитически:
image
Но делать это всё мы, конечно, не будем.

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

Шаг 1. Определение объекта балансового учёта


В рассматриваемом примере объект учёта — это стоимость товаров внешних интернет-магазинов, которые физически находятся в логистической инфраструктуре Ozon.

Что такое логистическая инфраструктура
Под логистической инфраструктурой понимаются:
  1. Сортировочные центры.
  2. Пункты выдачи заказов.
  3. Машины.
  4. Курьеры.
  5. И так далее. 



Если вы собираетесь автоматизировать балансовый учёт, у вас наверняка будут другие объекты. Но предположу, что наше решение подойдёт почти для любого. Если же объектов учёта множество, его можно применять для каждого из них параллельно.

Шаг 2. Определение состояний объекта балансового учёта


Перечислим состояния объекта учёта, которые могут нас интересовать. В нашем случае это: 
  1. Стоимость товара внешнего интернет-магазина появляется, когда покупатель его заказывает (я знаю о практике некоторых интернет-магазинов не фиксировать стоимость товара в момент его заказа покупателем, но мы такое пока не поддерживаем).
  2. Стоимость товара меняет своего владельца на компанию, которая доставит товар покупателю.
  3. Стоимость товара меняет своего владельца на физическое или юридическое лицо, которому его доставила транспортная компания.
  4. Стоимость товара меняет своего владельца на интернет-магазин отправителя ввиду возврата товара или компенсации его утраты.

Шаг 3. События, которые являются причинами изменений состояния


Рассмотрим события, которые изменяют состояние объекта учёта.

image

Поскольку нас не интересуют события и состояния товара (и его стоимости) во внешнем интернет-магазине, их мы обрабатывать не будем. 

В вашем проекте могут быть другие события и состояния. Просто подставьте их в эту модель.

Как наложить свою задачу на мою модель
Например, вам нужно автоматизировать учёт загруженности отдела, который вручную проверяет договоры, то есть сделать штуку, которая будет отвечать на вопросы вроде «Сколько договоров сейчас находится на проверке в отделе?», «Сколько их было вчера?» и так далее. Целью такой разработки может быть получение возможности видеть, когда отдел перегружен, а потом, глядишь, и прогнозировать это, чтобы предотвращать.

Берёте мою модель и накладываете на неё свои данные:

image



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

После того как мы поймали событие, которое предполагает новое состояние объекта учёта, нам необходимо определить текущее состояние последнего. Таким образом, мы будем иметь:

  1. Текущее состояние объекта учёта, потому что мы его только что определили.
  2. Предполагаемое состояние объекта учёта, потому что оно соответствует событию, которое мы поймали.

С этими данными нужно обратиться к таблице сочетаний текущих и предполагаемых состояний объекта.

Шаг 4. Сочетания текущих и предполагаемых состояний объекта учёта


Таблица сочетаний текущих и предполагаемых состояний объекта учёта нужна для того, чтобы:

  1. Не списывать с баланса те объекты учёта, которые на него не зачислялись.
  2. Выявлять ошибочные действия пользователей в системе и на основании этого:
    • ничего не делать с балансовым учётом
    • откатить старые неверные балансовые движения, заменяя их на новые верные.


Для создания такой таблицы необходимо определить все возможные парные сочетания состояний объекта балансового учёта. Для этого я рекомендую воспользоваться калькулятором комбинаций. Его интерфейс прост и интуитивно понятен. Я уверен, что вы легко разберётесь в нём самостоятельно, но всё же покажу, какие данные нужно вводить, чтобы получить все возможные парные комбинации состояний объекта учёта, которые разбираются в нашем примере.



В результате мы получим таблицу со всеми возможными комбинациями текущих состояний объекта учёта и его предполагаемых состояний.

image

Как вы можете заметить, мы разделили полученные комбинации на следующие типы:
  1. Нереальные (выделены серым цветом и зачёркнуты).
  2. Желаемые (выделены зелёным цветом).
  3. Ошибочно тупиковые (это проблемы, которые мы не можем решить откатом предыдущей операции с товаром, они выделены жёлтым цветом).
  4. Ошибочно нетупиковые (проблемы, которые мы можем решить откатом, они выделены голубым цветом).

Но это ещё не окончательный вариант нашей таблицы. Для каждого сочетания ещё нужно:
  1. Составить список действий, которые должен совершить сервис.
  2. Определить направление движения сторнирующих движений (если они нужны).
  3. Написать текст ошибки (если вы собираетесь вести лог).

Короче, у меня получилось вот это:
image

Повторный краткий обзор того, как будет работать предполагаемый сервис


Давайте ещё раз повторим, как будет работать наш сервис:

image

Внедрение без переноса остатков


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

Объекты учёта, которые мы не перенесли, в довольно скором времени физически примут конечный учётный статус и снимутся с забаланса.

Через какое-то время наш сервис будет показывать точное количество объектов учёта на балансе (так как новые стабильно будут фиксироваться). И эти показания будут точнее, чем даже если бы мы мигрировали остатки, потому что большое количество данных почти всегда содержит большое количество ошибок.

Комментарии (15)


  1. Fragster
    26.10.2021 18:58
    +1

    Не увидел в статье отказа от переноса остатков.


    1. Evsikoff Автор
      26.10.2021 19:14
      +1

      А где вы увидили их перенос?

      В конце объясняется почему при данном подходе к разработке их можно не переносить.


      1. Fragster
        27.10.2021 11:41

        Что делаете с тем, что уже на балансе в момент начала использования "новой" системы?


        1. Nikita22007
          27.10.2021 18:12
          +1

          Если я всё правильно понял, они показывают ошибку при отсутствии товара. И если он физически есть,а по базе его нет, то его просто дописывают в новую базу. Похоже на переучет, растянутый на месяцы(годы)


          1. Fragster
            27.10.2021 18:34

            Что мешает сделать "инвентаризацию" и "перенос остатков" в таком случае?

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


        1. Evsikoff Автор
          27.10.2021 22:42
          +1

          Как очень подробно описано в статье, ничего не делаем.

          С момента начала использования "новой" системы, мы относительно быстро доставим товар получателю или вернем продавцу или констатируем что он потерялся.

          Таким образом сравнительно быстро товара у нас не станет физически, и его отсутствие у нас физически будет равно отсутствию его на балансе.


  1. itmind
    27.10.2021 08:00
    +2

    Отчет об текущих остатках на балансе Ozon из такой системы не получить. Да и вообще как узнать хотя бы общую сумму на балансе Ozon на конкретную дату без переноса остатков? Только смотреть в старой учетной системе и вручную складывать с новой?

    Не понятно, как определяется "Текущее состояние". Например предполагаемое состояние "Выдача клиенту", текущее состояние неизвестно, т.к. остатки не переносились. Стоимость товара может принадлежать как внешнему интернет-магазину так и Ozon.


    1. Evsikoff Автор
      27.10.2021 10:05
      +1

      Учитывая систему "Подумай дважды", можно узнать текущее состояние на балансе Ozon, без суммирования со старой системой. Нужно лишь подождать пока новый сервис немного поработает.

      По "Текущим состоянием" понимается количество товаров в Ozon на данный момент.


      1. evkochurov
        27.10.2021 12:02
        +2

        Нужно лишь подождать пока новый сервис немного поработает

        Правильно ли я понимаю, что это "немного" находится в прямой зависимости от времени жизни отдельно взятого остатка на балансе сервиса? Если так, то такой подход не применим для учета долгоживущих остатков. Неликвиды на складе магазина - не редкость, даже у продуктового консервы год-другой могут валяться. Это стоило бы указать в статье.


        1. Evsikoff Автор
          27.10.2021 22:39
          +1

          Правильно, но в случае, если у вашего объекта учета очень длинный жизненный цикл, можно придумать новое событие, которое может посавить ваш объект на баланс.

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


          1. Fragster
            28.10.2021 12:42
            +1

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


            1. Evsikoff Автор
              28.10.2021 14:44
              +1

              Предположим цена у консервы из примера и правда поменялась из-за инвентаризации.

              Произошло событие изменения цены, товары очутились в моей балансовой системе.

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

              Таким образом вам не нужно переносить остатки при внедрении такового сервиса.


              1. Fragster
                28.10.2021 15:03

                В том то и дело, что цена от инвентарицазии не поменялась. Но вот провести её - как бы нужно. но при этом - как узнать, что списывается (недостача), что приходуется (излишки) (а это влияет на бух результат, прибыль и прочее), если остатков от предыдущей системы нет?


              1. evkochurov
                28.10.2021 15:29
                +1

                Инвентаризация - это стандартный способ актуализировать остатки при внедрении новой системы, если мы не очень доверяем остаткам в старой. По сути, как сказал @Fragster, инвентаризация по затрачиваемым усилиям фактически равна переносу остатков.


  1. VasilyErmak
    29.10.2021 21:05
    +4

    Я в 1С на переносе остатков не одну собаку съел.
    Описанное Вами решение, отлично подходит для вашей задачи. Автоматизируемый процесс динамичен и в нём есть акторы генерирующие события. Перенос остатков у Вас есть, только он по требованию. Через неделю после запуска в ней начнут крутиться новые объекты учёта, а вот код переноса остатков останется.
    Мало того это применимо только к отдельным разделам учёта. Если необходимо перенести данные учётной системы всего предприятия без этапа переноса остатков не обойтись.
    Описанный выше случай с неликвидами в принципе решается инвентаризацией, Хотя если кладовщики узнают, что от них зависит появление товара на складе в новой системе, они этим воспользуются. Есть ещё основные средства которые амортизируются годами.
    К взаиморасчётам такой подход вовсе не примени. Получается пока должник сам не заплатит, вы не узнаете, что он был вам должен.
    А для расчёта зарплаты (отпускных, больничных и т.д.) нужны данные за 2 года. По этой причине к примеру при переходе 1С ЗиУП 2.5 на 3.1 переносится почти всё.