15 июн. 2016 г.

Чем хороши радикальные изменения законодательства -- в офис наведываются давние и очень давние пользователи наших программ. На днях заглянул Евгений Александрович Кучерявенко, один из первых покупателей, пользователь Анжелики, а затем и Гедымина с 1996 года!
Стараниями Евгения Александровича все хозяйства Ивановского района систему полной автоматизации учета с/х предприятия, включая животноводство и растениеводство.

8 июн. 2016 г.

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

  1. Как можно чаще выполнять git pull для загрузки себе изменений, произведенных другими разработчиками.
  2. При работе в отдельной ветви, из которой позже изменения будут переданы в основную, регулярно выполнять git fetch, затем git merge master.
  3. При возникновении конфликтов принимаем чужие изменения (Accept theirs -- в Tortoise Git) и вносим в них свои правки.
  4. Если автоматический merge не прошел из-за конфликта, то, после его разрешения, на комите, будет показан список измененных файлов как нами, так и другими разработчиками. Если мы работаем с Tortoise Git ни в коем случае нельзя снимать галки с файлов (даже если лично мы их не меняли), иначе изменения других людей пропадут и в репозиторий запишется файл в том состоянии, в котором он был у нас на диске до merge.

7 июн. 2016 г.

Ветви Cash & Check

Две ветви добавлены в репозиторий gedemin-apps для текущей разработки проектов POSitive:Cash и POSitive:Check. Здесь будут находиться новейшие версии. После тестирования изменения будут скидываться в ветку master из которой у нас сейчас еженочно формируются дистрибутивы.

Посмотреть какие ветки присутствуют локально на компьютере:

git branch

Добавить и переключиться на локальную ветку cash, связать ее с веткой в центральном репозитории:

git checkout -b cash --track origin/cash

Переключиться на ветку master:

git checkout master

Переключиться на ветку cash:

git checkout cash

Получить изменения с сервера в локальную базу данных. Файлы изменены не будут!

git fetch

Применить полученные изменения из удаленной ветки к локальным файлам в текущей ветке:

git merge

Два предыдущих действия одной командой:

git pull

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

git branch --set-upstream-to=origin/cash cash

После чего делаем pull.

Мы в ветке cash. Принимаем изменения из ветки master:

git merge master

ВАЖНО! При возникновении конфликтов принять изменения другой стороны и внести свои коррективы.

Изменили некоторые файлы. Сохраняем изменения в локальном хранилище:

git commit -a -m "some changes were made"

Отправляем изменения в центральный репозиторий:

git push

После того, как изменения в ветке cash протестированы, отправляем их в ветку master:

git checkout master
git merge cash
git commit -a -m "New cash version"
git push

14 мая 2016 г.

True UTF-8 encoding

XML файл в кодировке UTF-8 можно сформировать используя только ASCII и кодируя все национальные символы через &#x0 последовательности. Дешево и сердито. Например, такой подход предложен здесь.

Но, если мы хотим, чтобы XML файл красиво отображался в текстовом редакторе, системе контроля версий и т.п., надо сохранить его в настоящей кодировке UTF-8. Для этого мы добавили в платформу функции WIN1251ToUTF8 и UTF8ToWIN1251, которые принимают на вход два потока и перекодируют данные между ними. В дополнение к ним идут функции ReplaceXMLTags и ExpandXMLTags.

Пример использования см. здесь.

12 мая 2016 г.

Деноминация 2016. Технические аспекты

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

В отличие от конкурирующих решений наш подход сохраняет всю историю, весь документооборот предприятия до 1 июля.

Первая проблема, которая возникает, это возможная потеря точности. Платформа использует тип Currency, который позволяет обрабатывать денежные величины с точностью до 4-го знака после запятой. После деноминации 0.0001 в новых деньгах будет соответствовать 1-му старому рублю. Все что меньше рубля потеряется (округлится по правилам математического округления). Хотя 1 неденоминированный рубль очень маленькая величина (коробок спичек сейчас стоит 400 рублей) можно представить базу данных, где мелкий товар учитывается поштучно и цена единицы содержит копейки -- пуговицы, швейные иголки, болты, гайки и т.п. Мы рекомендуем в таком случае провести на 30 июня 2016 г. переоценку товара, округлив учетные цены до целых рублей.

Вторая проблема -- округление до целого числа в скрипт-функциях и отчетных формах через вызов функций Round, Int, Fix, CLng, CInt или обращение к полю через свойство AsInteger. Мы внесем исправления в типовые прикладные решения, но решить задачу в общем случае, анализируя программный код и автоматически заменяя вызовы функций нам не представляется возможным. Единственный путь -- заранее создать копию рабочей базы данных, деноминировать ее и затем тестировать на ней все режимы работы и устранять выявленные проблемы.

Третья проблема заключается в том, что бухгалтерия предприятия до 20 июля производит закрытие июня и второго квартала. При этом вносятся и/или корректируются документы прошлых периодов, закрываются транзитные счета, выполняется трансформация баланса, рассчитываются промежуточные показатели и налоги. Согласно методическим разъяснениям налоговые декларации за июнь подаются в неденоминированных рублях.

Мы видим такое решение данной проблемы: предприятие продолжает после 1 июля вести учет в неденоминированных рублях при этом, непосредственно в печатных формах товарных накладных, денежные величины делятся на 10 000; суммы, поступающие из системы банк-клиент умножаются на 10 000, соответственно, суммы, уходящие в банк-клиент, делятся на 10 000; входящие накладные умножаются на 10 000 и т.д. После того, как налоговые декларации будут сданы можно будет проводить деноминацию базы данных и переходить на учет в деноминированных рублях.

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

Деноминация базы данных производится с помощью отдельной утилиты dnmn.exe, которая предоставляется клиентам на абонентском обслуживании. Мы обработали несколько десятков баз данных и сформировали список полей для деноминации. Если на вашем предприятии используются частные решения, то перед запуском процесса следует добавить нужные поля в список деноминируемых.

9 мая 2016 г.

Прямой доступ к DBF файлам

Устав бороться с "тормозами" и ошибками стандартных ODBC драйверов для доступа к DBF файлам мы включили компонент TDBF в исходный код Гедымина. Пример использования можно посмотреть тут.

Точных замеров и сравнений не проводили, но "на глаз" переход на встроенный компонент ускорил у одного из наших клиентов обработку больших массивов данных раза в три.

26 апр. 2016 г.

Сохранить имя пользователя и пароль в Git

При работе с приватными репозиториями github, при выполнении операция pull/push git будет постоянно запрашивать имя пользователя и пароль. Заставить систему запомнить введенные один раз значения можно следующим образом:

1. Переходим в окно командной строки, в папку с исходниками

2. Выполняем:

git config credential.helper store

git pull
Username: type your username
Password: type your password

При последующих операциях имя пользователя и пароль вводить уже не надо.

24 апр. 2016 г.

Задание на неделю #1. Firebird 3

Firebird Logo На этой неделе произошло событие, которого мы с нетерпением ждали шесть долгих лет -- вышла третья версия сервера Firebird. В двух словах предыстория такова: Interbase, от которого в 2000-м году отпочковался Firebird, был задуман в те времена, когда многопроцессорные системы с десятками гигабайт оперативной памяти встречались разве что в фантастических рассказах. Когда же научно-технический прогресс догнал и перегнал самые смелые фантазии, именно внутренняя архитектура сервера стала основным тормозом. По сути, в многопользовательском сценарии у системного администратора было два выбора: или использовать оперативную память для кэширования данных, но тогда запросы из всех подключений будут выполняться только на одном процессоре/ядре (архитектура SuperServer), или задействовать все процессоры/ядра, но тогда не будет общего кэша и скорость будет зависеть от производительности дисковой подсистемы (архитектура Classic). Тот случай, когда хрен редьки не слаще.

Изменения в архитектуре SuperServer движка Firebird 3 теперь позволяют последнему выполнять запросы сетевых клиентов параллельно на разных процессорах/ядрах, при доступе к общему кэшу, что теоретически делает SuperServer выбором по умолчанию при развертывании системы на предприятии. Как оно получится на практике -- зависит от надежности тройки. В начале 2000-х мы перевели всех клиентов с SuperServer на Classic по двум причинам: падение процесса SuperServer означает обрыв всех коннектов и потерю данных во всех открытых транзакциях у сетевых пользователей; выполнение тяжелого запроса одним из клиентов практически парализует работу всех остальных.

У одного нашего клиента база данных имеет размер 50 Гб, количество одновременных подключений 50-60, на сервере 128 Гб оперативной памяти и 20 физических ядер. План: использовать SuperServer, установить кэш размером 50 Гб и выделить под буфер сортировки 32 Гб. Учитывая, что дисковая подсистема построена на RAID контроллере с 2 Гб энергонезависимой памяти, теоретически, это позволит вообще исключить прямое обращение к дискам. Т.е. производительность сервера базы данных будет определяться процессором, памятью и скоростью обмена с RAID контроллером.

Напомним, что именно производительность дисковой подсистемы всегда возглавляла список факторов, влияющих на общую производительность СУБД. Мы ожидаем ускорения выполнения запросов минимум на порядок. О достигнутых результатах напишем в этом блоге.

Практически все нововведения из третьей версии найдут применение в Гедымине:

  • Передача НУЛЛ значений по сети битовой маской. Сейчас каждое поле с НУЛЛ значением занимает при передаче 4 байта + длина поля. Например, в таблице с бухгалтерскими проводками у нас десятки полей-аналитических признаков, которые могут быть не заполнены. Экономия может достигать нескольких сотен байт на каждую запись.
  • Возможность увеличить TCP буфер до 32 Кб и применить компрессию данных при передаче. В интернете есть свидетельства о том, что скорость возрастает десятикратно при подключении по сетям с большой латентностью. Например, к серверу в удаленном офисе по VPN каналу.
  • Шифрование файла базы данных. Теперь злоумышленник не сможет получить доступ к конфиденциальной информации просто переписав файл на сервер с чистой установкой Firebird и известным паролем к учетной записи SYSDBA.
  • Window функции в SQL запросах позволяют объединять вместе данные и агрегатные значения по заданным группам этих данных. До Firebird 3 такую задачу можно было решить либо подзапросами (крайне медленно, так как подзапрос будет выполняться для каждой записи), либо выполнением в цикле отдельных запросов для каждой группы и объединением их результатов в единый набор данных с помощью EXECUTE BLOCK или STORED PROCEDURE, либо алгоритмической обработкой на клиенте (например, внутри Fast Report при построении отчета).
  • “Linger” Database Closure for Superserver -- период времени, в течение которого сервер сохраняет в памяти все ресурсы, после закрытия последнего подключения к базе данных -- позволит ускорить загрузку пакета пространств имен, так как переподключение к базе данных выполняется после каждого ПИ, содержащего метаданные.
  • DDL триггеры позволят отказаться от выполнения процедуры at_p_sync при старте системы для синхронизации информации о структуре базы данных с содержимым AT_ таблиц.
  • Другие изменения и улучшения, на которых мы подробнее остановимся в следующих выпусках.
Исходный код Гедымина уже поправлен для совместимости с последней версией. После внутреннего тестирования мы опубликуем подробную инструкцию апгрейда для наших клиентов.

27 янв. 2016 г.

Поиск кольцевых ссылок в реляционной базе данных рекурсивным SQL запросом

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

Между тем, существует способ однозначного выявления циклов без "наматывания" кругов по графу. Рассмотрим граф, показанный на рисунке ниже:

В базе данных он хранится в виде матрицы смежностей в таблице t:

  CREATE TABLE t (
    a INTEGER,
    b INTEGER
  )

Соответственно, исходные данные:

  INSERT INTO t VALUES (1, 2);
  INSERT INTO t VALUES (2, 3);
  INSERT INTO t VALUES (3, 4);
  INSERT INTO t VALUES (3, 5);
  INSERT INTO t VALUES (3, 6);
  INSERT INTO t VALUES (2, 7);
  INSERT INTO t VALUES (6, 1);

Для поиска кольцевых ссылок напишем запрос:

with recursive tree as
  (
    select distinct
      a as initial, a, b, -1 as prev
    from
      t

    union all
    
    select
      iif(tr.initial <> tt.a, tr.initial, -1) as initial,
      tt.a,
      tt.b,
      tr.a as prev
    from
      t tt JOIN tree tr ON
        tr.b = tt.a and tr.initial > 0

  )
select
  *
from
  tree
where
  initial = -1

Запрос покажет все первые сегменты A - B (и предыдущие, т.е. сегменты непосредственно приведшие к циклу, PREV - A), всех возможных циклов в заданных данных. В нашем случае:

INITIAL A B PREV
-1 1 2 6
-1 2 3 1
-1 2 7 1
-1 3 4 2
-1 3 5 2
-1 3 6 2
-1 6 1 3

При необходимости проверить не входит ли в кольцо конкретный узел, следует прописать его ИД в первом запросе рекурсивного CTE:

with recursive tree as
  (
    select distinct
      a as initial, a, b, -1 as prev
    from
      t
    where 
      a = _some_id_

    union all
    
    select
      iif(tr.initial <> tt.a, tr.initial, -1) as initial,
      tt.a,
      tt.b,
      tr.a as prev
    from
      t tt JOIN tree tr ON
        tr.b = tt.a and tr.initial > 0

  )
select
  *
from
  tree
where
  initial = -1

8 янв. 2016 г.

Конвертация параметров складских документов

Параметры складских документов изначально хранились в двоичном поле OPTIONS, в таблице GD_DOCUMENTTYPE.

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

Новая версия gedemin.exe хранит параметры в реляционном виде в таблице GD_DOCUMENTTYPE_OPTION. Один параметр -- одна запись, связанная с GD_DOCUMENTTYPE.

Для просмотра параметров добавлен грид в окно со списком типов документов.

Правильный порядок конвертации такой:

  1. К эталонной БД, на которой идет разработка, однократно подключаемся из командной строки с параметром /cdo:

    gedemin.exe /cdo

  2. Произойдет автоматическая конвертация опций после чего следует сохранить измененные ПИ.
  3. Грузим обновленные ПИ на базы, где используются складские документы.
Мы прекрасно понимаем, что есть базы предприятий, где нельзя загрузить типовые ПИ без риска утерять изменения, сделанные "по месту". Если на таких базах нет необходимости в настройке параметров складских документов, то можно продолжать работать новым экзешником со старыми (двоичными) параметрами. Если настройка типов необходима, то следует сконвертировать параметры командой gedemin.exe /cdo.

При этом, следует учитывать, что одноименные с параметрами на эталонной базе параметры, при такой конвертации, получат другие РУИДы. В этом нет ничего страшного, если мы не собираемся распространять ПИ с базы предприятия на другие базы. Если же перенос потребуется -- следует загрузить параметры с эталонной БД для того, чтобы переписать РУИДы на типовые.

Последнее верно и для случая, когда разработка ведется на нескольких базах данных. Например, есть база ETALON, где разрабатывается типовой склад, и база ETALON_RESTORAN, где разрабатывается бэк-офис общепита. В этом случае последовательно выполняем:

  1. gedemin.exe /cdo на базе ETALON и сохраняем с нее ПИ.
  2. gedemin.exe /cdo на базе ETALON_RESTORAN и ЗАГРУЖАЕМ на неё общие ПИ с базы ETALON.
  3. Только после загрузки сохраняем ПИ с базы ETALON_RESTORAN.
Конвертация не изменяет двоичных опций в поле OPTIONS. Поэтому в непредвиденных случаях всегда можно вернуться к исходному состоянию просто удалив все записи из таблицы GD_DOCUMENTTYPE_OPTION.

13 дек. 2015 г.

Выполнение скрипт-функции по расписанию

Как организовать выполнение скрипт-функции по расписанию?

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

gedemin.exe /run 147000555_256456378

Выполняет скрипт-функцию с заданным РУИДом после загрузки платформы, инициализации подсистемы автозадач и выполнения всех автозадач, назначенных на старт системы (если таковые имеются).

gedemin.exe /run 147000555_256456378 /exit

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

gedemin.exe /exit

Завершение выполнения gedemin.exe сразу после загрузки, подключения к базе данных, инициализации подсистемы автозадач и выполнения всех автозадач, назначенных на старт системы.

Указанные параметры могут использоваться вместе с параметрами подключения к базе данных.

14 нояб. 2015 г.

Перезапуск платформы по расписанию

В идеальном мире, где идеальные люди запускают идеальные программы, ошибкам просто нет места. Но в реальной жизни shit, к сожалению, happens. Когда сроки истекли еще вчера, а технические требования заказчик меняет каждый раз, когда видит очередную версию, приходится действовать по методике "run and fire". И, если ошибки на экране в худшем случае заставят пользователя произнести несколько нецензурных слов, то неявные утечки памяти и редкие баги в серверном коде, способны свести с ума даже закаленного аса разработки и отладки.

Здесь на помощь приходит "quick and dirty" решение в виде автоматического перезапуска платформы по расписанию:

На приведенном выше скриншоте показаны настройки автозадачи для перезапуска платформы каждые 10 минут.

Стоит сделать замечание, что перезапуск не произойдет, если на экране открыто диалоговое окно в модальном режиме.