#6274 workerTimeControl #1858
|
@ -5,6 +5,12 @@ 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).
|
||||
|
||||
## [2352.01] - 2023-12-28
|
||||
|
||||
### Added
|
||||
### Changed
|
||||
### Fixed
|
||||
|
||||
## [2350.01] - 2023-12-14
|
||||
|
||||
### Added
|
||||
|
|
|
@ -2351,9 +2351,11 @@ INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `weekDays`)
|
|||
(8, 'indefinitely', 'mon,tue,wed,thu,fri,sat,sun'),
|
||||
(10, 'indefinitely', 'mon,tue,wed,thu,fri,sat,sun');
|
||||
|
||||
INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `started`, `ended`)
|
||||
INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `started`, `ended`, `weekDays`)
|
||||
VALUES
|
||||
(9, 'range', DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR));
|
||||
(9, 'range', DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR), 'mon'),
|
||||
(9, 'range', util.VN_CURDATE(), NULL, 'tue'),
|
||||
(9, 'range', NULL, util.VN_CURDATE(), 'wed');
|
||||
|
||||
INSERT INTO `vn`.`workerTimeControl`(`userFk`, `timed`, `manual`, `direction`, `isSendMail`)
|
||||
VALUES
|
||||
|
|
|
@ -3,99 +3,101 @@ const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
|||
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('getExternalCmrs', {
|
||||
description: 'Returns an array of external cmrs',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
},
|
||||
{
|
||||
arg: 'cmrFk',
|
||||
type: 'integer',
|
||||
description: 'Searchs the route by id',
|
||||
},
|
||||
{
|
||||
arg: 'ticketFk',
|
||||
type: 'integer',
|
||||
description: 'The worker id',
|
||||
},
|
||||
{
|
||||
arg: 'routeFk',
|
||||
type: 'integer',
|
||||
description: 'The route id',
|
||||
},
|
||||
{
|
||||
arg: 'country',
|
||||
type: 'string',
|
||||
description: 'The agencyMode id',
|
||||
},
|
||||
{
|
||||
arg: 'clientFk',
|
||||
type: 'integer',
|
||||
description: 'The vehicle id',
|
||||
},
|
||||
{
|
||||
arg: 'hasCmrDms',
|
||||
type: 'boolean',
|
||||
description: 'The vehicle id',
|
||||
},
|
||||
{
|
||||
arg: 'shipped',
|
||||
type: 'date',
|
||||
description: 'The to date filter',
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getExternalCmrs`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
Self.remoteMethod('getExternalCmrs', {
|
||||
description: 'Returns an array of external cmrs',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
},
|
||||
{
|
||||
arg: 'cmrFk',
|
||||
type: 'integer',
|
||||
description: 'Searchs the route by id',
|
||||
},
|
||||
{
|
||||
arg: 'ticketFk',
|
||||
type: 'integer',
|
||||
description: 'The worker id',
|
||||
},
|
||||
{
|
||||
arg: 'routeFk',
|
||||
type: 'integer',
|
||||
description: 'The route id',
|
||||
},
|
||||
{
|
||||
arg: 'country',
|
||||
type: 'string',
|
||||
description: 'The agencyMode id',
|
||||
},
|
||||
{
|
||||
arg: 'clientFk',
|
||||
type: 'integer',
|
||||
description: 'The vehicle id',
|
||||
},
|
||||
{
|
||||
arg: 'hasCmrDms',
|
||||
type: 'boolean',
|
||||
description: 'The vehicle id',
|
||||
},
|
||||
{
|
||||
arg: 'shipped',
|
||||
type: 'date',
|
||||
description: 'The to date filter',
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getExternalCmrs`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getExternalCmrs = async(
|
||||
filter,
|
||||
cmrFk,
|
||||
ticketFk,
|
||||
routeFk,
|
||||
country,
|
||||
clientFk,
|
||||
hasCmrDms,
|
||||
shipped,
|
||||
options
|
||||
) => {
|
||||
const params = {
|
||||
cmrFk,
|
||||
ticketFk,
|
||||
routeFk,
|
||||
country,
|
||||
clientFk,
|
||||
hasCmrDms,
|
||||
shipped,
|
||||
};
|
||||
const conn = Self.dataSource.connector;
|
||||
Self.getExternalCmrs = async(
|
||||
filter,
|
||||
cmrFk,
|
||||
ticketFk,
|
||||
routeFk,
|
||||
country,
|
||||
clientFk,
|
||||
hasCmrDms,
|
||||
shipped,
|
||||
options
|
||||
) => {
|
||||
const params = {
|
||||
cmrFk,
|
||||
ticketFk,
|
||||
routeFk,
|
||||
country,
|
||||
clientFk,
|
||||
hasCmrDms,
|
||||
shipped,
|
||||
};
|
||||
const conn = Self.dataSource.connector;
|
||||
|
||||
let where = buildFilter(params, (param, value) => {return {[param]: value}});
|
||||
filter = mergeFilters(filter, {where});
|
||||
let where = buildFilter(params, (param, value) => {
|
||||
return {[param]: value};
|
||||
});
|
||||
filter = mergeFilters(filter, {where});
|
||||
|
||||
if (!filter.where) {
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
filter.where = {'shipped': yesterday.toISOString().split('T')[0]}
|
||||
}
|
||||
if (!filter.where) {
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
filter.where = {'shipped': yesterday.toISOString().split('T')[0]};
|
||||
}
|
||||
|
||||
const myOptions = {};
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
let stmts = [];
|
||||
const stmt = new ParameterizedSQL(`
|
||||
let stmts = [];
|
||||
const stmt = new ParameterizedSQL(`
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT t.cmrFk,
|
||||
|
@ -129,13 +131,13 @@ module.exports = Self => {
|
|||
AND dm.code = 'DELIVERY'
|
||||
AND t.cmrFk
|
||||
) sub
|
||||
`);
|
||||
`);
|
||||
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
const itemsIndex = stmts.push(stmt) - 1;
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
const itemsIndex = stmts.push(stmt) - 1;
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql);
|
||||
return itemsIndex === 0 ? result : result[itemsIndex];
|
||||
};
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql);
|
||||
return itemsIndex === 0 ? result : result[itemsIndex];
|
||||
};
|
||||
};
|
||||
|
|
|
@ -63,7 +63,7 @@ module.exports = Self => {
|
|||
|
||||
const isAvailable = itemStock.available > 0;
|
||||
|
||||
if (!isAvailable)
|
||||
if (!isAvailable || !ctx.args.quantity)
|
||||
throw new UserError(`This item is not available`);
|
||||
|
||||
if (request.saleFk)
|
||||
|
|
|
@ -141,7 +141,6 @@ describe('ticket filter()', () => {
|
|||
});
|
||||
|
||||
it('should return the tickets that are not pending', async() => {
|
||||
pending('#6010 test intermitente');
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
|
|
|
@ -43,13 +43,9 @@ module.exports = Self => {
|
|||
const isTeamBoss = await models.ACL.checkAccessAcl(ctx, 'Worker', 'isTeamBoss', 'WRITE');
|
||||
const isHimself = userId == workerId;
|
||||
|
||||
if (!isSubordinate || (isSubordinate && isHimself && !isTeamBoss))
|
||||
if (!isSubordinate || (isHimself && !isTeamBoss))
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
const response = await Self.clockIn(workerId, args.timed, args.direction, myOptions);
|
||||
|
||||
await models.WorkerTimeControl.resendWeeklyHourEmail(ctx, workerId, args.timed, myOptions);
|
||||
|
||||
return response;
|
||||
return await Self.clockIn(workerId, args.timed, args.direction, myOptions);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'workerFk',
|
||||
type: 'integer',
|
||||
type: 'number',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -25,8 +25,8 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const query = `CALL vn.workerTimeControl_getClockIn(?, CURDATE())`;
|
||||
const [result] = await Self.rawSql(query, [workerFk], myOptions);
|
||||
const query = `CALL vn.workerTimeControl_getClockIn(?, ?)`;
|
||||
const [result] = await Self.rawSql(query, [workerFk, Date.vnNew()], myOptions);
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('resendWeeklyHourEmail', {
|
||||
description: 'Adds a new hour registry',
|
||||
description: 'Send the records for the week of the date provided',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('workerTimeControl clockIn()', () => {
|
||||
const workerId = 9;
|
||||
const inTime = '2001-01-01T00:00:00.000Z';
|
||||
|
||||
it('should correctly clock in', async() => {
|
||||
const tx = await models.WorkerTimeControl.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
await models.WorkerTimeControl.clockIn(workerId, inTime, 'in', options);
|
||||
const isClockIn = await models.WorkerTimeControl.findOne({
|
||||
where: {
|
||||
userFk: workerId
|
||||
}
|
||||
}, options);
|
||||
|
||||
expect(isClockIn).toBeDefined();
|
||||
expect(isClockIn.direction).toBe('in');
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('workerTimeControl getClockIn()', () => {
|
||||
it('should correctly get the timetable of a worker', async() => {
|
||||
const response = await models.WorkerTimeControl.getClockIn(1106, {});
|
||||
|
||||
expect(response.length).toEqual(4);
|
||||
const [inHrs, middleOutHrs, middleInHrs, outHrs] = response;
|
||||
|
||||
expect(inHrs['0daysAgo']).toEqual('07:00');
|
||||
expect(middleOutHrs['0daysAgo']).toEqual('10:00');
|
||||
expect(middleInHrs['0daysAgo']).toEqual('10:20');
|
||||
expect(outHrs['0daysAgo']).toEqual('14:50');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('workerTimeControl login()', () => {
|
||||
it('should correctly login', async() => {
|
||||
const response = await models.WorkerTimeControl.login(9, {});
|
||||
|
||||
expect(response.name).toBe('developer');
|
||||
});
|
||||
|
||||
it('should throw UserError if pin is not provided', async() => {
|
||||
try {
|
||||
await models.WorkerTimeControl.login();
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(UserError);
|
||||
expect(error.message).toBe('Indique el pin.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -35,44 +35,39 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
query = `
|
||||
SELECT *
|
||||
FROM vn.zoneEvent
|
||||
WHERE zoneFk = ?
|
||||
AND ((type = 'indefinitely')
|
||||
OR (type = 'day' AND dated BETWEEN ? AND ?)
|
||||
OR (type = 'range'
|
||||
AND (
|
||||
(started BETWEEN ? AND ?)
|
||||
OR
|
||||
(ended BETWEEN ? AND ?)
|
||||
OR
|
||||
(started <= ? AND ended >= ?)
|
||||
)
|
||||
)
|
||||
)
|
||||
ORDER BY type='indefinitely' DESC, type='range' DESC, type='day' DESC;`;
|
||||
const events = await Self.rawSql(query,
|
||||
[zoneFk, started, ended, started, ended, started, ended, started, ended], myOptions);
|
||||
ended = simpleDate(ended);
|
||||
started = simpleDate(started);
|
||||
|
||||
query = `
|
||||
SELECT e.*
|
||||
FROM vn.zoneExclusion e
|
||||
LEFT JOIN vn.zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
|
||||
WHERE e.zoneFk = ?
|
||||
AND e.dated BETWEEN ? AND ?
|
||||
AND eg.zoneExclusionFk IS NULL;`;
|
||||
SELECT *
|
||||
FROM vn.zoneEvent
|
||||
WHERE zoneFk = ?
|
||||
AND (IFNULL(started, ?) <= ? AND IFNULL(ended,?) >= ?)
|
||||
ORDER BY type='indefinitely' DESC, type='range' DESC, type='day' DESC;`;
|
||||
const events = await Self.rawSql(query,
|
||||
[zoneFk, started, ended, ended, started], myOptions);
|
||||
|
||||
query = `
|
||||
SELECT e.*
|
||||
FROM vn.zoneExclusion e
|
||||
LEFT JOIN vn.zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
|
||||
WHERE e.zoneFk = ?
|
||||
AND e.dated BETWEEN ? AND ?
|
||||
AND eg.zoneExclusionFk IS NULL;`;
|
||||
const exclusions = await Self.rawSql(query, [zoneFk, started, ended], myOptions);
|
||||
|
||||
query = `
|
||||
SELECT eg.*, e.zoneFk, e.dated, e.created, e.userFk
|
||||
FROM vn.zoneExclusion e
|
||||
LEFT JOIN vn.zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
|
||||
WHERE e.zoneFk = ?
|
||||
AND e.dated BETWEEN ? AND ?
|
||||
AND eg.zoneExclusionFk IS NOT NULL;`;
|
||||
SELECT eg.*, e.zoneFk, e.dated, e.created, e.userFk
|
||||
FROM vn.zoneExclusion e
|
||||
LEFT JOIN vn.zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id
|
||||
WHERE e.zoneFk = ?
|
||||
AND e.dated BETWEEN ? AND ?
|
||||
AND eg.zoneExclusionFk IS NOT NULL;`;
|
||||
const geoExclusions = await Self.rawSql(query, [zoneFk, started, ended], myOptions);
|
||||
|
||||
return {events, exclusions, geoExclusions};
|
||||
};
|
||||
function simpleDate(date) {
|
||||
return date.toISOString().split('T')[0];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('zone getEventsFiltered()', () => {
|
|||
|
||||
const result = await models.Zone.getEventsFiltered(9, today, today, options);
|
||||
|
||||
expect(result.events.length).toEqual(1);
|
||||
expect(result.events.length).toEqual(3);
|
||||
expect(result.exclusions.length).toEqual(0);
|
||||
|
||||
await tx.rollback();
|
||||
|
@ -47,11 +47,12 @@ describe('zone getEventsFiltered()', () => {
|
|||
const options = {transaction: tx};
|
||||
const date = Date.vnNew();
|
||||
date.setFullYear(date.getFullYear() - 2);
|
||||
const dateTomorrow = new Date(date.setDate(date.getDate() + 1));
|
||||
const dateTomorrow = new Date(date);
|
||||
dateTomorrow.setDate(dateTomorrow.getDate() + 1);
|
||||
|
||||
const result = await models.Zone.getEventsFiltered(9, date, dateTomorrow, options);
|
||||
|
||||
expect(result.events.length).toEqual(0);
|
||||
expect(result.events.length).toEqual(1);
|
||||
expect(result.exclusions.length).toEqual(0);
|
||||
|
||||
await tx.rollback();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-back",
|
||||
"version": "23.50.01",
|
||||
"version": "23.52.01",
|
||||
"author": "Verdnatura Levante SL",
|
||||
"description": "Salix backend",
|
||||
"license": "GPL-3.0",
|
||||
|
|
Loading…
Reference in New Issue