#1852 worker.time-control
This commit is contained in:
parent
4685b4166b
commit
03ab88bcd9
|
@ -1,7 +1,7 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
vn-chip {
|
vn-chip {
|
||||||
border-radius: 16px;
|
border-radius: 1em;
|
||||||
background-color: $color-bg;
|
background-color: $color-bg;
|
||||||
margin: 0 0.5em 0.5em 0;
|
margin: 0 0.5em 0.5em 0;
|
||||||
color: $color-font;
|
color: $color-font;
|
||||||
|
@ -11,7 +11,7 @@ vn-chip {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
height: 28px;
|
height: 2em;
|
||||||
padding: 0 .7em;
|
padding: 0 .7em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ vn-chip {
|
||||||
|
|
||||||
vn-avatar {
|
vn-avatar {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 28px;
|
height: 2em;
|
||||||
width: 28px;
|
width: 2em;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('addTime', {
|
Self.remoteMethodCtx('addTimeEntry', {
|
||||||
description: 'Adds a new hour registry',
|
description: 'Adds a new hour registry',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [{
|
accepts: [{
|
||||||
|
@ -16,12 +16,12 @@ module.exports = Self => {
|
||||||
root: true
|
root: true
|
||||||
}],
|
}],
|
||||||
http: {
|
http: {
|
||||||
path: `/addTime`,
|
path: `/addTimeEntry`,
|
||||||
verb: 'POST'
|
verb: 'POST'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.addTime = async(ctx, data) => {
|
Self.addTimeEntry = async(ctx, data) => {
|
||||||
const Worker = Self.app.models.Worker;
|
const Worker = Self.app.models.Worker;
|
||||||
const myUserId = ctx.req.accessToken.userId;
|
const myUserId = ctx.req.accessToken.userId;
|
||||||
const myWorker = await Worker.findOne({where: {userFk: myUserId}});
|
const myWorker = await Worker.findOne({where: {userFk: myUserId}});
|
|
@ -0,0 +1,35 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('deleteTimeEntry', {
|
||||||
|
description: 'Deletes a manual time entry for a worker if the user role is above the worker',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The time entry id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'boolean',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/deleteTimeEntry`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteTimeEntry = async(ctx, id) => {
|
||||||
|
const workerModel = Self.app.models.Worker;
|
||||||
|
|
||||||
|
const targetTimeEntry = await Self.findById(id);
|
||||||
|
|
||||||
|
const hasRightsToDelete = await workerModel.isSubordinate(ctx, targetTimeEntry.userFk);
|
||||||
|
|
||||||
|
if (!hasRightsToDelete)
|
||||||
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
|
return Self.rawSql('CALL vn.workerTimeControl_remove(?, ?)', [
|
||||||
|
targetTimeEntry.userFk, targetTimeEntry.timed]);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,67 @@
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getWorkedHours', {
|
||||||
|
description: 'returns the total worked hours per day for a given range of dates in format YYYY-mm-dd hh:mm:ss',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The worker id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'from',
|
||||||
|
type: 'Date',
|
||||||
|
required: true,
|
||||||
|
description: `The from date`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'to',
|
||||||
|
type: 'Date',
|
||||||
|
required: true,
|
||||||
|
description: `The to date`
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['Object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/getWorkedHours`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getWorkedHours = async(id, from, to) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const stmts = [];
|
||||||
|
|
||||||
|
let worker = await Self.app.models.Worker.findById(id);
|
||||||
|
let userId = worker.userFk;
|
||||||
|
|
||||||
|
stmts.push(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS
|
||||||
|
tmp.timeControlCalculate,
|
||||||
|
tmp.timeBusinessCalculate
|
||||||
|
`);
|
||||||
|
|
||||||
|
stmts.push(new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [userId, from, to]));
|
||||||
|
stmts.push(new ParameterizedSQL('CALL vn.timeBusiness_calculateByUser(?, ?, ?)', [userId, from, to]));
|
||||||
|
let resultIndex = stmts.push(`
|
||||||
|
SELECT tbc.dated, tbc.timeWorkSeconds expectedHours, tcc.timeWorkSeconds workedHours
|
||||||
|
FROM tmp.timeBusinessCalculate tbc
|
||||||
|
LEFT JOIN tmp.timeControlCalculate tcc ON tcc.dated = tbc.dated
|
||||||
|
`) - 1;
|
||||||
|
stmts.push(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS
|
||||||
|
tmp.timeControlCalculate,
|
||||||
|
tmp.timeBusinessCalculate
|
||||||
|
`);
|
||||||
|
|
||||||
|
let sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
let result = await conn.executeStmt(sql);
|
||||||
|
|
||||||
|
return result[resultIndex];
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('timeControl', {
|
||||||
|
description: 'Returns a range of worked hours for a given worker id',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The worker id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'dateFrom',
|
||||||
|
type: 'datetime',
|
||||||
|
required: true,
|
||||||
|
description: 'the date from the time control begins in format YYYY-mm-dd-hh:mm:ss'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'dateTo',
|
||||||
|
type: 'datetime',
|
||||||
|
required: true,
|
||||||
|
description: 'the date when the time control finishes in format YYYY-mm-dd-hh:mm:ss'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['Object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/timeControl`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.timeControl = async(id, from, to) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const stmts = [];
|
||||||
|
|
||||||
|
stmts.push(new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [id, from, to]));
|
||||||
|
|
||||||
|
let sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
let result = await conn.executeStmt(sql);
|
||||||
|
|
||||||
|
|
||||||
|
return result[0];
|
||||||
|
};
|
||||||
|
};
|
|
@ -2,7 +2,8 @@ const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/worker-time-control/filter')(Self);
|
require('../methods/worker-time-control/filter')(Self);
|
||||||
require('../methods/worker-time-control/addTime')(Self);
|
require('../methods/worker-time-control/addTimeEntry')(Self);
|
||||||
|
require('../methods/worker-time-control/deleteTimeEntry')(Self);
|
||||||
|
|
||||||
Self.rewriteDbError(function(err) {
|
Self.rewriteDbError(function(err) {
|
||||||
if (err.code === 'ER_DUP_ENTRY')
|
if (err.code === 'ER_DUP_ENTRY')
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
},
|
},
|
||||||
"order": {
|
"order": {
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"direction": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
@ -26,6 +29,11 @@
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Account",
|
"model": "Account",
|
||||||
"foreignKey": "userFk"
|
"foreignKey": "userFk"
|
||||||
|
},
|
||||||
|
"worker": {
|
||||||
|
"type": "hasOne",
|
||||||
|
"model": "Worker",
|
||||||
|
"foreignKey": "userFk"
|
||||||
},
|
},
|
||||||
"warehouse": {
|
"warehouse": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
|
|
|
@ -3,4 +3,5 @@ module.exports = Self => {
|
||||||
require('../methods/worker/mySubordinates')(Self);
|
require('../methods/worker/mySubordinates')(Self);
|
||||||
require('../methods/worker/isSubordinate')(Self);
|
require('../methods/worker/isSubordinate')(Self);
|
||||||
require('../methods/worker/getWorkerInfo')(Self);
|
require('../methods/worker/getWorkerInfo')(Self);
|
||||||
|
require('../methods/worker/getWorkedHours')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,28 +5,55 @@
|
||||||
data="$ctrl.hours">
|
data="$ctrl.hours">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<div class="main-with-right-menu">
|
<div class="main-with-right-menu">
|
||||||
<vn-card class="vn-pa-lg vn-w-md">
|
<vn-card class="vn-pa-lg vn-w-lg">
|
||||||
<vn-table model="model" auto-load="false">
|
<vn-table model="model" auto-load="false">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" center>
|
<vn-td ng-repeat="weekday in $ctrl.weekDays" center>
|
||||||
<div translate>{{::$ctrl.weekdayNames[$index].name}}</div>
|
<div class="weekday" translate>{{::$ctrl.weekdayNames[$index].name}}</div>
|
||||||
<span>{{::weekday.dated | date: 'dd'}}</span>
|
<div>
|
||||||
<span title="{{::weekday.dated | date: 'MMMM' | translate}}" translate>
|
<span>{{::weekday.dated | date: 'dd'}}</span>
|
||||||
{{::weekday.dated | date: 'MMMM'}}
|
<span title="{{::weekday.dated | date: 'MMMM' | translate}}" translate>
|
||||||
</span>
|
{{::weekday.dated | date: 'MMMM'}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<vn-chip
|
||||||
|
title="{{::weekday.event.name}}"
|
||||||
|
ng-class="{invisible: !weekday.event}">
|
||||||
|
<vn-avatar
|
||||||
|
ng-style="::{backgroundColor: weekday.event.color}">
|
||||||
|
</vn-avatar>
|
||||||
|
<div>
|
||||||
|
{{::weekday.event.name}}
|
||||||
|
</div>
|
||||||
|
</vn-chip>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" class="hours vn-pa-none" center>
|
<vn-td ng-repeat="weekday in $ctrl.weekDays" class="hours vn-pa-none" expand center>
|
||||||
<section ng-repeat="hour in weekday.hours" center>
|
<section ng-repeat="hour in weekday.hours" center>
|
||||||
<vn-icon
|
<vn-icon
|
||||||
icon="arrow_{{($index % 2) == 0 ? 'forward' : 'back'}}"
|
icon="{{
|
||||||
title="{{(($index % 2) == 0 ? 'In' : 'Out') | translate}}">
|
::hour.direction == 'in' ? 'arrow_forward'
|
||||||
|
: hour.direction == 'out' ? 'arrow_back'
|
||||||
|
: 'arrow_forward'
|
||||||
|
}}"
|
||||||
|
title="{{
|
||||||
|
::(hour.direction == 'in' ? 'In'
|
||||||
|
: hour.direction == 'out' ? 'Out'
|
||||||
|
: '') | translate
|
||||||
|
}}"
|
||||||
|
ng-class="::{'invisible': hour.direction == 'middle'}">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
<span class="chip {{$ctrl.hourColor(hour)}}">{{hour.timed | date: 'HH:mm'}}</span>
|
<vn-chip
|
||||||
|
ng-class="::{'colored': hour.manual}"
|
||||||
|
removable="::hour.manual"
|
||||||
|
translate-attr="{title: 'Category'}"
|
||||||
|
on-remove="$ctrl.showDeleteDialog(hour)">
|
||||||
|
{{::hour.timed | date: 'HH:mm'}}
|
||||||
|
</vn-chip>
|
||||||
</section>
|
</section>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
|
@ -34,7 +61,7 @@
|
||||||
<vn-tfoot>
|
<vn-tfoot>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" center>
|
<vn-td ng-repeat="weekday in $ctrl.weekDays" center>
|
||||||
{{$ctrl.getWeekdayTotalHours(weekday)}} h.
|
{{$ctrl.formatHours(weekday.workedHours)}} h.
|
||||||
</vn-td>
|
</vn-td>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
|
@ -51,6 +78,12 @@
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-side-menu side="right">
|
<vn-side-menu side="right">
|
||||||
<div class="vn-pa-md">
|
<div class="vn-pa-md">
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<vn-label-value
|
||||||
|
label="Finish at"
|
||||||
|
value="{{$ctrl.getFinishTime()}}">
|
||||||
|
</vn-label-value>
|
||||||
|
</div>
|
||||||
<div class="totalBox" style="text-align: center;">
|
<div class="totalBox" style="text-align: center;">
|
||||||
<h6 translate>Hours</h6>
|
<h6 translate>Hours</h6>
|
||||||
<vn-label-value
|
<vn-label-value
|
||||||
|
@ -86,4 +119,10 @@
|
||||||
<input type="button" response="CANCEL" translate-attr="{value: 'Cancel'}"/>
|
<input type="button" response="CANCEL" translate-attr="{value: 'Cancel'}"/>
|
||||||
<button response="ACCEPT" translate>Save</button>
|
<button response="ACCEPT" translate>Save</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="delete-entry-dialog"
|
||||||
|
on-response="$ctrl.deleteTimeEntry(response)"
|
||||||
|
message="This time entry will be deleted"
|
||||||
|
question="Are you sure you want to delete this entry?">
|
||||||
|
</vn-confirm>
|
|
@ -2,13 +2,15 @@ import ngModule from '../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
constructor($scope, $http, $stateParams, $element, vnWeekDays) {
|
constructor($scope, $http, $stateParams, $element, vnWeekDays, vnApp, $translate) {
|
||||||
this.$stateParams = $stateParams;
|
this.$stateParams = $stateParams;
|
||||||
this.$ = $scope;
|
this.$ = $scope;
|
||||||
this.$http = $http;
|
this.$http = $http;
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
this.weekDays = [];
|
this.weekDays = [];
|
||||||
this.weekdayNames = vnWeekDays.locales;
|
this.weekdayNames = vnWeekDays.locales;
|
||||||
|
this.vnApp = vnApp;
|
||||||
|
this.$translate = $translate;
|
||||||
}
|
}
|
||||||
|
|
||||||
$postLink() {
|
$postLink() {
|
||||||
|
@ -34,9 +36,20 @@ class Controller {
|
||||||
this.started = started;
|
this.started = started;
|
||||||
|
|
||||||
let ended = new Date(started.getTime());
|
let ended = new Date(started.getTime());
|
||||||
ended.setDate(ended.getDate() + 7);
|
ended.setHours(23, 59, 59, 59);
|
||||||
|
ended.setDate(ended.getDate() + 6);
|
||||||
this.ended = ended;
|
this.ended = ended;
|
||||||
|
|
||||||
|
this.weekDays = [];
|
||||||
|
let dayIndex = new Date(started.getTime());
|
||||||
|
|
||||||
|
while (dayIndex < ended) {
|
||||||
|
this.weekDays.push({
|
||||||
|
dated: new Date(dayIndex.getTime())
|
||||||
|
});
|
||||||
|
dayIndex.setDate(dayIndex.getDate() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
this.fetchHours();
|
this.fetchHours();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,23 +62,15 @@ class Controller {
|
||||||
|
|
||||||
set hours(value) {
|
set hours(value) {
|
||||||
this._hours = value;
|
this._hours = value;
|
||||||
this.weekDays = [];
|
|
||||||
if (!this.hours) return;
|
|
||||||
|
|
||||||
let dayIndex = new Date(this.started.getTime());
|
for (const weekDay of this.weekDays) {
|
||||||
|
if (value) {
|
||||||
while (dayIndex < this.ended) {
|
let day = weekDay.dated.getDay();
|
||||||
let weekDay = dayIndex.getDay();
|
weekDay.hours = value
|
||||||
|
.filter(hour => new Date(hour.timed).getDay() == day)
|
||||||
let hours = this.hours
|
.sort((a, b) => new Date(a.timed) - new Date(b.timed));
|
||||||
.filter(hour => new Date(hour.timed).getDay() == weekDay)
|
} else
|
||||||
.sort((a, b) => new Date(a.timed) - new Date(b.timed));
|
weekDay.hours = null;
|
||||||
|
|
||||||
this.weekDays.push({
|
|
||||||
dated: new Date(dayIndex.getTime()),
|
|
||||||
hours
|
|
||||||
});
|
|
||||||
dayIndex.setDate(dayIndex.getDate() + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,61 +79,89 @@ class Controller {
|
||||||
const filter = {
|
const filter = {
|
||||||
where: {and: [
|
where: {and: [
|
||||||
{timed: {gte: this.started}},
|
{timed: {gte: this.started}},
|
||||||
{timed: {lt: this.ended}}
|
{timed: {lte: this.ended}}
|
||||||
]}
|
]}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$.model.applyFilter(filter, params);
|
this.$.model.applyFilter(filter, params);
|
||||||
|
|
||||||
|
this.getAbsences();
|
||||||
|
this.getWorkedHours(this.started, this.ended);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasEvents(day) {
|
hasEvents(day) {
|
||||||
return day >= this.started && day < this.ended;
|
return day >= this.started && day < this.ended;
|
||||||
}
|
}
|
||||||
|
|
||||||
hourColor(weekDay) {
|
getWorkedHours(from, to) {
|
||||||
return weekDay.manual ? 'alert' : 'warning';
|
let params = {
|
||||||
|
id: this.$stateParams.id,
|
||||||
|
from: from,
|
||||||
|
to: to
|
||||||
|
};
|
||||||
|
|
||||||
|
const query = `api/workers/${this.$stateParams.id}/getWorkedHours`;
|
||||||
|
return this.$http.get(query, {params}).then(res => {
|
||||||
|
const workDays = res.data;
|
||||||
|
const map = new Map();
|
||||||
|
|
||||||
|
for (const workDay of workDays) {
|
||||||
|
workDay.dated = new Date(workDay.dated);
|
||||||
|
map.set(workDay.dated, workDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const weekDay of this.weekDays) {
|
||||||
|
const workDay = workDays.find(day => {
|
||||||
|
let from = new Date(day.dated);
|
||||||
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
let to = new Date(day.dated);
|
||||||
|
to.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
|
return weekDay.dated >= from && weekDay.dated <= to;
|
||||||
|
});
|
||||||
|
weekDay.expectedHours = workDay.expectedHours;
|
||||||
|
weekDay.workedHours = workDay.workedHours;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getWeekdayTotalHours(weekday) {
|
getFinishTime() {
|
||||||
if (weekday.hours.length == 0) return 0;
|
let weekOffset = new Date().getDay() - 1;
|
||||||
|
if (weekOffset < 0) weekOffset = 6;
|
||||||
|
const today = this.weekDays[weekOffset];
|
||||||
|
|
||||||
const hours = weekday.hours;
|
if (today && today.workedHours) {
|
||||||
|
const remainingTime = (today.expectedHours - today.workedHours) * 1000;
|
||||||
|
const lastKnownTime = new Date(today.hours[today.hours.length - 1].timed).getTime();
|
||||||
|
const finishTimeStamp = lastKnownTime + remainingTime;
|
||||||
|
|
||||||
let totalStamp = 0;
|
let finishDate = new Date(finishTimeStamp);
|
||||||
|
let hour = finishDate.getHours();
|
||||||
|
let minute = finishDate.getMinutes();
|
||||||
|
|
||||||
hours.forEach((hour, index) => {
|
if (hour < 10) hour = `0${hour}`;
|
||||||
let currentHour = new Date(hour.timed);
|
if (minute < 10) minute = `0${minute}`;
|
||||||
let previousHour = new Date(hour.timed);
|
|
||||||
|
|
||||||
if (index > 0 && (index % 2 == 1))
|
return `${hour}:${minute} h.`;
|
||||||
previousHour = new Date(hours[index - 1].timed);
|
}
|
||||||
|
|
||||||
const dif = Math.abs(previousHour - currentHour);
|
|
||||||
|
|
||||||
totalStamp += dif;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (totalStamp / 3600 / 1000 > 5)
|
|
||||||
totalStamp += (20 * 60 * 1000);
|
|
||||||
|
|
||||||
weekday.total = totalStamp;
|
|
||||||
|
|
||||||
return this.formatHours(totalStamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get weekTotalHours() {
|
get weekTotalHours() {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
|
|
||||||
this.weekDays.forEach(weekday => {
|
this.weekDays.forEach(weekday => {
|
||||||
if (weekday.total)
|
if (weekday.workedHours)
|
||||||
total += weekday.total;
|
total += weekday.workedHours;
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.formatHours(total);
|
return this.formatHours(total);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatHours(timestamp) {
|
formatHours(timestamp) {
|
||||||
let hour = Math.floor(timestamp / 3600 / 1000);
|
timestamp = timestamp || 0;
|
||||||
let min = Math.floor(timestamp / 60 / 1000 - 60 * hour);
|
|
||||||
|
let hour = Math.floor(timestamp / 3600);
|
||||||
|
let min = Math.floor(timestamp / 60 - 60 * hour);
|
||||||
|
|
||||||
if (hour < 10) hour = `0${hour}`;
|
if (hour < 10) hour = `0${hour}`;
|
||||||
if (min < 10) min = `0${min}`;
|
if (min < 10) min = `0${min}`;
|
||||||
|
@ -159,12 +192,75 @@ class Controller {
|
||||||
workerFk: this.$stateParams.id,
|
workerFk: this.$stateParams.id,
|
||||||
timed: this.newTime
|
timed: this.newTime
|
||||||
};
|
};
|
||||||
this.$http.post(`api/WorkerTimeControls/addTime`, data)
|
this.$http.post(`api/WorkerTimeControls/addTimeEntry`, data)
|
||||||
.then(() => this.fetchHours());
|
.then(() => this.fetchHours());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showDeleteDialog(hour) {
|
||||||
|
this.timeEntryToDelete = hour;
|
||||||
|
this.$.deleteEntryDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTimeEntry(response) {
|
||||||
|
if (response !== 'ACCEPT') return;
|
||||||
|
|
||||||
|
const entryId = this.timeEntryToDelete.id;
|
||||||
|
|
||||||
|
this.$http.post(`api/WorkerTimeControls/${entryId}/deleteTimeEntry`).then(() => {
|
||||||
|
this.fetchHours();
|
||||||
|
this.vnApp.showSuccess(this.$translate.instant('Entry removed'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAbsences() {
|
||||||
|
let params = {
|
||||||
|
workerFk: this.$stateParams.id,
|
||||||
|
started: this.started,
|
||||||
|
ended: this.ended
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.$http.get(`api/WorkerCalendars/absences`, {params})
|
||||||
|
.then(res => this.onData(res.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
onData(data) {
|
||||||
|
const events = {};
|
||||||
|
|
||||||
|
let addEvent = (day, event) => {
|
||||||
|
events[new Date(day).getTime()] = event;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.holidays) {
|
||||||
|
data.holidays.forEach(holiday => {
|
||||||
|
const holidayDetail = holiday.detail && holiday.detail.description;
|
||||||
|
const holidayType = holiday.type && holiday.type.name;
|
||||||
|
const holidayName = holidayDetail || holidayType;
|
||||||
|
|
||||||
|
addEvent(holiday.dated, {
|
||||||
|
name: holidayName,
|
||||||
|
color: '#ff0'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.absences) {
|
||||||
|
data.absences.forEach(absence => {
|
||||||
|
const type = absence.absenceType;
|
||||||
|
addEvent(absence.dated, {
|
||||||
|
name: type.name,
|
||||||
|
color: type.rgb
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.weekDays.forEach(day => {
|
||||||
|
const timestamp = day.dated.getTime();
|
||||||
|
if (events[timestamp])
|
||||||
|
day.event = events[timestamp];
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$scope', '$http', '$stateParams', '$element', 'vnWeekDays'];
|
Controller.$inject = ['$scope', '$http', '$stateParams', '$element', 'vnWeekDays', 'vnApp', '$translate'];
|
||||||
|
|
||||||
ngModule.component('vnWorkerTimeControl', {
|
ngModule.component('vnWorkerTimeControl', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -4,4 +4,7 @@ Hour: Hora
|
||||||
Hours: Horas
|
Hours: Horas
|
||||||
Add time: Añadir hora
|
Add time: Añadir hora
|
||||||
Week total: Total semana
|
Week total: Total semana
|
||||||
Current week: Semana actual
|
Current week: Semana actual
|
||||||
|
This time entry will be deleted: Se borrará la hora fichada
|
||||||
|
Are you sure you want to delete this entry?: ¿Seguro que quieres eliminarla?
|
||||||
|
Finish at: Termina a las
|
|
@ -1,7 +1,7 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
vn-worker-time-control {
|
vn-worker-time-control {
|
||||||
vn-thead > vn-tr > vn-td > div {
|
vn-thead > vn-tr > vn-td > div.weekday {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
color: $color-main
|
color: $color-main
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue