forked from CITKParus/P8-ExchangeService
Compare commits
9 Commits
113ccca2b8
...
45d149dd23
Author | SHA1 | Date | |
---|---|---|---|
|
45d149dd23 | ||
|
096fee6538 | ||
|
e6d6971391 | ||
|
ca64cbd6b4 | ||
|
0807b99b42 | ||
|
1d2ffb62a2 | ||
e2afde4398 | |||
|
127a7afe3f | ||
|
ecec0d70c1 |
@ -12,7 +12,7 @@ let common = {
|
||||
//Версия сервера приложений
|
||||
sVersion: "8.5.6.1",
|
||||
//Релиз сервера приложений
|
||||
sRelease: "2025.02.04",
|
||||
sRelease: "2025.04.29",
|
||||
//Таймаут останова сервера (мс)
|
||||
nTerminateTimeout: 60000,
|
||||
//Контролировать версию Системы
|
||||
|
@ -360,8 +360,8 @@ const appProcess = async prms => {
|
||||
default:
|
||||
//Установим флаг возврата полного ответа (и тела и заголовков)
|
||||
options.resolveWithFullResponse = true;
|
||||
//Установим таймаут подключения
|
||||
options.timeout = prms.function.nTimeoutConn ? prms.function.nTimeoutConn : null;
|
||||
//Установим таймаут подключения (если задан в настройках функции и ещё не задан в параметрах сообщения)
|
||||
if (prms.function.nTimeoutConn && !options.timeout) options.timeout = prms.function.nTimeoutConn;
|
||||
//Отправляем запрос
|
||||
serverResp = prms.function.nTimeoutAsynch
|
||||
? await wrapPromiseTimeout(prms.function.nTimeoutAsynch, rqp(options))
|
||||
|
@ -19,6 +19,7 @@ const { SDDAUTH_API_CLIENT_ID, SDEPARTMENT_NAME, SDEPARTMENT_ID } = require("./d
|
||||
// Список тегов которые должны содержать массив
|
||||
const tag = [
|
||||
"DocumentAttachments",
|
||||
"Metadata",
|
||||
"Signatures",
|
||||
"CorrectionRequests",
|
||||
"Receipts",
|
||||
@ -55,13 +56,13 @@ const buildOrganizationsByInnKppURL = srvRoot => {
|
||||
//Обернуть содержимое тега в массив
|
||||
const toArray = (obj, tags) => {
|
||||
for (const prop in obj) {
|
||||
const value = obj[prop];
|
||||
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) + "]");
|
||||
}
|
||||
if (typeof value === "object") {
|
||||
toArray(value, tag);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
@ -133,31 +134,31 @@ const buildHeaders = (sAPIClientId, sToken = null) => ({
|
||||
});
|
||||
|
||||
//Отбор организций
|
||||
const GetOrganizations = Organizations => {
|
||||
const getOrganizations = organizations => {
|
||||
//Параметры отбора
|
||||
let isRoaming = false;
|
||||
let isActive = true;
|
||||
//Итоговая выборка
|
||||
let Organization = { Organizations: [] };
|
||||
let organization = { Organizations: [] };
|
||||
//Найдем активную организацию не в роуминге
|
||||
Organization.Organizations[0] = Organizations.Organizations.find(Org => (Org.IsRoaming === isRoaming) && (Org.IsActive === isActive));
|
||||
organization.Organizations[0] = organizations.Organizations.find(org => (org.IsRoaming === isRoaming) && (org.IsActive === isActive));
|
||||
//Если не удалось получить организацию не в роуминге
|
||||
if (!Organization.Organizations[0]) {
|
||||
if (!organization.Organizations[0]) {
|
||||
//Если нет организации не в роуминге и найдено более одной организации
|
||||
if (Organizations.Organizations.length > 1) {
|
||||
if (organizations.Organizations.length > 1) {
|
||||
throw Error(`Найдено несколько организаций в роуминге.`);
|
||||
} else {
|
||||
//Вернем единственную найденую организацию
|
||||
return Organizations[0];
|
||||
return organizations[0];
|
||||
}
|
||||
} else {
|
||||
//Вернем найденую организацию
|
||||
return Organization;
|
||||
return organization;
|
||||
}
|
||||
};
|
||||
|
||||
//Получение организации по ИНН/КПП контрагента
|
||||
const GetOrganization = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
const getOrganization = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
//Параметры запроса
|
||||
let rqpOptions;
|
||||
let serverResp;
|
||||
@ -177,11 +178,11 @@ const GetOrganization = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
};
|
||||
|
||||
//Получение ящика организации по ИНН/КПП контрагента
|
||||
const GetOrganizationBoxId = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
const getOrganizationBoxId = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
//Параметры запроса
|
||||
let rqpOptions;
|
||||
let serverResp;
|
||||
let Organization = {};
|
||||
let organization = {};
|
||||
//Формируем запрос для получения BoxId по ИНН/КПП
|
||||
rqpOptions = {
|
||||
uri: buildOrganizationsByInnKppURL(sSrvRoot),
|
||||
@ -197,13 +198,13 @@ const GetOrganizationBoxId = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
serverResp = await rqp(rqpOptions);
|
||||
try {
|
||||
//Получим организацию не в роуминге (или единственную организацию в роуминге)
|
||||
serverResp = GetOrganizations(serverResp);
|
||||
serverResp = getOrganizations(serverResp);
|
||||
if (!serverResp?.Organizations[0]) {
|
||||
throw Error(`Не удалось получить ящик получателя для контрагента с ИНН: ${nInn} и КПП: ${nKpp}`);
|
||||
}
|
||||
} catch (e) {
|
||||
//Получим головную организацию по ИНН/КПП
|
||||
serverResp = await GetOrganization(sSrvRoot, headers, nInn, nKpp);
|
||||
serverResp = await getOrganization(sSrvRoot, headers, nInn, nKpp);
|
||||
};
|
||||
//Проверим соответствие КПП организации
|
||||
if ((serverResp?.Organizations[0]?.Kpp != nKpp) && (serverResp?.Organizations[0])) {
|
||||
@ -212,7 +213,7 @@ const GetOrganizationBoxId = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
//Если найден подходящий департамент - запомним идентификатор и выходим из цикла
|
||||
if (serverResp.Organizations[0].Departments[i]?.Kpp == nKpp) {
|
||||
//Сохраняем полученный ответ
|
||||
Organization.DepartmentId = serverResp.Organizations[0].Departments[i].DepartmentId;
|
||||
organization.DepartmentId = serverResp.Organizations[0].Departments[i].DepartmentId;
|
||||
break;
|
||||
};
|
||||
};
|
||||
@ -222,12 +223,12 @@ const GetOrganizationBoxId = async (sSrvRoot, headers, nInn, nKpp) => {
|
||||
throw new Error(`Не удалось получить ящик получателя для контрагента с ИНН: ${nInn} и КПП: ${nKpp}`);
|
||||
}
|
||||
//Не удалось получить департаментом с соответствующим КПП
|
||||
if ((serverResp?.Organizations[0]?.Kpp != nKpp) && (!Organization?.DepartmentId)) {
|
||||
if ((serverResp?.Organizations[0]?.Kpp != nKpp) && (!organization?.DepartmentId)) {
|
||||
throw new Error(`Не удалось получить ящик получателя для контрагента с ИНН: ${nInn} и КПП: ${nKpp}, у головной организации отсутствует подразделение с КПП: ${nKpp}`);
|
||||
}
|
||||
//Сохраняем полученный ответ
|
||||
Organization.BoxId = serverResp.Organizations[0].Boxes[0].BoxId;
|
||||
return Organization;
|
||||
organization.BoxId = serverResp.Organizations[0].Boxes[0].BoxId;
|
||||
return organization;
|
||||
} catch (e) {
|
||||
throw Error(`Ошибка при получении ящика получателя: ${e.message}`);
|
||||
};
|
||||
@ -298,7 +299,7 @@ const beforeMessagePost = async prms => {
|
||||
};
|
||||
//Переменные для запросов
|
||||
let serverResp;
|
||||
let Organization;
|
||||
let organization;
|
||||
try {
|
||||
//Выполним запрос
|
||||
serverResp = await rqp(rqpOptions);
|
||||
@ -319,11 +320,11 @@ const beforeMessagePost = async prms => {
|
||||
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;
|
||||
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 ((!obj.ToDepartmentId) && (organization.DepartmentId)) {
|
||||
obj.ToDepartmentId = organization.DepartmentId;
|
||||
};
|
||||
//Если пришел ответ
|
||||
if (prms.queue.blResp && serverResp.statusCode == 200) {
|
||||
@ -583,6 +584,7 @@ const afterEvent = async prms => {
|
||||
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}`);
|
||||
@ -601,6 +603,7 @@ const afterEvent = async prms => {
|
||||
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);
|
||||
@ -609,6 +612,7 @@ const afterEvent = async prms => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -817,14 +821,14 @@ const beforeDepartmentIdGet = async prms => {
|
||||
//Обработчик "После" отправки запроса на получение списка подразделений контрагента к сервису "ДИАДОК"
|
||||
const afterDepartmentIdGet = async prms => {
|
||||
let resu = null;
|
||||
let Organization = {};
|
||||
let organization = {};
|
||||
//Действие выполнено успешно
|
||||
if (prms.optionsResp.statusCode == 200) {
|
||||
try {
|
||||
try {
|
||||
//Получим организацию не в роуминге (или единственную организацию в роуминге)
|
||||
Organization = GetOrganizations(JSON.parse(prms.queue.blResp.toString()));
|
||||
if (!Organization) {
|
||||
organization = getOrganizations(JSON.parse(prms.queue.blResp.toString()));
|
||||
if (!organization) {
|
||||
throw Error(`Не удалось получить ящик для контрагента с ИНН: ${prms.options.nINN} и КПП: ${prms.options.nKPP}`);
|
||||
}
|
||||
} catch (e) {
|
||||
@ -833,10 +837,10 @@ const afterDepartmentIdGet = async prms => {
|
||||
//Считаем токен доступа из контекста сервиса
|
||||
let sToken = prms.service.sCtx;
|
||||
//Получим головную организацию по ИНН/КПП
|
||||
Organization = await GetOrganization(prms.service.sSrvRoot, buildHeaders(sAPIClientId, sToken), prms.options.nINN, prms.options.nKPP);
|
||||
organization = await getOrganization(prms.service.sSrvRoot, buildHeaders(sAPIClientId, sToken), prms.options.nINN, prms.options.nKPP);
|
||||
};
|
||||
//Преобразуем JSON ответ сервиса "ДИАДОК" в XML, понятный "Парус 8"
|
||||
resu = toXML({ root: Organization });
|
||||
resu = toXML({ root: organization });
|
||||
} catch (e) {
|
||||
throw new Error(`Неожиданный ответ сервера ЭДО "ДИАДОК". Ошибка интерпретации: ${e.message}`);
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
const { ServerError } = require("./../core/server_errors"); //Типовая ошибка
|
||||
const { SERR_APP_SERVER_BEFORE, SERR_DB_SERVER } = require("./../core/constants"); //Общесистемные константы
|
||||
const oracledb = require("oracledb"); //Работа с СУБД Oracle
|
||||
|
||||
//------------
|
||||
// Тело модуля
|
||||
@ -21,52 +20,37 @@ const before = async prms => {
|
||||
if (prms.options.qs && prms.options.qs.STOKEN && prms.options.qs.NPREVIEW && prms.options.qs.NACTUAL_CHECK) {
|
||||
//И есть подключение к БД
|
||||
if (prms.dbConn.bConnected) {
|
||||
let pooledConnection;
|
||||
try {
|
||||
//Считаем курсор с данными публикации
|
||||
pooledConnection = await prms.dbConn.connection.getConnection();
|
||||
let res = await pooledConnection.execute(
|
||||
"BEGIN PKG_EXS_EXT_OLAPP_RUN.EXTRACT(STOKEN => :STOKEN, NPREVIEW => :NPREVIEW, NACTUAL_CHECK => :NACTUAL_CHECK, RCDATA => :RCDATA); END;",
|
||||
{
|
||||
STOKEN: prms.options.qs.STOKEN,
|
||||
NPREVIEW: prms.options.qs.NPREVIEW,
|
||||
NACTUAL_CHECK: prms.options.qs.NACTUAL_CHECK,
|
||||
RCDATA: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
|
||||
},
|
||||
{ outFormat: oracledb.OBJECT }
|
||||
);
|
||||
//Установим заголовок ответа и начнём выдачу данных
|
||||
prms.res.set({
|
||||
"content-type": "application/json;charset=utf-8"
|
||||
});
|
||||
prms.res.write("[");
|
||||
//Обходим курсор и выдаём порционно ответ
|
||||
const rs = res.outBinds.RCDATA;
|
||||
let cnt = 1;
|
||||
let row;
|
||||
while ((row = await rs.getRow())) {
|
||||
prms.res.write(Buffer.from(`${cnt > 1 ? "," : ""}${JSON.stringify(row)}`));
|
||||
cnt++;
|
||||
let olappExtractData = await prms.dbConn.executeStored({
|
||||
sName: "PKG_EXS_EXT_OLAPP_RUN.EXTRACT",
|
||||
inPrms: {
|
||||
STOKEN: prms.options.qs.STOKEN,
|
||||
NPREVIEW: prms.options.qs.NPREVIEW,
|
||||
NACTUAL_CHECK: prms.options.qs.NACTUAL_CHECK
|
||||
},
|
||||
outPrms: {
|
||||
RCDATA: prms.dbConn.connector.DT_CURSOR
|
||||
}
|
||||
//Завершаем передачу
|
||||
prms.res.write("]");
|
||||
prms.res.end();
|
||||
await rs.close();
|
||||
//Дальше обрабатывать не надо
|
||||
return {
|
||||
bStopPropagation: true
|
||||
};
|
||||
} catch (e) {
|
||||
throw new ServerError(SERR_DB_SERVER, e.message);
|
||||
} finally {
|
||||
if (pooledConnection) {
|
||||
try {
|
||||
await pooledConnection.close();
|
||||
} catch (e) {
|
||||
throw new ServerError(SERR_DB_SERVER, e.message);
|
||||
}
|
||||
});
|
||||
//Установим заголовок ответа и начнём выдачу данных
|
||||
prms.res.set({
|
||||
"content-type": "application/json;charset=utf-8"
|
||||
});
|
||||
prms.res.write("[");
|
||||
if (olappExtractData)
|
||||
if (olappExtractData.RCDATA) {
|
||||
let cnt = 1;
|
||||
olappExtractData.RCDATA.map(row => {
|
||||
prms.res.write(Buffer.from(`${cnt > 1 ? "," : ""}${JSON.stringify(row)}`));
|
||||
cnt++;
|
||||
});
|
||||
}
|
||||
}
|
||||
//Завершаем передачу
|
||||
prms.res.write("]");
|
||||
prms.res.end();
|
||||
//Дальше обрабатывать не надо
|
||||
return {
|
||||
bStopPropagation: true
|
||||
};
|
||||
} else {
|
||||
throw new ServerError(SERR_DB_SERVER, "Нет подключения к БД");
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
const xml2js = require("xml2js"); //Конвертация XML в JSON и JSON в XML
|
||||
const cfg = require("./../config"); //Настройки сервера приложений
|
||||
const { makeErrorText, sendMail } = require("./../core/utils"); //Вспомогательные функции
|
||||
const oracledb = require("oracledb"); //Работа с СУБД Oracle
|
||||
|
||||
//---------------------
|
||||
// Глобальные константы
|
||||
@ -24,84 +23,37 @@ const NSTATUS_DONE = 3;
|
||||
// Тело модуля
|
||||
//------------
|
||||
|
||||
//Чтение данных из курсора
|
||||
const readCursorData = cursor => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let queryStream = cursor.toQueryStream();
|
||||
let rows = [];
|
||||
queryStream.on("data", row => {
|
||||
rows.push(row);
|
||||
});
|
||||
queryStream.on("error", err => {
|
||||
reject(new Error(err.message));
|
||||
});
|
||||
queryStream.on("close", () => {
|
||||
resolve(rows);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//Установка статуса отправки
|
||||
const setSendMsg = async prms => {
|
||||
let pooledConnection;
|
||||
try {
|
||||
pooledConnection = await prms.connection.getConnection();
|
||||
await pooledConnection.execute(
|
||||
"begin PKG_EXS_EXT_MAIL.EXSEXTMAIL_SET_STATUS(NRN => :NRN, SERR_TEXT => :SERR_TEXT, NSTATUS => :NSTATUS); end;",
|
||||
{ NRN: prms.nRn, SERR_TEXT: prms.sErrMsg, NSTATUS: prms.nStatus },
|
||||
{ autoCommit: true }
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(e.message);
|
||||
} finally {
|
||||
if (pooledConnection) {
|
||||
try {
|
||||
await pooledConnection.close();
|
||||
} catch (e) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
await prms.dbConn.executeStored({
|
||||
connection: prms.dbConn.connection,
|
||||
sName: "PKG_EXS_EXT_MAIL.EXSEXTMAIL_SET_STATUS",
|
||||
inPrms: {
|
||||
NRN: prms.nRn,
|
||||
SERR_TEXT: prms.sErrMsg,
|
||||
NSTATUS: prms.nStatus
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//Считывание записей прикладываемых документов
|
||||
const getMailAttach = async prms => {
|
||||
let pooledConnection;
|
||||
try {
|
||||
pooledConnection = await prms.connection.getConnection();
|
||||
let res = await pooledConnection.execute(
|
||||
"begin PKG_EXS_EXT_MAIL.GET_ATTACH(NIDENT => :NIDENT, RCDOCUMENTS => :RCDOCUMENTS); end;",
|
||||
{
|
||||
NIDENT: prms.nIdent,
|
||||
RCDOCUMENTS: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
|
||||
},
|
||||
{ outFormat: oracledb.OBJECT, autoCommit: true }
|
||||
);
|
||||
let rows = await readCursorData(res.outBinds.RCDOCUMENTS);
|
||||
let rowsRes = [];
|
||||
//Если результат запроса не пустой
|
||||
if (rows.length !== 0) {
|
||||
//Переводим BLOB в BUFFER и формируем формат аттача
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let rowContent = await rows[i].BDATA.getData();
|
||||
let attachData = await prms.dbConn.executeStored({
|
||||
connection: prms.dbConn.connection,
|
||||
sName: "PKG_EXS_EXT_MAIL.GET_ATTACH",
|
||||
inPrms: { NIDENT: prms.nIdent },
|
||||
outPrms: { RCDOCUMENTS: prms.dbConn.connector.DT_CURSOR }
|
||||
});
|
||||
let rowsRes = [];
|
||||
if (attachData)
|
||||
if (attachData.RCDOCUMENTS)
|
||||
attachData.RCDOCUMENTS.map(data => {
|
||||
rowsRes.push({
|
||||
filename: rows[i].FILENAME,
|
||||
content: rowContent
|
||||
filename: data.filename,
|
||||
content: data.bdata
|
||||
});
|
||||
}
|
||||
}
|
||||
return rowsRes;
|
||||
} catch (e) {
|
||||
throw new Error(e.message);
|
||||
} finally {
|
||||
if (pooledConnection) {
|
||||
try {
|
||||
await pooledConnection.close();
|
||||
} catch (e) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return rowsRes;
|
||||
};
|
||||
|
||||
//Разбор XML
|
||||
@ -125,7 +77,7 @@ const before = async prms => {
|
||||
parseRes = await parseXML(prms.queue.blMsg.toString());
|
||||
//Если есть присоединенные файлы - добавляем их
|
||||
if (parseRes.mail.ident) {
|
||||
parseRes.mail.attachments = await getMailAttach({ connection: prms.dbConn.connection, nIdent: parseRes.mail.ident });
|
||||
parseRes.mail.attachments = await getMailAttach({ dbConn: prms.dbConn, nIdent: parseRes.mail.ident });
|
||||
}
|
||||
//Если указан текст в обычном формате
|
||||
if (parseRes.mail.text) {
|
||||
@ -158,7 +110,7 @@ const before = async prms => {
|
||||
//Если имеется рег. номер записи очереди отправки E-mail - обновляем информацию о текущем сообщении
|
||||
if (parseRes.mail.nExsextmailId) {
|
||||
await setSendMsg({
|
||||
connection: prms.dbConn.connection,
|
||||
dbConn: prms.dbConn,
|
||||
nRn: parseRes.mail.nExsextmailId,
|
||||
sErrMsg: "",
|
||||
nStatus: NSTATUS_DONE
|
||||
@ -170,7 +122,7 @@ const before = async prms => {
|
||||
//Если имеется рег. номер записи очереди отправки E-mail - обновляем информацию о текущем сообщении
|
||||
if (parseRes.mail.nExsextmailId) {
|
||||
await setSendMsg({
|
||||
connection: prms.dbConn.connection,
|
||||
dbConn: prms.dbConn,
|
||||
nRn: parseRes.mail.nExsextmailId,
|
||||
sErrMsg: res,
|
||||
nStatus: NSTATUS_ERR
|
||||
|
Loading…
x
Reference in New Issue
Block a user