Merge branch 'dev' into 6028_route_getRouteByWorker
This commit is contained in:
commit
2d594a3a20
|
@ -10,5 +10,9 @@
|
||||||
"eslint.format.enable": true,
|
"eslint.format.enable": true,
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||||
}
|
},
|
||||||
|
"cSpell.words": [
|
||||||
|
"salix",
|
||||||
|
"fdescribe"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
76
CHANGELOG.md
76
CHANGELOG.md
|
@ -5,13 +5,85 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2350.01] - 2023-12-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
### Changed
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
|
||||||
|
## [2348.01] - 2023-11-30
|
||||||
|
|
||||||
|
### Características Añadidas 🆕
|
||||||
|
- **Tickets → Adelantar:** Permite mover lineas sin generar negativos
|
||||||
|
- **Tickets → Adelantar:** Permite modificar la fecha de los tickets
|
||||||
|
- **Trabajadores → Notificaciones:** Nueva sección (lilium)
|
||||||
|
|
||||||
|
### Correcciones 🛠️
|
||||||
|
- **Tickets → RocketChat:** Arreglada detección de cambios
|
||||||
|
|
||||||
|
|
||||||
|
## [2346.01] - 2023-11-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
### Changed
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
|
||||||
|
## [2342.01] - 2023-11-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Usuarios -> Foto) Se muestra la foto del trabajador
|
||||||
|
### Fixed
|
||||||
|
- (Usuarios -> Historial) Abre el descriptor del usuario correctamente
|
||||||
|
|
||||||
|
|
||||||
|
## [2340.01] - 2023-10-05
|
||||||
|
|
||||||
|
## [2338.01] - 2023-09-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Ticket -> Servicios) Se pueden abonar servicios
|
||||||
|
- (Facturas -> Datos básicos) Muestra valores por defecto
|
||||||
|
- (Facturas -> Borrado) Notificación al borrar un asiento ya enlazado en Sage
|
||||||
|
### Changed
|
||||||
|
- (Trabajadores -> Calendario) Icono de check arreglado cuando pulsas un tipo de dia
|
||||||
|
|
||||||
|
## [2336.01] - 2023-09-07
|
||||||
|
|
||||||
|
## [2334.01] - 2023-08-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (General -> Errores) Botón para enviar cau con los datos del error
|
||||||
|
|
||||||
|
|
||||||
|
## [2332.01] - 2023-08-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Trabajadores -> Gestión documental) Soporte para Docuware
|
||||||
|
- (General -> Agencia) Soporte para Viaexpress
|
||||||
|
- (Tickets -> SMS) Nueva sección en Lilium
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- (General -> Tickets) Devuelve el motivo por el cual no es editable
|
||||||
|
- (Desplegables -> Trabajadores) Mejorados
|
||||||
|
- (General -> Clientes) Razón social y dirección en mayúsculas
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- (Clientes -> SMS) Al pasar el ratón por encima muestra el mensaje completo
|
||||||
|
|
||||||
|
|
||||||
## [2330.01] - 2023-07-27
|
## [2330.01] - 2023-07-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- (Artículos -> Vista Previa) Añadido campo "Plástico reciclado"
|
||||||
|
- (Rutas -> Troncales) Nueva sección
|
||||||
|
- (Tickets -> Opciones) Opción establecer peso
|
||||||
|
- (Clientes -> SMS) Nueva sección
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- (General -> Iconos) Añadidos nuevos iconos
|
||||||
### Fixed
|
- (Clientes -> Razón social) Permite crear clientes con la misma razón social según el país
|
||||||
|
|
||||||
|
|
||||||
## [2328.01] - 2023-07-13
|
## [2328.01] - 2023-07-13
|
||||||
|
|
10
Dockerfile
10
Dockerfile
|
@ -1,4 +1,4 @@
|
||||||
FROM debian:bullseye-slim
|
FROM debian:bookworm-slim
|
||||||
ENV TZ Europe/Madrid
|
ENV TZ Europe/Madrid
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
@ -25,7 +25,13 @@ RUN apt-get update \
|
||||||
libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
|
libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
|
||||||
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \
|
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \
|
||||||
libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
|
libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
|
||||||
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
|
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
||||||
|
|
||||||
|
# Extra dependencies
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
samba-common-bin samba-dsdb-modules\
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& npm -g install pm2
|
&& npm -g install pm2
|
||||||
|
|
||||||
|
|
40
README.md
40
README.md
|
@ -8,7 +8,7 @@ Salix is also the scientific name of a beautifull tree! :)
|
||||||
|
|
||||||
Required applications.
|
Required applications.
|
||||||
|
|
||||||
* Node.js >= 16.x LTS
|
* Node.js
|
||||||
* Docker
|
* Docker
|
||||||
* Git
|
* Git
|
||||||
|
|
||||||
|
@ -17,20 +17,7 @@ You will need to install globally the following items.
|
||||||
$ sudo npm install -g jest gulp-cli
|
$ sudo npm install -g jest gulp-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
For the usage of jest --watch on macOs.
|
## Installing dependencies and launching
|
||||||
```
|
|
||||||
$ brew install watchman
|
|
||||||
```
|
|
||||||
* [watchman](https://facebook.github.io/watchman/)
|
|
||||||
|
|
||||||
## Linux Only Prerequisites
|
|
||||||
|
|
||||||
Your user must be on the docker group to use it so you will need to run this command:
|
|
||||||
```
|
|
||||||
$ sudo usermod -a -G docker yourusername
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting Started // Installing
|
|
||||||
|
|
||||||
Pull from repository.
|
Pull from repository.
|
||||||
|
|
||||||
|
@ -76,29 +63,6 @@ In Visual Studio Code we use the ESLint extension.
|
||||||
ext install dbaeumer.vscode-eslint
|
ext install dbaeumer.vscode-eslint
|
||||||
```
|
```
|
||||||
|
|
||||||
Gitlens for visualization of code authorship
|
|
||||||
```
|
|
||||||
ext install eamodio.gitlens
|
|
||||||
```
|
|
||||||
|
|
||||||
Spanish language pack
|
|
||||||
```
|
|
||||||
ext install ms-ceintl.vscode-language-pack-es
|
|
||||||
```
|
|
||||||
|
|
||||||
### Recommended extensions
|
|
||||||
|
|
||||||
Material icon Theme
|
|
||||||
```
|
|
||||||
ext install pkief.material-icon-theme
|
|
||||||
```
|
|
||||||
|
|
||||||
Material UI Themes
|
|
||||||
```
|
|
||||||
ext install equinusocio.vsc-material-theme
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Built With
|
## Built With
|
||||||
|
|
||||||
* [angularjs](https://angularjs.org/)
|
* [angularjs](https://angularjs.org/)
|
||||||
|
|
|
@ -26,15 +26,14 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
||||||
if (!recipientId) return false;
|
if (!recipientId) return false;
|
||||||
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
const sender = await models.VnUser.findById(userId, {fields: ['id']});
|
const sender = await models.VnUser.findById(userId, {fields: ['id']});
|
||||||
const recipient = await models.VnUser.findById(recipientId, null);
|
const recipient = await models.VnUser.findById(recipientId, null);
|
||||||
|
|
||||||
// Prevent sending messages to yourself
|
// Prevent sending messages to yourself
|
||||||
if (recipientId == userId) return false;
|
if (recipientId == userId) return false;
|
||||||
|
|
||||||
if (!recipient)
|
if (!recipient)
|
||||||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('getTickets', {
|
||||||
|
description: 'Make a new collection of tickets',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The collection id',
|
||||||
|
required: true,
|
||||||
|
http: {source: 'path'}
|
||||||
|
}, {
|
||||||
|
arg: 'print',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'True if you want to print'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/getTickets`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getTickets = async(ctx, id, print, options) => {
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
const $t = ctx.req.__;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
myOptions.userId = userId;
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions);
|
||||||
|
const sales = await Self.rawSql(`
|
||||||
|
SELECT s.ticketFk,
|
||||||
|
sgd.saleGroupFk,
|
||||||
|
s.id saleFk,
|
||||||
|
s.itemFk,
|
||||||
|
i.longName,
|
||||||
|
i.size,
|
||||||
|
ic.color,
|
||||||
|
o.code origin,
|
||||||
|
ish.packing,
|
||||||
|
ish.grouping,
|
||||||
|
s.isAdded,
|
||||||
|
s.originalQuantity,
|
||||||
|
s.quantity saleQuantity,
|
||||||
|
iss.quantity reservedQuantity,
|
||||||
|
SUM(iss.quantity) OVER (PARTITION BY s.id ORDER BY ish.id) accumulatedQuantity,
|
||||||
|
ROW_NUMBER () OVER (PARTITION BY s.id ORDER BY pickingOrder) currentItemShelving,
|
||||||
|
COUNT(*) OVER (PARTITION BY s.id ORDER BY s.id) totalItemShelving,
|
||||||
|
sh.code,
|
||||||
|
IFNULL(p2.code, p.code) parkingCode,
|
||||||
|
IFNULL(p2.pickingOrder, p.pickingOrder) pickingOrder,
|
||||||
|
iss.id itemShelvingSaleFk,
|
||||||
|
iss.isPicked
|
||||||
|
FROM ticketCollection tc
|
||||||
|
LEFT JOIN collection c ON c.id = tc.collectionFk
|
||||||
|
JOIN ticket t ON t.id = tc.ticketFk
|
||||||
|
JOIN sale s ON s.ticketFk = t.id
|
||||||
|
LEFT JOIN saleGroupDetail sgd ON sgd.saleFk = s.id
|
||||||
|
LEFT JOIN saleGroup sg ON sg.id = sgd.saleGroupFk
|
||||||
|
LEFT JOIN parking p2 ON p2.id = sg.parkingFk
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
LEFT JOIN itemShelvingSale iss ON iss.saleFk = s.id
|
||||||
|
LEFT JOIN itemShelving ish ON ish.id = iss.itemShelvingFk
|
||||||
|
LEFT JOIN shelving sh ON sh.code = ish.shelvingFk
|
||||||
|
LEFT JOIN parking p ON p.id = sh.parkingFk
|
||||||
|
LEFT JOIN itemColor ic ON ic.itemFk = s.itemFk
|
||||||
|
LEFT JOIN origin o ON o.id = i.originFk
|
||||||
|
WHERE tc.collectionFk = ?
|
||||||
|
GROUP BY s.id, ish.id, p.code, p2.code
|
||||||
|
ORDER BY pickingOrder;`, [id], myOptions);
|
||||||
|
|
||||||
|
if (print)
|
||||||
|
await Self.rawSql(`CALL vn.collection_printSticker(?, ?)`, [id, null], myOptions);
|
||||||
|
|
||||||
|
const collection = {collectionFk: id, tickets: []};
|
||||||
|
if (tickets && tickets.length) {
|
||||||
|
for (const ticket of tickets) {
|
||||||
|
const ticketId = ticket.ticketFk;
|
||||||
|
if (ticket.observaciones != '') {
|
||||||
|
for (observation of ticket.observaciones.split(' ')) {
|
||||||
|
if (['#', '@'].includes(observation.charAt(0))) {
|
||||||
|
promises.push(Self.app.models.Chat.send(ctx, observation,
|
||||||
|
$t('The ticket is in preparation', {
|
||||||
|
ticketId: ticketId,
|
||||||
|
ticketUrl: `${url}ticket/${ticketId}/summary`,
|
||||||
|
salesPersonId: ticket.salesPersonFk
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sales && sales.length) {
|
||||||
|
const barcodes = await Self.rawSql(`
|
||||||
|
SELECT s.id saleFk, b.code, c.id
|
||||||
|
FROM vn.sale s
|
||||||
|
LEFT JOIN vn.itemBarcode b ON b.itemFk = s.itemFk
|
||||||
|
LEFT JOIN vn.buy c ON c.itemFk = s.itemFk
|
||||||
|
LEFT JOIN vn.entry e ON e.id = c.entryFk
|
||||||
|
LEFT JOIN vn.travel tr ON tr.id = e.travelFk
|
||||||
|
WHERE s.ticketFk = ?
|
||||||
|
AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`,
|
||||||
|
[ticketId], myOptions);
|
||||||
|
ticket.sales = [];
|
||||||
|
for (const sale of sales) {
|
||||||
|
if (sale.ticketFk === ticketId) {
|
||||||
|
sale.Barcodes = [];
|
||||||
|
if (barcodes && barcodes.length) {
|
||||||
|
for (const barcode of barcodes) {
|
||||||
|
if (barcode.saleFk === sale.saleFk) {
|
||||||
|
for (const prop in barcode) {
|
||||||
|
if (['id', 'code'].includes(prop) && barcode[prop])
|
||||||
|
sale.Barcodes.push(barcode[prop].toString(), '0' + barcode[prop]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ticket.sales.push(sale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collection.tickets.push(ticket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
return collection;
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,133 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('newCollection', {
|
|
||||||
description: 'Make a new collection of tickets',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts: [{
|
|
||||||
arg: 'collectionFk',
|
|
||||||
type: 'Number',
|
|
||||||
required: false,
|
|
||||||
description: 'The collection id'
|
|
||||||
}, {
|
|
||||||
arg: 'sectorFk',
|
|
||||||
type: 'Number',
|
|
||||||
required: true,
|
|
||||||
description: 'The sector of worker'
|
|
||||||
}, {
|
|
||||||
arg: 'vWagons',
|
|
||||||
type: 'Number',
|
|
||||||
required: true,
|
|
||||||
description: 'The number of wagons'
|
|
||||||
}],
|
|
||||||
returns: {
|
|
||||||
type: 'Object',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/newCollection`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.newCollection = async(ctx, collectionFk, sectorFk, vWagons) => {
|
|
||||||
let query = '';
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
if (!collectionFk) {
|
|
||||||
query = `CALL vn.collectionTrain_newBeta(?,?,?)`;
|
|
||||||
const [result] = await Self.rawSql(query, [sectorFk, vWagons, userId], {userId});
|
|
||||||
if (result.length == 0)
|
|
||||||
throw new Error(`No collections for today`);
|
|
||||||
|
|
||||||
collectionFk = result[0].vCollectionFk;
|
|
||||||
}
|
|
||||||
|
|
||||||
query = `CALL vn.collectionTicket_get(?)`;
|
|
||||||
const [tickets] = await Self.rawSql(query, [collectionFk], {userId});
|
|
||||||
|
|
||||||
query = `CALL vn.collectionSale_get(?)`;
|
|
||||||
const [sales] = await Self.rawSql(query, [collectionFk], {userId});
|
|
||||||
|
|
||||||
query = `CALL vn.collectionPlacement_get(?)`;
|
|
||||||
const [placements] = await Self.rawSql(query, [collectionFk], {userId});
|
|
||||||
|
|
||||||
query = `CALL vn.collectionSticker_print(?,?)`;
|
|
||||||
await Self.rawSql(query, [collectionFk, sectorFk], {userId});
|
|
||||||
|
|
||||||
return makeCollection(tickets, sales, placements, collectionFk);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a collection json
|
|
||||||
* @param {*} tickets - Request tickets
|
|
||||||
* @param {*} sales - Request sales
|
|
||||||
* @param {*} placements - Request placements
|
|
||||||
* @param {*} collectionFk - Request placements
|
|
||||||
* @return {Object} Collection JSON
|
|
||||||
*/
|
|
||||||
async function makeCollection(tickets, sales, placements, collectionFk) {
|
|
||||||
let collection = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < tickets.length; i++) {
|
|
||||||
let ticket = {};
|
|
||||||
ticket['ticketFk'] = tickets[i]['ticketFk'];
|
|
||||||
ticket['level'] = tickets[i]['level'];
|
|
||||||
ticket['agencyName'] = tickets[i]['agencyName'];
|
|
||||||
ticket['warehouseFk'] = tickets[i]['warehouseFk'];
|
|
||||||
ticket['salesPersonFk'] = tickets[i]['salesPersonFk'];
|
|
||||||
|
|
||||||
let ticketSales = [];
|
|
||||||
|
|
||||||
for (let x = 0; x < sales.length; x++) {
|
|
||||||
if (sales[x]['ticketFk'] == ticket['ticketFk']) {
|
|
||||||
let sale = {};
|
|
||||||
sale['collectionFk'] = collectionFk;
|
|
||||||
sale['ticketFk'] = sales[x]['ticketFk'];
|
|
||||||
sale['saleFk'] = sales[x]['saleFk'];
|
|
||||||
sale['itemFk'] = sales[x]['itemFk'];
|
|
||||||
sale['quantity'] = sales[x]['quantity'];
|
|
||||||
if (sales[x]['quantityPicked'] != null)
|
|
||||||
sale['quantityPicked'] = sales[x]['quantityPicked'];
|
|
||||||
else
|
|
||||||
sale['quantityPicked'] = 0;
|
|
||||||
sale['longName'] = sales[x]['longName'];
|
|
||||||
sale['size'] = sales[x]['size'];
|
|
||||||
sale['color'] = sales[x]['color'];
|
|
||||||
sale['discount'] = sales[x]['discount'];
|
|
||||||
sale['price'] = sales[x]['price'];
|
|
||||||
sale['stems'] = sales[x]['stems'];
|
|
||||||
sale['category'] = sales[x]['category'];
|
|
||||||
sale['origin'] = sales[x]['origin'];
|
|
||||||
sale['clientFk'] = sales[x]['clientFk'];
|
|
||||||
sale['productor'] = sales[x]['productor'];
|
|
||||||
sale['reserved'] = sales[x]['reserved'];
|
|
||||||
sale['isPreviousPrepared'] = sales[x]['isPreviousPrepared'];
|
|
||||||
sale['isPrepared'] = sales[x]['isPrepared'];
|
|
||||||
sale['isControlled'] = sales[x]['isControlled'];
|
|
||||||
|
|
||||||
let salePlacements = [];
|
|
||||||
|
|
||||||
for (let z = 0; z < placements.length; z++) {
|
|
||||||
if (placements[z]['saleFk'] == sale['saleFk']) {
|
|
||||||
let placement = {};
|
|
||||||
placement['saleFk'] = placements[z]['saleFk'];
|
|
||||||
placement['itemFk'] = placements[z]['itemFk'];
|
|
||||||
placement['placement'] = placements[z]['placement'];
|
|
||||||
placement['shelving'] = placements[z]['shelving'];
|
|
||||||
placement['created'] = placements[z]['created'];
|
|
||||||
placement['visible'] = placements[z]['visible'];
|
|
||||||
placement['order'] = placements[z]['order'];
|
|
||||||
placement['grouping'] = placements[z]['grouping'];
|
|
||||||
salePlacements.push(placement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sale['placements'] = salePlacements;
|
|
||||||
ticketSales.push(sale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ticket['sales'] = ticketSales;
|
|
||||||
collection.push(ticket);
|
|
||||||
}
|
|
||||||
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('collection getTickets()', () => {
|
||||||
|
let ctx;
|
||||||
|
beforeAll(async() => {
|
||||||
|
ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get tickets, sales and barcodes from collection', async() => {
|
||||||
|
const tx = await models.Collection.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const collectionId = 1;
|
||||||
|
|
||||||
|
const collectionTickets = await models.Collection.getTickets(ctx, collectionId, null, options);
|
||||||
|
|
||||||
|
expect(collectionTickets.collectionFk).toEqual(collectionId);
|
||||||
|
expect(collectionTickets.tickets.length).toEqual(3);
|
||||||
|
expect(collectionTickets.tickets[0].ticketFk).toEqual(1);
|
||||||
|
expect(collectionTickets.tickets[1].ticketFk).toEqual(2);
|
||||||
|
expect(collectionTickets.tickets[2].ticketFk).toEqual(23);
|
||||||
|
expect(collectionTickets.tickets[0].sales[0].ticketFk).toEqual(1);
|
||||||
|
expect(collectionTickets.tickets[0].sales[1].ticketFk).toEqual(1);
|
||||||
|
expect(collectionTickets.tickets[0].sales[2].ticketFk).toEqual(1);
|
||||||
|
expect(collectionTickets.tickets[0].sales[0].Barcodes.length).toBeTruthy();
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,12 +0,0 @@
|
||||||
const {models} = require('vn-loopback/server/server');
|
|
||||||
|
|
||||||
describe('newCollection()', () => {
|
|
||||||
it('should return a new collection', async() => {
|
|
||||||
pending('#3400 analizar que hacer con rutas de back collection');
|
|
||||||
let ctx = {req: {accessToken: {userId: 1106}}};
|
|
||||||
let response = await models.Collection.newCollection(ctx, 1, 1, 1);
|
|
||||||
|
|
||||||
expect(response.length).toBeGreaterThan(0);
|
|
||||||
expect(response[0].ticketFk).toEqual(2);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -18,6 +18,14 @@ describe('setSaleQuantity()', () => {
|
||||||
|
|
||||||
it('should change quantity sale', async() => {
|
it('should change quantity sale', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
|
||||||
|
SELECT 100 as available;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const axios = require('axios');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('checkFile', {
|
Self.remoteMethod('checkFile', {
|
||||||
description: 'Check if exist docuware file',
|
description: 'Check if exist docuware file',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: [
|
accepts: [
|
||||||
|
@ -17,12 +15,16 @@ module.exports = Self => {
|
||||||
required: true,
|
required: true,
|
||||||
description: 'The fileCabinet name'
|
description: 'The fileCabinet name'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'The filter'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
arg: 'signed',
|
arg: 'signed',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
required: true,
|
|
||||||
description: 'If pdf is necessary to be signed'
|
description: 'If pdf is necessary to be signed'
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -34,7 +36,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.checkFile = async function(ctx, id, fileCabinet, signed) {
|
Self.checkFile = async function(id, fileCabinet, filter, signed) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const action = 'find';
|
const action = 'find';
|
||||||
|
|
||||||
|
@ -45,40 +47,34 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchFilter = {
|
if (!filter) {
|
||||||
condition: [
|
filter = {
|
||||||
{
|
condition: [
|
||||||
DBName: docuwareInfo.findById,
|
{
|
||||||
|
DBName: docuwareInfo.findById,
|
||||||
Value: [id]
|
Value: [id]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
sortOrder: [
|
sortOrder: [
|
||||||
{
|
{
|
||||||
Field: 'FILENAME',
|
Field: 'FILENAME',
|
||||||
Direction: 'Desc'
|
Direction: 'Desc'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
if (signed) {
|
||||||
|
filter.condition.push({
|
||||||
|
DBName: 'ESTADO',
|
||||||
|
Value: ['Firmado']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = await Self.getOptions();
|
const [response] = await Self.get(fileCabinet, filter);
|
||||||
|
if (!response) return false;
|
||||||
|
|
||||||
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
return {id: response['Document ID']};
|
||||||
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
|
|
||||||
|
|
||||||
const response = await axios.post(
|
|
||||||
`${options.url}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`,
|
|
||||||
searchFilter,
|
|
||||||
options.headers
|
|
||||||
);
|
|
||||||
const [documents] = response.data.Items;
|
|
||||||
if (!documents) return false;
|
|
||||||
|
|
||||||
const state = documents.Fields.find(field => field.FieldName == 'ESTADO');
|
|
||||||
if (signed && state.Item != 'Firmado') return false;
|
|
||||||
|
|
||||||
return {id: documents.Id};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +1,6 @@
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
/**
|
|
||||||
* Returns the dialog id
|
|
||||||
*
|
|
||||||
* @param {string} code - The fileCabinet name
|
|
||||||
* @param {string} action - The fileCabinet name
|
|
||||||
* @param {string} fileCabinetId - Optional The fileCabinet name
|
|
||||||
* @return {number} - The fileCabinet id
|
|
||||||
*/
|
|
||||||
Self.getDialog = async(code, action, fileCabinetId) => {
|
|
||||||
const docuwareInfo = await Self.app.models.Docuware.findOne({
|
|
||||||
where: {
|
|
||||||
code: code,
|
|
||||||
action: action
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!fileCabinetId) fileCabinetId = await Self.getFileCabinet(code);
|
|
||||||
|
|
||||||
const options = await Self.getOptions();
|
|
||||||
|
|
||||||
if (!process.env.NODE_ENV)
|
|
||||||
return Math.round();
|
|
||||||
|
|
||||||
const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers);
|
|
||||||
const dialogs = response.data.Dialog;
|
|
||||||
const dialogId = dialogs.find(dialogs => dialogs.DisplayName === docuwareInfo.dialogName).Id;
|
|
||||||
|
|
||||||
return dialogId;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the fileCabinetId
|
|
||||||
*
|
|
||||||
* @param {string} code - The fileCabinet code
|
|
||||||
* @return {number} - The fileCabinet id
|
|
||||||
*/
|
|
||||||
Self.getFileCabinet = async code => {
|
|
||||||
const options = await Self.getOptions();
|
|
||||||
const docuwareInfo = await Self.app.models.Docuware.findOne({
|
|
||||||
where: {
|
|
||||||
code: code
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!process.env.NODE_ENV)
|
|
||||||
return Math.round();
|
|
||||||
|
|
||||||
const fileCabinetResponse = await axios.get(`${options.url}/FileCabinets`, options.headers);
|
|
||||||
const fileCabinets = fileCabinetResponse.data.FileCabinet;
|
|
||||||
const fileCabinetId = fileCabinets.find(fileCabinet => fileCabinet.Name === docuwareInfo.fileCabinetName).Id;
|
|
||||||
|
|
||||||
return fileCabinetId;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns basic headers
|
* Returns basic headers
|
||||||
*
|
*
|
||||||
|
@ -75,4 +22,139 @@ module.exports = Self => {
|
||||||
headers
|
headers
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dialog id
|
||||||
|
*
|
||||||
|
* @param {string} code - The fileCabinet name
|
||||||
|
* @param {string} action - The fileCabinet name
|
||||||
|
* @param {string} fileCabinetId - Optional The fileCabinet name
|
||||||
|
* @return {number} - The fileCabinet id
|
||||||
|
*/
|
||||||
|
Self.getDialog = async(code, action, fileCabinetId) => {
|
||||||
|
if (!process.env.NODE_ENV)
|
||||||
|
return Math.floor(Math.random() + 100);
|
||||||
|
|
||||||
|
const docuwareInfo = await Self.app.models.Docuware.findOne({
|
||||||
|
where: {
|
||||||
|
code,
|
||||||
|
action
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!fileCabinetId) fileCabinetId = await Self.getFileCabinet(code);
|
||||||
|
|
||||||
|
const options = await Self.getOptions();
|
||||||
|
|
||||||
|
const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers);
|
||||||
|
const dialogs = response.data.Dialog;
|
||||||
|
const dialogId = dialogs.find(dialogs => dialogs.DisplayName === docuwareInfo.dialogName).Id;
|
||||||
|
|
||||||
|
return dialogId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fileCabinetId
|
||||||
|
*
|
||||||
|
* @param {string} code - The fileCabinet code
|
||||||
|
* @return {number} - The fileCabinet id
|
||||||
|
*/
|
||||||
|
Self.getFileCabinet = async code => {
|
||||||
|
if (!process.env.NODE_ENV)
|
||||||
|
return Math.floor(Math.random() + 100);
|
||||||
|
|
||||||
|
const options = await Self.getOptions();
|
||||||
|
const docuwareInfo = await Self.app.models.Docuware.findOne({
|
||||||
|
where: {
|
||||||
|
code
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileCabinetResponse = await axios.get(`${options.url}/FileCabinets`, options.headers);
|
||||||
|
const fileCabinets = fileCabinetResponse.data.FileCabinet;
|
||||||
|
const fileCabinetId = fileCabinets.find(fileCabinet => fileCabinet.Name === docuwareInfo.fileCabinetName).Id;
|
||||||
|
|
||||||
|
return fileCabinetId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns docuware data
|
||||||
|
*
|
||||||
|
* @param {string} code - The fileCabinet code
|
||||||
|
* @param {object} filter - The filter for docuware
|
||||||
|
* @param {object} parse - The fields parsed
|
||||||
|
* @return {object} - The data
|
||||||
|
*/
|
||||||
|
Self.get = async(code, filter, parse) => {
|
||||||
|
if (!process.env.NODE_ENV) return;
|
||||||
|
|
||||||
|
const options = await Self.getOptions();
|
||||||
|
const fileCabinetId = await Self.getFileCabinet(code);
|
||||||
|
const dialogId = await Self.getDialog(code, 'find', fileCabinetId);
|
||||||
|
|
||||||
|
const data = await axios.post(
|
||||||
|
`${options.url}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`,
|
||||||
|
filter,
|
||||||
|
options.headers
|
||||||
|
);
|
||||||
|
return parser(data.data, parse);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns docuware data
|
||||||
|
*
|
||||||
|
* @param {string} code - The fileCabinet code
|
||||||
|
* @param {any} id - The id of docuware
|
||||||
|
* @param {object} parse - The fields parsed
|
||||||
|
* @return {object} - The data
|
||||||
|
*/
|
||||||
|
Self.getById = async(code, id, parse) => {
|
||||||
|
if (!process.env.NODE_ENV) return;
|
||||||
|
|
||||||
|
const docuwareInfo = await Self.app.models.Docuware.findOne({
|
||||||
|
fields: ['findById'],
|
||||||
|
where: {
|
||||||
|
code,
|
||||||
|
action: 'find'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const filter = {
|
||||||
|
condition: [
|
||||||
|
{
|
||||||
|
DBName: docuwareInfo.findById,
|
||||||
|
Value: [id]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
return Self.get(code, filter, parse);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns docuware data filtered
|
||||||
|
*
|
||||||
|
* @param {array} data - The data
|
||||||
|
* @param {object} parse - The fields parsed
|
||||||
|
* @return {object} - The data parsed
|
||||||
|
*/
|
||||||
|
function parser(data, parse) {
|
||||||
|
if (!(data && data.Items)) return data;
|
||||||
|
|
||||||
|
const parsed = [];
|
||||||
|
for (item of data.Items) {
|
||||||
|
const itemParsed = {};
|
||||||
|
item.Fields.map(field => {
|
||||||
|
if (field.ItemElementName.includes('Date')) field.Item = toDate(field.Item);
|
||||||
|
if (!parse) return itemParsed[field.FieldLabel] = field.Item;
|
||||||
|
if (parse[field.FieldLabel])
|
||||||
|
itemParsed[parse[field.FieldLabel]] = field.Item;
|
||||||
|
});
|
||||||
|
parsed.push(itemParsed);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toDate(value) {
|
||||||
|
if (!value) return;
|
||||||
|
return new Date(Number(value.substring(6, 19)));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,7 +65,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const email = new Email('delivery-note', params);
|
const email = new Email('delivery-note', params);
|
||||||
|
|
||||||
const docuwareFile = await models.Docuware.download(ctx, id, 'deliveryNote');
|
const docuwareFile = await models.Docuware.download(id, 'deliveryNote');
|
||||||
|
|
||||||
return email.send({
|
return email.send({
|
||||||
overrideAttachments: true,
|
overrideAttachments: true,
|
||||||
|
|
|
@ -3,7 +3,7 @@ const axios = require('axios');
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('download', {
|
Self.remoteMethod('download', {
|
||||||
description: 'Download an docuware PDF',
|
description: 'Download an docuware PDF',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: [
|
accepts: [
|
||||||
|
@ -16,8 +16,12 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
arg: 'fileCabinet',
|
arg: 'fileCabinet',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The file cabinet',
|
description: 'The file cabinet'
|
||||||
http: {source: 'path'}
|
},
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'The filter'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: [
|
returns: [
|
||||||
|
@ -36,14 +40,15 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
http: {
|
http: {
|
||||||
path: `/:id/download/:fileCabinet`,
|
path: `/:id/download`,
|
||||||
verb: 'GET'
|
verb: 'GET'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.download = async function(ctx, id, fileCabinet) {
|
Self.download = async function(id, fileCabinet, filter) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, true);
|
|
||||||
|
const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, filter);
|
||||||
if (!docuwareFile) throw new UserError('The DOCUWARE PDF document does not exists');
|
if (!docuwareFile) throw new UserError('The DOCUWARE PDF document does not exists');
|
||||||
|
|
||||||
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
||||||
|
|
|
@ -1,81 +1,27 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
const axios = require('axios');
|
|
||||||
|
|
||||||
describe('docuware download()', () => {
|
describe('docuware download()', () => {
|
||||||
const ticketId = 1;
|
const ticketId = 1;
|
||||||
const userId = 9;
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
|
|
||||||
accessToken: {userId: userId},
|
|
||||||
headers: {origin: 'http://localhost:5000'},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const docuwareModel = models.Docuware;
|
const docuwareModel = models.Docuware;
|
||||||
const fileCabinetName = 'deliveryNote';
|
const fileCabinetName = 'deliveryNote';
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
spyOn(docuwareModel, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
|
||||||
spyOn(docuwareModel, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false if there are no documents', async() => {
|
it('should return false if there are no documents', async() => {
|
||||||
const response = {
|
spyOn(docuwareModel, 'get').and.returnValue((new Promise(resolve => resolve({Items: []}))));
|
||||||
data: {
|
|
||||||
Items: []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
|
|
||||||
|
|
||||||
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
|
const result = await models.Docuware.checkFile(ticketId, fileCabinetName, null, true);
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false if the document is unsigned', async() => {
|
|
||||||
const response = {
|
|
||||||
data: {
|
|
||||||
Items: [
|
|
||||||
{
|
|
||||||
Id: 1,
|
|
||||||
Fields: [
|
|
||||||
{
|
|
||||||
FieldName: 'ESTADO',
|
|
||||||
Item: 'Unsigned'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
|
|
||||||
|
|
||||||
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
|
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
expect(result).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the document data', async() => {
|
it('should return the document data', async() => {
|
||||||
const docuwareId = 1;
|
const docuwareId = 1;
|
||||||
const response = {
|
const response = [{
|
||||||
data: {
|
'Document ID': docuwareId
|
||||||
Items: [
|
}];
|
||||||
{
|
spyOn(docuwareModel, 'get').and.returnValue((new Promise(resolve => resolve(response))));
|
||||||
Id: docuwareId,
|
|
||||||
Fields: [
|
|
||||||
{
|
|
||||||
FieldName: 'ESTADO',
|
|
||||||
Item: 'Firmado'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
|
|
||||||
|
|
||||||
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
|
const result = await models.Docuware.checkFile(ticketId, fileCabinetName, null, true);
|
||||||
|
|
||||||
expect(result.id).toEqual(docuwareId);
|
expect(result.id).toEqual(docuwareId);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
const axios = require('axios');
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Docuware core', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
process.env.NODE_ENV = 'testing';
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
delete process.env.NODE_ENV;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOptions()', () => {
|
||||||
|
it('should return url and headers', async() => {
|
||||||
|
const result = await models.Docuware.getOptions();
|
||||||
|
|
||||||
|
expect(result.url).toBeDefined();
|
||||||
|
expect(result.headers).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDialog()', () => {
|
||||||
|
it('should return dialogId', async() => {
|
||||||
|
const dialogs = {
|
||||||
|
data: {
|
||||||
|
Dialog: [
|
||||||
|
{
|
||||||
|
DisplayName: 'find',
|
||||||
|
Id: 'getDialogTest'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs)));
|
||||||
|
const result = await models.Docuware.getDialog('deliveryNote', 'find', 'randomFileCabinetId');
|
||||||
|
|
||||||
|
expect(result).toEqual('getDialogTest');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFileCabinet()', () => {
|
||||||
|
it('should return fileCabinetId', async() => {
|
||||||
|
const code = 'deliveryNote';
|
||||||
|
const docuwareInfo = await models.Docuware.findOne({
|
||||||
|
where: {
|
||||||
|
code
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const dialogs = {
|
||||||
|
data: {
|
||||||
|
FileCabinet: [
|
||||||
|
{
|
||||||
|
Name: docuwareInfo.fileCabinetName,
|
||||||
|
Id: 'getFileCabinetTest'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs)));
|
||||||
|
const result = await models.Docuware.getFileCabinet(code);
|
||||||
|
|
||||||
|
expect(result).toEqual('getFileCabinetTest');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('get()', () => {
|
||||||
|
it('should return data without parse', async() => {
|
||||||
|
spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
|
spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
|
const data = {
|
||||||
|
data: {
|
||||||
|
id: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data)));
|
||||||
|
const result = await models.Docuware.get('deliveryNote');
|
||||||
|
|
||||||
|
expect(result.id).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return data with parse', async() => {
|
||||||
|
spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
|
spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
|
const data = {
|
||||||
|
data: {
|
||||||
|
Items: [{
|
||||||
|
Fields: [
|
||||||
|
{
|
||||||
|
ItemElementName: 'integer',
|
||||||
|
FieldLabel: 'firstRequiredField',
|
||||||
|
Item: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ItemElementName: 'string',
|
||||||
|
FieldLabel: 'secondRequiredField',
|
||||||
|
Item: 'myName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ItemElementName: 'integer',
|
||||||
|
FieldLabel: 'notRequiredField',
|
||||||
|
Item: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const parse = {
|
||||||
|
'firstRequiredField': 'id',
|
||||||
|
'secondRequiredField': 'name',
|
||||||
|
};
|
||||||
|
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data)));
|
||||||
|
const [result] = await models.Docuware.get('deliveryNote', null, parse);
|
||||||
|
|
||||||
|
expect(result.id).toEqual(1);
|
||||||
|
expect(result.name).toEqual('myName');
|
||||||
|
expect(result.notRequiredField).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getById()', () => {
|
||||||
|
it('should return data', async() => {
|
||||||
|
spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
|
spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
|
const data = {
|
||||||
|
data: {
|
||||||
|
id: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data)));
|
||||||
|
const result = await models.Docuware.getById('deliveryNote', 1);
|
||||||
|
|
||||||
|
expect(result.id).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -39,7 +39,7 @@ describe('docuware download()', () => {
|
||||||
spyOn(docuwareModel, 'checkFile').and.returnValue({});
|
spyOn(docuwareModel, 'checkFile').and.returnValue({});
|
||||||
spyOn(axios, 'get').and.returnValue(new stream.PassThrough({objectMode: true}));
|
spyOn(axios, 'get').and.returnValue(new stream.PassThrough({objectMode: true}));
|
||||||
|
|
||||||
const result = await models.Docuware.download(ctx, ticketId, fileCabinetName);
|
const result = await models.Docuware.download(ticketId, fileCabinetName);
|
||||||
|
|
||||||
expect(result[1]).toEqual('application/pdf');
|
expect(result[1]).toEqual('application/pdf');
|
||||||
expect(result[2]).toEqual(`filename="${ticketId}.pdf"`);
|
expect(result[2]).toEqual(`filename="${ticketId}.pdf"`);
|
||||||
|
|
|
@ -111,7 +111,7 @@ module.exports = Self => {
|
||||||
throw new UserError('Action not allowed on the test environment');
|
throw new UserError('Action not allowed on the test environment');
|
||||||
|
|
||||||
// delete old
|
// delete old
|
||||||
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
|
const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false);
|
||||||
if (docuwareFile) {
|
if (docuwareFile) {
|
||||||
const deleteJson = {
|
const deleteJson = {
|
||||||
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
|
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
|
||||||
|
|
|
@ -139,7 +139,7 @@ module.exports = Self => {
|
||||||
ftpClient.exec((err, response) => {
|
ftpClient.exec((err, response) => {
|
||||||
if (err || response.error) {
|
if (err || response.error) {
|
||||||
console.debug(`Error downloading checksum file... ${response.error}`);
|
console.debug(`Error downloading checksum file... ${response.error}`);
|
||||||
return reject(err);
|
return reject(response.error || err);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getList', {
|
||||||
|
description: 'Get list of the available and active notification subscriptions',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
description: 'User to modify',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/getList`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getList = async(id, options) => {
|
||||||
|
const activeNotificationsMap = new Map();
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const availableNotificationsMap = await Self.getAvailable(id, myOptions);
|
||||||
|
const activeNotifications = await Self.app.models.NotificationSubscription.find({
|
||||||
|
fields: ['id', 'notificationFk'],
|
||||||
|
include: {relation: 'notification'},
|
||||||
|
where: {userFk: id}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
for (active of activeNotifications) {
|
||||||
|
activeNotificationsMap.set(active.notificationFk, {
|
||||||
|
id: active.id,
|
||||||
|
notificationFk: active.notificationFk,
|
||||||
|
name: active.notification().name,
|
||||||
|
description: active.notification().description,
|
||||||
|
active: true
|
||||||
|
});
|
||||||
|
availableNotificationsMap.delete(active.notificationFk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
active: [...activeNotificationsMap.entries()],
|
||||||
|
available: [...availableNotificationsMap.entries()]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('NotificationSubscription getList()', () => {
|
||||||
|
it('should return a list of available and active notifications of a user', async() => {
|
||||||
|
const userId = 9;
|
||||||
|
const {active, available} = await models.NotificationSubscription.getList(userId);
|
||||||
|
const notifications = await models.Notification.find({});
|
||||||
|
const totalAvailable = notifications.length - active.length;
|
||||||
|
|
||||||
|
expect(active.length).toEqual(2);
|
||||||
|
expect(available.length).toEqual(totalAvailable);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,63 @@
|
||||||
|
const smtp = require('vn-print/core/smtp');
|
||||||
|
const config = require('vn-print/core/config');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('sendToSupport', {
|
||||||
|
description: 'Send mail to support',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'reason',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The reason'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'additionalData',
|
||||||
|
type: 'object',
|
||||||
|
required: true,
|
||||||
|
description: 'The additional data'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/send-to-support`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.sendToSupport = async(ctx, reason, additionalData) => {
|
||||||
|
const emailUser =
|
||||||
|
await Self.app.models.EmailUser.findById(ctx.req.accessToken.userId, {fields: ['email']});
|
||||||
|
|
||||||
|
let html = `<strong>Motivo</strong>:<br/>${reason}<br/>`;
|
||||||
|
html += `<strong>Usuario</strong>:<br/>${ctx.req.accessToken.userId} ${emailUser.email}<br/>`;
|
||||||
|
|
||||||
|
for (const data in additionalData)
|
||||||
|
html += `<strong>${data}</strong>:<br/>${tryParse(additionalData[data])}<br/>`;
|
||||||
|
|
||||||
|
const subjectReason = JSON.parse(additionalData?.httpRequest)?.data?.error;
|
||||||
|
smtp.send({
|
||||||
|
to: `${config.app.reportEmail}, ${emailUser.email}`,
|
||||||
|
subject:
|
||||||
|
'[Support-Salix] ' +
|
||||||
|
additionalData?.frontPath + ' ' +
|
||||||
|
subjectReason?.name + ':' +
|
||||||
|
subjectReason?.message,
|
||||||
|
html
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function tryParse(value) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
} catch {}
|
||||||
|
return JSON.stringify(value, null, ' ').split('\n').join('<br>');
|
||||||
|
} catch {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
module.exports = function(Self) {
|
||||||
|
Self.remoteMethod('getByUser', {
|
||||||
|
description: 'returns the starred modules for the current user',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'userId',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The user id',
|
||||||
|
required: true,
|
||||||
|
http: {source: 'path'}
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:userId/get-by-user`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getByUser = async userId => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const appNames = ['hedera'];
|
||||||
|
const filter = {
|
||||||
|
fields: ['appName', 'url'],
|
||||||
|
where: {
|
||||||
|
appName: {inq: appNames},
|
||||||
|
environment: process.env.NODE_ENV ?? 'development',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWorker = await models.Account.findById(userId, {fields: ['id']});
|
||||||
|
if (!isWorker)
|
||||||
|
return models.Url.find(filter);
|
||||||
|
|
||||||
|
appNames.push('salix');
|
||||||
|
return models.Url.find(filter);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getUrl', {
|
||||||
|
description: 'Returns the colling app name',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'app',
|
||||||
|
type: 'string',
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/getUrl`,
|
||||||
|
verb: 'get'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Self.getUrl = async(appName = 'salix') => {
|
||||||
|
const {url} = await Self.app.models.Url.findOne({
|
||||||
|
where: {
|
||||||
|
appName,
|
||||||
|
enviroment: process.env.NODE_ENV || 'development'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
const {models} = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('getByUser()', () => {
|
||||||
|
const worker = 1;
|
||||||
|
const notWorker = 2;
|
||||||
|
it(`should return only hedera url if not is worker`, async() => {
|
||||||
|
const urls = await models.Url.getByUser(notWorker);
|
||||||
|
|
||||||
|
expect(urls.length).toEqual(1);
|
||||||
|
expect(urls[0].appName).toEqual('hedera');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should return more than hedera url`, async() => {
|
||||||
|
const urls = await models.Url.getByUser(worker);
|
||||||
|
|
||||||
|
expect(urls.length).toBeGreaterThan(1);
|
||||||
|
expect(urls.find(url => url.appName == 'salix').appName).toEqual('salix');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
|
||||||
|
<soap12:Body>
|
||||||
|
<DeleteEnvio xmlns="http://82.223.6.71:82">
|
||||||
|
<IdCliente><%= viaexpressConfig.client %></IdCliente>
|
||||||
|
<Usuario><%= viaexpressConfig.user %></Usuario>
|
||||||
|
<Password><%= viaexpressConfig.password %></Password>
|
||||||
|
<etiqueta><%= externalId %></etiqueta>
|
||||||
|
</DeleteEnvio>
|
||||||
|
</soap12:Body>
|
||||||
|
</soap12:Envelope>
|
|
@ -0,0 +1,45 @@
|
||||||
|
const axios = require('axios');
|
||||||
|
const {DOMParser} = require('xmldom');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('deleteExpedition', {
|
||||||
|
description: 'Delete a shipment by providing the expedition ID, interacting with Viaexpress API',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'expeditionFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/deleteExpedition`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteExpedition = async expeditionFk => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const viaexpressConfig = await models.ViaexpressConfig.findOne({
|
||||||
|
fields: ['url']
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderedXml = await models.ViaexpressConfig.deleteExpeditionRenderer(expeditionFk);
|
||||||
|
const response = await axios.post(`${viaexpressConfig.url}ServicioVxClientes.asmx`, renderedXml, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/soap+xml; charset=utf-8'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const xmlString = response.data;
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
|
||||||
|
const resultElement = xmlDoc.getElementsByTagName('DeleteEnvioResult')[0];
|
||||||
|
const result = resultElement.textContent;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,44 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const ejs = require('ejs');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('deleteExpeditionRenderer', {
|
||||||
|
description: 'Renders the data from an XML',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'expeditionFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/deleteExpeditionRenderer`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteExpeditionRenderer = async expeditionFk => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const viaexpressConfig = await models.ViaexpressConfig.findOne({
|
||||||
|
fields: ['client', 'user', 'password']
|
||||||
|
});
|
||||||
|
|
||||||
|
const expedition = await models.Expedition.findOne({
|
||||||
|
fields: ['id', 'externalId'],
|
||||||
|
where: {id: expeditionFk}
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
viaexpressConfig,
|
||||||
|
externalId: expedition.externalId
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = fs.readFileSync(__dirname + '/deleteExpedition.ejs', 'utf-8');
|
||||||
|
const renderedXml = ejs.render(template, data);
|
||||||
|
return renderedXml;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,45 @@
|
||||||
|
const axios = require('axios');
|
||||||
|
const {DOMParser} = require('xmldom');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('internationalExpedition', {
|
||||||
|
description: 'Create an expedition and return a label',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'expeditionFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/internationalExpedition`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.internationalExpedition = async expeditionFk => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const viaexpressConfig = await models.ViaexpressConfig.findOne({
|
||||||
|
fields: ['url']
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderedXml = await models.ViaexpressConfig.renderer(expeditionFk);
|
||||||
|
const response = await axios.post(`${viaexpressConfig.url}ServicioVxClientes.asmx`, renderedXml, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/soap+xml; charset=utf-8'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const xmlString = response.data;
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
|
||||||
|
const referenciaVxElement = xmlDoc.getElementsByTagName('ReferenciaVx')[0];
|
||||||
|
const referenciaVx = referenciaVxElement.textContent;
|
||||||
|
|
||||||
|
return referenciaVx;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,126 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const ejs = require('ejs');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('renderer', {
|
||||||
|
description: 'Renders the data from an XML',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'expeditionFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/renderer`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.renderer = async expeditionFk => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const viaexpressConfig = await models.ViaexpressConfig.findOne({
|
||||||
|
fields: ['client', 'user', 'password', 'defaultWeight', 'deliveryType']
|
||||||
|
});
|
||||||
|
|
||||||
|
const expedition = await models.Expedition.findOne({
|
||||||
|
fields: ['id', 'ticketFk'],
|
||||||
|
where: {id: expeditionFk},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'ticket',
|
||||||
|
scope: {
|
||||||
|
fields: ['shipped', 'addressFk', 'clientFk', 'companyFk'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['mobile', 'phone', 'email']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'address',
|
||||||
|
scope: {
|
||||||
|
fields: [
|
||||||
|
'nickname',
|
||||||
|
'street',
|
||||||
|
'postalCode',
|
||||||
|
'city',
|
||||||
|
'mobile',
|
||||||
|
'phone',
|
||||||
|
'provinceFk'
|
||||||
|
],
|
||||||
|
include: {
|
||||||
|
relation: 'province',
|
||||||
|
scope: {
|
||||||
|
fields: ['name', 'countryFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'country',
|
||||||
|
scope: {
|
||||||
|
fields: ['code'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'company',
|
||||||
|
scope: {
|
||||||
|
fields: ['clientFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['socialName', 'mobile', 'phone', 'email', 'defaultAddressFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'defaultAddress',
|
||||||
|
scope: {
|
||||||
|
fields: [
|
||||||
|
'street',
|
||||||
|
'postalCode',
|
||||||
|
'city',
|
||||||
|
'mobile',
|
||||||
|
'phone',
|
||||||
|
'provinceFk'
|
||||||
|
],
|
||||||
|
include: {
|
||||||
|
relation: 'province',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const ticket = expedition.ticket();
|
||||||
|
const sender = ticket.company().client();
|
||||||
|
const shipped = ticket.shipped.toISOString();
|
||||||
|
const data = {
|
||||||
|
viaexpressConfig,
|
||||||
|
sender,
|
||||||
|
senderAddress: sender.defaultAddress(),
|
||||||
|
client: ticket.client(),
|
||||||
|
address: ticket.address(),
|
||||||
|
shipped
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = fs.readFileSync(__dirname + '/template.ejs', 'utf-8');
|
||||||
|
const renderedXml = ejs.render(template, data);
|
||||||
|
return renderedXml;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
|
||||||
|
<soap12:Body>
|
||||||
|
<PutExpedicionInternacional xmlns="http://82.223.6.71:82">
|
||||||
|
<ObjetoEnvio>
|
||||||
|
<Peso><%= viaexpressConfig.defaultWeight %></Peso>
|
||||||
|
<Bultos>1</Bultos>
|
||||||
|
<Reembolso>0</Reembolso>
|
||||||
|
<Fecha><%= shipped %></Fecha>
|
||||||
|
<ConRetorno>0</ConRetorno>
|
||||||
|
<Tipo><%= viaexpressConfig.deliveryType %></Tipo>
|
||||||
|
<Debidos>0</Debidos>
|
||||||
|
<Asegurado>0</Asegurado>
|
||||||
|
<Imprimir>0</Imprimir>
|
||||||
|
<ConDevolucionAlbaran>0</ConDevolucionAlbaran>
|
||||||
|
<Intradia>0</Intradia>
|
||||||
|
<Observaciones></Observaciones>
|
||||||
|
<AlbaranRemitente></AlbaranRemitente>
|
||||||
|
<Modo>0</Modo>
|
||||||
|
<TextoAgencia></TextoAgencia>
|
||||||
|
<Terminal></Terminal>
|
||||||
|
<ObjetoRemitente>
|
||||||
|
<RazonSocial><%= sender.socialName %></RazonSocial>
|
||||||
|
<Domicilio><%= senderAddress.street %></Domicilio>
|
||||||
|
<Cpostal><%= senderAddress.postalCode %></Cpostal>
|
||||||
|
<Poblacion><%= senderAddress.city %></Poblacion>
|
||||||
|
<Provincia><%= senderAddress.province().name %></Provincia>
|
||||||
|
<Contacto></Contacto>
|
||||||
|
<Telefono><%= senderAddress.mobile || senderAddress.phone || sender.mobile || sender.phone %></Telefono>
|
||||||
|
<Email><%= sender.email %></Email>
|
||||||
|
</ObjetoRemitente>
|
||||||
|
<ObjetoDestinatario>
|
||||||
|
<RazonSocial><%= address.nickname %></RazonSocial>
|
||||||
|
<Domicilio><%= address.street %></Domicilio>
|
||||||
|
<Cpostal><%= address.postalCode %></Cpostal>
|
||||||
|
<Poblacion><%= address.city %></Poblacion>
|
||||||
|
<Municipio></Municipio>
|
||||||
|
<Provincia><%= address.province().name %></Provincia>
|
||||||
|
<Contacto></Contacto>
|
||||||
|
<Telefono><%= address.mobile || address.phone || client.mobile || client.phone %></Telefono>
|
||||||
|
<Email><%= client.email %></Email>
|
||||||
|
<Pais><%= address.province().country().code %></Pais>
|
||||||
|
</ObjetoDestinatario>
|
||||||
|
<ObjetoLogin>
|
||||||
|
<IdCliente><%= viaexpressConfig.client %></IdCliente>
|
||||||
|
<Usuario><%= viaexpressConfig.user %></Usuario>
|
||||||
|
<Password><%= viaexpressConfig.password %></Password>
|
||||||
|
</ObjetoLogin>
|
||||||
|
</ObjetoEnvio>
|
||||||
|
</PutExpedicionInternacional>
|
||||||
|
</soap12:Body>
|
||||||
|
</soap12:Envelope>
|
|
@ -1,68 +0,0 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethod('addAlias', {
|
|
||||||
description: 'Add an alias if the user has the grant',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts: [
|
|
||||||
{
|
|
||||||
arg: 'ctx',
|
|
||||||
type: 'Object',
|
|
||||||
http: {source: 'context'}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'id',
|
|
||||||
type: 'number',
|
|
||||||
required: true,
|
|
||||||
description: 'The user id',
|
|
||||||
http: {source: 'path'}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'mailAlias',
|
|
||||||
type: 'number',
|
|
||||||
description: 'The new alias for user',
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
http: {
|
|
||||||
path: `/:id/addAlias`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.addAlias = async function(ctx, id, mailAlias, options) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
const myOptions = {};
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
|
||||||
Object.assign(myOptions, options);
|
|
||||||
|
|
||||||
const user = await Self.findById(userId, {fields: ['hasGrant']}, myOptions);
|
|
||||||
|
|
||||||
if (!user.hasGrant)
|
|
||||||
throw new UserError(`You don't have grant privilege`);
|
|
||||||
|
|
||||||
const account = await models.Account.findById(userId, {
|
|
||||||
fields: ['id'],
|
|
||||||
include: {
|
|
||||||
relation: 'aliases',
|
|
||||||
scope: {
|
|
||||||
fields: ['mailAlias']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
const aliases = account.aliases().map(alias => alias.mailAlias);
|
|
||||||
|
|
||||||
const hasAlias = aliases.includes(mailAlias);
|
|
||||||
if (!hasAlias)
|
|
||||||
throw new UserError(`You cannot assign an alias that you are not assigned to`);
|
|
||||||
|
|
||||||
return models.MailAliasAccount.create({
|
|
||||||
mailAlias: mailAlias,
|
|
||||||
account: id
|
|
||||||
}, myOptions);
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -47,7 +47,7 @@ module.exports = Self => {
|
||||||
const user = await Self.findById(userId, {fields: ['hasGrant']}, myOptions);
|
const user = await Self.findById(userId, {fields: ['hasGrant']}, myOptions);
|
||||||
|
|
||||||
const userToUpdate = await Self.findById(id, {
|
const userToUpdate = await Self.findById(id, {
|
||||||
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password'],
|
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password', 'email'],
|
||||||
include: {
|
include: {
|
||||||
relation: 'role',
|
relation: 'role',
|
||||||
scope: {
|
scope: {
|
||||||
|
|
|
@ -7,6 +7,11 @@ module.exports = Self => {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The user name or email',
|
description: 'The user name or email',
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'app',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The directory for mail'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
http: {
|
http: {
|
||||||
|
@ -15,7 +20,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.recoverPassword = async function(user) {
|
Self.recoverPassword = async function(user, app) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
const usesEmail = user.indexOf('@') !== -1;
|
const usesEmail = user.indexOf('@') !== -1;
|
||||||
|
@ -29,7 +34,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Self.resetPassword({email: user, emailTemplate: 'recover-password'});
|
await Self.resetPassword({email: user, emailTemplate: 'recover-password', app});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'EMAIL_NOT_FOUND')
|
if (err.code === 'EMAIL_NOT_FOUND')
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethod('removeAlias', {
|
|
||||||
description: 'Remove alias if the user has the grant',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts: [
|
|
||||||
{
|
|
||||||
arg: 'ctx',
|
|
||||||
type: 'Object',
|
|
||||||
http: {source: 'context'}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'id',
|
|
||||||
type: 'number',
|
|
||||||
required: true,
|
|
||||||
description: 'The user id',
|
|
||||||
http: {source: 'path'}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'mailAlias',
|
|
||||||
type: 'number',
|
|
||||||
description: 'The alias to delete',
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
http: {
|
|
||||||
path: `/:id/removeAlias`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.removeAlias = async function(ctx, id, mailAlias, options) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
const myOptions = {};
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
|
||||||
Object.assign(myOptions, options);
|
|
||||||
|
|
||||||
const canRemoveAlias = await models.ACL.checkAccessAcl(ctx, 'VnUser', 'canRemoveAlias', 'WRITE');
|
|
||||||
|
|
||||||
if (userId != id && !canRemoveAlias) throw new UserError(`You don't have grant privilege`);
|
|
||||||
|
|
||||||
const mailAliasAccount = await models.MailAliasAccount.findOne({
|
|
||||||
where: {
|
|
||||||
mailAlias: mailAlias,
|
|
||||||
account: id
|
|
||||||
}
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
await mailAliasAccount.destroy(myOptions);
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -49,23 +49,16 @@ module.exports = Self => {
|
||||||
if (vnUser.twoFactor)
|
if (vnUser.twoFactor)
|
||||||
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
||||||
}
|
}
|
||||||
|
return Self.validateLogin(user, password, ctx);
|
||||||
return Self.validateLogin(user, password);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.passExpired = async(vnUser, myOptions) => {
|
Self.passExpired = async vnUser => {
|
||||||
const today = Date.vnNew();
|
const today = Date.vnNew();
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
if (vnUser.passExpired && vnUser.passExpired.getTime() <= today.getTime()) {
|
if (vnUser.passExpired && vnUser.passExpired.getTime() <= today.getTime()) {
|
||||||
const $ = Self.app.models;
|
|
||||||
const changePasswordToken = await $.AccessToken.create({
|
|
||||||
scopes: ['changePassword'],
|
|
||||||
userId: vnUser.id
|
|
||||||
}, myOptions);
|
|
||||||
const err = new UserError('Pass expired', 'passExpired');
|
const err = new UserError('Pass expired', 'passExpired');
|
||||||
changePasswordToken.twoFactor = vnUser.twoFactor ? true : false;
|
err.details = {userId: vnUser.id, twoFactor: vnUser.twoFactor ? true : false};
|
||||||
err.details = {token: changePasswordToken};
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ const {models} = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('VnUser Sign-in()', () => {
|
describe('VnUser Sign-in()', () => {
|
||||||
const employeeId = 1;
|
const employeeId = 1;
|
||||||
const unauthCtx = {
|
const unAuthCtx = {
|
||||||
req: {
|
req: {
|
||||||
headers: {},
|
headers: {},
|
||||||
connection: {
|
connection: {
|
||||||
|
@ -12,10 +12,24 @@ describe('VnUser Sign-in()', () => {
|
||||||
},
|
},
|
||||||
args: {}
|
args: {}
|
||||||
};
|
};
|
||||||
const {VnUser, AccessToken} = models;
|
const {VnUser, AccessToken, SignInLog} = models;
|
||||||
describe('when credentials are correct', () => {
|
describe('when credentials are correct', () => {
|
||||||
|
it('should return the token if user uses email', async() => {
|
||||||
|
let login = await VnUser.signIn(unAuthCtx, 'salesAssistant@mydomain.com', 'nightmare');
|
||||||
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
let signInLog = await SignInLog.find({where: {token: accessToken.id}});
|
||||||
|
|
||||||
|
expect(signInLog.length).toEqual(1);
|
||||||
|
expect(signInLog[0].userFk).toEqual(accessToken.userId);
|
||||||
|
expect(signInLog[0].owner).toEqual(true);
|
||||||
|
expect(login.token).toBeDefined();
|
||||||
|
|
||||||
|
await VnUser.logout(ctx.req.accessToken.id);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return the token', async() => {
|
it('should return the token', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'salesAssistant', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
|
||||||
|
@ -25,7 +39,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the token if the user doesnt exist but the client does', async() => {
|
it('should return the token if the user doesnt exist but the client does', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'PetterParker', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'PetterParker', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
|
||||||
|
@ -40,7 +54,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await VnUser.signIn(unauthCtx, 'IDontExist', 'TotallyWrongPassword');
|
await VnUser.signIn(unAuthCtx, 'IDontExist', 'TotallyWrongPassword');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +75,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await employee.updateAttribute('twoFactor', 'email', options);
|
await employee.updateAttribute('twoFactor', 'email', options);
|
||||||
|
|
||||||
await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
|
await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
@ -86,7 +100,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await employee.updateAttribute('passExpired', yesterday, options);
|
await employee.updateAttribute('passExpired', yesterday, options);
|
||||||
|
|
||||||
await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
|
await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('updateUser', {
|
||||||
|
description: 'Update user data',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'integer',
|
||||||
|
description: 'The user id',
|
||||||
|
required: true,
|
||||||
|
http: {source: 'path'}
|
||||||
|
}, {
|
||||||
|
arg: 'name',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The user name',
|
||||||
|
}, {
|
||||||
|
arg: 'nickname',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The user nickname',
|
||||||
|
}, {
|
||||||
|
arg: 'email',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The user email'
|
||||||
|
}, {
|
||||||
|
arg: 'lang',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The user lang'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/:id/update-user`,
|
||||||
|
verb: 'PATCH'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.updateUser = async(ctx, id, name, nickname, email, lang) => {
|
||||||
|
await Self.userSecurity(ctx, id);
|
||||||
|
await Self.upsertWithWhere({id}, {name, nickname, email, lang});
|
||||||
|
};
|
||||||
|
};
|
|
@ -15,6 +15,9 @@
|
||||||
},
|
},
|
||||||
"Bank": {
|
"Bank": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"Buyer": {
|
||||||
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
"Campaign": {
|
"Campaign": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
@ -150,6 +153,9 @@
|
||||||
},
|
},
|
||||||
"PrintConfig": {
|
"PrintConfig": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"ViaexpressConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "Buyer",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "buyer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"userFk": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true,
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"nickname": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"accessType": "READ",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "employee",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -7,17 +7,14 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
if (!ctx.isNewInstance) return;
|
if (!ctx.isNewInstance) return;
|
||||||
|
|
||||||
let {message} = ctx.instance;
|
let {message} = ctx.instance;
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
|
|
||||||
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
|
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
|
||||||
if (!parts) return;
|
if (!parts) return;
|
||||||
|
|
||||||
const replacedParts = parts.map(part => {
|
const replacedParts = parts.map(part => {
|
||||||
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
|
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const [index, part] of parts.entries())
|
for (const [index, part] of parts.entries())
|
||||||
message = message.replace(part, replacedParts[index]);
|
message = message.replace(part, replacedParts[index]);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/collection/getCollection')(Self);
|
require('../methods/collection/getCollection')(Self);
|
||||||
require('../methods/collection/newCollection')(Self);
|
|
||||||
require('../methods/collection/getSectors')(Self);
|
require('../methods/collection/getSectors')(Self);
|
||||||
require('../methods/collection/setSaleQuantity')(Self);
|
require('../methods/collection/setSaleQuantity')(Self);
|
||||||
require('../methods/collection/previousLabel')(Self);
|
require('../methods/collection/previousLabel')(Self);
|
||||||
|
require('../methods/collection/getTickets')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,11 +18,21 @@
|
||||||
},
|
},
|
||||||
"expired": {
|
"expired": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
|
},
|
||||||
|
"supplierAccountFk": {
|
||||||
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"where" :{
|
"where" :{
|
||||||
"expired": null
|
"expired": null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"client": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Client",
|
||||||
|
"foreignKey": "clientFk"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
},
|
},
|
||||||
"isUeeMember": {
|
"isUeeMember": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"isSocialNameUnique": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
@ -39,4 +42,4 @@
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,5 +28,12 @@
|
||||||
"findById": {
|
"findById": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"dmsType": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "DmsType",
|
||||||
|
"foreignKey": "dmsTypeFk"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,74 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
require('../methods/notification/getList')(Self);
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
|
await checkModifyPermission(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.observe('before delete', async function(ctx) {
|
||||||
|
await checkModifyPermission(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function checkModifyPermission(ctx) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
const instance = ctx.instance;
|
||||||
const userId = ctx.options.accessToken.userId;
|
const userId = ctx.options.accessToken.userId;
|
||||||
const user = await ctx.instance.userFk;
|
|
||||||
const modifiedUser = await getUserToModify(null, user, models);
|
|
||||||
|
|
||||||
if (userId != modifiedUser.id && userId != modifiedUser.bossFk)
|
let notificationFk;
|
||||||
throw new UserError('You dont have permission to modify this user');
|
let workerId;
|
||||||
});
|
|
||||||
|
|
||||||
Self.remoteMethod('deleteNotification', {
|
if (instance) {
|
||||||
description: 'Deletes a notification subscription',
|
notificationFk = instance.notificationFk;
|
||||||
accepts: [
|
workerId = instance.userFk;
|
||||||
{
|
} else {
|
||||||
arg: 'ctx',
|
const notificationSubscription = await models.NotificationSubscription.findById(ctx.where.id);
|
||||||
type: 'object',
|
notificationFk = notificationSubscription.notificationFk;
|
||||||
http: {source: 'context'}
|
workerId = notificationSubscription.userFk;
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'notificationId',
|
|
||||||
type: 'number',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: 'object',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
verb: 'POST',
|
|
||||||
path: '/deleteNotification'
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
Self.deleteNotification = async function(ctx, notificationId) {
|
const worker = await models.Worker.findById(workerId, {fields: ['id', 'bossFk']});
|
||||||
const models = Self.app.models;
|
const available = await Self.getAvailable(workerId);
|
||||||
const user = ctx.req.accessToken.userId;
|
const hasAcl = available.has(notificationFk);
|
||||||
const modifiedUser = await getUserToModify(notificationId, null, models);
|
|
||||||
|
|
||||||
if (user != modifiedUser.id && user != modifiedUser.bossFk)
|
if (!hasAcl || (userId != worker.id && userId != worker.bossFk))
|
||||||
throw new UserError('You dont have permission to modify this user');
|
throw new UserError('The notification subscription of this worker cant be modified');
|
||||||
|
|
||||||
await models.NotificationSubscription.destroyById(notificationId);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getUserToModify(notificationId, userFk, models) {
|
|
||||||
let userToModify = userFk;
|
|
||||||
if (notificationId) {
|
|
||||||
const subscription = await models.NotificationSubscription.findById(notificationId);
|
|
||||||
userToModify = subscription.userFk;
|
|
||||||
}
|
|
||||||
return await models.Worker.findOne({
|
|
||||||
fields: ['id', 'bossFk'],
|
|
||||||
where: {
|
|
||||||
id: userToModify
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self.getAvailable = async function(userId, options) {
|
||||||
|
const availableNotificationsMap = new Map();
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const roles = await models.RoleMapping.find({
|
||||||
|
fields: ['roleId'],
|
||||||
|
where: {principalId: userId}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const availableNotifications = await models.NotificationAcl.find({
|
||||||
|
fields: ['notificationFk', 'roleFk'],
|
||||||
|
include: {relation: 'notification'},
|
||||||
|
where: {
|
||||||
|
roleFk: {
|
||||||
|
inq: roles.map(role => role.roleId),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
for (available of availableNotifications) {
|
||||||
|
availableNotificationsMap.set(available.notificationFk, {
|
||||||
|
id: null,
|
||||||
|
notificationFk: available.notificationFk,
|
||||||
|
name: available.notification().name,
|
||||||
|
description: available.notification().description,
|
||||||
|
active: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return availableNotificationsMap;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/osticket/osTicketReportEmail')(Self);
|
require('../methods/osticket/osTicketReportEmail')(Self);
|
||||||
require('../methods/osticket/closeTicket')(Self);
|
require('../methods/osticket/closeTicket')(Self);
|
||||||
|
require('../methods/osticket/sendToSupport')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,74 +1,126 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('loopback model NotificationSubscription', () => {
|
describe('loopback model NotificationSubscription', () => {
|
||||||
it('Should fail to delete a notification if the user is not editing itself or a subordinate', async() => {
|
it('should fail to add a notification subscription if the worker doesnt have ACLs', async() => {
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const user = 9;
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 62}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to add a notification subscription if the user isnt editing itself or subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 1}};
|
||||||
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 9}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to delete a notification subscription if the user isnt editing itself or subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const notificationSubscriptionId = 2;
|
const notificationSubscriptionId = 2;
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toContain('You dont have permission to modify this user');
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should delete a notification if the user is editing itself', async() => {
|
it('should add a notification subscription if the user is editing itself', async() => {
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const user = 9;
|
await models.NotificationSubscription.create({notificationFk: 2, userFk: 9}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a notification subscription if the user is editing itself', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
|
const notificationSubscriptionId = 6;
|
||||||
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a notification subscription if the user is editing a subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 5}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a notification subscription if the user is editing a subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 19}};
|
||||||
const notificationSubscriptionId = 4;
|
const notificationSubscriptionId = 4;
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
|
|
||||||
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
expect(deletedNotification).toBeNull();
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
error = e;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
it('Should delete a notification if the user is editing a subordinate', async() => {
|
expect(error).toBeUndefined();
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
const user = 9;
|
|
||||||
const notificationSubscriptionId = 5;
|
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
|
|
||||||
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
expect(deletedNotification).toBeNull();
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
||||||
|
|
||||||
describe('loopback model VnUser', () => {
|
describe('loopback model VnUser', () => {
|
||||||
it('should return true if the user has the given role', async() => {
|
it('should return true if the user has the given role', async() => {
|
||||||
|
@ -12,4 +13,42 @@ describe('loopback model VnUser', () => {
|
||||||
|
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('userSecurity', () => {
|
||||||
|
const itManagementId = 115;
|
||||||
|
const hrId = 37;
|
||||||
|
const employeeId = 1;
|
||||||
|
|
||||||
|
it('should check if you are the same user', async() => {
|
||||||
|
const ctx = {options: {accessToken: {userId: employeeId}}};
|
||||||
|
await models.VnUser.userSecurity(ctx, employeeId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check for higher privileges', async() => {
|
||||||
|
const ctx = {options: {accessToken: {userId: itManagementId}}};
|
||||||
|
await models.VnUser.userSecurity(ctx, employeeId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if you have medium privileges and the user email is not verified', async() => {
|
||||||
|
const ctx = {options: {accessToken: {userId: hrId}}};
|
||||||
|
await models.VnUser.userSecurity(ctx, employeeId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if you have medium privileges and the users email is verified', async() => {
|
||||||
|
const tx = await models.VnUser.beginTransaction({});
|
||||||
|
const ctx = {options: {accessToken: {userId: hrId}}};
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const userToUpdate = await models.VnUser.findById(1, null, options);
|
||||||
|
userToUpdate.updateAttribute('emailVerified', 1, options);
|
||||||
|
|
||||||
|
await models.VnUser.userSecurity(ctx, employeeId, options);
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (error) {
|
||||||
|
await tx.rollback();
|
||||||
|
|
||||||
|
expect(error).toEqual(new ForbiddenError());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/url/getByUser')(Self);
|
||||||
|
require('../methods/url/getUrl')(Self);
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/viaexpress-config/internationalExpedition')(Self);
|
||||||
|
require('../methods/viaexpress-config/renderer')(Self);
|
||||||
|
require('../methods/viaexpress-config/deleteExpedition')(Self);
|
||||||
|
require('../methods/viaexpress-config/deleteExpeditionRenderer')(Self);
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "ViaexpressConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "viaexpressConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"defaultWeight": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"deliveryType": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
const vnModel = require('vn-loopback/common/models/vn-model');
|
const vnModel = require('vn-loopback/common/models/vn-model');
|
||||||
const LoopBackContext = require('loopback-context');
|
|
||||||
const {Email} = require('vn-print');
|
const {Email} = require('vn-print');
|
||||||
|
const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
vnModel(Self);
|
vnModel(Self);
|
||||||
|
@ -12,8 +14,7 @@ module.exports = function(Self) {
|
||||||
require('../methods/vn-user/privileges')(Self);
|
require('../methods/vn-user/privileges')(Self);
|
||||||
require('../methods/vn-user/validate-auth')(Self);
|
require('../methods/vn-user/validate-auth')(Self);
|
||||||
require('../methods/vn-user/renew-token')(Self);
|
require('../methods/vn-user/renew-token')(Self);
|
||||||
require('../methods/vn-user/addAlias')(Self);
|
require('../methods/vn-user/update-user')(Self);
|
||||||
require('../methods/vn-user/removeAlias')(Self);
|
|
||||||
|
|
||||||
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');
|
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ module.exports = function(Self) {
|
||||||
Self.validatesFormatOf('email', {
|
Self.validatesFormatOf('email', {
|
||||||
message: 'Invalid email',
|
message: 'Invalid email',
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
allowBlank: true,
|
allowBlank: false,
|
||||||
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
|
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -98,11 +99,21 @@ module.exports = function(Self) {
|
||||||
const headers = httpRequest.headers;
|
const headers = httpRequest.headers;
|
||||||
const origin = headers.origin;
|
const origin = headers.origin;
|
||||||
|
|
||||||
|
const defaultHash = '/reset-password?access_token=$token$';
|
||||||
|
const recoverHashes = {
|
||||||
|
hedera: 'verificationToken=$token$'
|
||||||
|
};
|
||||||
|
|
||||||
|
const app = info.options?.app;
|
||||||
|
let recoverHash = app ? recoverHashes[app] : defaultHash;
|
||||||
|
recoverHash = recoverHash.replace('$token$', info.accessToken.id);
|
||||||
|
|
||||||
const user = await Self.app.models.VnUser.findById(info.user.id);
|
const user = await Self.app.models.VnUser.findById(info.user.id);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
recipient: info.email,
|
recipient: info.email,
|
||||||
lang: user.lang,
|
lang: user.lang,
|
||||||
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
|
url: origin + '/#!' + recoverHash
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = Object.assign({}, info.options);
|
const options = Object.assign({}, info.options);
|
||||||
|
@ -114,9 +125,48 @@ module.exports = function(Self) {
|
||||||
return email.send();
|
return email.send();
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.validateLogin = async function(user, password) {
|
/**
|
||||||
let loginInfo = Object.assign({password}, Self.userUses(user));
|
* Sign-in validate
|
||||||
token = await Self.login(loginInfo, 'user');
|
* @param {String} user The user
|
||||||
|
* @param {Object} userToken Options
|
||||||
|
* @param {Object} token accessToken
|
||||||
|
* @param {Object} ctx context
|
||||||
|
*/
|
||||||
|
Self.signInValidate = async(user, userToken, token, ctx) => {
|
||||||
|
const [[key, value]] = Object.entries(Self.userUses(user));
|
||||||
|
const isOwner = Self.rawSql(`SELECT ? = ? `, [userToken[key], value]);
|
||||||
|
await Self.app.models.SignInLog.create({
|
||||||
|
userName: user,
|
||||||
|
token: token.id,
|
||||||
|
userFk: userToken.id,
|
||||||
|
ip: ctx.req.ip,
|
||||||
|
owner: isOwner
|
||||||
|
});
|
||||||
|
if (!isOwner)
|
||||||
|
throw new UserError('Try again');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate login params
|
||||||
|
* @param {String} user The user
|
||||||
|
* @param {String} password
|
||||||
|
* @param {Object} ctx context
|
||||||
|
*/
|
||||||
|
Self.validateLogin = async function(user, password, ctx) {
|
||||||
|
const loginInfo = Object.assign({password}, Self.userUses(user));
|
||||||
|
const token = await Self.login(loginInfo, 'user');
|
||||||
|
|
||||||
|
const userToken = await token.user.get();
|
||||||
|
|
||||||
|
if (ctx)
|
||||||
|
await Self.signInValidate(user, userToken, token, ctx);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Self.app.models.Account.sync(userToken.name, password);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(err);
|
||||||
|
}
|
||||||
|
|
||||||
return {token: token.id, ttl: token.ttl};
|
return {token: token.id, ttl: token.ttl};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,48 +209,83 @@ module.exports = function(Self) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
||||||
.filter(acl => acl.property != 'changePassword');
|
.filter(acl => acl.property != 'changePassword');
|
||||||
|
|
||||||
// FIXME: https://redmine.verdnatura.es/issues/5761
|
Self.userSecurity = async(ctx, userId, options) => {
|
||||||
// Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
const models = Self.app.models;
|
||||||
// if (!ctx.args || !ctx.args.data.email) return;
|
const accessToken = ctx?.options?.accessToken || LoopBackContext.getCurrentContext().active.accessToken;
|
||||||
|
const ctxToken = {req: {accessToken}};
|
||||||
|
|
||||||
// const loopBackContext = LoopBackContext.getCurrentContext();
|
if (userId === accessToken.userId) return;
|
||||||
// const httpCtx = {req: loopBackContext.active};
|
|
||||||
// const httpRequest = httpCtx.req.http.req;
|
|
||||||
// const headers = httpRequest.headers;
|
|
||||||
// const origin = headers.origin;
|
|
||||||
// const url = origin.split(':');
|
|
||||||
|
|
||||||
// class Mailer {
|
const myOptions = {};
|
||||||
// async send(verifyOptions, cb) {
|
if (typeof options == 'object')
|
||||||
// const params = {
|
Object.assign(myOptions, options);
|
||||||
// url: verifyOptions.verifyHref,
|
|
||||||
// recipient: verifyOptions.to,
|
|
||||||
// lang: ctx.req.getLocale()
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const email = new Email('email-verify', params);
|
const hasHigherPrivileges = await models.ACL.checkAccessAcl(ctxToken, 'VnUser', 'higherPrivileges', myOptions);
|
||||||
// email.send();
|
if (hasHigherPrivileges) return;
|
||||||
|
|
||||||
// cb(null, verifyOptions.to);
|
const hasMediumPrivileges = await models.ACL.checkAccessAcl(ctxToken, 'VnUser', 'mediumPrivileges', myOptions);
|
||||||
// }
|
const user = await models.VnUser.findById(userId, {fields: ['id', 'emailVerified']}, myOptions);
|
||||||
// }
|
if (!user.emailVerified && hasMediumPrivileges) return;
|
||||||
|
|
||||||
// const options = {
|
throw new ForbiddenError();
|
||||||
// type: 'email',
|
};
|
||||||
// to: instance.email,
|
|
||||||
// from: {},
|
|
||||||
// redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`,
|
|
||||||
// template: false,
|
|
||||||
// mailer: new Mailer,
|
|
||||||
// host: url[1].split('/')[2],
|
|
||||||
// port: url[2],
|
|
||||||
// protocol: url[0],
|
|
||||||
// user: Self
|
|
||||||
// };
|
|
||||||
|
|
||||||
// await instance.verify(options);
|
Self.observe('after save', async ctx => {
|
||||||
// });
|
const instance = ctx?.instance;
|
||||||
|
const newEmail = instance?.email;
|
||||||
|
const oldEmail = ctx?.hookState?.oldInstance?.email;
|
||||||
|
if (!ctx.isNewInstance && (!newEmail || !oldEmail || newEmail == oldEmail)) return;
|
||||||
|
|
||||||
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
const httpCtx = {req: loopBackContext.active};
|
||||||
|
const httpRequest = httpCtx.req.http.req;
|
||||||
|
const headers = httpRequest.headers;
|
||||||
|
const origin = headers.origin;
|
||||||
|
const url = origin.split(':');
|
||||||
|
|
||||||
|
const env = process.env.NODE_ENV;
|
||||||
|
const liliumUrl = await Self.app.models.Url.findOne({
|
||||||
|
where: {
|
||||||
|
and: [
|
||||||
|
{appName: 'lilium'},
|
||||||
|
{environment: env}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class Mailer {
|
||||||
|
async send(verifyOptions, cb) {
|
||||||
|
const url = new URL(verifyOptions.verifyHref);
|
||||||
|
if (process.env.NODE_ENV) url.port = '';
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
url: url.href,
|
||||||
|
recipient: verifyOptions.to
|
||||||
|
};
|
||||||
|
|
||||||
|
const email = new Email('email-verify', params);
|
||||||
|
email.send();
|
||||||
|
|
||||||
|
cb(null, verifyOptions.to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
type: 'email',
|
||||||
|
to: newEmail,
|
||||||
|
from: {},
|
||||||
|
redirect: `${liliumUrl.url}verifyEmail?userId=${instance.id}`,
|
||||||
|
template: false,
|
||||||
|
mailer: new Mailer,
|
||||||
|
host: url[1].split('/')[2],
|
||||||
|
port: url[2],
|
||||||
|
protocol: url[0],
|
||||||
|
user: Self
|
||||||
|
};
|
||||||
|
|
||||||
|
await instance.verify(options, ctx.options);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,19 +13,12 @@
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"id": true
|
"id": true
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"mysql": {
|
|
||||||
"columnName": "name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
},
|
||||||
"roleFk": {
|
"roleFk": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
@ -45,6 +38,9 @@
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"emailVerified": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"created": {
|
"created": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
},
|
},
|
||||||
|
@ -84,7 +80,7 @@
|
||||||
"worker": {
|
"worker": {
|
||||||
"type": "hasOne",
|
"type": "hasOne",
|
||||||
"model": "Worker",
|
"model": "Worker",
|
||||||
"foreignKey": "userFk"
|
"foreignKey": "id"
|
||||||
},
|
},
|
||||||
"userConfig": {
|
"userConfig": {
|
||||||
"type": "hasOne",
|
"type": "hasOne",
|
||||||
|
@ -144,7 +140,8 @@
|
||||||
"image",
|
"image",
|
||||||
"hasGrant",
|
"hasGrant",
|
||||||
"realm",
|
"realm",
|
||||||
"email"
|
"email",
|
||||||
|
"emailVerified"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ BEGIN
|
||||||
isAllowedToWork
|
isAllowedToWork
|
||||||
FROM(SELECT t.dated,
|
FROM(SELECT t.dated,
|
||||||
b.id businessFk,
|
b.id businessFk,
|
||||||
w.userFk,
|
w.id,
|
||||||
b.departmentFk,
|
b.departmentFk,
|
||||||
IF(j.start = NULL, NULL, GROUP_CONCAT(DISTINCT LEFT(j.start,5) ORDER BY j.start ASC SEPARATOR ' - ')) hourStart ,
|
IF(j.start = NULL, NULL, GROUP_CONCAT(DISTINCT LEFT(j.start,5) ORDER BY j.start ASC SEPARATOR ' - ')) hourStart ,
|
||||||
IF(j.start = NULL, NULL, GROUP_CONCAT(DISTINCT LEFT(j.end,5) ORDER BY j.end ASC SEPARATOR ' - ')) hourEnd,
|
IF(j.start = NULL, NULL, GROUP_CONCAT(DISTINCT LEFT(j.end,5) ORDER BY j.end ASC SEPARATOR ' - ')) hourEnd,
|
||||||
|
@ -48,14 +48,14 @@ BEGIN
|
||||||
FROM time t
|
FROM time t
|
||||||
LEFT JOIN business b ON t.dated BETWEEN b.started AND IFNULL(b.ended, vDatedTo)
|
LEFT JOIN business b ON t.dated BETWEEN b.started AND IFNULL(b.ended, vDatedTo)
|
||||||
LEFT JOIN worker w ON w.id = b.workerFk
|
LEFT JOIN worker w ON w.id = b.workerFk
|
||||||
JOIN tmp.`user` u ON u.userFK = w.userFK
|
JOIN tmp.`user` u ON u.userFK = w.id
|
||||||
LEFT JOIN workCenter wc ON wc.id = b.workcenterFK
|
LEFT JOIN workCenter wc ON wc.id = b.workcenterFK
|
||||||
LEFT JOIN postgresql.calendar_labour_type cl ON cl.calendar_labour_type_id = b.calendarTypeFk
|
LEFT JOIN postgresql.calendar_labour_type cl ON cl.calendar_labour_type_id = b.calendarTypeFk
|
||||||
LEFT JOIN postgresql.journey j ON j.business_id = b.id AND j.day_id = WEEKDAY(t.dated) + 1
|
LEFT JOIN postgresql.journey j ON j.business_id = b.id AND j.day_id = WEEKDAY(t.dated) + 1
|
||||||
LEFT JOIN postgresql.calendar_employee ce ON ce.businessFk = b.id AND ce.date = t.dated
|
LEFT JOIN postgresql.calendar_employee ce ON ce.businessFk = b.id AND ce.date = t.dated
|
||||||
LEFT JOIN absenceType at2 ON at2.id = ce.calendar_state_id
|
LEFT JOIN absenceType at2 ON at2.id = ce.calendar_state_id
|
||||||
WHERE t.dated BETWEEN vDatedFrom AND vDatedTo
|
WHERE t.dated BETWEEN vDatedFrom AND vDatedTo
|
||||||
GROUP BY w.userFk, t.dated
|
GROUP BY w.id, t.dated
|
||||||
)sub;
|
)sub;
|
||||||
|
|
||||||
UPDATE tmp.timeBusinessCalculate t
|
UPDATE tmp.timeBusinessCalculate t
|
||||||
|
|
|
@ -74,7 +74,7 @@ BEGIN
|
||||||
clientFk,
|
clientFk,
|
||||||
dued,
|
dued,
|
||||||
companyFk,
|
companyFk,
|
||||||
cplusInvoiceType477Fk
|
siiTypeInvoiceOutFk
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
1,
|
1,
|
||||||
|
@ -118,13 +118,13 @@ BEGIN
|
||||||
SELECT 'UPDATE', account.myUser_getId(), ti.id, CONCAT('Crea factura ', vNewRef)
|
SELECT 'UPDATE', account.myUser_getId(), ti.id, CONCAT('Crea factura ', vNewRef)
|
||||||
FROM tmp.ticketToInvoice ti;
|
FROM tmp.ticketToInvoice ti;
|
||||||
|
|
||||||
CALL invoiceExpenceMake(vNewInvoiceId);
|
CALL invoiceExpenseMake(vNewInvoiceId);
|
||||||
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
|
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
|
||||||
|
|
||||||
UPDATE invoiceOut io
|
UPDATE invoiceOut io
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT SUM(amount) AS total
|
SELECT SUM(amount) AS total
|
||||||
FROM invoiceOutExpence
|
FROM invoiceOutExpense
|
||||||
WHERE invoiceOutFk = vNewInvoiceId
|
WHERE invoiceOutFk = vNewInvoiceId
|
||||||
) base
|
) base
|
||||||
JOIN (
|
JOIN (
|
||||||
|
@ -166,18 +166,18 @@ BEGIN
|
||||||
SET @vTaxableBaseServices := 0.00;
|
SET @vTaxableBaseServices := 0.00;
|
||||||
SET @vTaxCodeGeneral := NULL;
|
SET @vTaxCodeGeneral := NULL;
|
||||||
|
|
||||||
INSERT INTO vn.invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
INSERT INTO vn.invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
|
||||||
SELECT vNewInvoiceInId, @vTaxableBaseServices, sub.expenceFk, sub.taxTypeSageFk , sub.transactionTypeSageFk
|
SELECT vNewInvoiceInId, @vTaxableBaseServices, sub.expenseFk, sub.taxTypeSageFk , sub.transactionTypeSageFk
|
||||||
FROM (
|
FROM (
|
||||||
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase, i.expenceFk, i.taxTypeSageFk , i.transactionTypeSageFk, @vTaxCodeGeneral := i.taxClassCodeFk
|
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase, i.expenseFk, i.taxTypeSageFk , i.transactionTypeSageFk, @vTaxCodeGeneral := i.taxClassCodeFk
|
||||||
FROM tmp.ticketServiceTax tst
|
FROM tmp.ticketServiceTax tst
|
||||||
JOIN vn.invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
|
JOIN vn.invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
|
||||||
WHERE i.isService
|
WHERE i.isService
|
||||||
HAVING taxableBase
|
HAVING taxableBase
|
||||||
) sub;
|
) sub;
|
||||||
|
|
||||||
INSERT INTO vn.invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
INSERT INTO vn.invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
|
||||||
SELECT vNewInvoiceInId, SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral, @vTaxableBaseServices, 0) taxableBase, i.expenceFk, i.taxTypeSageFk , i.transactionTypeSageFk
|
SELECT vNewInvoiceInId, SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral, @vTaxableBaseServices, 0) taxableBase, i.expenseFk, i.taxTypeSageFk , i.transactionTypeSageFk
|
||||||
FROM tmp.ticketTax tt
|
FROM tmp.ticketTax tt
|
||||||
JOIN vn.invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
|
JOIN vn.invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
|
||||||
WHERE !i.isService
|
WHERE !i.isService
|
||||||
|
|
|
@ -96,7 +96,7 @@ BEGIN
|
||||||
clientFk,
|
clientFk,
|
||||||
dued,
|
dued,
|
||||||
companyFk,
|
companyFk,
|
||||||
cplusInvoiceType477Fk
|
siiTypeInvoiceOutFk
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
1,
|
1,
|
||||||
|
@ -139,13 +139,13 @@ BEGIN
|
||||||
SELECT 'UPDATE', account.myUser_getId(), ti.id, CONCAT('Crea factura ', vNewRef)
|
SELECT 'UPDATE', account.myUser_getId(), ti.id, CONCAT('Crea factura ', vNewRef)
|
||||||
FROM tmp.ticketToInvoice ti;
|
FROM tmp.ticketToInvoice ti;
|
||||||
|
|
||||||
CALL invoiceExpenceMake(vNewInvoiceId);
|
CALL invoiceExpenseMake(vNewInvoiceId);
|
||||||
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
|
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
|
||||||
|
|
||||||
UPDATE invoiceOut io
|
UPDATE invoiceOut io
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT SUM(amount) total
|
SELECT SUM(amount) total
|
||||||
FROM invoiceOutExpence
|
FROM invoiceOutExpense
|
||||||
WHERE invoiceOutFk = vNewInvoiceId
|
WHERE invoiceOutFk = vNewInvoiceId
|
||||||
) base
|
) base
|
||||||
JOIN (
|
JOIN (
|
||||||
|
@ -182,15 +182,15 @@ BEGIN
|
||||||
SET @vTaxableBaseServices := 0.00;
|
SET @vTaxableBaseServices := 0.00;
|
||||||
SET @vTaxCodeGeneral := NULL;
|
SET @vTaxCodeGeneral := NULL;
|
||||||
|
|
||||||
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
|
||||||
SELECT vNewInvoiceInFk,
|
SELECT vNewInvoiceInFk,
|
||||||
@vTaxableBaseServices,
|
@vTaxableBaseServices,
|
||||||
sub.expenceFk,
|
sub.expenseFk,
|
||||||
sub.taxTypeSageFk,
|
sub.taxTypeSageFk,
|
||||||
sub.transactionTypeSageFk
|
sub.transactionTypeSageFk
|
||||||
FROM (
|
FROM (
|
||||||
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
|
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
|
||||||
i.expenceFk,
|
i.expenseFk,
|
||||||
i.taxTypeSageFk,
|
i.taxTypeSageFk,
|
||||||
i.transactionTypeSageFk,
|
i.transactionTypeSageFk,
|
||||||
@vTaxCodeGeneral := i.taxClassCodeFk
|
@vTaxCodeGeneral := i.taxClassCodeFk
|
||||||
|
@ -200,11 +200,11 @@ BEGIN
|
||||||
HAVING taxableBase
|
HAVING taxableBase
|
||||||
) sub;
|
) sub;
|
||||||
|
|
||||||
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
|
||||||
SELECT vNewInvoiceInFk,
|
SELECT vNewInvoiceInFk,
|
||||||
SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
|
SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
|
||||||
@vTaxableBaseServices, 0) taxableBase,
|
@vTaxableBaseServices, 0) taxableBase,
|
||||||
i.expenceFk,
|
i.expenseFk,
|
||||||
i.taxTypeSageFk ,
|
i.taxTypeSageFk ,
|
||||||
i.transactionTypeSageFk
|
i.transactionTypeSageFk
|
||||||
FROM tmp.ticketTax tt
|
FROM tmp.ticketTax tt
|
||||||
|
|
|
@ -46,7 +46,7 @@ BEGIN
|
||||||
CONCAT('Cliente ', NEW.id),
|
CONCAT('Cliente ', NEW.id),
|
||||||
CONCAT('Recibida la documentación: ', vText)
|
CONCAT('Recibida la documentación: ', vText)
|
||||||
FROM worker w
|
FROM worker w
|
||||||
LEFT JOIN account.user u ON w.userFk = u.id AND u.active
|
LEFT JOIN account.user u ON w.id = u.id AND u.active
|
||||||
LEFT JOIN account.account ac ON ac.id = u.id
|
LEFT JOIN account.account ac ON ac.id = u.id
|
||||||
WHERE w.id = NEW.salesPersonFk;
|
WHERE w.id = NEW.salesPersonFk;
|
||||||
END IF;
|
END IF;
|
|
@ -96,7 +96,7 @@ BEGIN
|
||||||
clientFk,
|
clientFk,
|
||||||
dued,
|
dued,
|
||||||
companyFk,
|
companyFk,
|
||||||
cplusInvoiceType477Fk
|
siiTypeInvoiceOutFk
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
1,
|
1,
|
||||||
|
@ -135,13 +135,13 @@ BEGIN
|
||||||
INSERT INTO ticketTracking(stateFk,ticketFk,workerFk)
|
INSERT INTO ticketTracking(stateFk,ticketFk,workerFk)
|
||||||
SELECT * FROM tmp.updateInter;
|
SELECT * FROM tmp.updateInter;
|
||||||
|
|
||||||
CALL invoiceExpenceMake(vNewInvoiceId);
|
CALL invoiceExpenseMake(vNewInvoiceId);
|
||||||
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
|
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
|
||||||
|
|
||||||
UPDATE invoiceOut io
|
UPDATE invoiceOut io
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT SUM(amount) total
|
SELECT SUM(amount) total
|
||||||
FROM invoiceOutExpence
|
FROM invoiceOutExpense
|
||||||
WHERE invoiceOutFk = vNewInvoiceId
|
WHERE invoiceOutFk = vNewInvoiceId
|
||||||
) base
|
) base
|
||||||
JOIN (
|
JOIN (
|
||||||
|
@ -178,15 +178,15 @@ BEGIN
|
||||||
SET @vTaxableBaseServices := 0.00;
|
SET @vTaxableBaseServices := 0.00;
|
||||||
SET @vTaxCodeGeneral := NULL;
|
SET @vTaxCodeGeneral := NULL;
|
||||||
|
|
||||||
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
|
||||||
SELECT vNewInvoiceInFk,
|
SELECT vNewInvoiceInFk,
|
||||||
@vTaxableBaseServices,
|
@vTaxableBaseServices,
|
||||||
sub.expenceFk,
|
sub.expenseFk,
|
||||||
sub.taxTypeSageFk,
|
sub.taxTypeSageFk,
|
||||||
sub.transactionTypeSageFk
|
sub.transactionTypeSageFk
|
||||||
FROM (
|
FROM (
|
||||||
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
|
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
|
||||||
i.expenceFk,
|
i.expenseFk,
|
||||||
i.taxTypeSageFk,
|
i.taxTypeSageFk,
|
||||||
i.transactionTypeSageFk,
|
i.transactionTypeSageFk,
|
||||||
@vTaxCodeGeneral := i.taxClassCodeFk
|
@vTaxCodeGeneral := i.taxClassCodeFk
|
||||||
|
@ -196,11 +196,11 @@ BEGIN
|
||||||
HAVING taxableBase
|
HAVING taxableBase
|
||||||
) sub;
|
) sub;
|
||||||
|
|
||||||
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
|
||||||
SELECT vNewInvoiceInFk,
|
SELECT vNewInvoiceInFk,
|
||||||
SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
|
SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
|
||||||
@vTaxableBaseServices, 0) taxableBase,
|
@vTaxableBaseServices, 0) taxableBase,
|
||||||
i.expenceFk,
|
i.expenseFk,
|
||||||
i.taxTypeSageFk ,
|
i.taxTypeSageFk ,
|
||||||
i.transactionTypeSageFk
|
i.transactionTypeSageFk
|
||||||
FROM tmp.ticketTax tt
|
FROM tmp.ticketTax tt
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue