Синхронизация:Общие вопросы по синхронизации — различия между версиями

Материал из wiki.standart-n.ru
Перейти к: навигация, поиск
(Порядок действий)
(Пустые наименования)
(не показаны 34 промежуточные версии 3 участников)
Строка 371: Строка 371:
 
==Пустые наименования==
 
==Пустые наименования==
 
* Сначала нужно определиться чего именно не хватает. Партия цепляет варес, варес цепляет валс. Обычно не хватает или вареса или валса. Бывает, что все есть, но в валсе пустое значение (в этом случае нужно в менеджере два раза нажать на наименование и вставить оригинальное значение товара).  
 
* Сначала нужно определиться чего именно не хватает. Партия цепляет варес, варес цепляет валс. Обычно не хватает или вареса или валса. Бывает, что все есть, но в валсе пустое значение (в этом случае нужно в менеджере два раза нажать на наименование и вставить оригинальное значение товара).  
выполняем запрос
+
выполняем запрос, берем ware_id и выполняем следующий, смотрим есть ли значение наименование sname
  select * from warebase_g w where w.ware_id=:ware_id  
+
  '''select name_id, sname from wares where id=:ware_id'''
 
берем ware_id и выполняем следующий, смотрим есть ли значение наименование sname
 
берем ware_id и выполняем следующий, смотрим есть ли значение наименование sname
  select * from wares where wares.id=:ware_id
+
  '''select svalue from vals v where v.id=:name_id'''
берем ware_id и выполняем следующий, смотрим есть ли значение наименование sname
+
 
  select * from vals v where v.id=:name_id
+
Если '''в wares поле sname = null''', а '''svalue в vals имеет значение''' выполняем обновление:
Если все есть выполняем:
+
  update
  select * from
+
wares w
warebase_g w
+
set w.sname=(select v.svalue from vals v where v.id=w.name_id)
where w.sname is null
+
  where ((sname='') or (sname is null)) and (select v.svalue from vals v where v.id=w.name_id) is not null
/*Если значения наименования в wares и vals есть, но в warebase_g наименование пустое, выполнить update*/ select сохранен для предварительного просмотра значений
+
 
 +
после выполнения update wares обновляем сводные остатки:
 
  update
 
  update
 
  warebase_g w
 
  warebase_g w
 
  set w.part_id=part_id
 
  set w.part_id=part_id
  where w.sname is null
+
  where w.sname is null  
  
/*Если пустые наименования в wares а в vals значение есть, выполнить update*/ select сохранен для предварительного просмотра значений
+
/*Если не помогает делаем напрямую исправление наименования в сводных остатках по name_id*/
--select * from
+
 
  update
 
  update
  wares w
+
  warebase_g w
 
  set w.sname=(select v.svalue from vals v where v.id=w.name_id)
 
  set w.sname=(select v.svalue from vals v where v.id=w.name_id)
 
  where w.sname is null
 
  where w.sname is null
 
  
  
Строка 465: Строка 464:
  
 
В строке '''str='file:D:\sql\файл для отправки';''' указываем путь до файла с файлом SQL
 
В строке '''str='file:D:\sql\файл для отправки';''' указываем путь до файла с файлом SQL
+
 
 +
Теги: отправить, выполнить, большой SQL, SQL
 +
 
 
==Отправить сетку отображения (распространить вид) таблицы по сети==  
 
==Отправить сетку отображения (распространить вид) таблицы по сети==  
 
Для того что бы отправить сетку всем пользователям по сети или нужным профилям в пределах одной сети нужно выполнить следующее:
 
Для того что бы отправить сетку всем пользователям по сети или нужным профилям в пределах одной сети нужно выполнить следующее:
Строка 515: Строка 516:
  
 
Все проверяем через некоторое время исправления должны быть у всех пользователей.
 
Все проверяем через некоторое время исправления должны быть у всех пользователей.
 +
 +
==Как отправить поле типа Blob по синхронизации на примере таблицы Reports==
 +
 +
'''Внимание!!! Для сетей, где таблицы ходят по двухсторонней синхронизации смотри раздел выше "Добавить запись к частично-двусторонней синхронизации"'''
 +
 +
Для отправки добавления отчета в таблицу Reports используется штатный механизм отправки тасков процедура '''"UTPR_MASTERDETAIL"''' отправляем всем нужным профилям добавление отчета:
 +
 +
INSERT INTO REPORTS (PARENT_ID, STATUS, REPORTTYPE, SORTING, CAPTION, PARAMS, WDICT_ID)
 +
            VALUES (0, 0, 1, 400, ''Новый отчет'', ''[freereport]
 +
tmplpath=
 +
'', NULL);
 +
 +
Далее сохраняем поле DATA таблицы REPORTS с нужным нам отчетом в txt. Далее открываем таблицу любую таблицу которая ходит по двухсторонней синхронизации, например таблица G$PROFILES. Находим у таблицы поля типа Blob которые не используются, например, TMP_BLOB1 ... TMP_BLOB_N. Если нет можно создать и распространить по сети.
 +
Далее в данное поле сохраняем выгруженный нами отчет и Коммитим изменения - ''''' ЖДЕМ НЕСКОЛЬКО ЧАСОВ!!! ''''' что бы данные дошли до точек. После этого создаем новый таск используя процедуру '''UTPR_MASTERDETAIL''', где обновляем таблицу REPORTS по полю дата заменяю его значением из G$PROFILES:
 +
update reports r set r.data = (select TMP_BLOB1 from g$profiles where id=1 and TMP_BLOB1 is not null) where r.caption =''Новый отчет'';
 +
 +
Теги: отправить, поле, Blob, синхронизация, UTPR_MASTERDETAIL
 +
 +
==Как добавить на панель АРМ Кассира кнопку с нужным действием (ТМС, Отчет)==
 +
'''Важно!!! На момент проведения действия кассиры должны быть все закрыты!!! В противном случае, после закрытия АРМ Кассир все изменения будут заполнены текущими настройками '''
 +
Для рапространения требуется процедура '''"UTPR_MASTERDETAIL_KASSIR"''', если ее нет нужно добавить скрипт ниже:
 +
SET TERM ^ ;
 +
create or alter procedure UTPR_MASTERDETAIL_KASSIR
 +
as
 +
declare variable PROFILE_ID type of DM_ID;
 +
declare variable S DM_TEXT1024;
 +
declare variable STR DM_BLOBTEXT;
 +
begin
 +
  for select md.id from g$profiles md
 +
where md.dbsecurekey is not null and relationtype=1 and status=0
 +
into :profile_id do
 +
  begin
 +
    --str='';
 +
  insert into g$tasks (PROFILE_ID,TASK_TYPE,CAPTION,data) values
 +
  (:profile_id,4,'Кнопка в кассире', 'file:E:\Standart-N\tasks\kassabuttons.txt');
 +
  end
 +
end^
 +
SET TERM ; ^
 +
GRANT SELECT ON G$PROFILES TO PROCEDURE UTPR_MASTERDETAIL_KASSIR;
 +
GRANT INSERT ON G$TASKS TO PROCEDURE UTPR_MASTERDETAIL_KASSIR;
 +
1. На любой точке или копии базы сети выводим нужную кнопку на панель под определенным пользователем Кассы.
 +
 +
2. Заходим в таблицу USERS, находим пользователя под которым вывели кнопку и переходим в поле Data
 +
 +
3. В поле Data должна быть выбрана вкладка AsText - ANSI. В Верху на панели нажимает кнопку с обозначением Дискетки - сохранить.
 +
 +
4. В открывшемся окне выбираем нужную папку для сохранения, тип файла: Все файлы (*.*), набираем понятное имя файла и в конце обязательно добавляем ".ZIP", запоминаем путь, нажимаем кнопку Cохранить.
 +
 +
5. Переходим в каталог куда мы сохранили файл, находим его и распаковываем в папку и находим файл '''zkassa#user.ini'''. Открываем его на редактирование.
 +
 +
6. Выполняем поиск по файлу раздела [UserToolBar] - это радел где хранятся кнопки выведенные на панель - находим по названию нужную кнопку нам в добавляем в начале строки '''zkassa#user.ini>UserToolBar>>'''
 +
 +
7. Копируем строку в новый файл форма txt, сохраняем его на диске сервера синхронизации. В процедуре UTPR_MASTERDETAIL_KASSIR указываем путь к данному файлу
 +
 +
Gif на тему:
 +
 +
[[Файл:Add_bitton1.gif]]
 +
 +
Отправляем исправления по синхронизации ночью!!! когда все АРМ Кассир закрыты.
 +
 +
Для этого подготавливаем процедуру '''UTPR_MASTERDETAIL_KASSIR''' и планируем выполнение задания через ''Планировщик заданий Windows'' (команда taskschd.msc) ночью на запуск ТМС SpacePro которая будет вызывать нашу процедуру '''UTPR_MASTERDETAIL_KASSIR'''. Пример: ''D:\Standart-N\Standart-N\spacepro.exe +TMS=№_ТМС''
 +
 +
Теги: отправить, кнопку, Blob, синхронизация, USERS, UTPR_MASTERDETAIL_KASSIR, панель, АРМ Кассир
 +
 +
== Как обновить удаленно Distribute Client (dtclient) ==
 +
Для обновления заходим на сервер синхронизации где установлен установлен каталог синхронизации sinhro.
 +
 +
Внимание!!! Порядок действий не меняем - очень важен для конечного результата
 +
 +
1. Заходим в каталог shellupd, откидываем на всякий случай копию всех файлов в каталог back и копируем туда последнюю тестированную версию Distribute Client.
 +
 +
2. Удаляем файл data.zip и создаем на основе exe Distribute Client с таким же названием в этом же каталоге.
 +
 +
3. Смотрим версию Distribute Client.exe для этого открываем "Свойство" exe, вкладка "Подробно", Версия файла - запоминаем. Нам нужна будет версия между первой точкой.
 +
Например в окне Версия файла мы видим строке 2.2045.0.0, нам будет нужна только 2.2045
 +
 +
4. Открываем файл exe.ver на редактирование и записываем новую версию файла, на текущем примере это 2.2045
 +
 +
Кратко об этом gif:
 +
 +
[[Файл:Update_dtclient.gif]]
 +
 +
 +
Теги: обновить, Distribute Client, dtclient, синхронизация, DistributeClient
  
 
=Частые ошибки=
 
=Частые ошибки=
Строка 553: Строка 638:
 
удалить запись можно запросом :
 
удалить запись можно запросом :
  
   delete from g$distribute g where g.soper <> 2 and g.tablename='имя таблицы' and
+
   delete from g$distribute g where g.soper <> 2 and g.tablename=Upper('имя таблицы') and
 
   not exists(select id from имя таблицы d where d.d$uuid = g.uuid)
 
   not exists(select id from имя таблицы d where d.d$uuid = g.uuid)
 
Если просит удалить более 1000 записей, ток внедрению
 
Если просит удалить более 1000 записей, ток внедрению
Строка 565: Строка 650:
 
* Выключить роутер '''на 15 минут'''
 
* Выключить роутер '''на 15 минут'''
 
* Почистить таблицу g$distribute.
 
* Почистить таблицу g$distribute.
 +
* в 80 % случаев не верный тип поля (пример DM_ID и DM_ID_NULL) на серверной базе предпочительно разрешать NULL значения
  
 
==Incomplete Zip File==
 
==Incomplete Zip File==
Строка 582: Строка 668:
 
update g$tasks g set g.senddt = null where g.senddt is not null and g.enddt is null and g.checkprevtask is not null
 
update g$tasks g set g.senddt = null where g.senddt is not null and g.enddt is null and g.checkprevtask is not null
 
</pre>
 
</pre>
 +
 +
==conversion error from string ...At trigger 'GROUP_DETAIL_AD0_MMBSH' line: 9, col: 34==
 +
 +
 +
Для решение данной ошибки нужно подправить два тригера
 +
<pre>/* Trigger: GROUP_DETAIL_AD0_MMBSH */
 +
CREATE OR ALTER TRIGGER GROUP_DETAIL_AD0_MMBSH FOR GROUP_DETAIL
 +
ACTIVE AFTER DELETE POSITION 0
 +
AS
 +
begin
 +
  if (old.grouptable = 'PARTS.NAME_ID' ) then
 +
  update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
 +
  where name_id = old.grouptable_id;
 +
 +
  if ((old.grouptable = 'PARTS' ) and (old.id>0))then
 +
    update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
 +
      where part_id = cast(old.grouptable_id as bigint);
 +
end </pre>
 +
 +
<pre>/* Trigger: GROUP_DETAIL_BU0_MMBSH */
 +
CREATE OR ALTER TRIGGER GROUP_DETAIL_BU0_MMBSH FOR GROUP_DETAIL
 +
ACTIVE AFTER INSERT OR UPDATE POSITION 0
 +
AS
 +
begin
 +
  if (new.grouptable = 'PARTS.NAME_ID' ) then
 +
  update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
 +
  where name_id = new.grouptable_id;
 +
 +
  if ((new.grouptable = 'PARTS' ) and (new.id>0))then
 +
  update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
 +
  where part_id = new.grouptable_id;
 +
end </pre>

Версия 09:51, 7 ноября 2019

Содержание

Порядок действий, если не работает синхронизация

На стороне клиента

  • Обновить Distribute Client.
  • Убрать настройку перезапуск раз в n циклов.
  • Убрать галку хранить историю в g$distribute.
  • Проверить таймауты, ограничения пакетов.
  • Удалить все лишнее из папки с dtclient'ом (*.zip и *.rcvd файлы).
  • Выключить брэндмауэр и антивирус.
  • Запустить exe с ключом +detail
  • Запустить программу от имени администратора
  • Проверить, что дата/время на компьютере установлена верно.
  • Поискать запись по d$uuid в g$distribute в первой, серверной и второй базах, чтобы определить между какими базами проблема.
select * from g$distribute g where g.uuid="..."
  • Посмотреть когда были последние данные, которые пришли по синхронизации:
select * from g$distribute g order by  g.serverpacket desc
  • Если запись не ушла из первой базы в серверную, то сравните поле packet, а если она из серверной не дошла до второй базы, то сравните поле serverpacket.
  • Проверяем, что на странице [http://192.168.67.30/sinhro/] данный профиль не светится с флагом -1, иначе ошибка на сервере.
  • Проверить, что dt клиент включен.
  • url адрес который прописан в клиенте, открывается в браузере и выдает no input data
    • если не работает, проверить, что на сервере включен wamp.
    • иначе если URL начинается с 10, проверить, что поднят open vpn.
  • dt клиент не выдает ошибок при скачивании.
  • dt клиент что-то скачивет, пишется объем траффика.
  • если процесс скачивания идет очень долго, запустить dt клиент с ключом +detail и посмотреть на каких запросах тормозит.
  • если проблема с таблицей по старой синхронизации, взять максимальное значение поля packet по этой таблице по этому профилю на сервере:
 select max(packet) from :table where g$profile=:profle

и подставить его в генератор поля packet этой таблицы в клиентской базе:

 GEN_%TABLE%_PACKET
  • проапдейтить необходимые записи.
  • Посмотреть за какое число сейчас пытается скачать даннные клиент. Берем uuid последней записи:
select g.uuid from g$distribute g order by  g.serverpacket desc

Затем выполняем на сервере:

select g.insertdt, g.updatedt from g$distribute g where g.uuid="..."

Если даты старые (больше недели), то проверяем актуален ли serverpacket на клиенте. Выполняем на сервере:

select serverpacket from g$distribute where from_profile_id=:profile_id order by serverpacket desc

Выполняем на клиенте:

select serverpacket from g$distribute where 1=1 order by serverpacket desc

Сравниваем. Если на сервере значение меньше, то обновляем на клиенте:

update g$distribute set serverpacket=:serverpacket where serverpacket=(select max(g.serverpacket) from g$distribute g)
  • Проверить базу на ошибки.

Снова запускаем синхронизацию.


На сервере

Основное

  • Сервер доступен
  • База ZTRADE_G.FDB доступна
  • WAMP запущен
  • Служба StandartNDistributeQueueService запущена;

Дополнительно

  • Поискать запись по d$uuid в g$distribute в первой, серверной и второй базах, чтобы определить между какими базами проблема.
select * from g$distribute g where g.uuid="..."
  • Посмотреть когда были последние данные, которые пришли по синхронизации:
select * from g$distribute g where g.from_profile_id=:profile_id order by g.serverpacket desc
  • в таблице g$queue есть свежие данные:
select * from g$queue q where q.profile_id=:profile_id order by q.insertdt desc
  • в таблице docs есть свежие данные
select * from docs d where d.g$profile_id=:profile_id order by d.commitdate desc

Посмотрите на поле endflag: если там -1, значит ошибка.

  • профиль прописан в g$profiles, status=0, dbsecurekey not null, остальные колонки как у других подобных профилей.
  • в таблице G$DISTRIBUTE_VECTORS есть строчка с tablename нужной таблицы, где to_profile_id либо 0, либо номер базы, куда данные должны уйти.
  • в таблице G$DISTRIBUTE_X_TABLES по данному профилю настройки такие же как и у других профилей и там верно указано какой таблице на сервере соответствует таблица на клиенте.
 select * from docs order by docdate desc
  • если есть таблица g$queue, смотрим когда были последние пакеты и есть ли по ним ошибки.
 select * from g$queue q where q.profile_id=:profile_id
  • Firebird работает. Если linux, то
/etc/init.d/firebird<Tab> status

если нужно перезапустить:

/etc/init.d/firebird<Tab> restart
  • Wamp включен. Если linux, то
/etc/init.d/apache<Tab> status

если нужно перезапустить

/etc/init.d/apache<Tab> stop
/etc/init.d/apache<Tab> start
  • Удалить все содержимое каталогов users и queue. Если linux, то
cd ... <путь до скриптов>
cd users
<убедитесь, что вы точно зашли в папку users>
rm -R ./* -v
cd ../queue
<убедитесь, что вы зашли в папку queue>
rm -R ./* -v
  • Проверить, что дата/время на компьютере установлено верно. Если linux, то
date

если нужно установить время:

date +%T -s "11:14:00"
  • Проверить настройки в declare.php. Если linux, то
cd ... <путь до скриптов>
nano declare.php
  • Служба очереди работает.

Частые вопросы

Distribute Client не запускается, выходит ошибка "программа уже запущена"

Перезагрузите firebird или сделайте базу в даун и снова онлайн.

Как синхронизировать таблицу

В скриптах ниже нужно исправить MY_TABLE на название вашей таблицы.

В клиентской базе которую хотим распространить

  • Добавляем поля D$UUID и D$SRVUPDDT
ALTER TABLE MY_TABLE 
ADD D$UUID DM_UUID;
ALTER TABLE MY_TABLE 
ADD D$SRVUPDDT DM_DATETIME;
  • Делаем первичный ключ на поле d$uuid:
ALTER TABLE MY_TABLE
ADD CONSTRAINT PK_MY_TABLE
PRIMARY KEY (D$UUID);

или уникальный

ALTER TABLE MY_TABLE
ADD CONSTRAINT UNQ1_MY_TABLE
UNIQUE (D$UUID);
  • Генерируем значения поля d$uuid:
update MY_TABLE set d$uuid=UUID_TO_CHAR(GEN_UUID()), d$srvupddt='2000-01-01';
  • Создаем триггеры:
SET SQL DIALECT 3;
SET TERM ^ ;

CREATE OR ALTER TRIGGER MY_TABLE_BI_DISTR FOR MY_TABLE
ACTIVE BEFORE INSERT POSITION 0
AS
begin
   if (new.d$uuid is null) then
  begin
    /*Запрещаем менять данные, если централизованное управление*/
    /*
    if (cast((select param_value from params where param_id = 'CODE_PROFILE') as dm_id) <> 2) then
      exception ex_wrong_db;
    */
    new.d$uuid=UUID_TO_CHAR(GEN_UUID());
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('MY_TABLE,new.d$uuid,0,null) matching (TABLENAME,UUID);
  end
  if (new.d$srvupddt is null) then
    new.d$srvupddt='2000-01-01';
end
^

CREATE OR ALTER TRIGGER MY_TABLE_BU_DISTR FOR MY_TABLE
ACTIVE BEFORE UPDATE POSITION 0
AS
begin
  if (new.D$SRVUPDDT=old.D$SRVUPDDT) then
    begin
     /*Запрещаем менять данные, если централизованное управление*/
     /*
     if (cast((select param_value from params where param_id = 'CODE_PROFILE') as dm_id) <> 2) then
      exception ex_wrong_db
     */
     update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('MY_TABLE',new.d$uuid,1,null) matching (TABLENAME,UUID);
    end
end
^

CREATE OR ALTER TRIGGER MY_TABLE_AD_DISTR FOR MY_TABLE
ACTIVE AFTER DELETE POSITION 0
AS
begin
    /*Запрещаем менять данные, если централизованное управление*/
     /*
    if (cast((select param_value from params where param_id = 'CODE_PROFILE') as dm_id) <> 2) then
      exception ex_wrong_db;
    */
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('MY_TABLE',old.d$uuid,2,null) matching (TABLENAME,UUID);
end
^

SET TERM ; ^
  • Делаем update, чтобы отправить данные на сервер:
update MY_TABLE set id=id


  • Для того, что бы не было конфликтов ID на точках при двух сторонней синхронизации необходимо отправить по сети чистку данных в точках, если они есть:
delete from MY_TABLE

Настраиваем одностороннюю синхронизацию на сервере

  • Создаем таблицу с той структурой, которую сделали в клиентских базах (переносим только структуру).
  • Не забываем сделать поле d$uuid уникальным:
ALTER TABLE DOCS ADD CONSTRAINT PK_MY_TABLE PRIMARY KEY (D$UUID);
  • Добавляем поле G$PROFILE_ID
ALTER TABLE MY_TABLE
ADD G$PROFILE_ID DM_ID;
  • Создаем триггеры:
SET SQL DIALECT 3;
SET TERM ^ ;

CREATE OR ALTER TRIGGER MY_TABLE_BI_SRVSYNC FOR MY_TABLE
ACTIVE BEFORE INSERT POSITION 0
AS
begin
  select from_profile_id from g$distribute where uuid=new.d$uuid into new.g$profile_id;
end
^

CREATE OR ALTER TRIGGER MY_TABLE_BU_SRVSYNC FOR MY_TABLE
ACTIVE BEFORE UPDATE POSITION 0
AS
begin
  select from_profile_id from g$distribute where uuid=new.d$uuid into new.g$profile_id;
end
^

SET TERM ; ^

Настраиваем двухстороннюю синхронизацию на сервере

  • Создаем таблицу с той структурой, которую сделали в клиентских базах (переносим только структуру).
  • Если в таблице первоначально сделано уникальное поле G$PROFILE_ID, то нужно его удалить
ALTER TABLE MY_TABLE DROP CONSTRAINT FK_MY_TABLE_1;
ALTER TABLE MY_TABLES DROP G$PROFILE_ID;
  • Не забываем сделать поле d$uuid уникальным:
ALTER TABLE MY_TABLE ADD CONSTRAINT PK_MY_TABLE PRIMARY KEY (D$UUID);
  • Добавляем триггеры:
SET SQL DIALECT 3;
SET TERM ^ ;

CREATE OR ALTER TRIGGER MY_TABLE_AD0_DISTR FOR MY_TABLE
ACTIVE AFTER DELETE POSITION 0
AS
begin
  update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('MY_TABLE',old.d$uuid,2,0) matching (TABLENAME,UUID);
end
^

CREATE OR ALTER TRIGGER MY_TABLE_BI_DISTR FOR MY_TABLE
ACTIVE BEFORE INSERT POSITION 0
AS
begin
  if (new.d$uuid is null) then
  begin
    new.d$uuid=UUID_TO_CHAR(GEN_UUID());
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('MY_TABLE',new.d$uuid,0,0) matching (TABLENAME,UUID);
  end
  if (new.d$srvupddt is null) then
    new.d$srvupddt='2000-01-01';
end
^

CREATE OR ALTER TRIGGER MY_TABLE_BU0_DISTR FOR MY_TABLE
ACTIVE BEFORE UPDATE POSITION 0
AS
begin
  if (new.D$SRVUPDDT=old.D$SRVUPDDT) then
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('MY_TABLE',new.d$uuid,1,0) matching (TABLENAME,UUID);
end
^

SET TERM ; ^
  • Добавляем информацию о нашей таблице в G$DISTRIBUTE_VECTORS
INSERT INTO G$DISTRIBUTE_VECTORS (FROM_PROFILE_ID, TO_PROFILE_ID, TABLENAME, PERMISSION) VALUES (0, 0, 'MY_TABLE', 0);
  • Добавляем информацию о нашей таблице в G$DISTRIBUTE_X_TABLES, если профилей уже много в базе можно применить выполнение блока:
execute block
as
declare variable PROFILE_ID type of DM_ID;
begin
for select md.id from g$profiles md
where status =0 and md.dbsecurekey is not null and  relationtype=1 into :profile_id do
INSERT INTO G$DISTRIBUTE_X_TABLES (PROFILE_ID, TABLENAME, CLIENTTABLENAME)
                           VALUES (:profile_id, 'MY_TABLE', 'MY_TABLE');
end

Проверяем не исключена ли наша таблица на сервере wamp в getrelationtype.php выполняем поиск нашей таблицы. Данные отправляются если они попадают под условия return 0;

Дополнительно проверить что было отправлено при необходимости:

  • Для того, что бы не было конфликтов ID на точках необходимо отправить по сети чистку данных в точках:
delete from MY_TABLE

Добавить запись к частично-двусторонней синхронизации

  • Для чего нужно: требуется для настройки групп (GROUPS) где часть записей существует самостоятельно в клиентской базе, а часть нужно поддерживать в едином варианте по всей сети.
  • Допустим нужно запись ID=:ID таблицы MY_TABLE настроить так, что бы изменения "ходили" по двусторонней синхронизации.
  • Внести исправления в getrelationtype.php
  • Удалить все следы записи в серверной и сводных базах.
delete from MY_TABLE where ID=:ID; 
delete from g$distribute where tablename='MY_TABLE' where uuid=(select t.d$uuid from my_table t where t.ID=:ID);
  • Обеспечить идентичные d$uuid у нужной записи во всех клиентских базах, только аккуратно, что бы при установке новых d$uuid запись не пошла по синхронизации, для этого в запросе добавим Update d$srvupddt. Данные sql-запросы можно разослать с помощью g$tasks.
delete from g$distribute where tablename='MY_TABLE' where uuid=(select t.d$uuid from my_table t where t.ID=:ID);
update MY_TABLE set d$uuid=:new_d$uuid, d$srvupddt=current_timestamp where ID=:ID;
  • Теперь можно вносить изменения в записи и она разойдется по всей сети без ошибок.

Можно ли вручную перенести записи если они не приходят по синхронизации?

Теоретически если записи по непонятным причинам не доходят по синхронизации, а нужно, чтобы эти данные срочно появились в нужной базе, то теоретически можно вручную скопировать эти записи и вставить их в базу вручную через ibexpert. Главное перед этим посмотреть: данная таблица синхронизируется в одну сторону, или в обе стороны, т.е. в клиентской базе данные только этой базы, или вообще всей сети?

  • в первом случае при вставке записей не нужно заполнять поле d$uuid для того, чтобы оно сгенерировалось бы заново.
  • во втором случае нужно обязательно заполнить поле d$uuid оригинальными значениями.

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

Пустые наименования

  • Сначала нужно определиться чего именно не хватает. Партия цепляет варес, варес цепляет валс. Обычно не хватает или вареса или валса. Бывает, что все есть, но в валсе пустое значение (в этом случае нужно в менеджере два раза нажать на наименование и вставить оригинальное значение товара).

выполняем запрос, берем ware_id и выполняем следующий, смотрим есть ли значение наименование sname

select name_id, sname from wares where id=:ware_id

берем ware_id и выполняем следующий, смотрим есть ли значение наименование sname

select svalue from vals v where v.id=:name_id

Если в wares поле sname = null, а svalue в vals имеет значение выполняем обновление:

update
wares w
set w.sname=(select v.svalue from vals v where v.id=w.name_id)
where ((sname=) or (sname is null)) and (select v.svalue from vals v where v.id=w.name_id) is not null

после выполнения update wares обновляем сводные остатки:

update
warebase_g w
set w.part_id=part_id
where w.sname is null 

/*Если не помогает делаем напрямую исправление наименования в сводных остатках по name_id*/

update
warebase_g w
set w.sname=(select v.svalue from vals v where v.id=w.name_id)
where w.sname is null


  • Бывают случаи, когда наименование привелось к пустому значению, т.е. проблема в приведении. Попробуйте поискать связки:
select 'WARES', V.SVALUE
from WARES WL
left join VALS V on WL.NAME_ID = V.ID
where WL.ID = :ware_id
union
select 'WARES_LOG', V.SVALUE
from WARES_LOG WL
left join VALS V on WL.NAME_ID = V.ID
where WL.ID = :ware_id
  • Когда определись чего не хватает в текущей базе, попробовать поискать этот валс или варес в другой серверной, сводной или клиентской базе по его id.
  • Если проблема была в варесе, то скорей всего он есть где то в другой базе и его достаточно проапдейтить:
update wares w where w.id=:id
  • Если проблема с валсом, то нужно перепривязать данное поле вареса, которое на него ссылается, на другое значение (другой валс). Если, например, проблема с наименованием, а оригинальное наименование ссылается на другое значение, то возьмите его и вставьте в название.
update wares w set w.name_id=(select wa.orig_name_id from wares wa where wa.id=:ID) where w.id=:ID

Нужно чтобы закрытая аптека не висела бы на sinhro но чтобы данные по ней отображались в своднике

Поставить dbsecurekey в таблицы g$profiles в null.

На sinhro пишется ошибка загрузки пакета и дата очень старая а по docs дата недавняя

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

Что можно удалить если синхронизация много весит (каталог wamp занимаем очень много места, больше 1гб)?

  • В папке queue можно чистить все кроме последних 2х дней.
  • В папке users можно чистить все.

Как переотправить накладную требования/перемещение/переоценки по синхронизации

  • Нужно подключиться к базе, с которой была отправлена накладная.
  • Найти doc_id нужного документа по его номеру:
select * from docs d where d.caption containing :docnum
  • Переотправить накладную спомощью процедуры:
execute procedure GM$PR_PRIHOD_DOC_TREB(:doc_id);

Как отправить (выполнить) большой SQL на клиенте

Для этого используется процедура UTPR_MASTERDETAIL_CF. Если ее нет ниже SQL:

SET TERM ^ ;
create or alter procedure UTPR_MASTERDETAIL_CF
as
declare variable PROFILE_ID type of DM_ID;
declare variable S DM_TEXT1024;
declare variable STR DM_BLOBTEXT;
begin
 for select md.id from g$profiles md
where md.dbsecurekey is not null and md.relationtype=1 and status=0
--and  md.id in (100)
into :profile_id do
 begin
   str='file:D:\sql\файл для отправки';
--    select data from g$tasks where id=546430 into str;
   insert into g$tasks (PROFILE_ID,TASK_TYPE,CAPTION,data,checkprevtask) values (:profile_id,0, 'Описание task',:str,null);
 end
end^
SET TERM ; ^
GRANT SELECT ON G$PROFILES TO PROCEDURE UTPR_MASTERDETAIL_CF;
GRANT INSERT ON G$TASKS TO PROCEDURE UTPR_MASTERDETAIL_CF;
GRANT EXECUTE ON PROCEDURE UTPR_MASTERDETAIL_CF TO SYSDBA;

В строке str='file:D:\sql\файл для отправки'; указываем путь до файла с файлом SQL

Теги: отправить, выполнить, большой SQL, SQL

Отправить сетку отображения (распространить вид) таблицы по сети

Для того что бы отправить сетку всем пользователям по сети или нужным профилям в пределах одной сети нужно выполнить следующее:

1. Создаем нужную сетку (вид) которую нужно распространить.

2. Заходим в таблицу USERS, находим пользователя под которым у нас правильная сетка и переходим в поле Data

3. В поле Data должна быть выбрана вкладка AsText - ANSI. В Верху на панели нажимает кнопку с обозначением Дискетки - сохранить.

4. В открывшемся окне выбираем нужную папку для сохранения, тип файла: Все файлы (*.*), набираем понятное имя файла и в конце обязательно добавляем ".ZIP", запоминаем путь, нажимаем кнопку Cохранить.

5. Переходим в каталог куда мы сохранили файл, находим его и распаковываем в папку и переходим в эту папку

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

7. Архивируем нужную нам сетку или если их несколько то несколько в одном файле, запоминаем путь.

8. Открываем IBExpert, заходим в серверную базу, обычно называется ZTRADE_G.FDB, нам нужна процедура UTPR_MASTERDETAIL_GRID Заходим в нее. Если ее нет ниже SQL:

SET TERM ^ ;
create or alter procedure UTPR_MASTERDETAIL_GRID
as
declare variable PROFILE_ID type of DM_ID;
declare variable S DM_TEXT1024;
declare variable STR DM_BLOBTEXT;
begin
  for select md.id from g$profiles md
where md.dbsecurekey is not null and relationtype=1 and status=0
--and  md.id not in (57,157)
into :profile_id do
  begin
    str=;
 insert into g$tasks (PROFILE_ID,TASK_TYPE,CAPTION,data) values
  (:profile_id,2,'Сетка журнала документов -2', (select data from G$TASKS_TMPL where id = 0));
  end 
end^
SET TERM ; ^
GRANT SELECT ON G$PROFILES TO PROCEDURE UTPR_MASTERDETAIL_GRID;
GRANT INSERT ON G$TASKS TO PROCEDURE UTPR_MASTERDETAIL_GRID;
GRANT SELECT ON G$TASKS_TMPL TO PROCEDURE UTPR_MASTERDETAIL_GRID;
GRANT EXECUTE ON PROCEDURE UTPR_MASTERDETAIL_GRID TO SYSDBA;

9 В процедуре есть текст, типа (select data from G$TASKS_TMPL where id = 0) смотрим какой id указан и переходим в эту таблицу G$TASKS_TMPL. Под указанным ID в данном случае 0 открываем поле DATA должна быть выбрана вкладка AsText - ANSI.

10. Нажимаем кнопку Открыть, выбираем файл подготовленный в п.7, подтверждаем изменения - делаем коммит

11. В процедуре UTPR_MASTERDETAIL_GRID выбираем 1 профиль и выполняем процедуру. Через некоторое время проверяем изменения на объекте. Если все хорошо отправляем сетку всех профилей у кого она должна быть, внеся изменения в указанную процедуру.

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

Как отправить поле типа Blob по синхронизации на примере таблицы Reports

Внимание!!! Для сетей, где таблицы ходят по двухсторонней синхронизации смотри раздел выше "Добавить запись к частично-двусторонней синхронизации"

Для отправки добавления отчета в таблицу Reports используется штатный механизм отправки тасков процедура "UTPR_MASTERDETAIL" отправляем всем нужным профилям добавление отчета:

INSERT INTO REPORTS (PARENT_ID, STATUS, REPORTTYPE, SORTING, CAPTION, PARAMS, WDICT_ID)
            VALUES (0, 0, 1, 400, Новый отчет, [freereport]
tmplpath=
, NULL);

Далее сохраняем поле DATA таблицы REPORTS с нужным нам отчетом в txt. Далее открываем таблицу любую таблицу которая ходит по двухсторонней синхронизации, например таблица G$PROFILES. Находим у таблицы поля типа Blob которые не используются, например, TMP_BLOB1 ... TMP_BLOB_N. Если нет можно создать и распространить по сети. Далее в данное поле сохраняем выгруженный нами отчет и Коммитим изменения - ЖДЕМ НЕСКОЛЬКО ЧАСОВ!!! что бы данные дошли до точек. После этого создаем новый таск используя процедуру UTPR_MASTERDETAIL, где обновляем таблицу REPORTS по полю дата заменяю его значением из G$PROFILES:

update reports r set r.data = (select TMP_BLOB1 from g$profiles where id=1 and TMP_BLOB1 is not null) where r.caption =Новый отчет;

Теги: отправить, поле, Blob, синхронизация, UTPR_MASTERDETAIL

Как добавить на панель АРМ Кассира кнопку с нужным действием (ТМС, Отчет)

Важно!!! На момент проведения действия кассиры должны быть все закрыты!!! В противном случае, после закрытия АРМ Кассир все изменения будут заполнены текущими настройками 

Для рапространения требуется процедура "UTPR_MASTERDETAIL_KASSIR", если ее нет нужно добавить скрипт ниже:

SET TERM ^ ;
create or alter procedure UTPR_MASTERDETAIL_KASSIR
as
declare variable PROFILE_ID type of DM_ID;
declare variable S DM_TEXT1024;
declare variable STR DM_BLOBTEXT;
begin
  for select md.id from g$profiles md
where md.dbsecurekey is not null and relationtype=1 and status=0
into :profile_id do
  begin
    --str=;
 insert into g$tasks (PROFILE_ID,TASK_TYPE,CAPTION,data) values
  (:profile_id,4,'Кнопка в кассире', 'file:E:\Standart-N\tasks\kassabuttons.txt');
  end 
end^
SET TERM ; ^
GRANT SELECT ON G$PROFILES TO PROCEDURE UTPR_MASTERDETAIL_KASSIR;
GRANT INSERT ON G$TASKS TO PROCEDURE UTPR_MASTERDETAIL_KASSIR;

1. На любой точке или копии базы сети выводим нужную кнопку на панель под определенным пользователем Кассы.

2. Заходим в таблицу USERS, находим пользователя под которым вывели кнопку и переходим в поле Data

3. В поле Data должна быть выбрана вкладка AsText - ANSI. В Верху на панели нажимает кнопку с обозначением Дискетки - сохранить.

4. В открывшемся окне выбираем нужную папку для сохранения, тип файла: Все файлы (*.*), набираем понятное имя файла и в конце обязательно добавляем ".ZIP", запоминаем путь, нажимаем кнопку Cохранить.

5. Переходим в каталог куда мы сохранили файл, находим его и распаковываем в папку и находим файл zkassa#user.ini. Открываем его на редактирование.

6. Выполняем поиск по файлу раздела [UserToolBar] - это радел где хранятся кнопки выведенные на панель - находим по названию нужную кнопку нам в добавляем в начале строки zkassa#user.ini>UserToolBar>>

7. Копируем строку в новый файл форма txt, сохраняем его на диске сервера синхронизации. В процедуре UTPR_MASTERDETAIL_KASSIR указываем путь к данному файлу

Gif на тему:

Add bitton1.gif

Отправляем исправления по синхронизации ночью!!! когда все АРМ Кассир закрыты. 

Для этого подготавливаем процедуру UTPR_MASTERDETAIL_KASSIR и планируем выполнение задания через Планировщик заданий Windows (команда taskschd.msc) ночью на запуск ТМС SpacePro которая будет вызывать нашу процедуру UTPR_MASTERDETAIL_KASSIR. Пример: D:\Standart-N\Standart-N\spacepro.exe +TMS=№_ТМС

Теги: отправить, кнопку, Blob, синхронизация, USERS, UTPR_MASTERDETAIL_KASSIR, панель, АРМ Кассир

Как обновить удаленно Distribute Client (dtclient)

Для обновления заходим на сервер синхронизации где установлен установлен каталог синхронизации sinhro.

Внимание!!! Порядок действий не меняем - очень важен для конечного результата

1. Заходим в каталог shellupd, откидываем на всякий случай копию всех файлов в каталог back и копируем туда последнюю тестированную версию Distribute Client.

2. Удаляем файл data.zip и создаем на основе exe Distribute Client с таким же названием в этом же каталоге.

3. Смотрим версию Distribute Client.exe для этого открываем "Свойство" exe, вкладка "Подробно", Версия файла - запоминаем. Нам нужна будет версия между первой точкой.

Например в окне Версия файла мы видим строке 2.2045.0.0, нам будет нужна только 2.2045 

4. Открываем файл exe.ver на редактирование и записываем новую версию файла, на текущем примере это 2.2045

Кратко об этом gif:

Update dtclient.gif


Теги: обновить, Distribute Client, dtclient, синхронизация, DistributeClient

Частые ошибки

Column unknown

Ошибка, возникающая на сервере. Означает, что у клиента в синхронизируемой таблице есть колонка, которой нет в этой таблице на серверной базе. Нужно либо добавить данную колонку на сервере, либо если она не нужна, или она есть не во всех клиентских базах, то добавить ее в список XFIELDS(xfields, xfields0, xfields1) в declare.php.

Violation of PRIMARY or UNIQUE KEY constraint

Ошибка, возникающая когда на момент вставки записи в таблицу, в ней уже есть строчка с таким же d$uuid, но с другим сочетанием id и g$profile_id. Нужно либо перегенерировать d$uuid у той строчки, которая уже есть, либо удалить запись, у которой совпадают id и g$profile_id.

Foreign key... At trigger DOCS_TREB_BI

Cначала попробуйте удалить старые накладные требования (сделав бэкап).

delete from doc_detail_active_treb ddat where ddat.insertdt <= current_date-7;
delete from docs_treb dt where dt.insertdt <= current_date-7;

Проверьте в таблицы docs нет ли документов с rguid той записи, на которую ругается. Если такой документ уже есть, то измените ему rguid.

update docs d set d.rguid=replace(UUID_TO_CHAR( GEN_UUID()),'-','') where d.rguid=:rguid;

Multiple rows... PR_SETPARTREALQUANT... DOC_DETAIL_AI_WB

Ошибка на сервере. Возможно в таблице warebase_g две строчки с part_id той записи, на которую ругается синхронизация. Удалите одну из них.

Querypackets2013 Не найдена запись

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

  • Данную запись ктото случайно удалил. В этом случае нужно посмотреть на сервере с какого профиля она пришла (поле from_profile_id в g$distribute), подключиться к этой базе (также можно поискать в других сводных базах), найти эту запись, проапдейтить. В результате она должна придти на сервер и синхронизация снова пойдет.
  • Данной записи нигде нет. Возможно она очень старая и поэтому ее удалили специально. Вопрос: почему наша база пытается ее скачать? Возможно в нашей базе сбилось поле serverpacket, точнее ктото неправильно почистил g$distribute, и теперь эта база пытается скачать вообще все с нуля. Надо проверить: это новая база? Нужно определить актуальность данных проблемной базы. Выполняем запрос на сервере:
select * from g$distribute where from_profile_id=:profile_id order by serverpacket desc

Проверяем максимальное значение serverpacket в проблемной базе:

select max(g.serverpacket) from g$distribute g;

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

 delete from g$distribute g where g.soper <> 2 and g.tablename=Upper('имя таблицы') and
 not exists(select id from имя таблицы d where d.d$uuid = g.uuid)

Если просит удалить более 1000 записей, ток внедрению

Connection reset by peer

  • Проверить таймауты
  • Проверить ограничения пакетов
  • Выключить антивирус
  • Выключить брэндмауэр
  • Проверить системное время
  • Выключить роутер на 15 минут
  • Почистить таблицу g$distribute.
  • в 80 % случаев не верный тип поля (пример DM_ID и DM_ID_NULL) на серверной базе предпочительно разрешать NULL значения

Incomplete Zip File

Ошибка бывает на клиенте.

  • Скорей всего связана с блокировками синхронизации антивирусом.
  • Проверить правильность написания URL

wrong page type

Ошибка wrong page type page 79332 is of wrong type (expected 7, found 5)... падение базы. выполнить: mend - backup - restore.

Ошибка при сеансе обмена: checkprevtask: предыдущая задача не выполнена или выполнена с ошибкой

в глоб базе в табл g$task по профилю есть задача, которая не выполнилась по полю senddt отправка прошла , а по полю enddt нет. т е задача не завершилась в этом случае поле senddt устанавливаем в NULL . синхронизируем. ждем завершения задачи

update g$tasks g set g.senddt = null where g.senddt is not null and g.enddt is null and g.checkprevtask is not null

conversion error from string ...At trigger 'GROUP_DETAIL_AD0_MMBSH' line: 9, col: 34

Для решение данной ошибки нужно подправить два тригера

/* Trigger: GROUP_DETAIL_AD0_MMBSH */
CREATE OR ALTER TRIGGER GROUP_DETAIL_AD0_MMBSH FOR GROUP_DETAIL
ACTIVE AFTER DELETE POSITION 0
AS
begin
  if (old.grouptable = 'PARTS.NAME_ID' ) then
   update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
   where name_id = old.grouptable_id;

  if ((old.grouptable = 'PARTS' ) and (old.id>0))then
     update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
       where part_id = cast(old.grouptable_id as bigint);
end 
/* Trigger: GROUP_DETAIL_BU0_MMBSH */
CREATE OR ALTER TRIGGER GROUP_DETAIL_BU0_MMBSH FOR GROUP_DETAIL
ACTIVE AFTER INSERT OR UPDATE POSITION 0
AS
begin
  if (new.grouptable = 'PARTS.NAME_ID' ) then
   update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
   where name_id = new.grouptable_id;

  if ((new.grouptable = 'PARTS' ) and (new.id>0))then
   update warebase w set mmbsh = (select membership from PR_MEMBERSHIPS('PARTS=' || w.part_id || ';PARTS.NAME_ID=' || w.name_id || ';PARTS.IZG_ID=' || w.izg_id ||';',ascii_char(13)||ascii_char(10)))
   where part_id = new.grouptable_id;
end