934 lines
45 KiB
JavaScript
934 lines
45 KiB
JavaScript
/*
|
||
Сервис интеграции ПП Парус 8 с WEB API
|
||
Дополнительный модуль: Интеграция с ЭДО "ДИАДОК" (DIADOC)
|
||
*/
|
||
|
||
//------------------------------
|
||
// Подключение внешних библиотек
|
||
//------------------------------
|
||
|
||
const xml2js = require("xml2js"); //Конвертация XML в JSON и JSON в XML
|
||
const _ = require("lodash"); //Работа с коллекциями и объектами
|
||
const rqp = require("request-promise"); //Работа с HTTP/HTTPS запросами
|
||
const { SDDAUTH_API_CLIENT_ID, SDEPARTMENT_NAME, SDEPARTMENT_ID } = require("./diadoc_config"); //Ключ разработчика
|
||
|
||
//---------------------
|
||
// Глобальные константы
|
||
//---------------------
|
||
|
||
// Список тегов которые должны содержать массив
|
||
const tag = [
|
||
"DocumentAttachments",
|
||
"Metadata",
|
||
"Signatures",
|
||
"CorrectionRequests",
|
||
"Receipts",
|
||
"Resolutions",
|
||
"XmlSignatureRejections",
|
||
"RecipientTitles",
|
||
"Requests"
|
||
];
|
||
|
||
//------------
|
||
// Тело модуля
|
||
//------------
|
||
|
||
//формирования адреса запроса для получения данных о текущей организации пользователя
|
||
const buildMyOrganizationURL = srvRoot => {
|
||
return `${srvRoot.replace(/\/+$/, "")}/GetMyOrganizations`;
|
||
};
|
||
|
||
//формирования адреса запроса для получения данных о организации по идентификатору ящика
|
||
const buildOrganizationBoxIdURL = srvRoot => {
|
||
return `${srvRoot.replace(/\/+$/, "")}/GetBox`;
|
||
};
|
||
|
||
//формирования адреса запроса для получения данных о организации по ИНН/КПП
|
||
const buildOrganizationURL = srvRoot => {
|
||
return `${srvRoot.replace(/\/+$/, "")}/GetOrganization`;
|
||
};
|
||
|
||
//формирования адреса запроса для получения данных о организации по ИНН/КПП
|
||
const buildOrganizationsByInnKppURL = srvRoot => {
|
||
return `${srvRoot.replace(/\/+$/, "")}/GetOrganizationsByInnKpp`;
|
||
};
|
||
|
||
//Обернуть содержимое тега в массив
|
||
const toArray = (obj, tags) => {
|
||
for (const prop in obj) {
|
||
let value = obj[prop];
|
||
if (typeof value === "object") {
|
||
obj[prop] = toArray(value, tag);
|
||
}
|
||
if (tags.indexOf(prop) != -1 && !_.isArray(obj[prop])) {
|
||
obj[prop] = JSON.parse("[" + JSON.stringify(value) + "]");
|
||
}
|
||
}
|
||
return obj;
|
||
};
|
||
|
||
//Конвертация в XML
|
||
const toXML = obj => {
|
||
const builder = new xml2js.Builder();
|
||
return builder.buildObject(obj);
|
||
};
|
||
|
||
//Конвертация в XML
|
||
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 getAPIClientId = (nCompany, nJurPers = null) => {
|
||
//Ключи в diadoc_conf.js заданы
|
||
if (SDDAUTH_API_CLIENT_ID) {
|
||
//И это массив
|
||
if (Array.isArray(SDDAUTH_API_CLIENT_ID)) {
|
||
//Найдем значение в массиве
|
||
let keyFound = SDDAUTH_API_CLIENT_ID.find(keyItem => keyItem.nCompany === nCompany && keyItem.nJurPers === nJurPers);
|
||
//Найдено совпадение в массиве
|
||
if (keyFound) return keyFound.sAPIClientId;
|
||
else {
|
||
//Ищем с принадлежностью по умолчанию
|
||
keyFound = SDDAUTH_API_CLIENT_ID.find(keyItem => keyItem.nCompany === nCompany && !keyItem.nJurPers);
|
||
//Найдено совпадение в массиве
|
||
if (keyFound) return keyFound.sAPIClientId;
|
||
else return null;
|
||
}
|
||
} else return SDDAUTH_API_CLIENT_ID;
|
||
} else return null;
|
||
};
|
||
|
||
//Проверка ключа разработчика
|
||
const checkAPIClientId = sAPIClientId => {
|
||
if (!sAPIClientId) {
|
||
throw new Error('Не задан ключ разработчика. Запросите его у поставщика услуг ЭДО "ДИАДОК" и укажите в "./modules/diadoc_config.js".');
|
||
}
|
||
};
|
||
|
||
//Формиорвание заголовка сообщения
|
||
const buildHeaders = (sAPIClientId, sToken = null) => ({
|
||
"Content-type": "application/json",
|
||
"X-Solution-Info": "PARUS",
|
||
Authorization: `DiadocAuth ddauth_api_client_id=${sAPIClientId}${sToken ? `,ddauth_token=${sToken}` : ""}`,
|
||
Accept: "application/json"
|
||
});
|
||
|
||
//Отбор организций
|
||
const getOrganizations = organizations => {
|
||
//Параметры отбора
|
||
let isRoaming = false;
|
||
let isActive = true;
|
||
//Итоговая выборка
|
||
let organization = { Organizations: [] };
|
||
//Найдем активную организацию не в роуминге
|
||
organization.Organizations[0] = organizations.Organizations.find(org => (org.IsRoaming === isRoaming) && (org.IsActive === isActive));
|
||
//Если не удалось получить организацию не в роуминге
|
||
if (!organization.Organizations[0]) {
|
||
//Если нет организации не в роуминге и найдено более одной организации
|
||
if (organizations.Organizations.length > 1) {
|
||
throw Error(`Найдено несколько организаций в роуминге.`);
|
||
} else {
|
||
//Вернем единственную найденую организацию
|
||
return organizations[0];
|
||
}
|
||
} else {
|
||
//Вернем найденую организацию
|
||
return organization;
|
||
}
|
||
};
|
||
|
||
//Получение организации по ИНН/КПП контрагента
|
||
const getOrganization = async (sSrvRoot, headers, nInn, nKpp) => {
|
||
//Параметры запроса
|
||
let rqpOptions;
|
||
let serverResp;
|
||
//Формируем запрос для получения BoxId
|
||
rqpOptions = {
|
||
uri: buildOrganizationURL(sSrvRoot),
|
||
qs: {
|
||
inn: nInn,
|
||
kpp: nKpp
|
||
},
|
||
headers: headers,
|
||
json: true
|
||
};
|
||
//Выполним запрос
|
||
serverResp = { Organizations: [await rqp(rqpOptions)] };
|
||
return serverResp;
|
||
};
|
||
|
||
//Получение ящика организации по ИНН/КПП контрагента
|
||
const getOrganizationBoxId = async (sSrvRoot, headers, nInn, nKpp) => {
|
||
//Параметры запроса
|
||
let rqpOptions;
|
||
let serverResp;
|
||
let organization = {};
|
||
//Формируем запрос для получения BoxId по ИНН/КПП
|
||
rqpOptions = {
|
||
uri: buildOrganizationsByInnKppURL(sSrvRoot),
|
||
qs: {
|
||
inn: nInn,
|
||
kpp: nKpp
|
||
},
|
||
headers: headers,
|
||
json: true
|
||
};
|
||
try {
|
||
//Выполним запрос
|
||
serverResp = await rqp(rqpOptions);
|
||
try {
|
||
//Получим организацию не в роуминге (или единственную организацию в роуминге)
|
||
serverResp = getOrganizations(serverResp);
|
||
if (!serverResp?.Organizations[0]) {
|
||
throw Error(`Не удалось получить ящик получателя для контрагента с ИНН: ${nInn} и КПП: ${nKpp}`);
|
||
}
|
||
} catch (e) {
|
||
//Получим головную организацию по ИНН/КПП
|
||
serverResp = await getOrganization(sSrvRoot, headers, nInn, nKpp);
|
||
};
|
||
//Проверим соответствие КПП организации
|
||
if ((serverResp?.Organizations[0]?.Kpp != nKpp) && (serverResp?.Organizations[0])) {
|
||
//Если КПП не соответстует заданному - проверим, что в полученной организации есть департамент с заданным КПП
|
||
for (let i in serverResp.Organizations[0].Departments) {
|
||
//Если найден подходящий департамент - запомним идентификатор и выходим из цикла
|
||
if (serverResp.Organizations[0].Departments[i]?.Kpp == nKpp) {
|
||
//Сохраняем полученный ответ
|
||
organization.DepartmentId = serverResp.Organizations[0].Departments[i].DepartmentId;
|
||
break;
|
||
};
|
||
};
|
||
};
|
||
//Не удалось получить ящик получателя или полученая организация не соответствует заданному ИНН
|
||
if ((!serverResp?.Organizations[0]?.Boxes[0]?.BoxId) || (serverResp?.Organizations[0]?.Inn != nInn)) {
|
||
throw new Error(`Не удалось получить ящик получателя для контрагента с ИНН: ${nInn} и КПП: ${nKpp}`);
|
||
}
|
||
//Не удалось получить департаментом с соответствующим КПП
|
||
if ((serverResp?.Organizations[0]?.Kpp != nKpp) && (!organization?.DepartmentId)) {
|
||
throw new Error(`Не удалось получить ящик получателя для контрагента с ИНН: ${nInn} и КПП: ${nKpp}, у головной организации отсутствует подразделение с КПП: ${nKpp}`);
|
||
}
|
||
//Сохраняем полученный ответ
|
||
organization.BoxId = serverResp.Organizations[0].Boxes[0].BoxId;
|
||
return organization;
|
||
} catch (e) {
|
||
throw Error(`Ошибка при получении ящика получателя: ${e.message}`);
|
||
};
|
||
};
|
||
|
||
//Обработчик "До" подключения к сервису
|
||
const beforeConnect = async prms => {
|
||
//Подготовим параметры аутентификации
|
||
const loginAtribute = "login";
|
||
const passAtribute = "password";
|
||
let surl = prms.options.url;
|
||
surl = `${surl}?type=password`;
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Сформируем запрос на аутентификацию
|
||
return {
|
||
options: {
|
||
headers: buildHeaders(sAPIClientId),
|
||
url: surl,
|
||
body: JSON.stringify({
|
||
[loginAtribute]: prms.service.sSrvUser,
|
||
[passAtribute]: prms.service.sSrvPass
|
||
}),
|
||
simple: false
|
||
}
|
||
};
|
||
};
|
||
|
||
//Обработчик "После" подключения к сервису
|
||
const afterConnect = async prms => {
|
||
//Если пришла ошибка
|
||
if (prms.optionsResp.statusCode != 200) {
|
||
throw new Error(prms.queue.blResp.toString());
|
||
} else {
|
||
//Сохраним полученный токен доступа в контекст сервиса
|
||
return {
|
||
blResp: Buffer.from(prms.queue.blResp),
|
||
sCtx: prms.queue.blResp.toString(),
|
||
dCtxExp: addHours(new Date(), 23)
|
||
};
|
||
}
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на экспорт документа к сервису "ДИАДОК"
|
||
const beforeMessagePost = async prms => {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Формируем запрос
|
||
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());
|
||
//Формируем запрос для получения FromBoxId
|
||
let rqpOptions = {
|
||
uri: buildMyOrganizationURL(prms.service.sSrvRoot),
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
json: true
|
||
};
|
||
//Переменные для запросов
|
||
let serverResp;
|
||
let organization;
|
||
try {
|
||
//Выполним запрос
|
||
serverResp = await rqp(rqpOptions);
|
||
//Получим идентификатор организации по ИНН/КПП поставщика документа
|
||
for (let i in serverResp.Organizations) {
|
||
//Если найдена подходящая организация - запомним идентификатор и выходим из цикла
|
||
if (serverResp.Organizations[i].Inn == prms.options.inn_pr && serverResp.Organizations[i].Kpp == prms.options.kpp_pr) {
|
||
//Сохраняем полученный ответ
|
||
obj.FromBoxId = serverResp.Organizations[i].Boxes[0].BoxId;
|
||
break;
|
||
}
|
||
}
|
||
//Не удалось получить ящик отправителя
|
||
if (!obj.FromBoxId) {
|
||
throw new Error(`Не удалось получить ящик текущей организации с ИНН: ${prms.options.inn_pr} и КПП: ${prms.options.kpp_pr}`);
|
||
}
|
||
} catch (e) {
|
||
throw Error(`Ошибка при получении ящика текущей организации: ${e.message}`);
|
||
}
|
||
//Получим ящик получателя
|
||
organization = await getOrganizationBoxId(prms.service.sSrvRoot, buildHeaders(sAPIClientId, sToken), prms.options.inn_cs, prms.options.kpp_cs);
|
||
obj.ToBoxId = organization.BoxId;
|
||
//Если не заполнен идентификатор подразделения и при получении ящика удалось его подобрать
|
||
if ((!obj.ToDepartmentId) && (organization.DepartmentId)) {
|
||
obj.ToDepartmentId = organization.DepartmentId;
|
||
};
|
||
//Если пришел ответ
|
||
if (prms.queue.blResp && serverResp.statusCode == 200) {
|
||
//Вернем загруженный документ
|
||
return {
|
||
blResp: prms.queue.blResp
|
||
};
|
||
}
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
simple: false
|
||
},
|
||
blMsg: Buffer.from(JSON.stringify(obj))
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" запроса на экспорт документа к сервису "ДИАДОК"
|
||
const afterMessagePost = async prms => {
|
||
//Преобразуем JSON ответ сервиса "ДИАДОК" в XML, понятный "Парус 8"
|
||
let resu = null;
|
||
//Действие выполнено успешно
|
||
if (prms.optionsResp.statusCode == 200) {
|
||
try {
|
||
resu = toXML(JSON.parse(prms.queue.blResp.toString()));
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК". Ошибка интерпретации: ${e.message}`);
|
||
}
|
||
} else {
|
||
//Если пришел текст ошибки
|
||
if (prms.queue.blResp) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК": ${prms.queue.blResp.toString()}`);
|
||
} else {
|
||
throw new Error('Сервер ЭДО "ДИАДОК" не вернул ответ');
|
||
}
|
||
}
|
||
//Возврат результата
|
||
return {
|
||
blResp: Buffer.from(resu)
|
||
};
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на экспорт патча документа к сервису "ДИАДОК"
|
||
const beforeMessagePatchPost = async prms => {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Формируем запрос
|
||
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());
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
simple: false
|
||
},
|
||
blMsg: Buffer.from(JSON.stringify(obj))
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" запроса на экспорт патча документа к сервису "ДИАДОК"
|
||
const afterMessagePatchPost = async prms => {
|
||
let resu = null;
|
||
//Действие выполнено успешно
|
||
if (prms.optionsResp.statusCode == 200) {
|
||
try {
|
||
//Преобразуем JSON ответ сервиса "ДИАДОК" в XML, понятный "Парус 8"
|
||
resu = toXML(JSON.parse(prms.queue.blResp.toString()));
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК". Ошибка интерпретации: ${e.message}`);
|
||
}
|
||
} else {
|
||
//Если пришел текст ошибки
|
||
if (prms.queue.blResp) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК": ${prms.queue.blResp.toString()}`);
|
||
} else {
|
||
throw new Error('Сервер ЭДО "ДИАДОК" не вернул ответ');
|
||
}
|
||
}
|
||
//Возврат результата
|
||
return {
|
||
blResp: Buffer.from(resu)
|
||
};
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на получение новых событий к сервису "ДИАДОК"
|
||
const beforeEvent = async prms => {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Формируем запрос
|
||
try {
|
||
let sToken = null; //Токен доступа
|
||
let surl = prms.options.url; //Адрес запрос
|
||
let serverResp; //Результат запроса информации по текущей организации
|
||
let obj; //Тело запроса (JSON)
|
||
let rblMsg; //Буфер тела запроса
|
||
let sBoxId; //Идентификатор ящика текущей организации
|
||
let sDepartmentId; //Идентификатор подразделения
|
||
//Считаем токен доступа из контекста сервиса
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
//Формируем запрос для получения BoxId
|
||
let rqpOptions = {
|
||
uri: buildMyOrganizationURL(prms.service.sSrvRoot),
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
json: true
|
||
};
|
||
try {
|
||
//Выполним запрос
|
||
serverResp = await rqp(rqpOptions);
|
||
//Получим идентификатор организации по ИНН/КПП контрагента организации
|
||
for (let i in serverResp.Organizations) {
|
||
//Если найдена подходящая организация - запомним идентификатор и выходим из цикла
|
||
if (serverResp.Organizations[i].Inn == prms.options.inn && serverResp.Organizations[i].Kpp == prms.options.kpp) {
|
||
//Сохраняем полученный ответ
|
||
sBoxId = serverResp.Organizations[i].Boxes[0].BoxId;
|
||
//Если задано подразделение
|
||
if (prms.options.sdepartment_name) {
|
||
if (prms.options.sdepartment_name == SDEPARTMENT_NAME) {
|
||
sDepartmentId = SDEPARTMENT_ID;
|
||
} else {
|
||
//Получим идентификатор подразделения
|
||
for (let j in serverResp.Organizations[i].Departments) {
|
||
//Если нашлось подразделение - запомним идентификатор и выходим из цикла
|
||
if (serverResp.Organizations[i].Departments[j].Name == prms.options.sdepartment_name) {
|
||
sDepartmentId = serverResp.Organizations[i].Departments[j].DepartmentId;
|
||
break;
|
||
}
|
||
}
|
||
//Не удалось получить идентификатор подразделения
|
||
if (!sDepartmentId) {
|
||
throw new Error(`Не удалось получить идентификатор подразделения с наименованием "${prms.options.sdepartment_name}"`);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
//Не удалось получить ящик текущей организации
|
||
if (!sBoxId) {
|
||
throw new Error(`Не удалось получить ящик текущей организации с ИНН: ${prms.options.inn} и КПП: ${prms.options.kpp}`);
|
||
}
|
||
} catch (e) {
|
||
throw Error(`Ошибка при получении ящика текущей организации: ${e.message}`);
|
||
}
|
||
//Сформируем адрес запроса
|
||
surl = `${surl}?boxId=${sBoxId}`;
|
||
//Если действие не "Документооборот"
|
||
if (prms.options.saction != "DOCFLOWS") {
|
||
//Заполним параметры для отбора последних событий
|
||
if (prms.options.aftereventid) {
|
||
surl = `${surl}&afterEventId=${prms.options.aftereventid}`;
|
||
} else {
|
||
surl = `${surl}×tampFromTicks=${prms.options.timestampfromticks}`;
|
||
}
|
||
//Заполним идентификатор подразделения
|
||
if (prms.options.sdepartment_name && sDepartmentId) {
|
||
surl = `${surl}&departmentId=${sDepartmentId}`;
|
||
}
|
||
} else {
|
||
if (prms.queue.blMsg) {
|
||
//Конвертируем XML из "Парус 8" в понятный "ДИАДОК" JSON
|
||
obj = await toJSON(prms.queue.blMsg.toString());
|
||
rblMsg = Buffer.from(JSON.stringify(obj));
|
||
}
|
||
}
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
simple: false,
|
||
url: surl,
|
||
boxId: sBoxId
|
||
},
|
||
blMsg: rblMsg
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" запроса на получение новых событий к сервису "ДИАДОК"
|
||
const afterEvent = async prms => {
|
||
let sAPIClientId = null; //Ключ разработчика
|
||
let sToken = null; //Токен доступа
|
||
let resu = null; //Ответ сервера
|
||
let rqpOptions = null; //Параметры для запроса информации по ящику
|
||
let serverResp; //Результат запроса информации по организации
|
||
let resp = null; //Ответ сервера
|
||
let box = null; //Информация ящика
|
||
let boxIds = { boxIds: [] }; //Список уникальных ящиков контрагентов
|
||
//Действие выполнено успешно
|
||
if (prms.optionsResp.statusCode == 200) {
|
||
//Получим ключ разработчика
|
||
sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Разберем ответ сервера
|
||
resp = JSON.parse(prms.queue.blResp.toString());
|
||
//Получим список уникальных ящиков
|
||
for (let i in resp.Events) {
|
||
if (resp.Events[i]?.Message) {
|
||
if ((!boxIds.boxIds.find(box => box.boxId === resp.Events[i]?.Message.FromBoxId)) && (resp.Events[i]?.Message.FromBoxId)) {
|
||
boxIds.boxIds.push({ boxId: resp.Events[i]?.Message.FromBoxId });
|
||
}
|
||
if ((!boxIds.boxIds.find(box => box.boxId === resp.Events[i]?.Message.ToBoxId)) && (resp.Events[i]?.Message.ToBoxId)) {
|
||
boxIds.boxIds.push({ boxId: resp.Events[i]?.Message.ToBoxId });
|
||
}
|
||
}
|
||
}
|
||
//Если требуется выполнить запросы доп. информации по ящикам организации
|
||
if (boxIds.boxIds.length) {
|
||
//Считаем токен доступа из контекста сервиса
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
for (let i in boxIds.boxIds) {
|
||
rqpOptions = null;
|
||
//Формируем запрос для получения BoxId
|
||
rqpOptions = {
|
||
uri: buildOrganizationBoxIdURL(prms.service.sSrvRoot),
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
qs: {
|
||
boxId: boxIds.boxIds[i].boxId
|
||
},
|
||
json: true
|
||
};
|
||
try {
|
||
//Выполним запрос
|
||
serverResp = await rqp(rqpOptions);
|
||
if (serverResp?.Organization) {
|
||
//Запишем полученную информацию о контрагенте
|
||
boxIds.boxIds[i].Inn = serverResp?.Organization?.Inn;
|
||
boxIds.boxIds[i].Kpp = serverResp?.Organization?.Kpp;
|
||
boxIds.boxIds[i].FullName = serverResp?.Organization?.FullName;
|
||
boxIds.boxIds[i].ShortName = serverResp?.Organization?.ShortName;
|
||
boxIds.boxIds[i].FnsParticipantId = serverResp?.Organization?.FnsParticipantId;
|
||
} else {
|
||
//Не удалось получить информацию о контрагенте по ящику организации
|
||
throw new Error(`Не удалось получить информацию о контрагенте по ящику организации ${boxIds.boxIds[i].boxId}`);
|
||
}
|
||
} catch (e) {
|
||
throw Error(`Ошибка при получении информации о контрагенте по ящику организации: ${e.message}`);
|
||
}
|
||
}
|
||
//Добавим к ответу информацию о контрагентах ящика
|
||
for (let i in resp.Events) {
|
||
if (resp.Events[i]?.Message) {
|
||
//Если надо добавить информацию по ящику-отправителю
|
||
box = boxIds.boxIds.find(box => box.boxId === resp.Events[i]?.Message.FromBoxId);
|
||
if (box) {
|
||
resp.Events[i].Message.FromInn = box.Inn;
|
||
resp.Events[i].Message.FromKpp = box.Kpp;
|
||
resp.Events[i].Message.FromFullName = box.FullName;
|
||
resp.Events[i].Message.FromShortName = box.ShortName;
|
||
resp.Events[i].Message.FromFnsParticipantId = box.FnsParticipantId;
|
||
}
|
||
//Если надо добавить информацию по ящику-получателю
|
||
box = boxIds.boxIds.find(box => box.boxId === resp.Events[i]?.Message.ToBoxId);
|
||
if (box) {
|
||
resp.Events[i].Message.ToInn = box.Inn;
|
||
resp.Events[i].Message.ToKpp = box.Kpp;
|
||
resp.Events[i].Message.ToFullName = box.FullName;
|
||
resp.Events[i].Message.ToShortName = box.ShortName;
|
||
resp.Events[i].Message.ToFnsParticipantId = box.FnsParticipantId;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
try {
|
||
//Преобразуем JSON ответ сервиса "ДИАДОК" в XML, понятный "Парус 8"
|
||
resu = toXML({ root: resp });
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК". Ошибка интерпретации: ${e.message}`);
|
||
}
|
||
} else {
|
||
//Если пришел текст ошибки
|
||
if (prms.queue.blResp) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК": ${prms.queue.blResp.toString()}`);
|
||
} else {
|
||
throw new Error('Сервер ЭДО "ДИАДОК" не вернул ответ');
|
||
}
|
||
}
|
||
//Возврат результата
|
||
return {
|
||
blResp: Buffer.from(resu)
|
||
};
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на загрузку вложения
|
||
const beforeDocLoad = async prms => {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Формируем запрос
|
||
try {
|
||
//Считаем токен доступа из контекста сервиса
|
||
let sToken = null;
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
let surl = prms.options.url;
|
||
let entId;
|
||
let msgId = "messageId=";
|
||
//В зависимости от режима загрузки определим наименование узла
|
||
switch (prms.options.type) {
|
||
//Загрузка файла
|
||
case 0:
|
||
entId = "entityId=";
|
||
break;
|
||
//Загрузка PDF
|
||
case 1:
|
||
entId = "documentId=";
|
||
break;
|
||
//Загрузка Извещения о получении
|
||
case 2:
|
||
entId = "attachmentId=";
|
||
break;
|
||
//Загрузка Уведомления об уточнении
|
||
case 3:
|
||
entId = "attachmentId=";
|
||
break;
|
||
//Загрузка Титул отказа от подписи документа
|
||
case 4:
|
||
entId = "attachmentId=";
|
||
break;
|
||
//Загрузка Титула покупателя документа
|
||
case 5:
|
||
entId = "documentId=";
|
||
msgId = "letterId=";
|
||
break;
|
||
default:
|
||
}
|
||
surl = `${surl}?${msgId}${prms.options.smsgid}&${entId}${prms.options.sentid}`;
|
||
let obj;
|
||
let rblMsg;
|
||
if (prms.queue.blMsg && prms.options.type != 5) {
|
||
//Конвертируем XML из "Парус 8" в понятный "ДИАДОК" JSON
|
||
obj = await toJSON(prms.queue.blMsg.toString());
|
||
rblMsg = Buffer.from(JSON.stringify(obj));
|
||
} else {
|
||
if (prms.queue.blMsg) {
|
||
rblMsg = prms.queue.blMsg;
|
||
}
|
||
}
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
qs: {
|
||
boxId: prms.options.sboxid,
|
||
documentTypeNamedId: prms.options.documentTypeNamedId,
|
||
documentFunction: prms.options.documentFunction,
|
||
documentVersion: prms.options.documentVersion,
|
||
titleIndex: prms.options.titleIndex
|
||
},
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
url: surl,
|
||
simple: false
|
||
},
|
||
blMsg: rblMsg
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" отправки запроса на загрузку вложения
|
||
const afterDocLoad = async prms => {
|
||
if (prms.queue.blResp) {
|
||
//Если выполнено без ошибок и не требуется повторный запрос
|
||
if ((prms.optionsResp.statusCode == 200 || prms.optionsResp.statusCode == 404) && !prms.optionsResp.headers["retry-after"]) {
|
||
return;
|
||
} else {
|
||
let iterable = [1, 2, 3, 4, 5];
|
||
let serverResp;
|
||
//Если не превышает лимита запросов
|
||
for (let value of iterable) {
|
||
if (prms.optionsResp.statusCode != 200 || prms.optionsResp.headers["retry-after"]) {
|
||
//Если загружаем PDF
|
||
if (prms.options.type == 1 && prms.optionsResp.headers["retry-after"]) {
|
||
await new Promise(resolve => setTimeout(resolve, (Number(prms.optionsResp.headers["retry-after"]) + 1) * 1000));
|
||
} else {
|
||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||
}
|
||
//Выполним повторный запрос
|
||
serverResp = await rqp(prms.options);
|
||
//Сохраняем полученный ответ
|
||
prms.queue.blResp = Buffer.from(serverResp.body || "");
|
||
prms.optionsResp.statusCode = serverResp.statusCode;
|
||
prms.optionsResp.headers = serverResp.headers;
|
||
//Если пришел ответ
|
||
if (prms.queue.blResp && serverResp.statusCode == 200) {
|
||
//Вернем загруженный документ
|
||
return {
|
||
optionsResp: prms.optionsResp,
|
||
blResp: prms.queue.blResp
|
||
};
|
||
}
|
||
}
|
||
}
|
||
//Если был ответ от сервера с ошибкой (иначе мы сюда не попадём)
|
||
if (prms.queue.blResp) {
|
||
//Разберем сообщение об ошибке
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК": ${prms.queue.blResp.toString()}`);
|
||
}
|
||
}
|
||
} else {
|
||
throw new Error('Сервер ЭДО "ДИАДОК" не вернул ответ');
|
||
}
|
||
//Возврат результата
|
||
return;
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на удаление документа к сервису "ДИАДОК"
|
||
const beforeDocDelete = async prms => {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Формируем запрос
|
||
try {
|
||
//Считаем токен доступа из контекста сервиса
|
||
let sToken = null;
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
simple: false
|
||
}
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на получение списка подразделений контрагента к сервису "ДИАДОК"
|
||
const beforeDepartmentIdGet = async prms => {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Формируем запрос
|
||
try {
|
||
//Считаем токен доступа из контекста сервиса
|
||
let sToken = null;
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
simple: false
|
||
}
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" отправки запроса на получение списка подразделений контрагента к сервису "ДИАДОК"
|
||
const afterDepartmentIdGet = async prms => {
|
||
let resu = null;
|
||
let organization = {};
|
||
//Действие выполнено успешно
|
||
if (prms.optionsResp.statusCode == 200) {
|
||
try {
|
||
try {
|
||
//Получим организацию не в роуминге (или единственную организацию в роуминге)
|
||
organization = getOrganizations(JSON.parse(prms.queue.blResp.toString()));
|
||
if (!organization) {
|
||
throw Error(`Не удалось получить ящик для контрагента с ИНН: ${prms.options.nINN} и КПП: ${prms.options.nKPP}`);
|
||
}
|
||
} catch (e) {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Считаем токен доступа из контекста сервиса
|
||
let sToken = prms.service.sCtx;
|
||
//Получим головную организацию по ИНН/КПП
|
||
organization = await getOrganization(prms.service.sSrvRoot, buildHeaders(sAPIClientId, sToken), prms.options.nINN, prms.options.nKPP);
|
||
};
|
||
//Преобразуем JSON ответ сервиса "ДИАДОК" в XML, понятный "Парус 8"
|
||
resu = toXML({ root: organization });
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК". Ошибка интерпретации: ${e.message}`);
|
||
}
|
||
} else {
|
||
//Если пришел текст ошибки
|
||
if (prms.queue.blResp) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК": ${prms.queue.blResp.toString()}`);
|
||
} else {
|
||
throw new Error('Сервер ЭДО "ДИАДОК" не вернул ответ');
|
||
}
|
||
}
|
||
//Возврат результата
|
||
return {
|
||
blResp: Buffer.from(resu)
|
||
};
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на получение списка документов в сообщении к сервису "ДИАДОК"
|
||
const beforeDocumentsByMessageIdGet = async prms => {
|
||
//Получим ключ разработчика
|
||
let sAPIClientId = getAPIClientId(prms.options.nCompany, prms.options.nJurPers);
|
||
//Проверим ключ разработчика
|
||
checkAPIClientId(sAPIClientId);
|
||
//Формируем запрос
|
||
try {
|
||
//Считаем токен доступа из контекста сервиса
|
||
let sToken = null;
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: buildHeaders(sAPIClientId, sToken),
|
||
simple: false
|
||
}
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" отправки запроса на получение списка документов в сообщении к сервису "ДИАДОК"
|
||
const afterDocumentsByMessageIdGet = async prms => {
|
||
let resu = null;
|
||
//Действие выполнено успешно
|
||
if (prms.optionsResp.statusCode == 200) {
|
||
try {
|
||
//Разберем ответ сервера
|
||
resp = JSON.parse(prms.queue.blResp.toString());
|
||
//Преобразуем JSON ответ сервиса "ДИАДОК" в XML, понятный "Парус 8"
|
||
resu = toXML({ root: resp });
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК". Ошибка интерпретации: ${e.message}`);
|
||
}
|
||
} else {
|
||
//Если пришел текст ошибки
|
||
if (prms.queue.blResp) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК": ${prms.queue.blResp.toString()}`);
|
||
} else {
|
||
throw new Error('Сервер ЭДО "ДИАДОК" не вернул ответ');
|
||
}
|
||
}
|
||
//Возврат результата
|
||
return {
|
||
blResp: Buffer.from(resu)
|
||
};
|
||
};
|
||
|
||
//-----------------
|
||
// Интерфейс модуля
|
||
//-----------------
|
||
|
||
exports.beforeConnect = beforeConnect;
|
||
exports.afterConnect = afterConnect;
|
||
exports.beforeMessagePost = beforeMessagePost;
|
||
exports.afterMessagePost = afterMessagePost;
|
||
exports.beforeMessagePatchPost = beforeMessagePatchPost;
|
||
exports.afterMessagePatchPost = afterMessagePatchPost;
|
||
exports.beforeEvent = beforeEvent;
|
||
exports.afterEvent = afterEvent;
|
||
exports.beforeDocLoad = beforeDocLoad;
|
||
exports.afterDocLoad = afterDocLoad;
|
||
exports.beforeDocDelete = beforeDocDelete;
|
||
exports.beforeDepartmentIdGet = beforeDepartmentIdGet;
|
||
exports.afterDepartmentIdGet = afterDepartmentIdGet;
|
||
exports.beforeDocumentsByMessageIdGet = beforeDocumentsByMessageIdGet;
|
||
exports.afterDocumentsByMessageIdGet = afterDocumentsByMessageIdGet;
|