vv-iiko-pp-parser 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +151 -0
- package/README.md +582 -0
- package/dist/clients/clients.fetcher.d.ts +102 -0
- package/dist/clients/clients.fetcher.d.ts.map +1 -0
- package/dist/clients/clients.fetcher.js +196 -0
- package/dist/clients/clients.fetcher.js.map +1 -0
- package/dist/clients/clients.parser.d.ts +144 -0
- package/dist/clients/clients.parser.d.ts.map +1 -0
- package/dist/clients/clients.parser.js +473 -0
- package/dist/clients/clients.parser.js.map +1 -0
- package/dist/clients/clients.service.d.ts +90 -0
- package/dist/clients/clients.service.d.ts.map +1 -0
- package/dist/clients/clients.service.js +277 -0
- package/dist/clients/clients.service.js.map +1 -0
- package/dist/clients/clients.types.d.ts +127 -0
- package/dist/clients/clients.types.d.ts.map +1 -0
- package/dist/clients/clients.types.js +6 -0
- package/dist/clients/clients.types.js.map +1 -0
- package/dist/clients/index.d.ts +5 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +25 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/core/auth/iiko-auth.service.d.ts +52 -0
- package/dist/core/auth/iiko-auth.service.d.ts.map +1 -0
- package/dist/core/auth/iiko-auth.service.js +302 -0
- package/dist/core/auth/iiko-auth.service.js.map +1 -0
- package/dist/core/http/iiko-http.client.d.ts +13 -0
- package/dist/core/http/iiko-http.client.d.ts.map +1 -0
- package/dist/core/http/iiko-http.client.js +55 -0
- package/dist/core/http/iiko-http.client.js.map +1 -0
- package/dist/core/iiko-parser.config.d.ts +28 -0
- package/dist/core/iiko-parser.config.d.ts.map +1 -0
- package/dist/core/iiko-parser.config.js +76 -0
- package/dist/core/iiko-parser.config.js.map +1 -0
- package/dist/core/parser/iiko-parser.service.d.ts +15 -0
- package/dist/core/parser/iiko-parser.service.d.ts.map +1 -0
- package/dist/core/parser/iiko-parser.service.js +156 -0
- package/dist/core/parser/iiko-parser.service.js.map +1 -0
- package/dist/core/session/iiko-session.service.d.ts +68 -0
- package/dist/core/session/iiko-session.service.d.ts.map +1 -0
- package/dist/core/session/iiko-session.service.js +209 -0
- package/dist/core/session/iiko-session.service.js.map +1 -0
- package/dist/iiko-parser.d.ts +149 -0
- package/dist/iiko-parser.d.ts.map +1 -0
- package/dist/iiko-parser.js +209 -0
- package/dist/iiko-parser.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/company-profile.interface.d.ts +12 -0
- package/dist/interfaces/company-profile.interface.d.ts.map +1 -0
- package/dist/interfaces/company-profile.interface.js +3 -0
- package/dist/interfaces/company-profile.interface.js.map +1 -0
- package/dist/invoices/index.d.ts +6 -0
- package/dist/invoices/index.d.ts.map +1 -0
- package/dist/invoices/index.js +22 -0
- package/dist/invoices/index.js.map +1 -0
- package/dist/invoices/invoices.fetcher.d.ts +49 -0
- package/dist/invoices/invoices.fetcher.d.ts.map +1 -0
- package/dist/invoices/invoices.fetcher.js +141 -0
- package/dist/invoices/invoices.fetcher.js.map +1 -0
- package/dist/invoices/invoices.filters.d.ts +40 -0
- package/dist/invoices/invoices.filters.d.ts.map +1 -0
- package/dist/invoices/invoices.filters.js +81 -0
- package/dist/invoices/invoices.filters.js.map +1 -0
- package/dist/invoices/invoices.parser.d.ts +101 -0
- package/dist/invoices/invoices.parser.d.ts.map +1 -0
- package/dist/invoices/invoices.parser.js +540 -0
- package/dist/invoices/invoices.parser.js.map +1 -0
- package/dist/invoices/invoices.service.d.ts +82 -0
- package/dist/invoices/invoices.service.d.ts.map +1 -0
- package/dist/invoices/invoices.service.js +361 -0
- package/dist/invoices/invoices.service.js.map +1 -0
- package/dist/invoices/invoices.storage.service.d.ts +30 -0
- package/dist/invoices/invoices.storage.service.d.ts.map +1 -0
- package/dist/invoices/invoices.storage.service.js +135 -0
- package/dist/invoices/invoices.storage.service.js.map +1 -0
- package/dist/invoices/invoices.types.d.ts +94 -0
- package/dist/invoices/invoices.types.d.ts.map +1 -0
- package/dist/invoices/invoices.types.js +3 -0
- package/dist/invoices/invoices.types.js.map +1 -0
- package/dist/types.d.ts +138 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ClientsParser = void 0;
|
|
37
|
+
const cheerio = __importStar(require("cheerio"));
|
|
38
|
+
class ClientsParser {
|
|
39
|
+
/**
|
|
40
|
+
* Парсит HTML таблицу со списком клиентов.
|
|
41
|
+
*
|
|
42
|
+
* Поддерживает две структуры HTML:
|
|
43
|
+
* 1. Полный HTML с контейнером #clientsTable_ajax (используется на странице /en/cabinet/clients.html)
|
|
44
|
+
* 2. AJAX фрагмент только с <tbody class="ajax"> (используется в AJAX endpoint /en/cabinet/ajax/clients-list.html)
|
|
45
|
+
*
|
|
46
|
+
* Из каждой строки таблицы извлекает:
|
|
47
|
+
* - name: название клиента (текст из ссылки <a>)
|
|
48
|
+
* - clientId: идентификатор клиента (из href ?clientId=XXX)
|
|
49
|
+
* - shortName: краткое название (опционально, из <div class="small mt-2">)
|
|
50
|
+
*
|
|
51
|
+
* @param html HTML контент от /en/cabinet/ajax/clients-list.html или полная страница
|
|
52
|
+
* @returns Массив объектов с базовой информацией о клиентах
|
|
53
|
+
*/
|
|
54
|
+
parseClientsTable(html) {
|
|
55
|
+
const $ = cheerio.load(html);
|
|
56
|
+
const clients = [];
|
|
57
|
+
// Пытаемся найти таблицу в контейнере #clientsTable_ajax (полный HTML)
|
|
58
|
+
let $rows = $('#clientsTable_ajax tbody.ajax tr');
|
|
59
|
+
// Если не найдено, ищем напрямую tbody.ajax (AJAX фрагмент)
|
|
60
|
+
if ($rows.length === 0) {
|
|
61
|
+
$rows = $('tbody.ajax tr');
|
|
62
|
+
}
|
|
63
|
+
// Обрабатываем каждую строку таблицы
|
|
64
|
+
$rows.each((_, row) => {
|
|
65
|
+
const $row = $(row);
|
|
66
|
+
// Ищем второй столбец (td) с информацией о клиенте (первый столбец - тип)
|
|
67
|
+
const $nameTd = $row.find('td').eq(1);
|
|
68
|
+
// Извлекаем ссылку на клиента
|
|
69
|
+
const $link = $nameTd.find('a').first();
|
|
70
|
+
const href = $link.attr('href');
|
|
71
|
+
const name = $link.text().trim();
|
|
72
|
+
// Извлекаем clientId из URL
|
|
73
|
+
const clientIdMatch = href?.match(/clientId=(\d+)/);
|
|
74
|
+
const clientId = clientIdMatch ? clientIdMatch[1] : null;
|
|
75
|
+
if (name && clientId) {
|
|
76
|
+
clients.push({ name, clientId });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return clients;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Извлекает общее количество страниц из HTML пагинации.
|
|
83
|
+
*
|
|
84
|
+
* Основной способ:
|
|
85
|
+
* - Ищет все элементы пагинации: '.pagination a.page-link[data-page]'
|
|
86
|
+
* - Находит максимальное значение атрибута data-page
|
|
87
|
+
* - Возвращает его как totalPages
|
|
88
|
+
*
|
|
89
|
+
* Резервный способ (fallback):
|
|
90
|
+
* - Если пагинация отсутствует, ищет '.pagination-total'
|
|
91
|
+
* - Извлекает общее количество записей из текста
|
|
92
|
+
* - Вычисляет totalPages = Math.ceil(totalEntries / perPage)
|
|
93
|
+
*
|
|
94
|
+
* @param html HTML контент страницы со списком клиентов
|
|
95
|
+
* @param perPage Количество элементов на странице (для расчета fallback)
|
|
96
|
+
* @returns Общее количество страниц или 1, если определить не удалось
|
|
97
|
+
*/
|
|
98
|
+
parseTotalPages(html, perPage) {
|
|
99
|
+
const $ = cheerio.load(html);
|
|
100
|
+
// Основной способ: извлекаем максимальный data-page из пагинации
|
|
101
|
+
let maxPage = 0;
|
|
102
|
+
$('.pagination a.page-link[data-page]').each((_, element) => {
|
|
103
|
+
const pageAttr = $(element).attr('data-page');
|
|
104
|
+
if (pageAttr) {
|
|
105
|
+
const pageNum = parseInt(pageAttr, 10);
|
|
106
|
+
if (!isNaN(pageNum) && pageNum > maxPage) {
|
|
107
|
+
maxPage = pageNum;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
if (maxPage > 0) {
|
|
112
|
+
return maxPage;
|
|
113
|
+
}
|
|
114
|
+
// Резервный способ: расчет через общее количество записей
|
|
115
|
+
const paginationTotalText = $('.pagination-total').text().trim();
|
|
116
|
+
const totalEntriesMatch = paginationTotalText.match(/(\d+)/);
|
|
117
|
+
if (totalEntriesMatch) {
|
|
118
|
+
const totalEntries = parseInt(totalEntriesMatch[1], 10);
|
|
119
|
+
if (!isNaN(totalEntries) && totalEntries > 0) {
|
|
120
|
+
return Math.ceil(totalEntries / perPage);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Если ничего не найдено, возвращаем 1 (минимум одна страница)
|
|
124
|
+
return 1;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Парсит HTML страницу профиля клиента.
|
|
128
|
+
*
|
|
129
|
+
* Извлекает детальную информацию о клиенте со страницы /en/cabinet/client-area/index.html?clientId=XXX:
|
|
130
|
+
*
|
|
131
|
+
* - uuid: Уникальный идентификатор (UID) из кнопки "UID: XXX-XXX-XXX"
|
|
132
|
+
* - iikoVersion: Версия iiko из кнопки "Version iiko: X.X.XXXX"
|
|
133
|
+
* - email: Email из таблицы (строка "E-mail")
|
|
134
|
+
* - phone: Телефон из таблицы (строка "Phone")
|
|
135
|
+
* - address: Адрес из таблицы (строка "Address")
|
|
136
|
+
* - legalEntity: Название юридического лица (из строки "Legal entity", убираются кавычки «»)
|
|
137
|
+
* - tin: ИНН (Company tax id, только цифры)
|
|
138
|
+
*
|
|
139
|
+
* Нормализация данных:
|
|
140
|
+
* - UUID: возвращается как есть (формат: XXX-XXX-XXX)
|
|
141
|
+
* - TIN: удаляются все нечисловые символы (пробелы, дефисы и т.д.)
|
|
142
|
+
* - Legal Entity: удаляются внешние кавычки «», экранированные кавычки \" заменяются на "
|
|
143
|
+
*
|
|
144
|
+
* @param html HTML контент страницы профиля клиента
|
|
145
|
+
* @param clientId ID клиента для логирования ошибок
|
|
146
|
+
* @param name Имя клиента для включения в результат
|
|
147
|
+
* @returns Объект ClientData со всей информацией о клиенте или null при ошибке парсинга
|
|
148
|
+
*/
|
|
149
|
+
parseClientProfile(html, clientId, name) {
|
|
150
|
+
const $ = cheerio.load(html);
|
|
151
|
+
try {
|
|
152
|
+
// Извлечение UUID из кнопки "UID: XXX-XXX-XXX"
|
|
153
|
+
// Ищем кнопку с текстом "UID:" и внутри span.copy-to-clipboard-text
|
|
154
|
+
let uuid = '';
|
|
155
|
+
$('button').each((_, button) => {
|
|
156
|
+
const $button = $(button);
|
|
157
|
+
const buttonText = $button.text();
|
|
158
|
+
if (buttonText.includes('UID:')) {
|
|
159
|
+
uuid = $button.find('.copy-to-clipboard-text').text().trim();
|
|
160
|
+
return false; // break
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// Извлечение версии iiko из кнопки "Version iiko: X.X.XXXX"
|
|
164
|
+
let iikoVersion;
|
|
165
|
+
$('button').each((_, button) => {
|
|
166
|
+
const $button = $(button);
|
|
167
|
+
const buttonText = $button.text();
|
|
168
|
+
if (buttonText.includes('Version iiko:')) {
|
|
169
|
+
iikoVersion = $button.find('.copy-to-clipboard-text').text().trim();
|
|
170
|
+
return false; // break
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
// Извлечение контактной информации и юридических данных из таблицы
|
|
174
|
+
let email;
|
|
175
|
+
let phone;
|
|
176
|
+
let address;
|
|
177
|
+
let legalEntity;
|
|
178
|
+
let tin;
|
|
179
|
+
// Ищем все строки таблицы
|
|
180
|
+
$('table.table tr').each((_, row) => {
|
|
181
|
+
const $row = $(row);
|
|
182
|
+
const header = $row.find('th').text().trim();
|
|
183
|
+
const $td = $row.find('td');
|
|
184
|
+
const value = $td.text().trim();
|
|
185
|
+
if (header === 'E-mail') {
|
|
186
|
+
// Email может быть в виде ссылки <a href="mailto:...">
|
|
187
|
+
const emailLink = $td.find('a').first().text().trim();
|
|
188
|
+
email = emailLink || value || undefined;
|
|
189
|
+
}
|
|
190
|
+
else if (header === 'Phone') {
|
|
191
|
+
phone = value || undefined;
|
|
192
|
+
}
|
|
193
|
+
else if (header === 'Address') {
|
|
194
|
+
// Адрес может быть многострочным с <br>
|
|
195
|
+
// Заменяем <br> на пробелы и нормализуем
|
|
196
|
+
const addressHtml = $td.html() || '';
|
|
197
|
+
if (addressHtml) {
|
|
198
|
+
address = addressHtml
|
|
199
|
+
.replace(/<br\s*\/?>/gi, ' ') // Заменяем <br> на пробел
|
|
200
|
+
.replace(/<[^>]+>/g, '') // Удаляем HTML теги
|
|
201
|
+
.replace(/\s+/g, ' ') // Множественные пробелы -> один пробел
|
|
202
|
+
.trim() || undefined; // Убираем пробелы по краям
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (header === 'Legal entity') {
|
|
206
|
+
// Legal entity содержит юр.лицо и ИНН в одной ячейке
|
|
207
|
+
// Формат: - «ООО "Название"»<br>Company tax id: XXX XXX XXX
|
|
208
|
+
const fullText = $td.html() || '';
|
|
209
|
+
// Извлекаем юр.лицо (строка до <br> или "Company tax id:")
|
|
210
|
+
const legalEntityMatch = fullText.match(/- (.+?)(?:<br>|Company tax id:)/i);
|
|
211
|
+
if (legalEntityMatch) {
|
|
212
|
+
legalEntity = this.normalizeLegalEntity(legalEntityMatch[1]);
|
|
213
|
+
}
|
|
214
|
+
// Извлекаем ИНН (после "Company tax id:")
|
|
215
|
+
const tinMatch = fullText.match(/Company tax id:\s*(.+)/i);
|
|
216
|
+
if (tinMatch) {
|
|
217
|
+
tin = this.extractTin(tinMatch[1]);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
// Возвращаем полную информацию о клиенте
|
|
222
|
+
return {
|
|
223
|
+
uuid,
|
|
224
|
+
name,
|
|
225
|
+
clientId,
|
|
226
|
+
iikoVersion,
|
|
227
|
+
email,
|
|
228
|
+
phone,
|
|
229
|
+
address,
|
|
230
|
+
legalEntity,
|
|
231
|
+
tin,
|
|
232
|
+
isChain: false, // Определяется позже в service
|
|
233
|
+
members: [], // Заполняется позже в service если isChain=true
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
console.error(`Failed to parse client profile for clientId=${clientId}:`, error);
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Нормализует строку с названием юридического лица.
|
|
243
|
+
*
|
|
244
|
+
* Выполняет следующие преобразования:
|
|
245
|
+
* 1. Удаляет внешние кавычки-ёлочки («»)
|
|
246
|
+
* 2. Заменяет экранированные кавычки \" на обычные "
|
|
247
|
+
* 3. Удаляет лишние пробелы в начале и конце
|
|
248
|
+
*
|
|
249
|
+
* Примеры:
|
|
250
|
+
* - «ООО \"AEF GROUP\"» → ООО "AEF GROUP"
|
|
251
|
+
* - «ИП Иванов» → ИП Иванов
|
|
252
|
+
*
|
|
253
|
+
* @param text Исходная строка с названием юридического лица
|
|
254
|
+
* @returns Нормализованная строка или undefined, если входные данные пусты
|
|
255
|
+
*/
|
|
256
|
+
normalizeLegalEntity(text) {
|
|
257
|
+
if (!text)
|
|
258
|
+
return undefined;
|
|
259
|
+
// Удаляем внешние кавычки-ёлочки «»
|
|
260
|
+
let normalized = text.replace(/^«|»$/g, '');
|
|
261
|
+
// Заменяем экранированные кавычки \" на обычные "
|
|
262
|
+
normalized = normalized.replace(/\\"/g, '"');
|
|
263
|
+
return normalized.trim() || undefined;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Извлекает ИНН (Tax Identification Number) из текста.
|
|
267
|
+
*
|
|
268
|
+
* Ищет последовательность цифр в тексте и удаляет все нечисловые символы
|
|
269
|
+
* (пробелы, дефисы, точки и т.д.), оставляя только цифры.
|
|
270
|
+
*
|
|
271
|
+
* Примеры:
|
|
272
|
+
* - "304 467 088" → "304467088"
|
|
273
|
+
* - "12-34-567890" → "1234567890"
|
|
274
|
+
* - "123.456.789" → "123456789"
|
|
275
|
+
*
|
|
276
|
+
* @param text Исходная строка с ИНН
|
|
277
|
+
* @returns Строка, содержащая только цифры, или undefined, если ИНН не найден
|
|
278
|
+
*/
|
|
279
|
+
extractTin(text) {
|
|
280
|
+
if (!text)
|
|
281
|
+
return undefined;
|
|
282
|
+
// Ищем последовательность цифр (возможно с пробелами/разделителями)
|
|
283
|
+
const tinMatch = text.match(/\d[\d\s\-\.]+\d/);
|
|
284
|
+
if (!tinMatch)
|
|
285
|
+
return undefined;
|
|
286
|
+
// Удаляем ВСЕ нечисловые символы
|
|
287
|
+
const tin = tinMatch[0].replace(/\D/g, '');
|
|
288
|
+
return tin || undefined;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Определяет, является ли клиент Chain (группой компаний).
|
|
292
|
+
*
|
|
293
|
+
* Chain определяется наличием хотя бы одной из кнопок на странице client-area:
|
|
294
|
+
* - "Cloud license allocation"
|
|
295
|
+
* - "SaaS license allocation"
|
|
296
|
+
*
|
|
297
|
+
* Эти кнопки ведут на:
|
|
298
|
+
* - /client-area/licenses/cloud-mapping.html
|
|
299
|
+
* - /client-area/licenses/saas-mapping.html
|
|
300
|
+
*
|
|
301
|
+
* Метод ищет ссылки (<a>) содержащие эти пути в атрибуте href.
|
|
302
|
+
*
|
|
303
|
+
* @param html HTML контент страницы client-area
|
|
304
|
+
* @returns true если клиент является Chain, иначе false
|
|
305
|
+
*/
|
|
306
|
+
detectIsChain(html) {
|
|
307
|
+
const $ = cheerio.load(html);
|
|
308
|
+
// Ищем ссылки на allocation страницы
|
|
309
|
+
let hasCloudAllocation = false;
|
|
310
|
+
let hasSaasAllocation = false;
|
|
311
|
+
$('a').each((_, element) => {
|
|
312
|
+
const href = $(element).attr('href');
|
|
313
|
+
if (href) {
|
|
314
|
+
if (href.includes('cloud-mapping.html')) {
|
|
315
|
+
hasCloudAllocation = true;
|
|
316
|
+
}
|
|
317
|
+
if (href.includes('saas-mapping.html')) {
|
|
318
|
+
hasSaasAllocation = true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
return hasCloudAllocation || hasSaasAllocation;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Парсит HTML страницу allocation (cloud или saas).
|
|
326
|
+
*
|
|
327
|
+
* Извлекает членов chain из JSON данных в атрибуте data-template-params.
|
|
328
|
+
*
|
|
329
|
+
* Структура JSON:
|
|
330
|
+
* - tps: массив торговых точек (members chain)
|
|
331
|
+
* - mapping: распределение лицензий по продуктам
|
|
332
|
+
*
|
|
333
|
+
* Каждый элемент tps содержит:
|
|
334
|
+
* - id: внутренний ID
|
|
335
|
+
* - obfuscated_id: ID для URL (используется как clientId)
|
|
336
|
+
* - name: название торговой точки
|
|
337
|
+
* - link: ссылка на профиль (/client-area/index.html?clientId=XXX)
|
|
338
|
+
*
|
|
339
|
+
* Mapping содержит количество лицензий по формуле:
|
|
340
|
+
* mapping[productId][date][firmId][tpId] = количество
|
|
341
|
+
*
|
|
342
|
+
* Для подсчёта allocation суммируются все значения для каждого tp.
|
|
343
|
+
*
|
|
344
|
+
* @param html HTML контент страницы cloud-mapping или saas-mapping
|
|
345
|
+
* @returns Массив AllocationMember с информацией о членах и их allocation
|
|
346
|
+
*/
|
|
347
|
+
parseAllocationPage(html) {
|
|
348
|
+
const $ = cheerio.load(html);
|
|
349
|
+
const members = [];
|
|
350
|
+
try {
|
|
351
|
+
// Ищем form с data-template-params
|
|
352
|
+
const $form = $('form[data-template-params]');
|
|
353
|
+
if ($form.length === 0) {
|
|
354
|
+
console.warn('⚠️ No form with data-template-params found');
|
|
355
|
+
return members;
|
|
356
|
+
}
|
|
357
|
+
// Извлекаем JSON из атрибута
|
|
358
|
+
const dataAttr = $form.attr('data-template-params');
|
|
359
|
+
if (!dataAttr) {
|
|
360
|
+
console.warn('⚠️ data-template-params attribute is empty');
|
|
361
|
+
return members;
|
|
362
|
+
}
|
|
363
|
+
// Декодируем HTML entities (" → ")
|
|
364
|
+
const jsonStr = dataAttr
|
|
365
|
+
.replace(/"/g, '"')
|
|
366
|
+
.replace(/&/g, '&')
|
|
367
|
+
.replace(/</g, '<')
|
|
368
|
+
.replace(/>/g, '>');
|
|
369
|
+
// Парсим JSON
|
|
370
|
+
const data = JSON.parse(jsonStr);
|
|
371
|
+
if (!data.tps || !Array.isArray(data.tps)) {
|
|
372
|
+
console.warn('⚠️ No tps array in data-template-params');
|
|
373
|
+
return members;
|
|
374
|
+
}
|
|
375
|
+
// Создаём Map для подсчёта allocation каждого member
|
|
376
|
+
const allocationMap = new Map();
|
|
377
|
+
// Если есть mapping, подсчитываем allocation
|
|
378
|
+
if (data.mapping && typeof data.mapping === 'object') {
|
|
379
|
+
// mapping[productId][date][firmId][tpId] = qty
|
|
380
|
+
for (const productId in data.mapping) {
|
|
381
|
+
const productData = data.mapping[productId];
|
|
382
|
+
for (const date in productData) {
|
|
383
|
+
const dateData = productData[date];
|
|
384
|
+
for (const firmId in dateData) {
|
|
385
|
+
const firmData = dateData[firmId];
|
|
386
|
+
for (const tpId in firmData) {
|
|
387
|
+
const qty = parseInt(firmData[tpId], 10);
|
|
388
|
+
if (!isNaN(qty) && qty > 0) {
|
|
389
|
+
const current = allocationMap.get(tpId) || 0;
|
|
390
|
+
allocationMap.set(tpId, current + qty);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// Обрабатываем каждую торговую точку
|
|
398
|
+
for (const tp of data.tps) {
|
|
399
|
+
// obfuscated_id используется в URL как clientId
|
|
400
|
+
const clientId = tp.obfuscated_id?.toString() || tp.id?.toString();
|
|
401
|
+
const name = tp.name || '';
|
|
402
|
+
if (!clientId || !name) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
// Получаем allocation для этого tp
|
|
406
|
+
const allocation = allocationMap.get(tp.id?.toString() || '') || 0;
|
|
407
|
+
members.push({
|
|
408
|
+
name,
|
|
409
|
+
clientId,
|
|
410
|
+
allocation,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
console.log(`📊 Parsed ${members.length} members from data-template-params`);
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
console.error('❌ Error parsing data-template-params:', error);
|
|
417
|
+
// Fallback: пытаемся парсить старым способом через HTML таблицу
|
|
418
|
+
return this.parseAllocationPageLegacy(html);
|
|
419
|
+
}
|
|
420
|
+
return members;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Legacy метод парсинга allocation через HTML таблицу.
|
|
424
|
+
*
|
|
425
|
+
* Используется как fallback если JSON парсинг не сработал.
|
|
426
|
+
* Ищет таблицу с членами chain и input полями с allocation.
|
|
427
|
+
*
|
|
428
|
+
* @param html HTML контент страницы allocation
|
|
429
|
+
* @returns Массив AllocationMember
|
|
430
|
+
*/
|
|
431
|
+
parseAllocationPageLegacy(html) {
|
|
432
|
+
const $ = cheerio.load(html);
|
|
433
|
+
const members = [];
|
|
434
|
+
// Ищем таблицу в блоке "Store of the chain"
|
|
435
|
+
$('table tbody tr').each((_, row) => {
|
|
436
|
+
const $row = $(row);
|
|
437
|
+
// Первый столбец содержит ссылку на клиента
|
|
438
|
+
const $firstTd = $row.find('td').first();
|
|
439
|
+
const $link = $firstTd.find('a');
|
|
440
|
+
if ($link.length === 0) {
|
|
441
|
+
return; // skip rows without links
|
|
442
|
+
}
|
|
443
|
+
const name = $link.text().trim();
|
|
444
|
+
const href = $link.attr('href');
|
|
445
|
+
// Извлекаем clientId из URL
|
|
446
|
+
const clientIdMatch = href?.match(/clientId=(\d+)/);
|
|
447
|
+
const clientId = clientIdMatch ? clientIdMatch[1] : null;
|
|
448
|
+
if (!clientId || !name) {
|
|
449
|
+
return; // skip invalid rows
|
|
450
|
+
}
|
|
451
|
+
// Ищем input поле с allocation
|
|
452
|
+
let allocation = 0;
|
|
453
|
+
$row.find('input[type="number"]').each((_, input) => {
|
|
454
|
+
const value = $(input).attr('value') || $(input).val();
|
|
455
|
+
if (value) {
|
|
456
|
+
const parsed = parseInt(value.toString(), 10);
|
|
457
|
+
if (!isNaN(parsed)) {
|
|
458
|
+
allocation = parsed;
|
|
459
|
+
return false; // break
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
members.push({
|
|
464
|
+
name,
|
|
465
|
+
clientId,
|
|
466
|
+
allocation,
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
return members;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
exports.ClientsParser = ClientsParser;
|
|
473
|
+
//# sourceMappingURL=clients.parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.parser.js","sourceRoot":"","sources":["../../src/clients/clients.parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { IikoSessionService } from '../core/session/iiko-session.service';
|
|
2
|
+
import { ParserProgressCallback } from '../types';
|
|
3
|
+
import { ClientsFilter, ClientData } from './clients.types';
|
|
4
|
+
export declare class ClientsService {
|
|
5
|
+
private sessionService;
|
|
6
|
+
private progressCallback?;
|
|
7
|
+
private fetcher;
|
|
8
|
+
private parser;
|
|
9
|
+
constructor(sessionService: IikoSessionService, progressCallback?: ParserProgressCallback | undefined);
|
|
10
|
+
/**
|
|
11
|
+
* Загружает и парсит список клиентов с пагинацией.
|
|
12
|
+
*
|
|
13
|
+
* Алгоритм работы:
|
|
14
|
+
* 1. Инициализирует сессию (должна быть выполнена до вызова через ensureSession())
|
|
15
|
+
* 2. Загружает страницы клиентов через /en/cabinet/ajax/clients-list.html
|
|
16
|
+
* 3. Парсит каждую страницу, извлекая name и clientId из HTML таблицы
|
|
17
|
+
* 4. Останавливается когда:
|
|
18
|
+
* - Получена пустая страница (нет больше клиентов)
|
|
19
|
+
* - Достигнут лимит maxPages
|
|
20
|
+
* - Получено меньше элементов чем perPage (последняя страница)
|
|
21
|
+
* 5. Для каждого клиента загружает детальную информацию с /en/cabinet/client-area/index.html
|
|
22
|
+
* 6. Парсит профиль клиента (UUID, iiko версия, email, phone, юр.лицо, ИНН)
|
|
23
|
+
*
|
|
24
|
+
* Параметры пагинации:
|
|
25
|
+
* - query: поисковый запрос (фильтрация по имени или UID на стороне сервера)
|
|
26
|
+
* - perPage: количество клиентов на странице (по умолчанию: 20)
|
|
27
|
+
* - maxPages: максимальное количество страниц для загрузки (по умолчанию: все)
|
|
28
|
+
*
|
|
29
|
+
* Progress события:
|
|
30
|
+
* - fetch_clients: при загрузке каждой страницы списка
|
|
31
|
+
* - parse_clients: после завершения парсинга всех страниц
|
|
32
|
+
* - fetch_client_details: при загрузке деталей каждого клиента
|
|
33
|
+
*
|
|
34
|
+
* @param filter Опциональные параметры фильтрации и пагинации
|
|
35
|
+
* @returns Массив клиентов с полной информацией (UUID, версия, контакты, юр.данные)
|
|
36
|
+
*/
|
|
37
|
+
fetchAndParseClients(filter?: ClientsFilter): Promise<ClientData[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Обогащает список клиентов детальной информацией с поддержкой Chain логики.
|
|
40
|
+
*
|
|
41
|
+
* Алгоритм:
|
|
42
|
+
* 1. Создаёт Map для дедупликации (clientId → ClientData)
|
|
43
|
+
* 2. Создаёт Set для отслеживания членов chain (чтобы исключить их из root списка)
|
|
44
|
+
* 3. Для каждого клиента:
|
|
45
|
+
* - Загружает профиль
|
|
46
|
+
* - Проверяет uuid/clientId (пропускает если нет)
|
|
47
|
+
* - Определяет isChain
|
|
48
|
+
* - Если chain: загружает allocation и собирает members
|
|
49
|
+
* 4. Финальный результат: только клиенты, которые НЕ являются members других chain
|
|
50
|
+
*
|
|
51
|
+
* @param clients Список клиентов с базовой информацией (name, clientId)
|
|
52
|
+
* @returns Массив клиентов с полной информацией, chain содержат members[], дедуплицированные
|
|
53
|
+
*/
|
|
54
|
+
private enrichWithDetails;
|
|
55
|
+
/**
|
|
56
|
+
* Загружает членов chain из allocation страниц.
|
|
57
|
+
*
|
|
58
|
+
* Алгоритм:
|
|
59
|
+
* 1. Загружает cloud-mapping.html и saas-mapping.html
|
|
60
|
+
* 2. Парсит списки members из обеих страниц
|
|
61
|
+
* 3. Объединяет данные (по clientId):
|
|
62
|
+
* - Если member есть только в cloud → cloudAllocation > 0, saasAllocation = 0
|
|
63
|
+
* - Если member есть только в saas → cloudAllocation = 0, saasAllocation > 0
|
|
64
|
+
* - Если member есть в обоих → оба allocation > 0
|
|
65
|
+
* 4. Для каждого member загружает полный профиль (UUID, email, phone, etc.)
|
|
66
|
+
* 5. Возвращает массив ClientMember
|
|
67
|
+
*
|
|
68
|
+
* @param chainClientId ID chain для загрузки allocation
|
|
69
|
+
* @param chainName Имя chain для логирования
|
|
70
|
+
* @returns Массив ClientMember с полной информацией и allocation
|
|
71
|
+
*/
|
|
72
|
+
private loadChainMembers;
|
|
73
|
+
/**
|
|
74
|
+
* Отправляет событие прогресса в callback, если он был передан.
|
|
75
|
+
*
|
|
76
|
+
* Используется для информирования внешнего кода о текущем состоянии выполнения:
|
|
77
|
+
* - fetch_clients: загрузка страниц списка клиентов
|
|
78
|
+
* - parse_clients: завершение парсинга списка
|
|
79
|
+
* - fetch_client_details: загрузка деталей каждого клиента
|
|
80
|
+
* - detect_chain: определение chain
|
|
81
|
+
* - load_chain_members: загрузка членов chain
|
|
82
|
+
*
|
|
83
|
+
* Безопасно проверяет наличие callback перед вызовом.
|
|
84
|
+
*
|
|
85
|
+
* @param stage Стадия выполнения операции
|
|
86
|
+
* @param data Данные о прогрессе (текущее значение, общее количество, сообщение)
|
|
87
|
+
*/
|
|
88
|
+
private report;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=clients.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.service.d.ts","sourceRoot":"","sources":["../../src/clients/clients.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAGlD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAkD,MAAM,iBAAiB,CAAC;AAE5G,qBAAa,cAAc;IAKvB,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,gBAAgB,CAAC;IAL3B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAgB;gBAGpB,cAAc,EAAE,kBAAkB,EAClC,gBAAgB,CAAC,EAAE,sBAAsB,YAAA;IAMnD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACU,oBAAoB,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAyDhF;;;;;;;;;;;;;;;OAeG;YACW,iBAAiB;IAyE/B;;;;;;;;;;;;;;;;OAgBG;YACW,gBAAgB;IA8E9B;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,MAAM;CAaf"}
|