4856-worker.time-control #1375

Merged
joan merged 32 commits from 4856-worker.time-control into dev 2023-03-16 06:39:02 +00:00
15 changed files with 477 additions and 58 deletions

View File

@ -8,13 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2310.01] - 2023-03-23 ## [2310.01] - 2023-03-23
### Added ### Added
- - (Trabajadores -> Control de horario) Ahora se puede confirmar/no confirmar el registro horario de cada semana desde esta sección
### Changed ### Changed
- -
### Fixed ### Fixed
- (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo" - (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo"
- (General) Al pasar el ratón por encima del icono de "Borrar" en un campo, se hacía más grande afectando a la interfaz - (General) Al pasar el ratón por encima del icono de "Borrar" en un campo, se hacía más grande afectando a la interfaz
## [2308.01] - 2023-03-09 ## [2308.01] - 2023-03-09

View File

@ -2825,4 +2825,11 @@ INSERT INTO `vn`.`deviceProductionUser` (`deviceProductionFk`, `userFk`, `create
(1, 1, util.VN_NOW()), (1, 1, util.VN_NOW()),
(3, 3, util.VN_NOW()); (3, 3, util.VN_NOW());
INSERT INTO `vn`.`workerTimeControlMail` (`id`, `workerFk`, `year`, `week`, `state`, `updated`, `sendedCounter`, `reason`)
VALUES
(1, 9, 2000, 49, 'REVISE', util.VN_NOW(), 1, 'test2'),
(2, 9, 2000, 50, 'SENDED', util.VN_NOW(), 1, NULL),
(3, 9, 2000, 51, 'CONFIRMED', util.VN_NOW(), 1, NULL),
(4, 9, 2001, 1, 'SENDED', util.VN_NOW(), 1, NULL);

View File

@ -24,7 +24,7 @@
<div class="weekdays"> <div class="weekdays">
<section <section
ng-repeat="day in ::$ctrl.weekDays" ng-repeat="day in ::$ctrl.weekDays"
translate-attr="::{title: day.name}" translate-attr="::{title: day.name}"
ng-click="$ctrl.selectWeekDay($event, day.index)"> ng-click="$ctrl.selectWeekDay($event, day.index)">
<span>{{::day.localeChar}}</span> <span>{{::day.localeChar}}</span>
</section> </section>
@ -57,4 +57,4 @@
</section> </section>
</div> </div>
</div> </div>
</div> </div>

View File

@ -15,9 +15,9 @@ export default class Calendar extends FormInput {
constructor($element, $scope, vnWeekDays, moment) { constructor($element, $scope, vnWeekDays, moment) {
super($element, $scope); super($element, $scope);
this.weekDays = vnWeekDays.locales; this.weekDays = vnWeekDays.locales;
this.defaultDate = Date.vnNew();
this.displayControls = true; this.displayControls = true;
this.moment = moment; this.moment = moment;
this.defaultDate = Date.vnNew();
} }
/** /**
@ -207,14 +207,23 @@ export default class Calendar extends FormInput {
} }
repeatLast() { repeatLast() {
if (!this.formatDay) return; if (this.formatDay) {
const days = this.element.querySelectorAll('.days > .day');
for (let i = 0; i < days.length; i++) {
this.formatDay({
$day: this.days[i],
$element: days[i]
});
}
}
let days = this.element.querySelectorAll('.days > .day'); if (this.formatWeek) {
for (let i = 0; i < days.length; i++) { const weeks = this.element.querySelectorAll('.weeks > .day');
this.formatDay({ for (const week of weeks) {
$day: this.days[i], this.formatWeek({
$element: days[i] $element: week
}); });
}
} }
} }
} }
@ -228,6 +237,7 @@ ngModule.vnComponent('vnCalendar', {
hasEvents: '&?', hasEvents: '&?',
getClass: '&?', getClass: '&?',
formatDay: '&?', formatDay: '&?',
formatWeek: '&?',
displayControls: '<?', displayControls: '<?',
hideYear: '<?', hideYear: '<?',
hideContiguous: '<?', hideContiguous: '<?',

View File

@ -0,0 +1,61 @@
module.exports = Self => {
Self.remoteMethodCtx('getMailStates', {
description: 'Get the states of a month about time control mail',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
description: 'The worker id',
http: {source: 'path'}
},
{
arg: 'month',
type: 'number',
description: 'The number of the month'
},
{
arg: 'year',
type: 'number',
description: 'The number of the year'
}],
returns: [{
type: ['object'],
root: true
}],
http: {
path: `/:id/getMailStates`,
verb: 'GET'
}
});
Self.getMailStates = async(ctx, workerId, options) => {
const models = Self.app.models;
const args = ctx.args;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const times = await models.Time.find({
fields: ['week'],
where: {
month: args.month,
year: args.year
}
}, myOptions);
const weeks = times.map(time => time.week);
const weekNumbersSet = new Set(weeks);
const weekNumbers = Array.from(weekNumbersSet);
const workerTimeControlMails = await models.WorkerTimeControlMail.find({
where: {
workerFk: workerId,
year: args.year,
week: {inq: weekNumbers}
}
}, myOptions);
return workerTimeControlMails;
};
};

View File

@ -0,0 +1,29 @@
const models = require('vn-loopback/server/server').models;
describe('workerTimeControl getMailStates()', () => {
const workerId = 9;
const ctx = {args: {
month: 12,
year: 2000
}};
it('should get the states of a month about time control mail', async() => {
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
const response = await models.WorkerTimeControl.getMailStates(ctx, workerId, options);
expect(response[0].state).toEqual('REVISE');
expect(response[1].state).toEqual('SENDED');
expect(response[2].state).toEqual('CONFIRMED');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -47,6 +47,10 @@ module.exports = Self => {
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const isHimself = userId == args.workerId;
if (!isHimself)
throw new UserError(`You don't have enough privileges`);
const workerTimeControlMail = await models.WorkerTimeControlMail.findOne({ const workerTimeControlMail = await models.WorkerTimeControlMail.findOne({
where: { where: {
workerFk: args.workerId, workerFk: args.workerId,
@ -60,8 +64,6 @@ module.exports = Self => {
const oldState = workerTimeControlMail.state; const oldState = workerTimeControlMail.state;
const oldReason = workerTimeControlMail.reason; const oldReason = workerTimeControlMail.reason;
if (oldState == args.state) throw new UserError('Already has this status');
await workerTimeControlMail.updateAttributes({ await workerTimeControlMail.updateAttributes({
state: args.state, state: args.state,
reason: args.reason || null reason: args.reason || null

View File

@ -14,6 +14,9 @@
"year": { "year": {
"type": "number" "type": "number"
}, },
"month": {
"type": "number"
},
"week": { "week": {
"type": "number" "type": "number"
} }

View File

@ -8,6 +8,7 @@ module.exports = Self => {
require('../methods/worker-time-control/sendMail')(Self); require('../methods/worker-time-control/sendMail')(Self);
require('../methods/worker-time-control/updateWorkerTimeControlMail')(Self); require('../methods/worker-time-control/updateWorkerTimeControlMail')(Self);
require('../methods/worker-time-control/weeklyHourRecordEmail')(Self); require('../methods/worker-time-control/weeklyHourRecordEmail')(Self);
require('../methods/worker-time-control/getMailStates')(Self);
Self.rewriteDbError(function(err) { Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY') if (err.code === 'ER_DUP_ENTRY')

View File

@ -25,11 +25,11 @@
<div class="totalBox vn-mb-sm" style="text-align: center;"> <div class="totalBox vn-mb-sm" style="text-align: center;">
<h6>{{'Contract' | translate}} #{{$ctrl.businessId}}</h6> <h6>{{'Contract' | translate}} #{{$ctrl.businessId}}</h6>
<div> <div>
{{'Used' | translate}} {{$ctrl.contractHolidays.holidaysEnjoyed || 0}} {{'Used' | translate}} {{$ctrl.contractHolidays.holidaysEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.contractHolidays.totalHolidays || 0}} {{'days' | translate}} {{'of' | translate}} {{$ctrl.contractHolidays.totalHolidays || 0}} {{'days' | translate}}
</div> </div>
<div> <div>
{{'Spent' | translate}} {{$ctrl.contractHolidays.hoursEnjoyed || 0}} {{'Spent' | translate}} {{$ctrl.contractHolidays.hoursEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.contractHolidays.totalHours || 0}} {{'hours' | translate}} {{'of' | translate}} {{$ctrl.contractHolidays.totalHours || 0}} {{'hours' | translate}}
</div> </div>
<div> <div>
@ -40,11 +40,11 @@
<div class="totalBox" style="text-align: center;"> <div class="totalBox" style="text-align: center;">
<h6>{{'Year' | translate}} {{$ctrl.year}}</h6> <h6>{{'Year' | translate}} {{$ctrl.year}}</h6>
<div> <div>
{{'Used' | translate}} {{$ctrl.yearHolidays.holidaysEnjoyed || 0}} {{'Used' | translate}} {{$ctrl.yearHolidays.holidaysEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.yearHolidays.totalHolidays || 0}} {{'days' | translate}} {{'of' | translate}} {{$ctrl.yearHolidays.totalHolidays || 0}} {{'days' | translate}}
</div> </div>
<div> <div>
{{'Spent' | translate}} {{$ctrl.yearHolidays.hoursEnjoyed || 0}} {{'Spent' | translate}} {{$ctrl.yearHolidays.hoursEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.yearHolidays.totalHours || 0}} {{'hours' | translate}} {{'of' | translate}} {{$ctrl.yearHolidays.totalHours || 0}} {{'hours' | translate}}
</div> </div>
</div> </div>
@ -66,7 +66,7 @@
order="businessFk DESC" order="businessFk DESC"
limit="5"> limit="5">
<tpl-item> <tpl-item>
<div>#{{businessFk}}</div> <div>#{{businessFk}}</div>
<div class="text-caption text-secondary"> <div class="text-caption text-secondary">
{{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}} {{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}}
</div> </div>
@ -87,17 +87,17 @@
<vn-chip> <vn-chip>
<vn-avatar class="festive"> <vn-avatar class="festive">
</vn-avatar> </vn-avatar>
<span translate>Festive</span> <span translate>Festive</span>
</vn-chip> </vn-chip>
<vn-chip> <vn-chip>
<vn-avatar class="today"> <vn-avatar class="today">
</vn-avatar> </vn-avatar>
<span translate>Current day</span> <span translate>Current day</span>
</vn-chip> </vn-chip>
</div> </div>
</div> </div>
</vn-side-menu> </vn-side-menu>
<vn-confirm <vn-confirm
vn-id="confirm" vn-id="confirm"
message="This item will be deleted" message="This item will be deleted"
question="Are you sure you want to continue?"> question="Are you sure you want to continue?">

View File

@ -1,7 +1,7 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="WorkerTimeControls/filter" url="WorkerTimeControls/filter"
filter="::$ctrl.filter" filter="::$ctrl.filter"
data="$ctrl.hours"> data="$ctrl.hours">
</vn-crud-model> </vn-crud-model>
<vn-card class="vn-pa-lg vn-w-lg"> <vn-card class="vn-pa-lg vn-w-lg">
@ -16,7 +16,7 @@
{{::weekday.dated | date: 'MMMM'}} {{::weekday.dated | date: 'MMMM'}}
</span> </span>
</div> </div>
<vn-chip <vn-chip
title="{{::weekday.event.name}}" title="{{::weekday.event.name}}"
ng-class="{invisible: !weekday.event}"> ng-class="{invisible: !weekday.event}">
<vn-avatar <vn-avatar
@ -66,7 +66,7 @@
</vn-td> </vn-td>
</vn-tr> </vn-tr>
<vn-tr> <vn-tr>
<vn-td center ng-repeat="weekday in $ctrl.weekDays"> <vn-td center ng-repeat="weekday in $ctrl.weekDays">
<vn-icon-button <vn-icon-button
icon="add_circle" icon="add_circle"
vn-tooltip="Add time" vn-tooltip="Add time"
@ -78,27 +78,43 @@
</vn-table> </vn-table>
</vn-card> </vn-card>
<vn-button-bar class="vn-pa-xs vn-w-lg"> <vn-button-bar ng-show="$ctrl.state" class="vn-w-lg">
<vn-button <vn-button
label="Satisfied" label="Satisfied"
disabled="$ctrl.state == 'CONFIRMED'"
ng-if="$ctrl.isHimSelf"
ng-click="$ctrl.isSatisfied()"> ng-click="$ctrl.isSatisfied()">
</vn-button> </vn-button>
<vn-button <vn-button
label="Not satisfied" label="Not satisfied"
disabled="$ctrl.state == 'REVISE'"
ng-if="$ctrl.isHimSelf"
ng-click="reason.show()"> ng-click="reason.show()">
</vn-button> </vn-button>
<vn-button
label="Reason"
ng-if="$ctrl.reason && ($ctrl.isHimSelf || $ctrl.isHr)"
ng-click="reason.show()">
</vn-button>
<vn-button
label="Resend"
ng-click="sendEmailConfirmation.show()"
class="right"
vn-tooltip="Resend email of this week to the user"
ng-show="::$ctrl.isHr">
</vn-button>
</vn-button-bar> </vn-button-bar>
<vn-side-menu side="right"> <vn-side-menu side="right">
<div class="vn-pa-md"> <div class="vn-pa-md">
<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
label="Week total" label="Week total"
value="{{$ctrl.weekTotalHours}} h."> value="{{$ctrl.weekTotalHours}} h.">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
label="Finish at" label="Finish at"
value="{{$ctrl.getFinishTime()}}"> value="{{$ctrl.getFinishTime()}}">
</vn-label-value> </vn-label-value>
</div> </div>
@ -106,6 +122,8 @@
vn-id="calendar" vn-id="calendar"
class="vn-pt-md" class="vn-pt-md"
ng-model="$ctrl.date" ng-model="$ctrl.date"
format-week="$ctrl.formatWeek($element)"
on-move="$ctrl.getMailStates($date)"
has-events="$ctrl.hasEvents($day)"> has-events="$ctrl.hasEvents($day)">
</vn-calendar> </vn-calendar>
</div> </div>
@ -166,15 +184,31 @@
vn-id="reason" vn-id="reason"
on-accept="$ctrl.isUnsatisfied()"> on-accept="$ctrl.isUnsatisfied()">
<tpl-body> <tpl-body>
<vn-textarea <div class="reasonDialog">
label="Reason" <vn-textarea
ng-model="$ctrl.reason" label="Reason"
required="true" ng-model="$ctrl.reason"
rows="3"> disabled="!$ctrl.isHimSelf"
</vn-textarea> rows="5"
required="true">
</vn-textarea>
</div>
</tpl-body> </tpl-body>
<tpl-buttons> <tpl-buttons ng-if="$ctrl.isHimSelf">
<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-dialog
vn-id="sendEmailConfirmation"
on-accept="$ctrl.resendEmail()"
message="Send time control email">
<tpl-body style="min-width: 500px;">
<span translate>Are you sure you want to send it?</span>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Confirm</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,6 +1,7 @@
import ngModule from '../module'; import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
import './style.scss'; import './style.scss';
import UserError from 'core/lib/user-error';
class Controller extends Section { class Controller extends Section {
constructor($element, $, vnWeekDays) { constructor($element, $, vnWeekDays) {
@ -24,12 +25,31 @@ class Controller extends Section {
} }
this.date = initialDate; this.date = initialDate;
this.getMailStates(this.date);
}
get isHr() {
return this.aclService.hasAny(['hr']);
}
get isHimSelf() {
const userId = window.localStorage.currentUserWorkerId;
return userId == this.$params.id;
} }
get worker() { get worker() {
return this._worker; return this._worker;
} }
get weekNumber() {
return this.getWeekNumber(this.date);
}
set weekNumber(value) {
this._weekNumber = value;
}
set worker(value) { set worker(value) {
this._worker = value; this._worker = value;
} }
@ -68,6 +88,27 @@ class Controller extends Section {
} }
this.fetchHours(); this.fetchHours();
this.getWeekData();
}
getWeekData() {
const filter = {
where: {
workerFk: this.$params.id,
year: this._date.getFullYear(),
week: this.getWeekNumber(this._date)
}
};
this.$http.get('WorkerTimeControlMails', {filter})
.then(res => {
const workerTimeControlMail = res.data;
if (!workerTimeControlMail.length) {
this.state = null;
return;
}
this.state = workerTimeControlMail[0].state;
this.reason = workerTimeControlMail[0].reason;
});
} }
/** /**
@ -294,42 +335,56 @@ class Controller extends Section {
this.$.editEntry.show($event); this.$.editEntry.show($event);
} }
getWeekNumber(currentDate) { getWeekNumber(date) {
const startDate = new Date(currentDate.getFullYear(), 0, 1); const tempDate = new Date(date);
let days = Math.floor((currentDate - startDate) / let dayOfWeek = tempDate.getDay();
(24 * 60 * 60 * 1000)); dayOfWeek = (dayOfWeek === 0) ? 7 : dayOfWeek;
return Math.ceil(days / 7); const firstDayOfWeek = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() - (dayOfWeek - 1));
const firstDayOfYear = new Date(tempDate.getFullYear(), 0, 1);
const differenceInMilliseconds = firstDayOfWeek.getTime() - firstDayOfYear.getTime();
const weekNumber = Math.floor(differenceInMilliseconds / (1000 * 60 * 60 * 24 * 7)) + 1;
return weekNumber;
} }
isSatisfied() { isSatisfied() {
const weekNumber = this.getWeekNumber(this.date);
const params = { const params = {
workerId: this.worker.id, workerId: this.worker.id,
year: this.date.getFullYear(), year: this.date.getFullYear(),
week: weekNumber, week: this.weekNumber,
state: 'CONFIRMED' state: 'CONFIRMED'
}; };
const query = `WorkerTimeControls/updateWorkerTimeControlMail`; const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
this.$http.post(query, params).then(() => { this.$http.post(query, params).then(() => {
this.getMailStates(this.date);
this.getWeekData();
this.vnApp.showSuccess(this.$t('Data saved!')); this.vnApp.showSuccess(this.$t('Data saved!'));
}); });
} }
isUnsatisfied() { isUnsatisfied() {
const weekNumber = this.getWeekNumber(this.date); if (!this.reason) throw new UserError(`You must indicate a reason`);
const params = { const params = {
workerId: this.worker.id, workerId: this.worker.id,
year: this.date.getFullYear(), year: this.date.getFullYear(),
week: weekNumber, week: this.weekNumber,
state: 'REVISE', state: 'REVISE',
reason: this.reason reason: this.reason
}; };
const query = `WorkerTimeControls/updateWorkerTimeControlMail`; const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
this.$http.post(query, params).then(() => { this.$http.post(query, params).then(() => {
this.getMailStates(this.date);
this.getWeekData();
this.vnApp.showSuccess(this.$t('Data saved!')); this.vnApp.showSuccess(this.$t('Data saved!'));
}); });
} }
changeState(state, reason) {
this.state = state;
this.reason = reason;
this.repaint();
}
save() { save() {
try { try {
const entry = this.selectedRow; const entry = this.selectedRow;
@ -345,6 +400,77 @@ class Controller extends Section {
this.vnApp.showError(this.$t(e.message)); this.vnApp.showError(this.$t(e.message));
} }
} }
resendEmail() {
const timestamp = this.date.getTime() / 1000;
const url = `${window.location.origin}/#!/worker/${this.worker.id}/time-control?timestamp=${timestamp}`;
const params = {
recipient: this.worker.user.emailUser.email,
week: this.weekNumber,
year: this.date.getFullYear(),
url: url,
};
this.$http.post(`WorkerTimeControls/weekly-hour-hecord-email`, params)
.then(() => {
this.vnApp.showSuccess(this.$t('Email sended'));
});
}
getTime(timeString) {
const [hours, minutes, seconds] = timeString.split(':');
return [parseInt(hours), parseInt(minutes), parseInt(seconds)];
}
getMailStates(date) {
const params = {
month: date.getMonth() + 1,
year: date.getFullYear()
};
const query = `WorkerTimeControls/${this.$params.id}/getMailStates`;
this.$http.get(query, {params})
.then(res => {
this.workerTimeControlMails = res.data;
this.repaint();
});
}
formatWeek($element) {
const weekNumberHTML = $element.firstElementChild;
const weekNumberValue = weekNumberHTML.innerHTML;
if (!this.workerTimeControlMails) return;
const workerTimeControlMail = this.workerTimeControlMails.find(
workerTimeControlMail => workerTimeControlMail.week == weekNumberValue
);
if (!workerTimeControlMail) return;
const state = workerTimeControlMail.state;
if (state == 'CONFIRMED') {
weekNumberHTML.classList.remove('revise');
weekNumberHTML.classList.remove('sended');
weekNumberHTML.classList.add('confirmed');
weekNumberHTML.setAttribute('title', 'Conforme');
}
if (state == 'REVISE') {
weekNumberHTML.classList.remove('confirmed');
weekNumberHTML.classList.remove('sended');
weekNumberHTML.classList.add('revise');
weekNumberHTML.setAttribute('title', 'No conforme');
}
if (state == 'SENDED') {
weekNumberHTML.classList.add('sended');
weekNumberHTML.setAttribute('title', 'Pendiente');
}
}
repaint() {
let calendars = this.element.querySelectorAll('vn-calendar');
for (let calendar of calendars)
calendar.$ctrl.repaint();
}
} }
Controller.$inject = ['$element', '$scope', 'vnWeekDays']; Controller.$inject = ['$element', '$scope', 'vnWeekDays'];

View File

@ -5,12 +5,14 @@ describe('Component vnWorkerTimeControl', () => {
let $scope; let $scope;
let $element; let $element;
let controller; let controller;
let $httpParamSerializer;
beforeEach(ngModule('worker')); beforeEach(ngModule('worker'));
beforeEach(inject(($componentController, $rootScope, $stateParams, _$httpBackend_) => { beforeEach(inject(($componentController, $rootScope, $stateParams, _$httpBackend_, _$httpParamSerializer_) => {
$stateParams.id = 1; $stateParams.id = 1;
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$scope = $rootScope.$new(); $scope = $rootScope.$new();
$element = angular.element('<vn-worker-time-control></vn-worker-time-control>'); $element = angular.element('<vn-worker-time-control></vn-worker-time-control>');
controller = $componentController('vnWorkerTimeControl', {$element, $scope}); controller = $componentController('vnWorkerTimeControl', {$element, $scope});
@ -82,6 +84,9 @@ describe('Component vnWorkerTimeControl', () => {
$httpBackend.whenRoute('GET', 'Workers/:id/getWorkedHours') $httpBackend.whenRoute('GET', 'Workers/:id/getWorkedHours')
.respond(response); .respond(response);
$httpBackend.whenRoute('GET', 'WorkerTimeControlMails')
.respond([]);
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
let weekOffset = today.getDay() - 1; let weekOffset = today.getDay() - 1;
@ -97,7 +102,6 @@ describe('Component vnWorkerTimeControl', () => {
controller.ended = ended; controller.ended = ended;
controller.getWorkedHours(controller.started, controller.ended); controller.getWorkedHours(controller.started, controller.ended);
$httpBackend.flush(); $httpBackend.flush();
expect(controller.weekDays.length).toEqual(7); expect(controller.weekDays.length).toEqual(7);
@ -152,5 +156,120 @@ describe('Component vnWorkerTimeControl', () => {
expect(controller.date.toDateString()).toEqual(date.toDateString()); expect(controller.date.toDateString()).toEqual(date.toDateString());
}); });
}); });
describe('getWeekData() ', () => {
it(`should make a query an then update the state and reason`, () => {
const today = Date.vnNew();
const response = [
{
state: 'SENDED',
reason: null
}
];
controller._date = today;
$httpBackend.whenRoute('GET', 'WorkerTimeControlMails')
.respond(response);
controller.getWeekData();
$httpBackend.flush();
expect(controller.state).toBe('SENDED');
expect(controller.reason).toBe(null);
});
});
describe('isSatisfied() ', () => {
it(`should make a query an then call three methods`, () => {
const today = Date.vnNew();
jest.spyOn(controller, 'getWeekData').mockReturnThis();
jest.spyOn(controller, 'getMailStates').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess');
controller.$.model = {applyFilter: jest.fn().mockReturnValue(Promise.resolve())};
controller.worker = {id: 1};
controller.date = today;
controller.weekNumber = 1;
$httpBackend.expect('POST', 'WorkerTimeControls/updateWorkerTimeControlMail').respond();
controller.isSatisfied();
$httpBackend.flush();
expect(controller.getMailStates).toHaveBeenCalledWith(controller.date);
expect(controller.getWeekData).toHaveBeenCalled();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('isUnsatisfied() ', () => {
it(`should throw an error is reason is empty`, () => {
let error;
try {
controller.isUnsatisfied();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(`You must indicate a reason`);
});
it(`should make a query an then call three methods`, () => {
const today = Date.vnNew();
jest.spyOn(controller, 'getWeekData').mockReturnThis();
jest.spyOn(controller, 'getMailStates').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess');
controller.$.model = {applyFilter: jest.fn().mockReturnValue(Promise.resolve())};
controller.worker = {id: 1};
controller.date = today;
controller.weekNumber = 1;
controller.reason = 'reason';
$httpBackend.expect('POST', 'WorkerTimeControls/updateWorkerTimeControlMail').respond();
controller.isSatisfied();
$httpBackend.flush();
expect(controller.getMailStates).toHaveBeenCalledWith(controller.date);
expect(controller.getWeekData).toHaveBeenCalled();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('resendEmail() ', () => {
it(`should make a query an then call showSuccess method`, () => {
const today = Date.vnNew();
jest.spyOn(controller, 'getWeekData').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess');
controller.$.model = {applyFilter: jest.fn().mockReturnValue(Promise.resolve())};
controller.worker = {id: 1};
controller.worker = {user: {emailUser: {email: 'employee@verdnatura.es'}}};
controller.date = today;
controller.weekNumber = 1;
$httpBackend.expect('POST', 'WorkerTimeControls/weekly-hour-hecord-email').respond();
controller.resendEmail();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('getMailStates() ', () => {
it(`should make a query an then call showSuccess method`, () => {
const today = Date.vnNew();
jest.spyOn(controller, 'repaint').mockReturnThis();
controller.$params = {id: 1};
$httpBackend.expect('GET', `WorkerTimeControls/1/getMailStates?month=1&year=2001`).respond();
controller.getMailStates(today);
$httpBackend.flush();
expect(controller.repaint).toHaveBeenCalled();
});
});
}); });
}); });

View File

@ -13,4 +13,10 @@ Entry removed: Fichada borrada
The entry type can't be empty: El tipo de fichada no puede quedar vacía The entry type can't be empty: El tipo de fichada no puede quedar vacía
Satisfied: Conforme Satisfied: Conforme
Not satisfied: No conforme Not satisfied: No conforme
Reason: Motivo Reason: Motivo
Resend: Reenviar
Email sended: Email enviado
You must indicate a reason: Debes indicar un motivo
Send time control email: Enviar email control horario
Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
Resend email of this week to the user: Reenviar email de esta semana al usuario

View File

@ -14,7 +14,7 @@ vn-worker-time-control {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 4px 0; padding: 4px 0;
& > vn-icon { & > vn-icon {
color: $color-font-secondary; color: $color-font-secondary;
padding-right: 1px; padding-right: 1px;
@ -24,8 +24,29 @@ vn-worker-time-control {
.totalBox { .totalBox {
max-width: none max-width: none
} }
}
.reasonDialog{
min-width: 500px;
} }
.edit-time-entry { .edit-time-entry {
width: 200px width: 200px
} }
.right {
float: right;
}
.confirmed {
color: #97B92F;
}
.revise {
color: #f61e1e;
}
.sended {
color: #d19b25;
}