@@ -142,7 +142,6 @@ lucide.createIcons();
var UPLOAD_URL = 'https://contracts.kube5s.ru/lucee/upload.cfm';
var CONVERT_URL = 'https://contracts.kube5s.ru/convert-doc';
var SITE_URL = ''; // same origin for api calls
-var LUCEE_PROXY = 'https://contracts.kube5s.ru/lucee'; // SSE через ВМ (без буферизации)
var fileInput = document.getElementById('fileInput');
var parseBtn = document.getElementById('parseBtn');
@@ -333,7 +332,7 @@ document.getElementById('llmBtn').addEventListener('click', async function() {
var diffBody = document.getElementById('diffBody');
var diffStatus = document.getElementById('diffStatus');
diffCard.style.display = 'block';
- diffBody.innerHTML = '';
+ diffBody.innerHTML = '⏳ LLM-извлечение...';
diffStatus.textContent = '⏳ 0.0с';
var llmStart = Date.now();
@@ -341,98 +340,65 @@ document.getElementById('llmBtn').addEventListener('click', async function() {
diffStatus.textContent = '⏳ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
}, 200);
- // SSE — прогресс по каждому файлу
- var es = new EventSource(LUCEE_PROXY + '/process.cfm?contract_id=' + contractId);
-
- es.onmessage = function(e) {
- var d = JSON.parse(e.data);
-
- if (d.type === 'extract_start') {
- diffBody.innerHTML += '
⏳ ' + d.filename + ' → LLM... 0.0с
';
- }
- else if (d.type === 'extract_done') {
- var el = document.getElementById('ext-' + d.supplement_id);
- if (el) el.innerHTML = '✓
' + d.filename + ': ' + d.count + ' строк (' + d.time_s + 'с)';
- }
- else if (d.type === 'extract_skip') {
- diffBody.innerHTML += '
⏭ ' + d.filename + ' (уже извлечено, ' + d.count + ' строк)
';
- }
- else if (d.type === 'extract_error') {
- var el = document.getElementById('ext-' + d.supplement_id);
- if (el) el.innerHTML = '✗
' + d.filename + ': ' + d.error + '';
- else diffBody.innerHTML += '
✗ ' + d.filename + ': ' + d.error + '
';
- }
- else if (d.type === 'done') {
- clearInterval(timerInterval);
- es.close();
- diffStatus.textContent = '✓ ' + d.total_time_s + 'с';
- btn.innerHTML = '
✓ Готово';
- document.getElementById('chatCard').style.display = 'block';
-
- // Сравнение
- diffBody.innerHTML += '
📊 Сравнение:
';
- var all = d.all_changes || [];
- if (all.length === 0) {
- diffBody.innerHTML += '
Нет допников для сравнения.
';
- } else {
- all.forEach(function(ch) {
- var s = ch.SUMMARY || {};
- diffBody.innerHTML += '
📄 Допник (' + ch.TYPE + ')
';
- diffBody.innerHTML += '
+' + (s.ADDED||0) + ' ~' + (s.CHANGED||0) + ' -' + (s.DELETED||0) + '
';
-
- var changes = ch.CHANGES || [];
- if (changes.length === 0) {
- diffBody.innerHTML += '
Нет изменений
';
+ // 1. Extract for each supplement
+ try {
+ var resp = await fetch('/api.cfm?action=query&sql=' + encodeURIComponent("SELECT id, type FROM supplements WHERE contract_id='" + contractId + "' ORDER BY created_at"));
+ var data = await resp.json();
+ if (data.OK && data.ROWS) {
+ for (var i = 0; i < data.ROWS.length; i++) {
+ try {
+ var er = await fetch('/extractor.cfm?supplement_id=' + data.ROWS[i].id);
+ var ed = await er.json();
+ if (ed.OK) {
+ var info = (ed.SKIPPED ? '⏭ ' : '✓ ') + (data.ROWS[i].type || '') + ': ' + ed.ROWS_SAVED + ' строк';
+ diffBody.innerHTML += '
' + info + '
';
} else {
- var tbl = '
| № | Услуга | Цена | Объём | Сумма | Начало |
';
- changes.forEach(function(c) {
- var cls = c.CHANGE_TYPE === 'added' ? 'diff-added' : c.CHANGE_TYPE === 'deleted' ? 'diff-deleted' : c.CHANGE_TYPE === 'changed' ? 'diff-changed' : '';
- var vals = c.NEW_VALUES && Object.keys(c.NEW_VALUES).length > 0 ? c.NEW_VALUES : (c.OLD_VALUES || {});
- tbl += '| ' + (c.ROW_NUM||'') + ' | ' + (vals.NAME||'') + ' | ' + _dc(c,'PRICE') + ' | ' + _dc(c,'QTY') + ' | ' + _dc(c,'SUM') + ' | ' + (vals.DATE_START||'') + ' |
';
- });
- tbl += '
';
- diffBody.innerHTML += tbl;
+ diffBody.innerHTML += '
✗ ' + (ed.ERROR||'') + '
';
}
- });
+ } catch(e) {}
}
- lucide.createIcons();
}
- else if (d.type === 'error') {
- clearInterval(timerInterval);
- es.close();
- diffBody.innerHTML += '
✗ ' + d.message + '
';
- btn.innerHTML = '
Сравнить (LLM)';
- btn.disabled = false;
+ } catch(e) {}
+
+ // 2. Differ
+ diffBody.innerHTML += '
📊 Сравнение:
';
+ try {
+ var resp = await fetch('/differ.cfm?contract_id=' + contractId);
+ var data = await resp.json();
+ if (data.OK && data.ALL_CHANGES) {
+ var all = data.ALL_CHANGES;
+ diffBody.innerHTML = '';
+ all.forEach(function(ch) {
+ var s = ch.SUMMARY || {};
+ diffBody.innerHTML += '
📄 Допник (' + ch.TYPE + ')
';
+ diffBody.innerHTML += '
+' + (s.ADDED||0) + ' ~' + (s.CHANGED||0) + ' -' + (s.DELETED||0) + '
';
+ diffStatus.textContent = 'изм:' + (s.CHANGED||0) + ' +' + (s.ADDED||0) + ' -' + (s.DELETED||0);
+
+ var changes = ch.CHANGES || [];
+ if (changes.length === 0) {
+ diffBody.innerHTML += '
Нет изменений
';
+ } else {
+ var tbl = '
| № | Услуга | Цена | Объём | Сумма | Начало |
';
+ changes.forEach(function(c) {
+ var cls = c.CHANGE_TYPE === 'added' ? 'diff-added' : c.CHANGE_TYPE === 'deleted' ? 'diff-deleted' : c.CHANGE_TYPE === 'changed' ? 'diff-changed' : '';
+ var vals = c.NEW_VALUES && Object.keys(c.NEW_VALUES).length > 0 ? c.NEW_VALUES : (c.OLD_VALUES || {});
+ tbl += '| ' + (c.ROW_NUM||'') + ' | ' + (vals.NAME||'') + ' | ' + _dc(c,'PRICE') + ' | ' + _dc(c,'QTY') + ' | ' + _dc(c,'SUM') + ' | ' + (vals.DATE_START||'') + ' |
';
+ });
+ tbl += '
';
+ diffBody.innerHTML += tbl;
+ }
+ });
+ } else {
+ diffBody.innerHTML += '
✗ ' + (data.ERROR||'') + '
';
}
- };
+ } catch(e) { diffBody.innerHTML += '
✗ ' + e.message + '
'; }
- es.onerror = function() {
- clearInterval(timerInterval);
- es.close();
- if (diffBody.innerHTML === '') {
- diffBody.innerHTML = '
✗ Ошибка соединения
';
- }
- btn.innerHTML = '
Сравнить (LLM)';
- btn.disabled = false;
- lucide.createIcons();
- };
+ clearInterval(timerInterval);
+ diffStatus.textContent = '✓ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
- // Таймер для файловых строк
- var fileTimerInterval = setInterval(function() {
- var now = Date.now();
- document.querySelectorAll('.file-timer').forEach(function(el) {
- var start = parseInt(el.dataset.start);
- if (start) el.textContent = ((now - start) / 1000).toFixed(1) + 'с';
- });
- }, 200);
-
- // Сохранить интервалы для очистки
- es._fileTimer = fileTimerInterval;
- var origClose = es.close;
- es.close = function() {
- clearInterval(fileTimerInterval);
- origClose.call(es);
- };
+ btn.innerHTML = '
✓ Готово';
+ document.getElementById('chatCard').style.display = 'block';
+ lucide.createIcons();
});
function _dc(c, field) {