Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5070-route-tickets-link-maps
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Alexandre Riera 2023-03-01 11:20:24 +01:00
commit 48da6403fa
530 changed files with 17054 additions and 27127 deletions

View File

@ -5,18 +5,57 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2304.01] - 2023-02-09
## [2308.01] - 2023-03-09
### Added
- (Rutas) Al descargar varias facturas se comprime en un zip
- (Trabajadores -> Nuevo trabajador) Nueva sección
- (Proveedores -> Datos fiscales) Añadido checkbox 'Vies'
- (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
- (Artículos -> Etiquetas) Permite intercambiar la relevancia entre dos etiquetas.
-
## [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
### Added
- (Rutas) Al descargar varias facturas se comprime en un zip
- (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
- (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"
- (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
- (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 -> 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

View File

@ -3,9 +3,9 @@ module.exports = Self => {
description: 'Send email to the user',
accepts: [
{
arg: 'email',
arg: 'user',
type: 'string',
description: 'The email of user',
description: 'The user name or email',
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 usesEmail = user.indexOf('@') !== -1;
if (!usesEmail) {
const account = await models.Account.findOne({
fields: ['email'],
where: {name: user}
});
user = account.email;
}
try {
await models.user.resetPassword({email, emailTemplate: 'recover-password'});
await models.user.resetPassword({email: user, emailTemplate: 'recover-password'});
} catch (err) {
if (err.code === 'EMAIL_NOT_FOUND')
return;

View File

@ -22,7 +22,7 @@ module.exports = Self => {
Self.latest = async filter => {
const conn = Self.dataSource.connector;
const minDate = new Date();
const minDate = Date.vnNew();
minDate.setFullYear(minDate.getFullYear() - 1);
const where = {dated: {gte: minDate}};

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
describe('campaign latest()', () => {
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 randomIndex = Math.floor(Math.random() * result.length);
const campaignDated = result[randomIndex].dated;
@ -12,7 +12,7 @@ describe('campaign latest()', () => {
});
it('should return the campaigns from the current year', async() => {
const now = new Date();
const now = Date.vnNew();
const currentYear = now.getFullYear();
const result = await app.models.Campaign.latest({
where: {dated: {like: `%${currentYear}%`}}

View File

@ -4,7 +4,7 @@ describe('campaign upcoming()', () => {
it('should return the upcoming campaign but from the last year', async() => {
const response = await app.models.Campaign.upcoming();
const campaignDated = response.dated;
const now = new Date();
const now = Date.vnNew();
expect(campaignDated).toEqual(jasmine.any(Date));
expect(campaignDated).toBeLessThanOrEqual(now);

View File

@ -14,7 +14,7 @@ module.exports = Self => {
});
Self.upcoming = async() => {
const minDate = new Date();
const minDate = Date.vnNew();
minDate.setFullYear(minDate.getFullYear() - 1);
return Self.findOne({

View File

@ -21,7 +21,7 @@ module.exports = Self => {
if (!this.login) return;
if (Date.now() > this.login.expires)
if (Date.vnNow() > this.login.expires)
this.login = await requestToken();
return this.login;
@ -48,7 +48,7 @@ module.exports = Self => {
userId: requestData.userId,
token: requestData.authToken
},
expires: Date.now() + (1000 * 60 * tokenLifespan)
expires: Date.vnNow() + (1000 * 60 * tokenLifespan)
};
}
}

View File

@ -33,7 +33,7 @@ module.exports = Self => {
await models.Chat.create({
senderFk: sender.id,
recipient: to,
dated: new Date(),
dated: Date.vnNew(),
checkUserStatus: 0,
message: message,
status: 0,

View File

@ -49,7 +49,7 @@ module.exports = Self => {
await models.Chat.create({
senderFk: sender.id,
recipient: `@${recipient.name}`,
dated: new Date(),
dated: Date.vnNew(),
checkUserStatus: 1,
message: message,
status: 0,

View File

@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models;
describe('Chat sendCheckingPresence()', () => {
const today = new Date();
const today = Date.vnNew();
today.setHours(6, 0);
const chatModel = models.Chat;

View File

@ -24,7 +24,7 @@ module.exports = Self => {
}
});
Self.setSaleQuantity = async(saleId, quantity) => {
Self.setSaleQuantity = async(saleId, quantity, options) => {
const models = Self.app.models;
const myOptions = {};
let tx;

View File

@ -32,7 +32,7 @@ module.exports = Self => {
where: {code: 'trash'}
}, myOptions);
const date = new Date();
const date = Date.vnNew();
date.setMonth(date.getMonth() - 4);
const dmsToDelete = await models.Dms.find({

View File

@ -163,7 +163,7 @@ module.exports = Self => {
fields: ['alertLevel']
});
signedTime ? signedTime != undefined : signedTime = new Date();
signedTime ? signedTime != undefined : signedTime = Date.vnNew();
if (alertLevel >= 2) {
let dir;

View File

@ -127,7 +127,7 @@ module.exports = Self => {
const uploadOptions = {
headers: {
'Content-Type': 'multipart/form-data',
'X-File-ModifiedDate': new Date(),
'X-File-ModifiedDate': Date.vnNew(),
'Cookie': options.headers.headers.Cookie,
...data.getHeaders()
},

View File

@ -230,7 +230,7 @@ module.exports = Self => {
UPDATE edi.tableConfig
SET updated = ?
WHERE fileName = ?
`, [new Date(), baseName], options);
`, [Date.vnNew(), baseName], options);
}
console.log(`Updated table ${toTable}\n`);

View File

@ -74,10 +74,13 @@ module.exports = Self => {
const container = await models.ImageContainer.getContainer(collection);
const rootPath = container.client.root;
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 = {
path: `${fileSrc}/${image.name}.png`,
path: `${fileSrc}/${fileName}`,
contentType: 'image/png',
name: `${image.name}.png`
name: image.name
};
if (!fs.existsSync(file.path)) return [];

View File

@ -1,6 +1,7 @@
const UserError = require('vn-loopback/util/user-error');
const fs = require('fs-extra');
const path = require('path');
const uuid = require('uuid');
module.exports = Self => {
Self.remoteMethodCtx('upload', {
@ -18,12 +19,6 @@ module.exports = Self => {
type: 'string',
description: 'The collection name',
required: true
},
{
arg: 'fileName',
type: 'string',
description: 'The file name',
required: true
}],
returns: {
type: 'Object',
@ -56,10 +51,12 @@ module.exports = Self => {
const [uploadedFile] = Object.values(uploaded.files).map(file => {
return file[0];
});
const file = await TempContainer.getFile(tempContainer.name, uploadedFile.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) {
if (fs.existsSync(srcFile))
await fs.unlink(srcFile);

View File

@ -32,7 +32,7 @@ module.exports = Self => {
if (!config.cleanDays) return;
const cleanDate = new Date();
const cleanDate = Date.vnNew();
cleanDate.setDate(cleanDate.getDate() - config.cleanDays);
await models.NotificationQueue.destroyAll({

View File

@ -10,7 +10,7 @@ describe('Notification Clean()', () => {
const notification = await models.Notification.findOne({}, options);
const notificationConfig = await models.NotificationConfig.findOne({});
const cleanDate = new Date();
const cleanDate = Date.vnNew();
cleanDate.setDate(cleanDate.getDate() - (notificationConfig.cleanDays + 1));
let before;

View File

@ -20,10 +20,9 @@
"type": "date"
}
},
"scope": {
"where" :{
"expired": null
}
}
}
}

View File

@ -77,16 +77,15 @@ module.exports = Self => {
const newImage = await Self.upsertWithWhere(data, {
name: fileName,
collectionFk: collectionName,
updated: (new Date).getTime()
updated: Date.vnNow()
}, myOptions);
// Resizes and saves the image
const container = await models.ImageContainer.container(collectionName);
const rootPath = container.client.root;
const collectionDir = path.join(rootPath, collectionName);
const file = `${fileName}.png`;
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 type = imageType(buffer);
@ -102,7 +101,8 @@ module.exports = Self => {
width: bmpData.width,
height: bmpData.height,
channels: 4
}
},
failOn: 'none'
};
}
@ -120,7 +120,7 @@ module.exports = Self => {
const sizes = collection.sizes();
for (let size of sizes) {
const dstDir = path.join(collectionDir, `${size.width}x${size.height}`);
const dstFile = path.join(dstDir, file);
const dstFile = path.join(dstDir, fileName);
const resizeOpts = {
withoutEnlargement: true,
fit: size.crop ? 'cover' : 'inside'

View File

@ -6,6 +6,16 @@
"table": "util.notificationAcl"
}
},
"properties":{
"notificationFk": {
"id": true,
"type": "number"
},
"roleFk":{
"id": true,
"type": "number"
}
},
"relations": {
"notification": {
"type": "belongsTo",

View File

@ -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
}
});
}
};

View File

@ -7,15 +7,18 @@
}
},
"properties": {
"notificationFk": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
"description": "Primary key"
},
"notificationFk": {
"type": "number",
"description": "Foreign key to Notification"
},
"userFk": {
"type": "number",
"id": true,
"description": "Identifier"
"description": "Foreign key to Account"
}
},
"relations": {

View File

@ -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;
}
});
});

View File

@ -30,7 +30,10 @@ async function test() {
const bootOptions = {dataSources};
const app = require('vn-loopback/server/server');
app.boot(bootOptions);
await new Promise((resolve, reject) => {
app.boot(bootOptions,
err => err ? reject(err) : resolve());
});
const Jasmine = require('jasmine');
const jasmine = new Jasmine();

View File

@ -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