303 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Сервис интеграции ПП Парус 8 с WEB API
Дополнительный модуль: Интеграция с ЭДО "СБИС" (SBIS)
*/
//------------------------------
// Подключение внешних библиотек
//------------------------------
const xml2js = require("xml2js"); //Конвертация XML в JSON и JSON в XML
const _ = require("lodash"); //Работа с коллекциями и объектами
const rqp = require("request-promise"); //Работа с HTTP/HTTPS запросами
const { SMCHD_STORAGE_SYSTEM } = require("./sbis_config"); //Система хранения МЧД
//---------------------
// Глобальные константы
//---------------------
// Список тегов которые должны содержать массив
const tag = [
"Вложение",
"Редакция",
"ДокументОснование",
"ДокументСледствие",
"Подпись",
"Событие",
"Этап",
"Действие",
"Сертификат",
"Доверенность"
];
// Список имен тегов для замены ([Старое значение], [Новое значение])
const replaceTags = [
['"Иные получатели":', '"ИныеПолучатели":'],
['"Создатель документа":', '"СоздательДокумента":'],
];
//------------
// Тело модуля
//------------
//Замена наименований тегов (для корректной работы toXML)
const replaceTag = (obj) => {
for (let value of replaceTags) {
obj = obj.replace(new RegExp(value[0], 'g'), value[1]);
}
return obj;
};
//Обернуть содержимое тега в массив
const toArray = (obj, tags) => {
for (const prop in obj) {
const value = obj[prop];
if (tags.indexOf(prop) != -1 && !_.isArray(obj[prop])) {
obj[prop] = JSON.parse("[" + JSON.stringify(value) + "]");
}
if (typeof value === "object") {
toArray(value, tag);
}
}
return obj;
};
//Конвертация в XML
const toXML = obj => {
const builder = new xml2js.Builder();
return builder.buildObject(obj);
};
//Конвертация в JSON
const parseXML = xmlDoc => {
return new Promise((resolve, reject) => {
xml2js.parseString(xmlDoc, { explicitArray: false, mergeAttrs: true }, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
};
//Конвертация в JSON
const toJSON = async obj => {
let result = await parseXML(obj);
result = result.root;
toArray(result, tag);
return result;
};
//Добавление определённого количетсва часов к дате
const addHours = (dDate, nHours) => {
dDate.setTime(dDate.getTime() + nHours * 60 * 60 * 1000);
return new Date(dDate);
};
//Обработчик "До" подключения к сервису
const beforeConnect = async prms => {
//Подготовим параметры аутентификации
const prmAtribute = "Параметр";
const loginAtribute = "Логин";
const passAtribute = "Пароль";
//Сформируем запрос на аутентификацию
return {
options: {
headers: {
"content-type": "application/json;charset=utf-8"
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "СБИС.Аутентифицировать",
params: {
[prmAtribute]: {
[loginAtribute]: prms.service.sSrvUser,
[passAtribute]: prms.service.sSrvPass
}
},
id: 0
}),
simple: false
}
};
};
//Обработчик "После" подключения к сервису
const afterConnect = async prms => {
let resp = null;
//Разберем ответ
if (prms.queue.blResp) {
try {
resp = JSON.parse(prms.queue.blResp.toString());
} catch (e) {
throw new Error(`Неожиданный ответ сервера ЭДО "СБИС". Ошибка интерпретации: ${e.message}`);
}
} else {
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
}
//Если в нём нет ошибок
if (!resp.error) {
//Сохраним полученный токен доступа в контекст сервиса
return {
blResp: Buffer.from(resp.result),
sCtx: resp.result,
dCtxExp: addHours(new Date(), 23)
};
} else {
throw new Error(`Сервер ЭДО "СБИС" вернул ошибку: ${resp.error.message ? resp.error.message : "Неожиданная ошибка"}`);
}
};
//Обработчик "До" отправки запроса к сервису "СБИС"
const beforeDocParse = async prms => {
try {
//Считаем токен доступа из контекста сервиса
let sToken = null;
if (prms.service.sCtx) {
sToken = prms.service.sCtx;
}
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
if (!sToken) return { bUnAuth: true };
//Конвертируем XML из "Парус 8" в понятный "СБИСу" JSON
let obj = await toJSON(prms.queue.blMsg.toString());
//В зависимости от режима загрузки определим наименование узла
switch (obj.method) {
//Подготовка действия
case "СБИС.ПодготовитьДействие":
//Если требуется использовать МЧД
if (obj.params?.Документ?.Этап[0]?.Действие?.Сертификат?.Доверенность) {
obj.params.Документ.Этап[0].Действие.Сертификат.Доверенность.СистемаХраненияМЧД = SMCHD_STORAGE_SYSTEM;
}
break;
//Выполнение действия
case "СБИС.ВыполнитьДействие":
//Если требуется использовать МЧД
if (obj.params?.Документ?.Этап[0]?.Вложение?.Подпись?.Сертификат?.Доверенность) {
obj.params.Документ.Этап[0].Вложение.Подпись.Сертификат.Доверенность.СистемаХраненияМЧД = SMCHD_STORAGE_SYSTEM;
} else if (obj.params?.Документ?.Этап[0]?.Вложение[0]?.Подпись?.Сертификат?.Доверенность) {
for (i = 0; i < obj.params.Документ.Этап[0].Вложение.length; i++) {
obj.params.Документ.Этап[0].Вложение[i].Подпись.Сертификат.Доверенность.СистемаХраненияМЧД = SMCHD_STORAGE_SYSTEM;
}
}
break;
default:
}
//Собираем и отдаём общий результат работы
return {
options: {
headers: {
"Content-type": "application/json; charset=utf-8",
"X-SBISSessionID": sToken,
srv: 1
},
simple: false,
func: obj.method
},
blMsg: Buffer.from(JSON.stringify(obj))
};
} catch (e) {
throw Error(e);
}
};
//Обработчик "После" запроса к сервису "СБИС"
const afterDocParse = async prms => {
//Преобразуем JSON ответ сервиса "СБИС" в XML, понятный "Парус 8"
let resu = null;
if (prms.queue.blResp) {
try {
resu = toXML(JSON.parse(replaceTag(prms.queue.blResp.toString())));
} catch (e) {
throw new Error(`Неожиданный ответ сервера ЭДО "СБИС". Ошибка интерпретации: ${e.message}`);
}
} else {
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
}
//Возврат результата
return {
blResp: Buffer.from(resu)
};
};
//Обработчик "До" отправки запроса на загрузку вложения
const beforeAttParse = async prms => {
try {
//Считаем токен доступа из контекста сервиса
let sToken = null;
if (prms.service.sCtx) {
sToken = prms.service.sCtx;
}
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
if (!sToken) return { bUnAuth: true };
//Собираем и отдаём общий результат работы
return {
options: {
headers: {
"Content-type": "application/json; charset=utf-8",
"X-SBISSessionID": sToken,
srv: 1
},
simple: false
}
};
} catch (e) {
throw Error(e);
}
};
//Обработчик "После" отправки запроса на загрузку вложения
const afterAttParse = async prms => {
if (prms.queue.blResp) {
if (prms.optionsResp.statusCode == 200) {
return;
} else {
let iterable = [1, 2, 3, 4, 5];
//Если не превышает лимита запросов
for (let value of iterable) {
if (prms.optionsResp.statusCode != 200) {
//Выполним повторный запрос
await new Promise(resolve => setTimeout(resolve, 2000));
let serverResp = await rqp(prms.options);
//Сохраняем полученный ответ
prms.queue.blResp = Buffer.from(serverResp.body || "");
prms.optionsResp.statusCode = serverResp.statusCode;
//Если пришел ответ
if (prms.queue.blResp && serverResp.statusCode == 200) {
//Вернем загруженный документ
return {
blResp: prms.queue.blResp
};
}
}
}
//Если был ответ от сервера с ошибкой (иначе мы сюда не попадём)
if (prms.queue.blResp) {
//Разберем сообщение об ошибке
let resu = null;
try {
resu = JSON.parse(prms.queue.blResp.toString());
} catch (e) {
throw new Error(`Неожиданный ответ сервера ЭДО "СБИС": ${prms.queue.blResp.toString()}`);
}
throw new Error(`Сервер ЭДО "СБИС" вернул ошибку: ${resu?.error?.message}`);
} else {
//Возврат результата
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
}
}
} else {
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
}
//Возврат результата
return;
};
//-----------------
// Интерфейс модуля
//-----------------
exports.beforeConnect = beforeConnect;
exports.afterConnect = afterConnect;
exports.beforeDocParse = beforeDocParse;
exports.afterDocParse = afterDocParse;
exports.beforeAttParse = beforeAttParse;
exports.afterAttParse = afterAttParse;