WorkerTimerControl App
gitea/salix/test This commit looks good Details

This commit is contained in:
quique 2019-06-03 09:20:20 +02:00
parent a009329bdd
commit a6a6ea5b06
17 changed files with 604 additions and 0 deletions

View File

@ -0,0 +1,15 @@
CREATE TABLE `vn`.`device` (
`id` INT NOT NULL,
`sn` VARCHAR(50) NULL,
`model` VARCHAR(50) NULL,
`userFk` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
INDEX `device_fk1_idx` (`userFk` ASC),
CONSTRAINT `device_fk1`
FOREIGN KEY (`userFk`)
REFERENCES `account`.`user` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
ALTER TABLE `vn`.`device`
CHANGE COLUMN `id` `id` INT(11) NOT NULL AUTO_INCREMENT ;

View File

@ -1585,3 +1585,49 @@ INSERT INTO `vn`.`zoneCalendar`(`zoneFk`, `delivered`)
(8, DATE_ADD(CURDATE(), INTERVAL +4 DAY)),
(8, DATE_ADD(CURDATE(), INTERVAL +5 DAY)),
(8, DATE_ADD(CURDATE(), INTERVAL +6 DAY));
INSERT INTO `vn`.`workerTimeControl`(`userFk`,`timed`,`manual`)
VALUES
(106, CONCAT(CURDATE(), ' 07:00'), TRUE),
(106, CONCAT(CURDATE(), ' 10:00'), TRUE),
(106, CONCAT(CURDATE(), ' 10:10'), TRUE),
(106, CONCAT(CURDATE(), ' 15:00'), TRUE);
INSERT INTO `vn`.`dmsType`(`id`, `name`, `path`, `readRoleFk`, `writeRoleFk`, `code`)
VALUES
(1, 'Facturas Recibidas', 'recibidas', NULL, NULL, 'invoiceIn'),
(2, 'Doc oficial', 'oficial', NULL, NULL, 'officialDoc'),
(3, 'Laboral', 'laboral', NULL, NULL, 'hhrrData'),
(4, 'Albaranes recibidos', 'entradas', NULL, NULL, 'deliveryNote'),
(5, 'Otros', 'otros', 1, NULL, 'miscellaneous'),
(6, 'Pruebas', 'pruebas', NULL, NULL, 'tests'),
(7, 'IAE Clientes', 'IAE_Clientes', NULL, NULL, 'economicActivitiesTax'),
(8, 'Fiscal', 'fiscal', NULL, NULL, 'fiscal'),
(9, 'Vehiculos', 'vehiculos', NULL, NULL, 'vehicles'),
(10, 'Plantillas', 'plantillas', NULL, NULL, 'templates'),
(11, 'Contratos', 'contratos', NULL, NULL, 'contracts'),
(12, 'ley de pagos', 'ley pagos', NULL, NULL, 'paymentsLaw'),
(13, 'Basura', 'basura', NULL, NULL, 'trash'),
(14, 'Ticket', 'tickets', 1, NULL, 'ticket'),
(15, 'Presupuestos', 'Presupuestos', NULL, NULL, 'budgets'),
(16, 'Logistica', 'logistica', NULL, NULL, 'logistics'),
(17, 'cmr', 'cmr', NULL, NULL, 'cmr'),
(18, 'dua', 'dua', NULL, NULL, 'dua'),
(19, 'inmovilizado', 'inmovilizado', NULL, NULL, 'fixedAssets');
INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `workerFk`, `warehouseFk`, `companyFk`, `hardCopyNumber`, `hasFile`, `reference`, `description`, `created`)
VALUES
(1, 14, '1.pdf', 5, 1, 442, NULL, FALSE, 'Ticket:11', 'Ticket:11 dms for the ticket', CURDATE()),
(2, 5, '2.pdf', 5, 1, 442, 1, TRUE, 'Client:101', 'Client:101 dms for the client', CURDATE()),
(3, 5, '3.pdf', 5, 1, 442, NULL, TRUE, 'Client: 101', 'Client:101 readme', CURDATE());
INSERT INTO `vn`.`ticketDms`(`ticketFk`, `dmsFk`)
VALUES
(11, 1);
INSERT INTO `vn`.`clientDms`(`clientFk`, `dmsFk`)
VALUES
(101, 2),
(101, 3);
INSERT INTO `vn`.`device` (`sn`, `model`, `userFk`) VALUES ('aaa', 'android', '9');

View File

@ -0,0 +1,61 @@
/*
Author : Enrique Blasco BLanquer
Date: 27 de mayo de 2019
*/
module.exports = Self => {
Self.remoteMethodCtx('checkUuid', {
description: 'Check UUID from user',
accessType: 'WRITE',
accepts: [{
arg: 'data',
type: 'object',
required: true,
description: 'uuid,model',
http: {source: 'body'}
}],
returns: [{
type: 'Object',
root: true
}],
http: {
path: `/checkUuid`,
verb: 'POST'
}
});
Self.checkUuid = async(ctx, data) => {
const myUserId = ctx.req.accessToken.userId;
// 1 Check is a registered user with a uuid
let deviceUser = await Self.findOne({where: {userFk: myUserId, sn: data.uuid}});
if (deviceUser != null)
return {'state': true, 'mng': ''};
else {
// 2 If it does not exist it can be for two reasons:
// 2.1 It is the first time that the application enters so we have to register a new user associated with the user
// 2.2 Has the user associated with a different uuid, so we deny access.
let device = await Self.findOne({where: {userFk: myUserId}});
if (device != null) {
// The device is already registered by another user, access is denied
return {'state': false, 'mng': 'Ya estas regisgtrado en otro dispositivo, contacta con los dioses de informática.'};
} else {
// Check that the device is free
let aDevice = await Self.findOne({where: {sn: data.uuid}});
if (aDevice != null)
return {'state': false, 'mng': 'El dispositivo esta siendo usado por otro usuario'};
else {
// It's the first time you access the application, insert
/* await Self.rawSql('INSERT INTO vn2008.device (sn, model) VALUES (?,?);', [data.uuid, data.model]);*/
await Self.create({
sn: data.uuid,
model: data.model,
userFk: myUserId
});
return {'state': true, 'mng': 'Nuevo dispositivo registrado'};
}
}
}
};
};

View File

@ -0,0 +1,11 @@
const app = require('vn-loopback/server/server');
describe('device checkUuid()', () => {
it('should return an state equal to false', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let data = {'uuid': '123', 'model': 'ihpne kike molon'};
let result = await app.models.Device.checkUuid(ctx, data);
expect(result.name).toBeFalsy();
});
});

View File

@ -0,0 +1,48 @@
/*
Author : Enrique Blasco BLanquer
Date: 27 de mayo de 2019
*/
module.exports = Self => {
Self.remoteMethodCtx('addAutoTime', {
description: 'Adds a new hour registry by app in manual 0',
accessType: 'WRITE',
accepts: [{
arg: 'data',
type: 'object',
required: true,
description: 'timed',
http: {source: 'body'}
}],
returns: [{
type: 'Object',
root: true
}],
http: {
path: `/addAutoTime`,
verb: 'POST'
}
});
Self.addAutoTime = async(ctx, data) => {
const myUserId = ctx.req.accessToken.userId;
// get all worked time control, needed to calculate order
let hours = await Self.rawSql(`SELECT * FROM vn.workerTimeControl
WHERE userFk = ?
AND DATE(timed) = CURDATE()
ORDER BY timed DESC LIMIT 1`, [myUserId]);
// 1 get next order
let order = 0;
if (hours.length > 0)
order = hours[hours.length - 1].order;
// 2 create element in db
return Self.create({
userFk: myUserId,
timed: data.timed,
order: order + 1,
manual: 0
});
};
};

View File

@ -0,0 +1,121 @@
/*
Author : Enrique Blasco BLanquer
Date: 28 de mayo de 2019
*/
module.exports = Self => {
Self.remoteMethodCtx('getHoursWorked', {
description: 'Get worked hours in current week, month and year',
accessType: 'WRITE',
returns: [{
type: 'Object',
root: true
}],
http: {
path: `/getHoursWorked`,
verb: 'GET'
}
});
Self.getHoursWorked = async(ctx, data) => {
let totalHours = 0; // total hours worked in one year
let totalMinutes = 0; // total minutes worked in one year
let totalHoursMonth = 0; // total hours worked in one month
let totalMinutesMonth = 0; // total minutes worked in one month
let totalHoursWeek = 0; // total hours worked in one week
let totalMinutesWeek = 0; // total minutes worked in one week
const myUserId = ctx.req.accessToken.userId; // user id
let today = new Date(); // needed to calculate total hours worked to current date
let fromDate = today.getFullYear() + '-01-01'; // from date, current year
let toDate = today.getFullYear() + '-12-31'; // to date, current year
// 1 hours worked in a year
let hoursYear = await Self.rawSql(`SELECT wtc.userFk, DATE(wtc.timed) dated,
UNIX_TIMESTAMP(MIN(timed))timedStart,
SEC_TO_TIME(SUM(if( mod(wtc.order,2)=1,
UNIX_TIMESTAMP(timed) *-1,
UNIX_TIMESTAMP(timed)))) timeWorkDay
FROM vn.workerTimeControl wtc
WHERE wtc.timed BETWEEN ? AND ? AND userFk = ?
GROUP BY wtc.userFk,dated ORDER BY dated DESC`, [fromDate, toDate, myUserId]);
// 2 Get days of week
let week = [];
// Starting Monday not Sunday
let current = new Date();
current.setDate((current.getDate() - current.getDay() + 1));
for (let i = 0; i < 7; i++) {
week.push(
new Date(current)
);
current.setDate(current.getDate() + 1);
}
// 3 I have all timed control for one year... NOW I CALCULATE TOTAL HOURS IN YEAR, MONTH, WEEK, Let's GO!
for (hour of hoursYear) {
if (parseInt(hour.timeWorkDay.split(':')[0]) > 0) {
// YEAR
totalHours += parseInt(hour.timeWorkDay.split(':')[0]);
totalMinutes += parseInt(hour.timeWorkDay.split(':')[1]);
// If it exceeds 5 hours we add 20 minutes of breakfast.
if (parseInt(hour.timeWorkDay.split(':')[0]) >= 5)
totalMinutes += 20;
// MONTH
if ((new Date(hour.dated)).getMonth() == today.getMonth()) {
totalHoursMonth += parseInt(hour.timeWorkDay.split(':')[0]);
totalMinutesMonth += parseInt(hour.timeWorkDay.split(':')[1]);
// If it exceeds 5 hours we add 20 minutes of breakfast.
if (parseInt(hour.timeWorkDay.split(':')[0]) >= 5)
totalMinutesMonth += 20;
}
// WEEK
for (day of week) {
let dayOfWeek = new Date(day);
let dayOfCurrentWeek = new Date(hour.dated);
if (dayOfWeek.getMonth() == dayOfCurrentWeek.getMonth() && dayOfWeek.getDate() == dayOfCurrentWeek.getDate()) {
totalHoursWeek += parseInt(hour.timeWorkDay.split(':')[0]);
totalMinutesWeek += parseInt(hour.timeWorkDay.split(':')[1]);
// If it exceeds 5 hours we add 20 minutes of breakfast.
if (parseInt(hour.timeWorkDay.split(':')[0]) >= 5)
totalMinutesWeek += 20;
break;
}
}
}
}
// TOTAL WORKED HOURS IN THE YEAR
totalHours += totalMinutes / 60;
totalHours = decimalToHour(totalHours);
// TOTAL WORKED HOURS IN THE MONTH
totalHoursMonth += totalMinutesMonth / 60;
totalHoursMonth = decimalToHour(totalHoursMonth);
// TOTAL WORKED HOURS IN THE WEEK
totalHoursWeek += totalMinutesWeek / 60;
totalHoursWeek = decimalToHour(totalHoursWeek);
return {
'totalWorekdYear': totalHours,
'totalWorekdMonth': totalHoursMonth,
'totalWorkedWeek': totalHoursWeek
};
};
};
/*
function to calculate hours and minutes from decimal value
*/
function decimalToHour(value) {
let decimalTime = parseFloat(value);
decimalTime = decimalTime * 60 * 60;
let hoursDay = Math.floor((decimalTime / (60 * 60)));
decimalTime = decimalTime - (hoursDay * 60 * 60);
let minutesDay = Math.floor((decimalTime / 60));
return hoursDay + ':' + minutesDay;
}

View File

@ -0,0 +1,78 @@
/*
Author : Enrique Blasco BLanquer
Date: 29 de mayo de 2019
*/
module.exports = Self => {
Self.remoteMethodCtx('getWorkedWeek', {
description: 'get worked week info',
accessType: 'WRITE',
returns: [{
type: 'Object',
root: true
}],
http: {
path: `/getWorkedWeek`,
verb: 'GET'
}
});
Self.getWorkedWeek = async(ctx, data) => {
const myUserId = ctx.req.accessToken.userId; // user id
let lastDate = new Date('1986-09-24'); // reference date
let diff = 0; // difference of value between two dates
let total = 0; // total hours
// 1 Get days of week
let week = [];
// 2 Starting Monday not Sunday
let current = new Date();
current.setDate((current.getDate() - current.getDay() + 1));
for (let i = 0; i < 7; i++) {
week.push(
new Date(current)
);
current.setDate(current.getDate() + 1);
}
let fromDate = week[0].getFullYear() + '-' + (week[0].getMonth() + 1) + '-' + week[0].getDate();
let toDate = week[week.length - 1].getFullYear() + '-' + (week[week.length - 1].getMonth() + 1) + '-' + week[week.length - 1].getDate();
// 3 hours worked in a current week
let hoursWeek = await Self.rawSql(`SELECT wtc.timed ,wtc.order
FROM vn.workerTimeControl wtc
WHERE userFk = ?
AND DATE(timed) BETWEEN ? AND ? ORDER BY timed DESC;`, [myUserId, fromDate, toDate]);
// 4 treat data
let isFirst = true;
for (let i = hoursWeek.length - 1; i >= 0; i--) {
let d = new Date(hoursWeek[i].timed);
if (isFirst) {
lastDate = d;
isFirst = false;
} else {
if (lastDate.getDate() === d.getDate()) {
diff += Math.abs(d.getTime() - lastDate.getTime());
lastDate = d;
} else {
total += diff;
diff = 0;
lastDate = d;
}
}
}
total += diff;
// 5 calculate hours and minutes
let decimalTime = total / 1000 / 3600;
decimalTime = decimalTime * 60 * 60;
let hours = Math.floor((decimalTime / (60 * 60)));
decimalTime = decimalTime - (hours * 60 * 60);
let minutes = Math.floor((decimalTime / 60));
return {'timeds': hoursWeek, 'totalWorked': hours + ':' + minutes};
};
};

View File

@ -0,0 +1,11 @@
const app = require('vn-loopback/server/server');
describe('workerTimeControl addAutoTime()', () => {
it('should return an undefined value', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let data = {'timed': new Date()};
let result = await app.models.WorkerTimeControl.addAutoTime(ctx, data);
expect(result).toBeUndefined();
});
});

View File

@ -0,0 +1,10 @@
const app = require('vn-loopback/server/server');
describe('workerTimeControl getHoursWorked()', () => {
it('should return an totalWorkedYear to be defined', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let result = await app.models.WorkerTimeControl.getHoursWorked(ctx, null);
expect(result.totalWorekdYear).toBeDefined();
});
});

View File

@ -0,0 +1,10 @@
const app = require('vn-loopback/server/server');
describe('workerTimeControl getWorkedWeek()', () => {
it('should return an timeds to be defined', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let result = await app.models.WorkerTimeControl.getWorkedWeek(ctx, null);
expect(result.timeds).toBeDefined();
});
});

View File

@ -0,0 +1,98 @@
/*
Author : Enrique Blasco BLanquer
Date: 28 de mayo de 2019
*/
module.exports = Self => {
Self.remoteMethodCtx('getWorkerInfo', {
description: 'Get worker info (name, isWorking, total worked hours ...)',
accessType: 'WRITE',
returns: [{
type: 'Object',
root: true
}],
http: {
path: `/getWorkerInfo`,
verb: 'GET'
}
});
Self.getWorkerInfo = async(ctx, data) => {
let prevHour = new Date(); // default value to start work
let diff = 0; // difference of value between two dates
let isOdd = true; // determine if timed is odd or not in db
const myUserId = ctx.req.accessToken.userId; // get user id
// 1 get name and photo for the user
let [user] = await Self.rawSql(`SELECT u.name, t.Foto FROM vn.user u INNER JOIN vn2008.Trabajadores t ON u.id = t.user_id WHERE id = ?;`, [myUserId]);
// 2 get all jornaly work time registered
let workedHours = await Self.rawSql(`SELECT * FROM vn.workerTimeControl WHERE userFk = ? AND DATE(timed) = CURDATE() ORDER BY timed ASC;`, [myUserId]);
let today = new Date();
// 3 get the number of hours to work in one day
let date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
let [hoursForDay] = await Self.rawSql(`SELECT cl.hours_week AS hoursWeek,
GROUP_CONCAT(DISTINCT LEFT(j.start,2) ORDER BY j.start ASC SEPARATOR '-') start ,
GROUP_CONCAT(DISTINCT LEFT(j.end,2) ORDER BY j.end ASC SEPARATOR '-') end,
CAST(IFNULL((SUM(TIME_TO_SEC(j.end))-SUM(TIME_TO_SEC(j.start)))/3600,if(cl.hours_week=40
AND DAYOFWEEK(t.dated) IN(2,3,4,5,6),8,0)) AS DECIMAL(10,2)) workingHours
FROM vn.time t
LEFT JOIN postgresql.business b ON t.dated BETWEEN b.date_start AND ifnull(b.date_end,? )
LEFT JOIN postgresql.profile AS pr ON b.client_id = pr.profile_id
LEFT JOIN postgresql.person AS p ON pr.person_id = p.person_id
LEFT JOIN vn.worker AS w ON p.id_trabajador = w.id
LEFT JOIN postgresql.business_labour AS bl ON b.business_id = bl.business_id
LEFT JOIN postgresql.calendar_labour_type AS cl ON bl.calendar_labour_type_id = cl.calendar_labour_type_id
LEFT JOIN postgresql.journey AS j ON j.business_id = b.business_id and j.day_id=WEEKDAY(t.dated)+1
WHERE t.dated BETWEEN ? AND ? AND userFk = ?
GROUP BY w.userFk,dated`, [date, date, date, myUserId]);
// 4 Add all the hours and see the total worked
for (hour of workedHours) {
if (!isOdd)
diff += Math.abs((new Date(hour.timed)).getTime() - prevHour.getTime());
else
prevHour = new Date(hour.timed);
isOdd = !isOdd;
}
// 5 calculate hours and minutes from a number value
let decimalTime = diff / 1000 / 3600;
decimalTime = decimalTime * 60 * 60;
let hours = Math.floor((decimalTime / (60 * 60)));
decimalTime = decimalTime - (hours * 60 * 60);
let minutes = Math.floor((decimalTime / 60));
// 6 default total hours
let totalHours = '7:40';
let hoursWeek = 40;
// 7 Get the hours you have to work today and the hours to work in a week
if (hoursForDay != null) {
// If it exceeds 5 hours we take 20 minutes of breakfast.
if (hoursForDay.workingHours > 5)
hoursForDay.workingHours -= 20 * 0.016666;
let decimalTime = parseFloat(hoursForDay.workingHours);
decimalTime = decimalTime * 60 * 60;
let hoursDay = Math.floor((decimalTime / (60 * 60)));
decimalTime = decimalTime - (hoursDay * 60 * 60);
let minutesDay = Math.floor((decimalTime / 60));
totalHours = hoursDay + ':' + minutesDay;
}
// 8 return value
if (hoursForDay != null)
hoursWeek = hoursForDay.hoursWeek;
return {
'name': user.name,
'hours': hours,
'minutes': minutes,
'today': today,
'isWorking': !isOdd,
'lastDate': prevHour,
'totalHours': totalHours,
'hoursWeek': hoursWeek
};
};
};

View File

@ -43,5 +43,11 @@
},
"WorkerCalendar": {
"dataSource": "vn"
},
"WorkerTimeControl": {
"dataSource": "vn"
},
"Device": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,11 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
require('../methods/device/checkUuid')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError(``);
return err;
});
};

View File

@ -0,0 +1,28 @@
{
"name": "Device",
"base": "VnModel",
"options": {
"mysql": {
"table": "device"
}
},
"properties": {
"id": {
"id": true,
"type": "Number"
},
"sn": {
"type": "String"
},
"model": {
"type": "String"
}
},
"relations": {
"user": {
"type": "belongsTo",
"model": "Account",
"foreignKey": "userFk"
}
}
}

View File

@ -0,0 +1,13 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
require('../methods/worker-time-control/addAutoTime')(Self);
require('../methods/worker-time-control/getHoursWorked')(Self);
require('../methods/worker-time-control/getWorkedWeek')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError(`The introduced hour already exists`);
return err;
});
};

View File

@ -0,0 +1,36 @@
{
"name": "WorkerTimeControl",
"base": "VnModel",
"options": {
"mysql": {
"table": "workerTimeControl"
}
},
"properties": {
"id": {
"id": true,
"type": "Number"
},
"timed": {
"type": "Date"
},
"manual": {
"type": "Boolean"
},
"order": {
"type": "Number"
}
},
"relations": {
"user": {
"type": "belongsTo",
"model": "Account",
"foreignKey": "userFk"
},
"warehouse": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseFk"
}
}
}

View File

@ -1,3 +1,4 @@
module.exports = Self => {
require('../methods/worker/filter')(Self);
require('../methods/worker/getWorkerInfo')(Self);
};