Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5149-check-ticket.refund
gitea/salix/pipeline/head Build queued...
Details
gitea/salix/pipeline/head Build queued...
Details
This commit is contained in:
commit
2e09845dbd
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -5,19 +5,56 @@ 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).
|
||||||
|
|
||||||
|
## [2308.01] - 2023-03-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Client -> Descriptor) Nuevo icono $ con barrotes para los clientes con impago
|
||||||
|
- (Trabajador -> Datos Básicos) Añadido nuevo campo Taquilla
|
||||||
|
- (Trabajador -> PDA) Nueva sección
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
-
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
-
|
||||||
|
|
||||||
|
## [2306.01] - 2023-02-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Tickets -> Datos Básicos) Mensaje de confirmación al intentar generar tickets con negativos
|
||||||
|
- (Artículos) El visible y disponible se calcula a partir de un almacén diferente dependiendo de la sección en la que te encuentres. Se ha añadido un icono que informa sobre a partir de que almacén se esta calculando.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- (General -> Inicio) Ahora permite recuperar la contraseña tanto con el correo de recuperación como el usuario
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- (Monitor de tickets) Cuando ordenas por columna, ya no se queda deshabilitado el botón de 'Actualizar'
|
||||||
|
- (Zone -> Días de entrega) Al hacer click en un día, muestra correctamente las zonas
|
||||||
|
- (Artículos) El disponible en la vista previa se muestra correctamente
|
||||||
|
|
||||||
## [2304.01] - 2023-02-09
|
## [2304.01] - 2023-02-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- (Rutas) Al descargar varias facturas se comprime en un zip
|
- (Rutas) Al descargar varias facturas se comprime en un zip
|
||||||
- (Trabajadores -> Nuevo trabajador) Nueva sección
|
- (Trabajadores -> Nuevo trabajador) Nueva sección
|
||||||
|
- (Tickets -> Adelantar tickets) Añadidos campos "líneas" y "litros" al ticket origen
|
||||||
|
- (Tickets -> Adelantar tickets) Nuevo icono muestra cuando las agencias de los tickets origen/destino son distintas
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- (Entradas -> Compras) Cambiados los campos "Precio Grouping/Packing" por "PVP" y "Precio" por "Coste"
|
- (Entradas -> Compras) Cambiados los campos "Precio Grouping/Packing" por "PVP" y "Precio" por "Coste"
|
||||||
- (Artículos -> Últimas entradas) Cambiados los campos "P.P.U." y "P.P.P." por "PVP"
|
- (Artículos -> Últimas entradas) Cambiados los campos "P.P.U." y "P.P.P." por "PVP"
|
||||||
|
- (Rutas -> Sumario/Tickets) Actualizados campos de los tickets
|
||||||
|
- (Proveedores -> Crear/Editar) Permite añadir Proveedores con la misma razón social pero con países distintos
|
||||||
|
- (Tickets -> Adelantar tickets) Cambiados selectores de estado por checks "Pendiente origen/destino"
|
||||||
|
- (Tickets -> Adelantar tickets) Cambiado stock de destino a origen.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- (Artículos -> Etiquetas) Permite intercambiar la relevancia entre dos etiquetas.
|
- (Artículos -> Etiquetas) Permite intercambiar la relevancia entre dos etiquetas.
|
||||||
|
- (Cliente -> Datos Fiscales) No se permite seleccionar 'Notificar vía e-mail' a los clientes sin e-mail
|
||||||
|
- (Tickets -> Datos básicos) Permite guardar la hora de envío
|
||||||
- (Tickets -> Añadir pago) Eliminado "null" en las referencias
|
- (Tickets -> Añadir pago) Eliminado "null" en las referencias
|
||||||
|
- (Tickets -> Adelantar tickets) Permite ordenar por importe
|
||||||
|
- (Tickets -> Adelantar tickets) El filtrado por encajado muestra también los tickets sin tipo de encajado
|
||||||
|
|
||||||
## [2302.01] - 2023-01-26
|
## [2302.01] - 2023-01-26
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ module.exports = Self => {
|
||||||
description: 'Send email to the user',
|
description: 'Send email to the user',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'email',
|
arg: 'user',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The email of user',
|
description: 'The user name or email',
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -15,11 +15,20 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.recoverPassword = async function(email) {
|
Self.recoverPassword = async function(user) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const usesEmail = user.indexOf('@') !== -1;
|
||||||
|
if (!usesEmail) {
|
||||||
|
const account = await models.Account.findOne({
|
||||||
|
fields: ['email'],
|
||||||
|
where: {name: user}
|
||||||
|
});
|
||||||
|
user = account.email;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await models.user.resetPassword({email, emailTemplate: 'recover-password'});
|
await models.user.resetPassword({email: user, emailTemplate: 'recover-password'});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'EMAIL_NOT_FOUND')
|
if (err.code === 'EMAIL_NOT_FOUND')
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -22,7 +22,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.latest = async filter => {
|
Self.latest = async filter => {
|
||||||
const conn = Self.dataSource.connector;
|
const conn = Self.dataSource.connector;
|
||||||
const minDate = new Date();
|
const minDate = Date.vnNew();
|
||||||
minDate.setFullYear(minDate.getFullYear() - 1);
|
minDate.setFullYear(minDate.getFullYear() - 1);
|
||||||
|
|
||||||
const where = {dated: {gte: minDate}};
|
const where = {dated: {gte: minDate}};
|
||||||
|
|
|
@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('campaign latest()', () => {
|
describe('campaign latest()', () => {
|
||||||
it('should return the campaigns from the last year', async() => {
|
it('should return the campaigns from the last year', async() => {
|
||||||
const now = new Date();
|
const now = Date.vnNew();
|
||||||
const result = await app.models.Campaign.latest();
|
const result = await app.models.Campaign.latest();
|
||||||
const randomIndex = Math.floor(Math.random() * result.length);
|
const randomIndex = Math.floor(Math.random() * result.length);
|
||||||
const campaignDated = result[randomIndex].dated;
|
const campaignDated = result[randomIndex].dated;
|
||||||
|
@ -12,7 +12,7 @@ describe('campaign latest()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the campaigns from the current year', async() => {
|
it('should return the campaigns from the current year', async() => {
|
||||||
const now = new Date();
|
const now = Date.vnNew();
|
||||||
const currentYear = now.getFullYear();
|
const currentYear = now.getFullYear();
|
||||||
const result = await app.models.Campaign.latest({
|
const result = await app.models.Campaign.latest({
|
||||||
where: {dated: {like: `%${currentYear}%`}}
|
where: {dated: {like: `%${currentYear}%`}}
|
||||||
|
|
|
@ -4,7 +4,7 @@ describe('campaign upcoming()', () => {
|
||||||
it('should return the upcoming campaign but from the last year', async() => {
|
it('should return the upcoming campaign but from the last year', async() => {
|
||||||
const response = await app.models.Campaign.upcoming();
|
const response = await app.models.Campaign.upcoming();
|
||||||
const campaignDated = response.dated;
|
const campaignDated = response.dated;
|
||||||
const now = new Date();
|
const now = Date.vnNew();
|
||||||
|
|
||||||
expect(campaignDated).toEqual(jasmine.any(Date));
|
expect(campaignDated).toEqual(jasmine.any(Date));
|
||||||
expect(campaignDated).toBeLessThanOrEqual(now);
|
expect(campaignDated).toBeLessThanOrEqual(now);
|
||||||
|
|
|
@ -14,7 +14,7 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.upcoming = async() => {
|
Self.upcoming = async() => {
|
||||||
const minDate = new Date();
|
const minDate = Date.vnNew();
|
||||||
minDate.setFullYear(minDate.getFullYear() - 1);
|
minDate.setFullYear(minDate.getFullYear() - 1);
|
||||||
|
|
||||||
return Self.findOne({
|
return Self.findOne({
|
||||||
|
|
|
@ -21,7 +21,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (!this.login) return;
|
if (!this.login) return;
|
||||||
|
|
||||||
if (Date.now() > this.login.expires)
|
if (Date.vnNow() > this.login.expires)
|
||||||
this.login = await requestToken();
|
this.login = await requestToken();
|
||||||
|
|
||||||
return this.login;
|
return this.login;
|
||||||
|
@ -48,7 +48,7 @@ module.exports = Self => {
|
||||||
userId: requestData.userId,
|
userId: requestData.userId,
|
||||||
token: requestData.authToken
|
token: requestData.authToken
|
||||||
},
|
},
|
||||||
expires: Date.now() + (1000 * 60 * tokenLifespan)
|
expires: Date.vnNow() + (1000 * 60 * tokenLifespan)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ module.exports = Self => {
|
||||||
await models.Chat.create({
|
await models.Chat.create({
|
||||||
senderFk: sender.id,
|
senderFk: sender.id,
|
||||||
recipient: to,
|
recipient: to,
|
||||||
dated: new Date(),
|
dated: Date.vnNew(),
|
||||||
checkUserStatus: 0,
|
checkUserStatus: 0,
|
||||||
message: message,
|
message: message,
|
||||||
status: 0,
|
status: 0,
|
||||||
|
|
|
@ -49,7 +49,7 @@ module.exports = Self => {
|
||||||
await models.Chat.create({
|
await models.Chat.create({
|
||||||
senderFk: sender.id,
|
senderFk: sender.id,
|
||||||
recipient: `@${recipient.name}`,
|
recipient: `@${recipient.name}`,
|
||||||
dated: new Date(),
|
dated: Date.vnNew(),
|
||||||
checkUserStatus: 1,
|
checkUserStatus: 1,
|
||||||
message: message,
|
message: message,
|
||||||
status: 0,
|
status: 0,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('Chat sendCheckingPresence()', () => {
|
describe('Chat sendCheckingPresence()', () => {
|
||||||
const today = new Date();
|
const today = Date.vnNew();
|
||||||
today.setHours(6, 0);
|
today.setHours(6, 0);
|
||||||
const chatModel = models.Chat;
|
const chatModel = models.Chat;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.setSaleQuantity = async(saleId, quantity) => {
|
Self.setSaleQuantity = async(saleId, quantity, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
|
|
@ -32,7 +32,7 @@ module.exports = Self => {
|
||||||
where: {code: 'trash'}
|
where: {code: 'trash'}
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
const date = new Date();
|
const date = Date.vnNew();
|
||||||
date.setMonth(date.getMonth() - 4);
|
date.setMonth(date.getMonth() - 4);
|
||||||
|
|
||||||
const dmsToDelete = await models.Dms.find({
|
const dmsToDelete = await models.Dms.find({
|
||||||
|
|
|
@ -163,7 +163,7 @@ module.exports = Self => {
|
||||||
fields: ['alertLevel']
|
fields: ['alertLevel']
|
||||||
});
|
});
|
||||||
|
|
||||||
signedTime ? signedTime != undefined : signedTime = new Date();
|
signedTime ? signedTime != undefined : signedTime = Date.vnNew();
|
||||||
|
|
||||||
if (alertLevel >= 2) {
|
if (alertLevel >= 2) {
|
||||||
let dir;
|
let dir;
|
||||||
|
|
|
@ -127,7 +127,7 @@ module.exports = Self => {
|
||||||
const uploadOptions = {
|
const uploadOptions = {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
'X-File-ModifiedDate': new Date(),
|
'X-File-ModifiedDate': Date.vnNew(),
|
||||||
'Cookie': options.headers.headers.Cookie,
|
'Cookie': options.headers.headers.Cookie,
|
||||||
...data.getHeaders()
|
...data.getHeaders()
|
||||||
},
|
},
|
||||||
|
|
|
@ -230,7 +230,7 @@ module.exports = Self => {
|
||||||
UPDATE edi.tableConfig
|
UPDATE edi.tableConfig
|
||||||
SET updated = ?
|
SET updated = ?
|
||||||
WHERE fileName = ?
|
WHERE fileName = ?
|
||||||
`, [new Date(), baseName], options);
|
`, [Date.vnNew(), baseName], options);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Updated table ${toTable}\n`);
|
console.log(`Updated table ${toTable}\n`);
|
||||||
|
|
|
@ -74,10 +74,13 @@ module.exports = Self => {
|
||||||
const container = await models.ImageContainer.getContainer(collection);
|
const container = await models.ImageContainer.getContainer(collection);
|
||||||
const rootPath = container.client.root;
|
const rootPath = container.client.root;
|
||||||
const fileSrc = path.join(rootPath, collection, size);
|
const fileSrc = path.join(rootPath, collection, size);
|
||||||
|
|
||||||
|
const ext = image.name.substring((image.name.length - 4));
|
||||||
|
const fileName = ext !== '.png' ? `${image.name}.png` : image.name;
|
||||||
const file = {
|
const file = {
|
||||||
path: `${fileSrc}/${image.name}.png`,
|
path: `${fileSrc}/${fileName}`,
|
||||||
contentType: 'image/png',
|
contentType: 'image/png',
|
||||||
name: `${image.name}.png`
|
name: image.name
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!fs.existsSync(file.path)) return [];
|
if (!fs.existsSync(file.path)) return [];
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const uuid = require('uuid');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('upload', {
|
Self.remoteMethodCtx('upload', {
|
||||||
|
@ -18,12 +19,6 @@ module.exports = Self => {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The collection name',
|
description: 'The collection name',
|
||||||
required: true
|
required: true
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'fileName',
|
|
||||||
type: 'string',
|
|
||||||
description: 'The file name',
|
|
||||||
required: true
|
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
|
@ -56,10 +51,12 @@ module.exports = Self => {
|
||||||
const [uploadedFile] = Object.values(uploaded.files).map(file => {
|
const [uploadedFile] = Object.values(uploaded.files).map(file => {
|
||||||
return file[0];
|
return file[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
|
const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
|
||||||
srcFile = path.join(file.client.root, file.container, file.name);
|
srcFile = path.join(file.client.root, file.container, file.name);
|
||||||
|
|
||||||
await models.Image.registerImage(args.collection, srcFile, args.fileName, args.id);
|
const fileName = `${uuid.v4()}.png`;
|
||||||
|
await models.Image.registerImage(args.collection, srcFile, fileName, args.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (fs.existsSync(srcFile))
|
if (fs.existsSync(srcFile))
|
||||||
await fs.unlink(srcFile);
|
await fs.unlink(srcFile);
|
||||||
|
|
|
@ -32,7 +32,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (!config.cleanDays) return;
|
if (!config.cleanDays) return;
|
||||||
|
|
||||||
const cleanDate = new Date();
|
const cleanDate = Date.vnNew();
|
||||||
cleanDate.setDate(cleanDate.getDate() - config.cleanDays);
|
cleanDate.setDate(cleanDate.getDate() - config.cleanDays);
|
||||||
|
|
||||||
await models.NotificationQueue.destroyAll({
|
await models.NotificationQueue.destroyAll({
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe('Notification Clean()', () => {
|
||||||
const notification = await models.Notification.findOne({}, options);
|
const notification = await models.Notification.findOne({}, options);
|
||||||
const notificationConfig = await models.NotificationConfig.findOne({});
|
const notificationConfig = await models.NotificationConfig.findOne({});
|
||||||
|
|
||||||
const cleanDate = new Date();
|
const cleanDate = Date.vnNew();
|
||||||
cleanDate.setDate(cleanDate.getDate() - (notificationConfig.cleanDays + 1));
|
cleanDate.setDate(cleanDate.getDate() - (notificationConfig.cleanDays + 1));
|
||||||
|
|
||||||
let before;
|
let before;
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
"type": "date"
|
"type": "date"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"scope": {
|
"scope": {
|
||||||
"where" :{
|
"where" :{
|
||||||
"expired": null
|
"expired": null
|
||||||
|
|
|
@ -77,16 +77,15 @@ module.exports = Self => {
|
||||||
const newImage = await Self.upsertWithWhere(data, {
|
const newImage = await Self.upsertWithWhere(data, {
|
||||||
name: fileName,
|
name: fileName,
|
||||||
collectionFk: collectionName,
|
collectionFk: collectionName,
|
||||||
updated: (new Date).getTime()
|
updated: Date.vnNow()
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
// Resizes and saves the image
|
// Resizes and saves the image
|
||||||
const container = await models.ImageContainer.container(collectionName);
|
const container = await models.ImageContainer.container(collectionName);
|
||||||
const rootPath = container.client.root;
|
const rootPath = container.client.root;
|
||||||
const collectionDir = path.join(rootPath, collectionName);
|
const collectionDir = path.join(rootPath, collectionName);
|
||||||
const file = `${fileName}.png`;
|
|
||||||
const dstDir = path.join(collectionDir, 'full');
|
const dstDir = path.join(collectionDir, 'full');
|
||||||
const dstFile = path.join(dstDir, file);
|
const dstFile = path.join(dstDir, fileName);
|
||||||
|
|
||||||
const buffer = readChunk.sync(srcFilePath, 0, 12);
|
const buffer = readChunk.sync(srcFilePath, 0, 12);
|
||||||
const type = imageType(buffer);
|
const type = imageType(buffer);
|
||||||
|
@ -102,7 +101,8 @@ module.exports = Self => {
|
||||||
width: bmpData.width,
|
width: bmpData.width,
|
||||||
height: bmpData.height,
|
height: bmpData.height,
|
||||||
channels: 4
|
channels: 4
|
||||||
}
|
},
|
||||||
|
failOn: 'none'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ module.exports = Self => {
|
||||||
const sizes = collection.sizes();
|
const sizes = collection.sizes();
|
||||||
for (let size of sizes) {
|
for (let size of sizes) {
|
||||||
const dstDir = path.join(collectionDir, `${size.width}x${size.height}`);
|
const dstDir = path.join(collectionDir, `${size.width}x${size.height}`);
|
||||||
const dstFile = path.join(dstDir, file);
|
const dstFile = path.join(dstDir, fileName);
|
||||||
const resizeOpts = {
|
const resizeOpts = {
|
||||||
withoutEnlargement: true,
|
withoutEnlargement: true,
|
||||||
fit: size.crop ? 'cover' : 'inside'
|
fit: size.crop ? 'cover' : 'inside'
|
||||||
|
|
|
@ -6,6 +6,16 @@
|
||||||
"table": "util.notificationAcl"
|
"table": "util.notificationAcl"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"properties":{
|
||||||
|
"notificationFk": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"roleFk":{
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
"notification": {
|
"notification": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.observe('before save', async function(ctx) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
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)
|
||||||
|
throw new UserError('You dont have permission to modify this user');
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.remoteMethod('deleteNotification', {
|
||||||
|
description: 'Deletes a notification subscription',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'ctx',
|
||||||
|
type: 'object',
|
||||||
|
http: {source: 'context'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'notificationId',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
verb: 'POST',
|
||||||
|
path: '/deleteNotification'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteNotification = async function(ctx, notificationId) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const user = ctx.req.accessToken.userId;
|
||||||
|
const modifiedUser = await getUserToModify(notificationId, null, models);
|
||||||
|
|
||||||
|
if (user != modifiedUser.id && user != modifiedUser.bossFk)
|
||||||
|
throw new UserError('You dont have permission to modify this user');
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -7,15 +7,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"notificationFk": {
|
"id": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"id": true,
|
"id": true,
|
||||||
"description": "Identifier"
|
"description": "Primary key"
|
||||||
|
},
|
||||||
|
"notificationFk": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Foreign key to Notification"
|
||||||
},
|
},
|
||||||
"userFk": {
|
"userFk": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"id": true,
|
"description": "Foreign key to Account"
|
||||||
"description": "Identifier"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('loopback model NotificationSubscription', () => {
|
||||||
|
it('Should fail to delete a notification if the user is not editing itself or a subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const user = 9;
|
||||||
|
const notificationSubscriptionId = 2;
|
||||||
|
const ctx = {req: {accessToken: {userId: user}}};
|
||||||
|
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();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should delete a notification if the user is editing itself', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const user = 9;
|
||||||
|
const notificationSubscriptionId = 4;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should delete a notification if the user is editing a subordinate', async() => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `vn`.`supplier` ADD UNIQUE (name, countryFk);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue