Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5036-regularizar-historicos

This commit is contained in:
Alexandre Riera 2023-02-03 12:03:16 +01:00
commit 0c91f95762
297 changed files with 1742 additions and 976 deletions

View File

@ -5,10 +5,10 @@ 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
## [2306.01] - 2023-02-23
### Added
- (Trabajadores -> Nuevo trabajador) Nueva sección
-
### Changed
-
@ -16,6 +16,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
-
## [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
### Added

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

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

@ -77,7 +77,7 @@ 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

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

@ -0,0 +1 @@
ALTER TABLE `vn`.`supplier` ADD UNIQUE (name, countryFk);

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Tag', 'onSubmit', 'WRITE', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (model,property,accessType,principalId)
VALUES
('NotificationSubscription','*','*','employee'),
('NotificationAcl','*','READ','employee');

View File

@ -0,0 +1,110 @@
DROP PROCEDURE IF EXISTS vn.ticket_canAdvance;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canAdvance`(vDateFuture DATE, vDateToAdvance DATE, vWarehouseFk INT)
BEGIN
/**
* Devuelve los tickets y la cantidad de lineas de venta que se pueden adelantar.
*
* @param vDateFuture Fecha de los tickets que se quieren adelantar.
* @param vDateToAdvance Fecha a cuando se quiere adelantar.
* @param vWarehouseFk Almacén
*/
DECLARE vDateInventory DATE;
SELECT inventoried INTO vDateInventory FROM vn.config;
DROP TEMPORARY TABLE IF EXISTS tmp.stock;
CREATE TEMPORARY TABLE tmp.stock
(itemFk INT PRIMARY KEY,
amount INT)
ENGINE = MEMORY;
INSERT INTO tmp.stock(itemFk, amount)
SELECT itemFk, SUM(quantity) amount FROM
(
SELECT itemFk, quantity
FROM vn.itemTicketOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM vn.itemEntryIn
WHERE landed >= vDateInventory
AND landed < vDateFuture
AND isVirtualStock = FALSE
AND warehouseInFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM vn.itemEntryOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseOutFk = vWarehouseFk
) t
GROUP BY itemFk HAVING amount != 0;
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
CREATE TEMPORARY TABLE tmp.filter
(INDEX (id))
SELECT s.ticketFk futureId,
t2.ticketFk id,
count(DISTINCT s.id) saleCount,
t2.state,
t2.isNotValidated,
st.name futureState,
st.isNotValidated futureIsNotValidated,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
t2.ipt,
t.workerFk,
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
CAST(COUNT(*) AS DECIMAL(10,0)) `futureLines`,
t2.shipped,
t.shipped futureShipped,
t2.totalWithVat,
t.totalWithVat futureTotalWithVat,
t2.agency,
am.name futureAgency,
t2.lines,
t2.liters,
SUM((s.quantity <= IFNULL(st.amount,0))) hasStock
FROM vn.ticket t
JOIN vn.ticketState ts ON ts.ticketFk = t.id
JOIN vn.state st ON st.id = ts.stateFk
JOIN vn.saleVolume sv ON t.id = sv.ticketFk
JOIN (SELECT
t2.id ticketFk,
t2.addressFk,
st.isNotValidated,
st.name state,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
t2.shipped,
t2.totalWithVat,
am.name agency,
CAST(SUM(litros) AS DECIMAL(10,0)) liters,
CAST(COUNT(*) AS DECIMAL(10,0)) `lines`
FROM vn.ticket t2
JOIN vn.saleVolume sv ON t2.id = sv.ticketFk
JOIN vn.sale s ON s.ticketFk = t2.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.ticketState ts ON ts.ticketFk = t2.id
JOIN vn.state st ON st.id = ts.stateFk
JOIN vn.agencyMode am ON t2.agencyModeFk = am.id
LEFT JOIN vn.itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
WHERE t2.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
AND t2.warehouseFk = vWarehouseFk
GROUP BY t2.id) t2 ON t2.addressFk = t.addressFk
JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.agencyMode am ON t.agencyModeFk = am.id
LEFT JOIN vn.itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
LEFT JOIN tmp.stock st ON st.itemFk = s.itemFk
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
AND t.warehouseFk = vWarehouseFk
GROUP BY t.id;
DROP TEMPORARY TABLE tmp.stock;
END$$
DELIMITER ;

View File

@ -0,0 +1,4 @@
ALTER TABLE
`util`.`notificationSubscription`
ADD
CONSTRAINT `notificationSubscription_UN` UNIQUE KEY (`notificationFk`, `userFk`);

View File

@ -0,0 +1,6 @@
UPDATE `vn`.`client`
SET isToBeMailed = FALSE
WHERE
mailAddress is NULL
AND email is NULL
AND isToBeMailed = TRUE;

View File

@ -0,0 +1,7 @@
ALTER TABLE `util`.`notificationSubscription`
ADD `id` int(11) auto_increment NULL,
DROP PRIMARY KEY,
ADD CONSTRAINT PRIMARY KEY (`id`);
ALTER TABLE `util`.`notificationSubscription`
ADD KEY `notificationSubscription_ibfk_1` (`notificationFk`);

View File

@ -2,7 +2,33 @@ CREATE SCHEMA IF NOT EXISTS `vn2008`;
CREATE SCHEMA IF NOT EXISTS `tmp`;
UPDATE `util`.`config`
SET `environment`= 'test';
SET `environment`= 'development';
-- FOR MOCK vn.time
DROP PROCEDURE IF EXISTS `vn`.`mockVnTime`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`mockVnTime`()
BEGIN
DECLARE vDate DATE;
SET vDate = '2000-01-01';
WHILE ( YEAR(vDate) <= 2002 ) DO
INSERT IGNORE INTO vn.`time` (dated, period, `month`, `year`, `day`, week, yearMonth, salesYear)
VALUES (vDate, CONCAT(YEAR(vDate), (WEEK(vDate)+1)), MONTH(vDate), YEAR(vDate), DAY(vDate), WEEK(vDate)+1, CONCAT(YEAR(vDate), MONTH(vDate)), YEAR(vDate));
SET vDate = DATE_ADD(vDate, INTERVAL 1 DAY);
END WHILE;
END$$
DELIMITER ;
CALL `vn`.`mockVnTime`();
DROP PROCEDURE IF EXISTS `vn`.`mockVnTime`;
-- END MOCK vn.time
ALTER TABLE `vn`.`itemTaxCountry` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1;
@ -934,10 +960,10 @@ INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `freightItemFk`,
(7, 2, 4, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), 1, 18, NULL, 94, NULL,NULL),
(8, 3, 5, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), 1, 18, NULL, 94, 1, NULL),
(9, 3, 6, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 18, NULL, 94, 2, NULL),
(10, 7, 7, 71, NOW(), 1, 18, NULL, 94, 3, NULL),
(11, 7, 8, 71, NOW(), 1, 18, NULL, 94, 3, NULL),
(12, 7, 9, 71, NOW(), 1, 18, NULL, 94, 3, NULL),
(13, 1, 10,71, NOW(), 1, 18, NULL, 94, 3, NULL);
(10, 7, 7, 71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL),
(11, 7, 8, 71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL),
(12, 7, 9, 71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL),
(13, 1, 10,71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL);
INSERT INTO `vn`.`expeditionState`(`id`, `created`, `expeditionFk`, `typeFk`, `userFk`)
@ -1910,7 +1936,7 @@ DROP TEMPORARY TABLE IF EXISTS tmp.worker;
CREATE TEMPORARY TABLE tmp.worker
(PRIMARY KEY (id))
ENGINE = MEMORY
SELECT w.id, w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL +1 YEAR)), '-12-25'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
SELECT w.id, w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)), '-12-25') as started, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR)), '-12-25') as ended, CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
FROM `vn`.`worker` `w`;
INSERT INTO `vn`.`business`(`id`, `workerFk`, `companyCodeFk`, `started`, `ended`, `workerBusiness`, `reasonEndFk`, `notes`, `departmentFk`, `workerBusinessProfessionalCategoryFk`, `calendarTypeFk`, `isHourlyLabor`, `workerBusinessAgreementFk`, `workcenterFk`)
@ -1920,7 +1946,7 @@ DROP TEMPORARY TABLE IF EXISTS tmp.worker;
CREATE TEMPORARY TABLE tmp.worker
(PRIMARY KEY (id))
ENGINE = MEMORY
SELECT '1111' as 'id', w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-24'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
SELECT '1111' as 'id', w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -2 YEAR)), '-12-25') as started, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)) as ended, '-12-24'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
FROM `vn`.`worker` `w`
WHERE `w`.`id` = 1109;
@ -1954,10 +1980,6 @@ INSERT INTO `vn`.`workerBusinessType` (`id`, `name`, `isFullTime`, `isPermanent`
(100, 'INDEFINIDO A TIEMPO COMPLETO', 1, 1, 1),
(109, 'CONVERSION DE TEMPORAL EN INDEFINIDO T.COMPLETO', 1, 1, 1);
INSERT INTO `vn`.`businessCategory` (`id`, `description`, `rate`)
VALUES
(1, 'basic employee', 1);
UPDATE `vn`.`business` b
SET `rate` = 7,
`workerBusinessCategoryFk` = 1,
@ -2705,7 +2727,10 @@ INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`)
VALUES
(1, 1109),
(1, 1110),
(3, 1109);
(3, 1109),
(1,9),
(1,3);
INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`)
VALUES

View File

@ -3,18 +3,17 @@ USE `util`;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`mockedDate`;
CREATE FUNCTION `util`.`mockedDate`()
CREATE FUNCTION `util`.`mockedDate`()
RETURNS DATETIME
DETERMINISTIC
BEGIN
RETURN NOW();
-- '2022-01-19 08:00:00'
RETURN CONVERT_TZ('2001-01-01 11:00:00', 'utc', 'Europe/Madrid');
END ;;
DELIMITER ;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`VN_CURDATE`;
CREATE FUNCTION `util`.`VN_CURDATE`()
CREATE FUNCTION `util`.`VN_CURDATE`()
RETURNS DATE
DETERMINISTIC
BEGIN
@ -24,7 +23,7 @@ DELIMITER ;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`VN_CURTIME`;
CREATE FUNCTION `util`.`VN_CURTIME`()
CREATE FUNCTION `util`.`VN_CURTIME`()
RETURNS TIME
DETERMINISTIC
BEGIN
@ -34,10 +33,10 @@ DELIMITER ;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`VN_NOW`;
CREATE FUNCTION `util`.`VN_NOW`()
CREATE FUNCTION `util`.`VN_NOW`()
RETURNS DATETIME
DETERMINISTIC
BEGIN
RETURN mockedDate();
RETURN mockedDate();
END ;;
DELIMITER ;
DELIMITER ;

View File

@ -80203,4 +80203,3 @@ USE `vncontrol`;
-- Dump completed on 2022-11-21 7:57:28

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('buyUltimate()', () => {
const today = new Date();
const today = Date.vnNew();
it(`should create buyUltimate temporal table and update it's values`, async() => {
let stmts = [];
let stmt;

View File

@ -5,7 +5,7 @@ describe('buyUltimateFromInterval()', () => {
let today;
let future;
beforeAll(() => {
let now = new Date();
let now = Date.vnNew();
now.setHours(0, 0, 0, 0);
today = now;

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('ticket ticketCalculateClon()', () => {
const today = new Date();
const today = Date.vnNew();
it('should add the ticket to the order containing the original ticket', async() => {
let stmts = [];
let stmt;

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('ticket ticketCreateWithUser()', () => {
const today = new Date();
const today = Date.vnNew();
it('should confirm the procedure creates the expected ticket', async() => {
let stmts = [];
let stmt;

View File

@ -3,9 +3,9 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('timeBusiness_calculateByUser()', () => {
it('should return the expected hours for today', async() => {
let start = new Date();
let start = Date.vnNew();
start.setHours(0, 0, 0, 0);
let end = new Date();
let end = Date.vnNew();
end.setHours(0, 0, 0, 0);
let stmts = [];

View File

@ -3,11 +3,11 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('timeControl_calculateByUser()', () => {
it(`should return today's worked hours`, async() => {
let start = new Date();
let start = Date.vnNew();
start.setHours(0, 0, 0, 0);
start.setDate(start.getDate() - 1);
let end = new Date();
let end = Date.vnNew();
end.setHours(0, 0, 0, 0);
end.setDate(end.getDate() + 1);
@ -17,7 +17,7 @@ describe('timeControl_calculateByUser()', () => {
stmts.push('START TRANSACTION');
stmts.push(`
DROP TEMPORARY TABLE IF EXISTS
DROP TEMPORARY TABLE IF EXISTS
tmp.timeControlCalculate,
tmp.timeBusinessCalculate
`);
@ -48,14 +48,14 @@ describe('timeControl_calculateByUser()', () => {
});
it(`should return the worked hours between last sunday and monday`, async() => {
let lastSunday = new Date();
let lastSunday = Date.vnNew();
let daysSinceSunday = lastSunday.getDay();
if (daysSinceSunday === 0) // this means today is sunday but you need the previous sunday :)
daysSinceSunday = 7;
lastSunday.setHours(23, 0, 0, 0);
lastSunday.setDate(lastSunday.getDate() - daysSinceSunday);
let monday = new Date();
let monday = Date.vnNew();
let daysSinceMonday = daysSinceSunday - 1; // aiming for monday (today could be monday)
monday.setHours(7, 0, 0, 0);
monday.setDate(monday.getDate() - daysSinceMonday);
@ -66,7 +66,7 @@ describe('timeControl_calculateByUser()', () => {
stmts.push('START TRANSACTION');
stmts.push(`
DROP TEMPORARY TABLE IF EXISTS
DROP TEMPORARY TABLE IF EXISTS
tmp.timeControlCalculate,
tmp.timeBusinessCalculate
`);

View File

@ -6,7 +6,7 @@ describe('zone zone_getLanded()', () => {
let stmts = [];
let stmt;
stmts.push('START TRANSACTION');
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
let params = {
@ -40,7 +40,7 @@ describe('zone zone_getLanded()', () => {
it(`should return data for a shipped tomorrow`, async() => {
let stmts = [];
let stmt;
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
stmts.push('START TRANSACTION');

View File

@ -436,7 +436,7 @@ let actions = {
},
pickDate: async function(selector, date) {
date = date || new Date();
date = date || Date.vnNew();
const timeZoneOffset = date.getTimezoneOffset() * 60000;
const localDate = (new Date(date.getTime() - timeZoneOffset))

View File

@ -31,7 +31,7 @@ export default {
},
recoverPassword: {
recoverPasswordButton: 'vn-login a[ui-sref="recover-password"]',
email: 'vn-recover-password vn-textfield[ng-model="$ctrl.email"]',
email: 'vn-recover-password vn-textfield[ng-model="$ctrl.user"]',
sendEmailButton: 'vn-recover-password vn-submit',
},
accountIndex: {
@ -562,15 +562,15 @@ export default {
payoutBank: '.vn-dialog vn-autocomplete[ng-model="$ctrl.bankFk"]',
payoutDescription: 'vn-textfield[ng-model="$ctrl.receipt.description"]',
submitPayout: '.vn-dialog button[response="accept"]',
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
searchWeeklyResult: 'vn-ticket-weekly-index vn-card smart-table slot-table table tbody tr',
searchResultDate: 'vn-ticket-summary [label=Landed] span',
topbarSearch: 'vn-searchbar',
moreMenu: 'vn-ticket-index vn-icon-button[icon=more_vert]',
fourthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tbody vn-tr:nth-child(4)',
fiveWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tbody vn-tr:nth-child(5)',
weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr',
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-tr:nth-child(1) [ng-model="weekly.agencyModeFk"]',
fourthWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(4)',
fiveWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(5)',
weeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table table tbody tr',
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(1) vn-icon-button[icon="delete"]',
firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(1) [ng-model="weekly.agencyModeFk"]',
acceptDeleteTurn: '.vn-confirm.shown button[response="accept"]'
},
createTicketView: {
@ -778,8 +778,8 @@ export default {
ipt: 'vn-autocomplete[label="Destination IPT"]',
tableIpt: 'vn-autocomplete[name="ipt"]',
tableFutureIpt: 'vn-autocomplete[name="futureIpt"]',
futureState: 'vn-autocomplete[label="Origin Grouped State"]',
state: 'vn-autocomplete[label="Destination Grouped State"]',
futureState: 'vn-check[label="Pending Origin"]',
state: 'vn-check[label="Pending Destination"]',
warehouseFk: 'vn-autocomplete[label="Warehouse"]',
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
moveButton: 'vn-button[vn-tooltip="Advance tickets"]',
@ -944,9 +944,9 @@ export default {
routeSummary: {
header: 'vn-route-summary > vn-card > h5',
cost: 'vn-route-summary vn-label-value[label="Cost"]',
firstTicketID: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2) > span',
firstTicketID: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(10) > span',
firstTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor',
firstAlias: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(3) > span',
firstAlias: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(5) > span',
firstClientDescriptor: '.vn-popover.shown vn-client-descriptor',
goToRouteSummaryButton: 'vn-route-summary > vn-card > h5 > a',

View File

@ -1,6 +1,7 @@
require('@babel/register')({presets: ['@babel/env']});
require('core-js/stable');
require('regenerator-runtime/runtime');
require('vn-loopback/server/boot/date')();
const axios = require('axios');
const Docker = require('../../db/docker.js');

View File

@ -26,7 +26,7 @@ describe('RecoverPassword path', async() => {
expect(message.text).toContain('Notification sent!');
});
it('should send email', async() => {
it('should send email using email', async() => {
await page.waitForState('login');
await page.waitToClick(selectors.recoverPassword.recoverPasswordButton);
@ -37,4 +37,16 @@ describe('RecoverPassword path', async() => {
expect(message.text).toContain('Notification sent!');
});
it('should send email using username', async() => {
await page.waitForState('login');
await page.waitToClick(selectors.recoverPassword.recoverPasswordButton);
await page.write(selectors.recoverPassword.email, 'BruceWayne');
await page.waitToClick(selectors.recoverPassword.sendEmailButton);
const message = await page.waitForSnackbar();
await page.waitForState('login');
expect(message.text).toContain('Notification sent!');
});
});

View File

@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Client credit insurance path', () => {
let browser;
let page;
let previousMonth = new Date();
let previousMonth = Date.vnNew();
previousMonth.setMonth(previousMonth.getMonth() - 1);
beforeAll(async() => {

View File

@ -22,7 +22,8 @@ describe('Worker time control path', () => {
const hankPymId = 1107;
it('should go to the next month, go to current month and go 1 month in the past', async() => {
let date = new Date();
let date = Date.vnNew();
date.setDate(1);
date.setMonth(date.getMonth() + 1);
let month = date.toLocaleString('default', {month: 'long'});
@ -31,7 +32,8 @@ describe('Worker time control path', () => {
expect(result).toContain(month);
date = new Date();
date = Date.vnNew();
date.setDate(1);
month = date.toLocaleString('default', {month: 'long'});
await page.click(selectors.workerTimeControl.previousMonthButton);
@ -39,7 +41,8 @@ describe('Worker time control path', () => {
expect(result).toContain(month);
date = new Date();
date = Date.vnNew();
date.setDate(1);
date.setMonth(date.getMonth() - 1);
const timestamp = Math.round(date.getTime() / 1000);
month = date.toLocaleString('default', {month: 'long'});

View File

@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Worker calendar path', () => {
const reasonableTimeBetweenClicks = 300;
const date = new Date();
const date = Date.vnNew();
const lastYear = (date.getFullYear() - 1).toString();
let browser;

View File

@ -22,7 +22,7 @@ describe('Item fixed prices path', () => {
});
it('should fill the fixed price data', async() => {
const now = new Date();
const now = Date.vnNew();
await page.autocompleteSearch(selectors.itemFixedPrice.fourthWarehouse, 'Warehouse one');
await page.writeOnEditableTD(selectors.itemFixedPrice.fourthGroupingPrice, '1');
await page.writeOnEditableTD(selectors.itemFixedPrice.fourthPackingPrice, '1');

View File

@ -93,7 +93,7 @@ describe('Ticket Edit basic data path', () => {
it(`should split ticket without negatives`, async() => {
const newAgency = 'Gotham247';
const newDate = new Date();
const newDate = Date.vnNew();
newDate.setDate(newDate.getDate() - 1);
await page.accessToSearchResult('14');
@ -127,7 +127,7 @@ describe('Ticket Edit basic data path', () => {
});
it(`should old ticket have old date and agency`, async() => {
const oldDate = new Date();
const oldDate = Date.vnNew();
const oldAgency = 'Super-Man delivery';
await page.accessToSearchResult('14');

View File

@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Ticket create path', () => {
let browser;
let page;
let nextMonth = new Date();
let nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
beforeAll(async() => {

View File

@ -50,7 +50,7 @@ describe('Ticket Advance path', () => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal');
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.ipt);
@ -62,7 +62,7 @@ describe('Ticket Advance path', () => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'Horizontal');
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.futureIpt);
@ -70,26 +70,36 @@ describe('Ticket Advance path', () => {
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search with the origin grouped state', async() => {
it('should search with the origin pending state', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.futureState, 'Free');
await page.waitToClick(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search with the destination grouped state', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.state, 'Free');
await page.waitToClick(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
@ -116,42 +126,7 @@ describe('Ticket Advance path', () => {
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search in smart-table with stock', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.write(selectors.ticketAdvance.tableStock, '5');
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 2);
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search in smart-table with especified Lines', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.write(selectors.ticketAdvance.tableLines, '0');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search in smart-table with especified Liters', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.write(selectors.ticketAdvance.tableLiters, '0');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should check the three last tickets and move to the future', async() => {
it('should check the one ticket and move to the present', async() => {
await page.waitToClick(selectors.ticketAdvance.multiCheck);
await page.waitToClick(selectors.ticketAdvance.moveButton);
await page.waitToClick(selectors.ticketAdvance.acceptButton);

View File

@ -18,7 +18,7 @@ describe('Route basic Data path', () => {
});
it('should edit the route basic data', async() => {
const nextMonth = new Date();
const nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
await page.autocompleteSearch(selectors.routeBasicData.worker, 'adminBossNick');

View File

@ -19,7 +19,7 @@ describe('InvoiceIn basic data path', () => {
});
it(`should edit the invoiceIn basic data`, async() => {
const now = new Date();
const now = Date.vnNew();
await page.pickDate(selectors.invoiceInBasicData.issued, now);
await page.pickDate(selectors.invoiceInBasicData.operated, now);
await page.autocompleteSearch(selectors.invoiceInBasicData.supplier, 'Verdnatura');

View File

@ -100,7 +100,7 @@ describe('InvoiceOut descriptor path', () => {
});
it(`should check the invoiceOut booked in the summary data`, async() => {
let today = new Date();
let today = Date.vnNew();
let day = today.getDate();
if (day < 10) day = `0${day}`;

View File

@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Travel create path', () => {
let browser;
let page;
const date = new Date();
const date = Date.vnNew();
const day = 15;
date.setDate(day);

View File

@ -22,7 +22,7 @@ describe('Travel basic data path', () => {
});
it('should set a wrong delivery date then receive an error on submit', async() => {
const lastMonth = new Date();
const lastMonth = Date.vnNew();
lastMonth.setMonth(lastMonth.getMonth() - 1);
await page.pickDate(selectors.travelBasicData.deliveryDate, lastMonth);

View File

@ -123,7 +123,7 @@ describe('Travel descriptor path', () => {
});
it('should update the landed date to a future date to enable cloneWithEntries', async() => {
const nextMonth = new Date();
const nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
await page.pickDate(selectors.travelBasicData.deliveryDate, nextMonth);
await page.waitToClick(selectors.travelBasicData.save);

View File

@ -87,6 +87,7 @@ ngModule.vnComponent('vnButtonMenu', {
selectFields: '<?',
initialData: '<?',
showFilter: '<?',
fields: '<?',
field: '=?',
url: '@?',
data: '<?',

View File

@ -15,7 +15,7 @@ export default class Calendar extends FormInput {
constructor($element, $scope, vnWeekDays, moment) {
super($element, $scope);
this.weekDays = vnWeekDays.locales;
this.defaultDate = new Date();
this.defaultDate = Date.vnNew();
this.displayControls = true;
this.moment = moment;
}
@ -115,8 +115,8 @@ export default class Calendar extends FormInput {
let wday = date.getDay();
let month = date.getMonth();
const currentDay = new Date().getDate();
const currentMonth = new Date().getMonth();
const currentDay = Date.vnNew().getDate();
const currentMonth = Date.vnNew().getMonth();
let classes = {
today: day === currentDay && month === currentMonth,

View File

@ -2,7 +2,7 @@ describe('Component vnCalendar', () => {
let controller;
let $element;
let date = new Date();
let date = Date.vnNew();
date.setHours(0, 0, 0, 0);
date.setDate(1);
@ -48,7 +48,7 @@ describe('Component vnCalendar', () => {
it(`should return the selected element, then emit a 'selection' event`, () => {
jest.spyOn(controller, 'emit');
const day = new Date();
const day = Date.vnNew();
day.setHours(0, 0, 0, 0);
const clickEvent = new Event('click');

View File

@ -147,28 +147,17 @@ export default class CrudModel extends ModelProxy {
this.moreRows = null;
}
/**
* Saves current changes on the server.
*
* @return {Promise} The save request promise
*/
save() {
if (!this.isChanged)
return this.$q.resolve();
getChanges() {
if (!this.isChanged) return null;
let deletes = [];
let updates = [];
let creates = [];
let orgDeletes = [];
let orgUpdates = [];
let orgCreates = [];
const deletes = [];
const updates = [];
const creates = [];
let pk = this.primaryKey;
const pk = this.primaryKey;
for (let row of this.removed) {
for (let row of this.removed)
deletes.push(row.$orgRow[pk]);
orgDeletes.push(row);
}
for (let row of this.data) {
if (row.$isNew) {
@ -178,7 +167,6 @@ export default class CrudModel extends ModelProxy {
data[prop] = row[prop];
}
creates.push(row);
orgCreates.push(row);
} else if (row.$oldData) {
let data = {};
for (let prop in row.$oldData)
@ -187,28 +175,38 @@ export default class CrudModel extends ModelProxy {
data,
where: {[pk]: row.$orgRow[pk]}
});
orgUpdates.push(row);
}
}
let changes = {deletes, updates, creates};
const changes = {deletes, updates, creates};
for (let prop in changes) {
if (changes[prop].length === 0)
changes[prop] = undefined;
}
if (!changes)
return this.$q.resolve();
return changes;
}
/**
* Saves current changes on the server.
*
* @return {Promise} The save request promise
*/
save() {
const pk = this.primaryKey;
const changes = this.getChanges();
if (!changes) return this.$q.resolve();
const creates = changes.creates || [];
let url = this.saveUrl ? this.saveUrl : `${this._url}/crud`;
return this.$http.post(url, changes)
.then(res => {
const created = res.data;
// Apply new data to created instances
for (let i = 0; i < orgCreates.length; i++) {
const row = orgCreates[i];
for (let i = 0; i < creates.length; i++) {
const row = creates[i];
row[pk] = created[i][pk];
for (let prop in row) {

View File

@ -4,7 +4,7 @@ describe('Component vnDatePicker', () => {
let $ctrl;
let today;
today = new Date();
today = Date.vnNew();
today.setHours(0, 0, 0, 0);
beforeEach(ngModule('vnCore'));

View File

@ -31,7 +31,7 @@ export default class InputTime extends Field {
date = this.modelDate
? new Date(this.modelDate)
: new Date();
: Date.vnNew();
date.setHours(split[0], split[1], 0, 0);
}

View File

@ -20,7 +20,7 @@ describe('Component vnInputTime', () => {
describe('field() setter', () => {
it(`should display the formated the date`, () => {
let date = new Date();
let date = Date.vnNew();
$ctrl.field = date;
let displayed = $filter('date')(date, 'HH:mm');

View File

@ -478,8 +478,8 @@ export default class SmartTable extends Component {
const params = {q: JSON.stringify(stateFilter)};
this.$state.go(this.$state.current.name, params, {location: 'replace'});
this.refresh();
this.$state.go(this.$state.current.name, params, {location: 'replace'})
.then(() => this.refresh());
}
applySort() {
@ -499,8 +499,8 @@ export default class SmartTable extends Component {
stateFilter.tableOrder = order;
const params = {q: JSON.stringify(stateFilter)};
this.$state.go(this.$state.current.name, params, {location: 'replace'});
this.refresh();
this.$state.go(this.$state.current.name, params, {location: 'replace'})
.then(() => this.refresh());
}
filterSanitizer(field) {
@ -589,7 +589,7 @@ export default class SmartTable extends Component {
refresh() {
this.isRefreshing = true;
this.model.refresh()
.then(() => this.isRefreshing = false);
.finally(() => this.isRefreshing = false);
}
}

View File

@ -160,7 +160,7 @@ describe('Component smartTable', () => {
describe('applySort()', () => {
it('should call the $state go and model refresh without making changes on the model order', () => {
controller.$state = {
go: jest.fn(),
go: jest.fn().mockReturnValue(new Promise(resolve => resolve())),
current: {
name: 'section'
}
@ -171,13 +171,12 @@ describe('Component smartTable', () => {
expect(controller.model.order).toBeUndefined();
expect(controller.$state.go).toHaveBeenCalled();
expect(controller.refresh).toHaveBeenCalled();
});
it('should call the $state go and model refresh after setting model order according to the controller sortCriteria', () => {
const orderBy = {field: 'myField', sortType: 'ASC'};
controller.$state = {
go: jest.fn(),
go: jest.fn().mockReturnValue(new Promise(resolve => resolve())),
current: {
name: 'section'
}
@ -190,7 +189,6 @@ describe('Component smartTable', () => {
expect(controller.model.order).toEqual(`${orderBy.field} ${orderBy.sortType}`);
expect(controller.$state.go).toHaveBeenCalled();
expect(controller.refresh).toHaveBeenCalled();
});
});
@ -293,12 +291,10 @@ describe('Component smartTable', () => {
controller.$inputsScope = {
searchProps: {}
};
jest.spyOn(controller, 'refresh');
controller.defaultFilter();
expect(controller.model.addFilter).toHaveBeenCalled();
expect(controller.refresh).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,2 @@
import * as date from 'vn-loopback/server/boot/date';
date.default();

View File

@ -10,3 +10,4 @@ import './week-days';
import './report';
import './email';
import './file';
import './date';

View File

@ -42,6 +42,7 @@ vn-log {
& > td.after,
& > th.after {
width: 40%;
white-space: pre-line;
}
}
}

View File

@ -1,7 +1,7 @@
<h5 class="vn-mb-md vn-mt-lg" translate>Recover password</h5>
<vn-textfield
label="Email"
ng-model="$ctrl.email"
label="User or recovery email"
ng-model="$ctrl.user"
vn-focus>
</vn-textfield>
<div

View File

@ -20,7 +20,7 @@ export default class Controller {
submit() {
const params = {
email: this.email
user: this.user
};
this.$http.post('Accounts/recoverPassword', params)

View File

@ -1,3 +1,4 @@
Recover password: Recuperar contraseña
We will sent you an email to recover your password: Te enviaremos un correo para restablecer tu contraseña
Notification sent!: ¡Notificación enviada!
User or recovery email: Usuario o correo de recuperación

View File

@ -197,7 +197,7 @@ export default class UploadPhoto extends Component {
timeout: this.canceler.promise,
transformRequest: ([file]) => {
const formData = new FormData();
const now = new Date();
const now = Date.vnNew();
const timestamp = now.getTime();
const fileName = `${file.name}_${timestamp}`;

View File

@ -1,5 +1,5 @@
import './module';
import './routes';
import './components';
import './services';
import './styles';
import 'vn-loopback/server/boot/date';

View File

@ -14,6 +14,10 @@ import './modules/ticket/front/module.js';
import './modules/travel/front/module.js';
import './modules/worker/front/module.js';
import './modules/shelving/front/module.js';
import 'vn-loopback/server/boot/date';
// Set NODE_ENV
process.env.NODE_ENV = 'development';
core.run(vnInterceptor => {
vnInterceptor.setApiPath(null);
@ -39,3 +43,4 @@ window.ngModule = function(moduleName, ...args) {
return angular.mock.module(...fns);
};

View File

@ -1,5 +1,6 @@
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
/* eslint max-len: ["error", { "code": 150 }]*/
module.exports = {
name: 'front end',

View File

@ -67,7 +67,7 @@
"Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})",
"Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Change quantity": "{{concept}} change of {{oldQuantity}} to {{newQuantity}}",
"Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
"Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
"Claim state has changed to incomplete": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*",
"Claim state has changed to canceled": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*",
"Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member",
@ -137,15 +137,18 @@
"Password does not meet requirements": "Password does not meet requirements",
"You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies",
"Not enough privileges to edit a client": "Not enough privileges to edit a client",
"Claim pickup order sent": "Claim pickup order sent [{{claimId}}]({{{claimUrl}}}) to client *{{clientName}}*",
"Claim pickup order sent": "Claim pickup order sent [{{claimId}}]({{{claimUrl}}}) to client *{{clientName}}*",
"You don't have grant privilege": "You don't have grant privilege",
"You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user",
"Email verify": "Email verify",
"Email verify": "Email verify",
"Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) merged with [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})",
"Sale(s) blocked, please contact production": "Sale(s) blocked, please contact production",
"App locked": "App locked by user {{userId}}",
"The sales of the receiver ticket can't be modified": "The sales of the receiver ticket can't be modified",
"Receipt's bank was not found": "Receipt's bank was not found",
"This receipt was not compensated": "This receipt was not compensated",
"Client's email was not found": "Client's email was not found"
"The sales of the receiver ticket can't be modified": "The sales of the receiver ticket can't be modified",
"Receipt's bank was not found": "Receipt's bank was not found",
"This receipt was not compensated": "This receipt was not compensated",
"Client's email was not found": "Client's email was not found",
"It is not possible to modify tracked sales": "It is not possible to modify tracked sales",
"It is not possible to modify sales that their articles are from Floramondo": "It is not possible to modify sales that their articles are from Floramondo",
"It is not possible to modify cloned sales": "It is not possible to modify cloned sales",
"Valid priorities: 1,2,3": "Valid priorities: 1,2,3"
}

View File

@ -84,7 +84,6 @@
"The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"Sale(s) blocked, contact production": "Linea(s) bloqueada(s), contacte con produccion",
"Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
@ -259,5 +258,11 @@
"Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload file": "Error al subir archivo",
"The DOCUWARE PDF document does not exists": "The DOCUWARE PDF document does not exists"
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
"There is no assigned email for this client": "No hay correo asignado para este cliente"
}

View File

@ -0,0 +1,17 @@
module.exports = () => {
Date.vnUTC = () => {
const env = process.env.NODE_ENV;
if (!env || env === 'development')
return new Date(Date.UTC(2001, 0, 1, 11));
return new Date(Date.UTC());
};
Date.vnNew = () => {
return new Date(Date.vnUTC());
};
Date.vnNow = () => {
return new Date(Date.vnUTC()).getTime();
};
};

View File

@ -95,7 +95,7 @@ module.exports = Self => {
}, myOptions);
const claim = await models.Claim.findOne(filter, myOptions);
const today = new Date();
const today = Date.vnNew();
const newRefundTicket = await models.Ticket.create({
clientFk: claim.ticket().clientFk,
@ -172,7 +172,7 @@ module.exports = Self => {
async function saveObservation(observation, options) {
const query = `INSERT INTO vn.ticketObservation (ticketFk, observationTypeFk, description) VALUES(?, ?, ?)
ON DUPLICATE KEY
ON DUPLICATE KEY
UPDATE description = CONCAT(vn.ticketObservation.description, VALUES(description),' ')`;
await Self.rawSql(query, [
observation.ticketFk,

View File

@ -60,7 +60,7 @@ module.exports = Self => {
const landedPlusWeek = new Date(ticket.landed);
landedPlusWeek.setDate(landedPlusWeek.getDate() + 7);
const hasClaimManagerRole = await models.Account.hasRole(userId, 'claimManager', myOptions);
const isClaimable = landedPlusWeek >= new Date();
const isClaimable = landedPlusWeek >= Date.vnNew();
if (ticket.isDeleted)
throw new UserError(`You can't create a claim for a removed ticket`);

View File

@ -1,6 +1,6 @@
module.exports = Self => {
Self.remoteMethodCtx('regularizeClaim', {
description: `Imports lines from claimBeginning to a new ticket
description: `Imports lines from claimBeginning to a new ticket
with specific shipped, landed dates, agency and company`,
accessType: 'WRITE',
accepts: [{
@ -135,10 +135,10 @@ module.exports = Self => {
}
async function getTicketId(params, options) {
const minDate = new Date();
const minDate = Date.vnNew();
minDate.setHours(0, 0, 0, 0);
const maxDate = new Date();
const maxDate = Date.vnNew();
maxDate.setHours(23, 59, 59, 59);
let ticket = await Self.app.models.Ticket.findOne({
@ -155,8 +155,8 @@ module.exports = Self => {
}
async function createTicket(ctx, options) {
ctx.args.shipped = new Date();
ctx.args.landed = new Date();
ctx.args.shipped = Date.vnNew();
ctx.args.landed = Date.vnNew();
ctx.args.agencyModeId = null;
ctx.args.routeId = null;

View File

@ -54,7 +54,7 @@ describe('Claim createFromSales()', () => {
try {
const options = {transaction: tx};
const todayMinusEightDays = new Date();
const todayMinusEightDays = Date.vnNew();
todayMinusEightDays.setDate(todayMinusEightDays.getDate() - 8);
const ticket = await models.Ticket.findById(ticketId, null, options);
@ -85,7 +85,7 @@ describe('Claim createFromSales()', () => {
try {
const options = {transaction: tx};
const todayMinusEightDays = new Date();
const todayMinusEightDays = Date.vnNew();
todayMinusEightDays.setDate(todayMinusEightDays.getDate() - 8);
const ticket = await models.Ticket.findById(ticketId, null, options);

View File

@ -1,7 +1,7 @@
const app = require('vn-loopback/server/server');
describe('Update Claim', () => {
const newDate = new Date();
const newDate = Date.vnNew();
const originalData = {
ticketFk: 3,
clientFk: 1101,

View File

@ -1,7 +1,7 @@
const app = require('vn-loopback/server/server');
describe('Update Claim', () => {
const newDate = new Date();
const newDate = Date.vnNew();
const original = {
ticketFk: 3,
clientFk: 1101,

View File

@ -19,10 +19,26 @@ class Controller extends ModuleCard {
}, {
relation: 'ticket',
scope: {
fields: ['zoneFk'],
include: {
relation: 'zone'
}
fields: ['zoneFk', 'addressFk'],
include: [
{
relation: 'zone',
scope: {
fields: ['name']
}
},
{
relation: 'address',
scope: {
fields: ['provinceFk'],
include: {
relation: 'province',
scope: {
fields: ['name']
}
}
}
}]
}
}, {
relation: 'claimState',

View File

@ -55,9 +55,13 @@
<span
ng-click="zoneDescriptor.show($event, $ctrl.claim.ticket.zoneFk)"
class="link">
{{$ctrl.claim.ticket.zoneFk}}
{{$ctrl.claim.ticket.zone.name}}
</span>
</vn-label-value>
<vn-label-value
label="Province"
value="{{$ctrl.claim.ticket.address.province.name}}">
</vn-label-value>
<vn-label-value
label="Ticket">
<span

View File

@ -32,7 +32,7 @@ module.exports = Self => {
c.id AS clientFk,
c.email AS clientEmail,
eu.email salesPersonEmail
FROM client c
FROM client c
JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
JOIN ticket t ON t.clientFk = c.id
JOIN sale s ON s.ticketFk = t.id
@ -61,10 +61,10 @@ module.exports = Self => {
SET status = 'printed',
printed = ?
WHERE id = ?`,
[new Date(), queue.id]);
[Date.vnNew(), queue.id]);
} catch (error) {
await Self.rawSql(`
UPDATE clientConsumptionQueue
UPDATE clientConsumptionQueue
SET status = ?
WHERE id = ?`,
[error.message, queue.id]);

View File

@ -51,7 +51,7 @@ module.exports = function(Self) {
Self.createReceipt = async(ctx, options) => {
const models = Self.app.models;
const args = ctx.args;
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
let tx;

View File

@ -74,7 +74,7 @@ module.exports = function(Self) {
]
}, myOptions);
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
const data = await Self.rawSql(query, [id, date], myOptions);

View File

@ -25,7 +25,7 @@ module.exports = Self => {
if (typeof options == 'object')
Object.assign(myOptions, options);
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
const [debt] = await Self.rawSql(query, [clientFk, date], myOptions);

View File

@ -32,14 +32,14 @@ module.exports = Self => {
if (typeof options == 'object')
Object.assign(myOptions, options);
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const ticket = await Self.app.models.Ticket.findById(ticketId, null, myOptions);
const query = `
SELECT
SELECT
t.id,
t.shipped,
a.name AS agencyName,
a.name AS agencyName,
w.name AS warehouseName,
ad.nickname AS nickname,
ad.city AS city,
@ -52,7 +52,7 @@ module.exports = Self => {
JOIN vn.warehouse w ON t.warehouseFk = w.id
JOIN vn.address ad ON t.addressFk = ad.id
JOIN vn.province pr ON ad.provinceFk = pr.id
WHERE t.shipped >= ? AND t.clientFk = ? AND ts.alertLevel = 0
WHERE t.shipped >= ? AND t.clientFk = ? AND ts.alertLevel = 0
AND t.id <> ? AND t.warehouseFk = ?
ORDER BY t.shipped
LIMIT 10`;

View File

@ -125,7 +125,7 @@ module.exports = Self => {
async function getRecoveries(recoveryModel, clientId, options) {
const filter = {
where: {
and: [{clientFk: clientId}, {or: [{finished: null}, {finished: {gt: Date.now()}}]}]
and: [{clientFk: clientId}, {or: [{finished: null}, {finished: {gt: Date.vnNow()}}]}]
},
limit: 1
};

View File

@ -23,7 +23,7 @@ describe('Client createWithInsurance', () => {
try {
const options = {transaction: tx};
const data = {clientFk: 1101, started: Date.now(), credit: 999, grade: 255};
const data = {clientFk: 1101, started: Date.vnNow(), credit: 999, grade: 255};
const result = await models.CreditClassification.createWithInsurance(data, options);

View File

@ -51,7 +51,7 @@ module.exports = Self => {
const stmts = [];
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const stmt = new ParameterizedSQL(
`SELECT *
@ -65,14 +65,14 @@ module.exports = Self => {
co.created,
co.text observation,
uw.id workerFk,
uw.name workerName,
uw.name workerName,
c.creditInsurance,
d.defaulterSinced
FROM vn.defaulter d
JOIN vn.client c ON c.id = d.clientFk
LEFT JOIN vn.clientObservation co ON co.clientFk = c.id
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN account.user uw ON uw.id = co.workerFk
LEFT JOIN account.user uw ON uw.id = co.workerFk
WHERE
d.created = ?
AND d.amount > 0

View File

@ -27,7 +27,7 @@ module.exports = Self => {
if (typeof options == 'object')
Object.assign(myOptions, options);
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const query = `
SELECT count(*) AS hasActiveRecovery

View File

@ -41,7 +41,7 @@ module.exports = Self => {
// Disable old mandate
if (oldMandate)
oldMandate.updateAttribute('finished', new Date());
oldMandate.updateAttribute('finished', Date.vnNew());
// Create a new mandate
await models.Mandate.create({

View File

@ -141,6 +141,16 @@ module.exports = Self => {
done();
}
Self.validateAsync('isToBeMailed', isToBeMailed, {
message: 'There is no assigned email for this client'
});
function isToBeMailed(err, done) {
if (this.isToBeMailed == true && !this.email)
err();
done();
}
Self.validateAsync('defaultAddressFk', isActive,
{message: 'Unable to default a disabled consignee'}
);

View File

@ -59,16 +59,14 @@ class Controller extends Dialog {
if (value) {
const accountingType = value.accountingType;
if (this.originalDescription) {
this.receipt.description =
`${accountingType && accountingType.receiptDescription}, ${this.originalDescription}`;
} else {
this.receipt.description =
`${accountingType && accountingType.receiptDescription}`;
}
if (accountingType.receiptDescription != null) {
this.receipt.description = accountingType.receiptDescription;
if (this.originalDescription) this.receipt.description += `, ${this.originalDescription}`;
} else if (this.originalDescription)
this.receipt.description = this.originalDescription;
this.maxAmount = accountingType && accountingType.maxAmount;
this.receipt.payed = new Date();
this.receipt.payed = Date.vnNew();
if (accountingType.daysInFuture)
this.receipt.payed.setDate(this.receipt.payed.getDate() + accountingType.daysInFuture);
}

View File

@ -17,11 +17,11 @@ class Controller extends Section {
}
setDefaultFilter() {
const minDate = new Date();
const minDate = Date.vnNew();
minDate.setHours(0, 0, 0, 0);
minDate.setMonth(minDate.getMonth() - 2);
const maxDate = new Date();
const maxDate = Date.vnNew();
maxDate.setHours(23, 59, 59, 59);
this.filterParams = {

Some files were not shown because too many files have changed in this diff Show More