DEPO на Firebird в мастере: подготовка базы одним действием, как для MSSQL

Мастер, шаг «Учёт DEPO», и «Обмен с DEPO» в настройках: СУБД «Firebird» (Fancy на Firebird 2.5).
- Подключение: драйвер nakagami/firebirdsql, путь к файлу базы или псевдоним, WIN1251. Запросы bj к DEPO
  (T-SQL) подключение переводит само (fancyfb.go): механически (dbo., TOP, ISNULL, GETDATE, LEN, N, exec)
  вне строк и комментариев, точные тексты — fsql/fb.go (каталоги, LIKE [_], COUNT(*) OVER, SELECT без FROM,
  PATINDEX); @имена — позиционные ?, GUID — в верхнем регистре. Системные запросы из кода — в fsql/depocatalog.go.
- «Подготовить» логином SYSDBA (depo_setup_fb.go): проверка базы и UDF Fancy, логин SYSDBA_BJ (Fancy даёт права
  на документы логинам SYSDBA%; для Firebird — обычный пользователь), оператор-робот Fancy, кодировщик M2M,
  привязки FTV_M2M_SETTINGS по признакам Fancy, таблицы, процедуры, права; обработку запускает bj.
  «Получить скрипт» — то же для isql в WIN1251; письмо администратору, установка процедур, плашка обновления.
- Скрипты Firebird встроены (depoprocs/firebird, тест сверяет с deploy/); количества — DOUBLE PRECISION: из
  диалекта 3 в базу диалекта 1 процедуру с NUMERIC(18,7) не создать.
- Проверено на копии базы Fancy (отдельный экземпляр bj): мастер с нуля — все проверки, BJ_PING; тестовый режим
  DEPO — К1 Бубликов, зачисление и списание до исполнения без ошибок; очередь, справочник НРД.
  Тест FBLive готовит на базе все запросы bj к DEPO: 94 из 101, оставшиеся 7 — ЛК и ручной ввод (следующий этап).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-27 18:55:02 +03:00
parent 7073ce2a33
commit 50e420f4a0
30 changed files with 2698 additions and 71 deletions
@@ -140,12 +140,13 @@
{{$sub := .Sub}}
{{if eq $sub "setup"}}
<p>bj сверяет каждый перевод с учётом DEPO и сам создаёт в нём поручения. Четыре вопроса по порядку — из ответов bj соберёт подготовку базы.</p>
{{$job := .Settings.Fancy.DepoJobMode}}{{$pg := eq .Settings.Fancy.DBType "postgres"}}
<form method="post" action="/admin/setup/wizard-depo/setup" id="wiz-depo" class="dq{{if $pg}} is-pg{{end}}">
{{$job := .Settings.Fancy.DepoJobMode}}{{$pg := eq .Settings.Fancy.DBType "postgres"}}{{$fb := eq .Settings.Fancy.DBType "firebird"}}
<form method="post" action="/admin/setup/wizard-depo/setup" id="wiz-depo" class="dq{{if $pg}} is-pg{{end}}{{if $fb}} is-fb{{end}}">
<section class="dq-s">
<h3 class="dq-h">Какая СУБД у DEPO</h3>
<div class="dq-tiles">
<label class="dq-tile"><input type="radio" name="db_type" value="mssql" {{if not $pg}}checked{{end}}><span><b>Microsoft SQL Server</b></span></label>
<div class="dq-tiles dq-3">
<label class="dq-tile"><input type="radio" name="db_type" value="mssql" {{if and (not $pg) (not $fb)}}checked{{end}}><span><b>Microsoft SQL Server</b></span></label>
<label class="dq-tile"><input type="radio" name="db_type" value="firebird" {{if $fb}}checked{{end}}><span><b>Firebird</b></span></label>
<label class="dq-tile"><input type="radio" name="db_type" value="postgres" {{if $pg}}checked{{end}}><span><b>PostgreSQL</b></span></label>
</div>
</section>
@@ -154,10 +155,11 @@
<h3 class="dq-h">Где база DEPO</h3>
<div class="dq-grid dq-srv">
<label>Адрес сервера<input type="text" name="server" data-need="адрес сервера DEPO" value="{{.Settings.Fancy.Server}}" placeholder="IP-адрес или имя\экземпляр" required autofocus spellcheck="false"></label>
<label>Порт<input type="text" name="port" value="{{if .Settings.Fancy.Port}}{{.Settings.Fancy.Port}}{{else if $pg}}5432{{else}}1433{{end}}" inputmode="numeric"></label>
<label>Имя базы<input type="text" name="depo_database" data-need="имя базы DEPO" value="{{.Settings.Fancy.DepoDatabase}}" placeholder="как называется на сервере" required pattern="[A-Za-z][A-Za-z0-9_]*" title="латинские буквы, цифры и _, начинается с буквы" spellcheck="false"></label>
<label>Порт<input type="text" name="port" value="{{if .Settings.Fancy.Port}}{{.Settings.Fancy.Port}}{{else if $pg}}5432{{else if $fb}}3050{{else}}1433{{end}}" inputmode="numeric"></label>
<label><span data-nofb>Имя базы</span><span data-fb>Путь к базе</span><input type="text" name="depo_database" data-need="имя базы DEPO" value="{{.Settings.Fancy.DepoDatabase}}" placeholder="{{if $fb}}D:\Fancy\DEPO.GDB или псевдоним{{else}}как называется на сервере{{end}}" required {{if not $fb}}pattern="[A-Za-z][A-Za-z0-9_]*" {{end}}title="латинские буквы, цифры и _, начинается с буквы" spellcheck="false"></label>
</div>
<p class="dq-note">Точное имя базы учёта DEPO — bj будет работать только с ней. Перед подготовкой bj проверит, что в ней есть учётные таблицы DEPO, и в чужую базу ничего не запишет.</p>
<p class="dq-note" data-nofb>Точное имя базы учёта DEPO — bj будет работать только с ней. Перед подготовкой bj проверит, что в ней есть учётные таблицы DEPO, и в чужую базу ничего не запишет.</p>
<p class="dq-note" data-fb>Путь к файлу базы учёта на сервере Firebird — как в подключении Fancy (например, <span class="dq-mono">D:\Fancy\Bases\DEPO.GDB</span>), или её псевдоним из aliases.conf. Перед подготовкой bj проверит, что это база DEPO и функции Fancy на сервере доступны.</p>
</section>
<section class="dq-s">
@@ -171,6 +173,7 @@
<p class="dq-note">Задание называется по базе: у каждой базы DEPO на сервере — своё. Если обработку этой базы уже запускает задание, впишите его имя — второе создано не будет.</p>
</div>
<p class="dq-note" data-pg>Для PostgreSQL обработку запускает сервер bj — выбирать не нужно.</p>
<p class="dq-note" data-fb>Для Firebird обработку запускает сервер bj — выбирать не нужно.</p>
</section>
<section class="dq-s">
@@ -182,7 +185,7 @@
<label class="dq-alt"><input type="radio" name="mode" value="existing"> База уже подготовлена раньше — у bj есть свой логин</label>
<div class="dq-panel dq-box" id="wiz-depo-auto">
<div class="dq-box-h">Администратор SQL <span>— нужен один раз, чтобы подготовить базу; bj его не сохраняет</span></div>
<div class="dq-box-h"><span data-nofb>Администратор SQL</span><span data-fb>Администратор сервера Firebird (SYSDBA)</span> <span>— нужен один раз, чтобы подготовить базу; bj его не сохраняет</span></div>
<div class="dq-grid dq-2">
<label>Логин<input type="text" name="admin_user" data-need="логин администратора SQL" autocomplete="off" spellcheck="false" placeholder="{{if $pg}}postgres{{else}}sa{{end}}"></label>
<label>Пароль<input type="password" name="admin_password" data-need="пароль администратора SQL" autocomplete="new-password"></label>
@@ -197,11 +200,12 @@
<label><span class="dq-lbl-row">Пароль <button type="button" class="dq-link" id="wiz-depo-gen">↻ другой пароль</button></span><input type="text" name="bj_password" data-need="пароль bj" value="{{.DepoPass}}" autocomplete="off" spellcheck="false" class="dq-mono"></label>
</div>
<p class="dq-note">bj придумал их сам. Можно заменить своими — <b>bj сохранит и будет использовать ровно то, что здесь написано</b>: подготовка создаст на сервере этот логин с этим паролем<span id="wiz-depo-login-script"> (в скрипте — они же)</span>.</p>
<p class="dq-note" data-fb>Для Firebird логин bj начинается с SYSDBA: Fancy даёт права на документы только таким логинам. Для сервера Firebird это обычный пользователь — у него будут только права, которые выдаст подготовка: чтение учёта, таблицы обмена, запуск обработки. bj заведёт его в Fancy оператором-роботом — по нему Fancy нумерует документы. Firebird 2.5 учитывает первые 8 знаков пароля.</p>
<p class="dq-warn">Если логин с таким именем на сервере уже есть, ему будет поставлен этот пароль — программа, которая им пользуется, перестанет входить. Тогда впишите другое имя.</p>
</div>
<div class="dq-panel" id="wiz-depo-autogo">
<p class="dq-note">bj проверит, что это база DEPO, создаст свой логин, таблицы обмена<span data-ms> и задание обработки</span>, выдаст права и проверит подключение.</p>
<p class="dq-note">bj проверит, что это база DEPO, создаст свой логин, таблицы обмена<span data-ms> и задание обработки</span><span data-fb>, оператора Fancy, процедуры обработки</span>, выдаст права и проверит подключение.</p>
<button type="submit" class="btn dq-go">Подготовить</button>
</div>
@@ -214,7 +218,7 @@
</div>
<pre class="copyable" id="wiz-depo-sql" data-msg="Впишите имя базы DEPO — скрипт появится здесь."></pre>
</div>
<p class="dq-note"><span data-ms>Администратор выполняет скрипт в SSMS или sqlcmd под логином с правами sysadmin.</span><span data-pg>Администратор выполняет скрипт в psql суперпользователем (postgres).</span> Когда выполнит — нажмите «Проверить подключение».</p>
<p class="dq-note"><span data-ms>Администратор выполняет скрипт в SSMS или sqlcmd под логином с правами sysadmin.</span><span data-pg>Администратор выполняет скрипт в psql суперпользователем (postgres).</span><span data-fb>Администратор выполняет скрипт утилитой isql под SYSDBA с кодировкой WIN1251 — команда в начале скрипта; файл скачивается в WIN1251.</span> Когда выполнит — нажмите «Проверить подключение».</p>
{{template "letterCard" (letter "dba")}}
<button type="submit" class="btn dq-go">Проверить подключение</button>
</div>
@@ -238,10 +242,14 @@
var f=document.getElementById('wiz-depo'), pre=document.getElementById('wiz-depo-sql');
var timer=0, seq=0, last='';
function sync(){ // СУБД меняет подсказки, порт и обязательность имени базы; способ — какую панель показать
var pg=f.db_type.value==='postgres', m=f.mode.value;
f.classList.toggle('is-pg', pg);
if(pg&&f.port.value==='1433') f.port.value='5432'; else if(!pg&&f.port.value==='5432') f.port.value='1433';
f.admin_user.placeholder=pg?'postgres':'sa';
var t=f.db_type.value, pg=t==='postgres', fb=t==='firebird', m=f.mode.value;
f.classList.toggle('is-pg', pg); f.classList.toggle('is-fb', fb);
if(['1433','5432','3050'].indexOf(f.port.value)>=0) f.port.value={mssql:'1433',postgres:'5432',firebird:'3050'}[t];
f.admin_user.placeholder=pg?'postgres':fb?'SYSDBA':'sa';
var dbi=f.depo_database; // Firebird: путь к файлу базы или псевдоним, а не имя
if(fb){ dbi.removeAttribute('pattern'); dbi.placeholder='D:\\Fancy\\DEPO.GDB или псевдоним'; }
else { dbi.setAttribute('pattern','[A-Za-z][A-Za-z0-9_]*'); dbi.placeholder='как называется на сервере'; }
if(fb&&f.bj_user.value==='bj_user') f.bj_user.value='SYSDBA_BJ'; else if(!fb&&f.bj_user.value==='SYSDBA_BJ') f.bj_user.value='bj_user';
var jn=f.depo_job_name, dbv=(f.depo_database.value||'').trim(); // имя задания — по базе, пока не поправили руками
if(jn.dataset.auto==='1') jn.value=dbv?'bj2depo_'+dbv:'';
document.getElementById('wiz-depo-jobbox').hidden=f.depo_job.value!=='agent';
+3 -2
View File
@@ -535,7 +535,8 @@ details:has(> pre.copyable) { min-width:0; max-width:100%; }
.dq-sql pre:empty::before { content:attr(data-msg); color:var(--muted); font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; font-size:13px; }
.dq-sql pre { max-height:380px; min-height:140px; font-size:12px; margin:0; transition:opacity .2s; }
.dq-sql pre.dq-busy { opacity:.5; }
.dq.is-pg [data-ms], .dq:not(.is-pg) [data-pg] { display:none !important; }
.dq.is-pg [data-ms], .dq:not(.is-pg) [data-pg], .dq.is-fb [data-ms], .dq:not(.is-fb) [data-fb], .dq.is-fb [data-nofb] { display:none !important; }
.dq-tiles.dq-3 { grid-template-columns:repeat(3,minmax(0,1fr)); }
.dq-jobname { margin-top:14px; max-width:520px; }
.dq-jobname label { display:block; margin:0; font-size:13.5px; font-weight:500; color:var(--text-2); }
.dq-jobname input { display:block; width:100%; box-sizing:border-box; margin-top:5px; }
@@ -568,7 +569,7 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
.dq-run-msg { margin-top:8px; padding:10px 12px; border-radius:8px; background:var(--err-weak, rgba(200,40,40,.08)); font-size:13.5px; overflow-wrap:anywhere; white-space:pre-wrap; }
@keyframes dq-spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .dq-st.is-run .dq-st-ic { animation:none; border-color:var(--accent); } }
@media (max-width:600px) { .dq-tiles, .dq-srv, .dq-2 { grid-template-columns:minmax(0,1fr); } .dq-sql-head { flex-wrap:wrap; } .dq-sql-st { flex-basis:100%; } }
@media (max-width:600px) { .dq-tiles, .dq-tiles.dq-3, .dq-srv, .dq-2 { grid-template-columns:minmax(0,1fr); } .dq-sql-head { flex-wrap:wrap; } .dq-sql-st { flex-basis:100%; } }
</style>
</head>
<body>
@@ -3,11 +3,12 @@
{{define "fancySection"}}
<div class="card" id="sec-fancy-conn">
<h2><span class="dot {{if .Settings.Fancy.DepoTablesCheckOK}}ok{{else if .Settings.Fancy.Server}}warn{{else}}err{{end}}"></span>Подключение к серверу БД клиента</h2>
<p class="muted">Сервер учётной базы Fancy DEPO: картотека лиц, счета и договоры, остатки, опции (признак квалифицированного инвестора), трансферные таблицы обмена. Поддерживаются <strong>Microsoft SQL Server</strong> и <strong>PostgreSQL</strong>. База указывается ниже, в карточке «Трансферные таблицы DEPO».</p>
<p class="muted">Сервер учётной базы Fancy DEPO: картотека лиц, счета и договоры, остатки, опции (признак квалифицированного инвестора), трансферные таблицы обмена. Поддерживаются <strong>Microsoft SQL Server</strong>, <strong>Firebird</strong> (Fancy на Firebird 2.5) и <strong>PostgreSQL</strong>. База указывается ниже, в карточке «Трансферные таблицы DEPO»; у Firebird — путь к файлу базы на сервере или псевдоним.</p>
<form method="post" action="/admin/setup/fancy" style="margin-top:12px;display:grid;gap:10px;max-width:560px">
<label>Тип СУБД
<select name="db_type" id="fancy-dbtype" style="width:100%" onchange="var p=document.getElementById('fancy-port');if(!p.value||p.value=='1433'||p.value=='5432'){p.value=this.value=='postgres'?'5432':'1433';}">
<option value="mssql" {{if not .Settings.Fancy.IsPostgres}}selected{{end}}>Microsoft SQL Server</option>
<select name="db_type" id="fancy-dbtype" style="width:100%" onchange="var p=document.getElementById('fancy-port');if(!p.value||p.value=='1433'||p.value=='5432'||p.value=='3050'){p.value={mssql:'1433',postgres:'5432',firebird:'3050'}[this.value];}">
<option value="mssql" {{if and (not .Settings.Fancy.IsPostgres) (not .Settings.Fancy.IsFirebird)}}selected{{end}}>Microsoft SQL Server</option>
<option value="firebird" {{if .Settings.Fancy.IsFirebird}}selected{{end}}>Firebird</option>
<option value="postgres" {{if .Settings.Fancy.IsPostgres}}selected{{end}}>PostgreSQL</option>
</select></label>
<label>Сервер (host или host\instance)
@@ -30,7 +31,7 @@
<div class="card" id="sec-fancy-depo-tables">
<h2><span class="dot {{if .Settings.Fancy.DepoTablesCheckOK}}ok{{else if .Settings.Fancy.Server}}warn{{else}}err{{end}}"></span>Трансферные таблицы DEPO</h2>
<p class="muted">Через эти таблицы bj обменивается с учётной системой DEPO: поручения <code>FTV_M2M</code> и их строки <code>FTV_M2M_LINES</code>, события <code>FTV_M2M_EVENTS</code>, справочник участников НРД <code>FTV_M2M_HANDBOOK</code>. Таблицы создаются в базе DEPO на сервере из подключения выше ({{if .DepoTables.Postgres}}PostgreSQL{{else}}Microsoft SQL Server{{end}}). Создание можно повторять: добавляются только недостающие таблицы, колонки и индексы, данные не меняются.</p>
<p class="muted">Через эти таблицы bj обменивается с учётной системой DEPO: поручения <code>FTV_M2M</code> и их строки <code>FTV_M2M_LINES</code>, события <code>FTV_M2M_EVENTS</code>, справочник участников НРД <code>FTV_M2M_HANDBOOK</code>. Таблицы создаются в базе DEPO на сервере из подключения выше ({{if .DepoTables.Postgres}}PostgreSQL{{else if .DepoTables.Firebird}}Firebird{{else}}Microsoft SQL Server{{end}}). Создание можно повторять: добавляются только недостающие таблицы, колонки и индексы, данные не меняются.</p>
<form method="post" action="/admin/setup/fancy/depo-tables" style="margin-top:10px;display:grid;gap:10px;max-width:720px">
<input type="hidden" name="section" value="sec-fancy-depo-tables">
<label>База DEPO
@@ -46,9 +47,9 @@
<form method="post" action="/admin/setup/fancy/depo-tables/create" style="margin-top:12px;display:grid;gap:10px;max-width:720px">
<input type="hidden" name="section" value="sec-fancy-depo-tables">
<div><b>Логин с правами на базу {{.DepoTables.DB}}</b><br>
<span class="muted" style="font-size:12px">{{if .DepoTables.Postgres}}Владелец базы или суперпользователь.{{else}}Роль db_owner в базе (или db_ddladmin + db_securityadmin).{{end}} Если оставить поля пустыми, bj попробует своим логином {{.DepoTables.User}}.</span></div>
<span class="muted" style="font-size:12px">{{if .DepoTables.Postgres}}Владелец базы или суперпользователь.{{else if .DepoTables.Firebird}}SYSDBA: таблицы ставятся вместе с привязками, процедурами и правами.{{else}}Роль db_owner в базе (или db_ddladmin + db_securityadmin).{{end}} Если оставить поля пустыми, bj попробует своим логином {{.DepoTables.User}}.</span></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
<label>Логин <input type="text" name="admin_user" autocomplete="off" placeholder="{{if .DepoTables.Postgres}}postgres{{else}}sa{{end}}" style="width:100%"></label>
<label>Логин <input type="text" name="admin_user" autocomplete="off" placeholder="{{if .DepoTables.Postgres}}postgres{{else if .DepoTables.Firebird}}SYSDBA{{else}}sa{{end}}" style="width:100%"></label>
<label>Пароль <input type="password" name="admin_password" autocomplete="new-password" style="width:100%"></label>
</div>
<div><button type="submit" class="btn btn-ok" onclick="return confirm('Создать трансферные таблицы в базе {{.DepoTables.DB}}?')">⚙ Создать таблицы</button></div>
@@ -150,7 +151,7 @@
{{if not $f.Server}}<a class="btn" href="/admin/wizard?step=3" style="flex:none">Открыть мастер</a>{{end}}</div>
{{if $f.Server}}
<dl class="kv">
<dt>База DEPO</dt><dd><b>{{$f.DepoDB}}</b> на {{$f.Server}}{{if $f.Port}}:{{$f.Port}}{{end}} · {{if eq $f.DBType "postgres"}}PostgreSQL{{else}}Microsoft SQL Server{{end}}</dd>
<dt>База DEPO</dt><dd><b>{{$f.DepoDB}}</b> на {{$f.Server}}{{if $f.Port}}:{{$f.Port}}{{end}} · {{if eq $f.DBType "postgres"}}PostgreSQL{{else if eq $f.DBType "firebird"}}Firebird{{else}}Microsoft SQL Server{{end}}</dd>
<dt>Логин bj</dt><dd><code>{{$f.User}}</code></dd>
<dt>Торговые счета клиентов</dt><dd>номер начинается с <code>{{depoAccPrefix}}</code></dd>
<dt>Признак ИИС</dt><dd>договор начинается с <code>{{if $f.IISContractMarker}}{{$f.IISContractMarker}}{{else}}БРИИС{{end}}</code> — такие переводы отклоняются кодом M2M06</dd>