diff --git a/db/changes/10230-oktoberFest/00-ACL.sql b/db/changes/10230-oktoberFest/00-ACL.sql new file mode 100644 index 000000000..0e5c9e1dc --- /dev/null +++ b/db/changes/10230-oktoberFest/00-ACL.sql @@ -0,0 +1 @@ +UPDATE salix.ACL t SET t.principalId = 'salesAssistant' WHERE t.id = 234 \ No newline at end of file diff --git a/db/changes/10230-oktoberFest/00-department.sql b/db/changes/10230-oktoberFest/00-department.sql new file mode 100644 index 000000000..38d4f6875 --- /dev/null +++ b/db/changes/10230-oktoberFest/00-department.sql @@ -0,0 +1,4 @@ +ALTER TABLE `vn`.department + ADD notificationEmail VARCHAR(150) null; + +UPDATE vn.department t SET t.notificationEmail = 'direccioncomercial@verdnatura.es' WHERE t.id = 43 \ No newline at end of file diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index ac0386876..62a5b7ca2 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -42,8 +42,8 @@ INSERT INTO `vn`.`worker`(`id`,`code`, `firstName`, `lastName`, `userFk`, `bossF FROM `vn`.`user`; UPDATE `vn`.`worker` SET bossFk = NULL WHERE id = 20; -UPDATE `vn`.`worker` SET bossFk = 20 - WHERE id = 1 OR id = 9; +UPDATE `vn`.`worker` SET bossFk = 20 WHERE id = 1 OR id = 9; +UPDATE `vn`.`worker` SET bossFk = 19 WHERE id = 18; DELETE FROM `vn`.`worker` WHERE firstName ='customer'; @@ -1696,6 +1696,10 @@ UPDATE `postgresql`.`business_labour` bl SET bl.`professional_category_id` = 31 WHERE p.`Id_trabajador` = 110; +UPDATE `postgresql`.`business_labour` bl + SET bl.`department_id` = 43 +WHERE business_id IN(18, 19); + INSERT INTO `postgresql`.`media`(`media_id`, `media_type_id`, `value`, `sort`) VALUES (1, 10, 600123321, 0), diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 114b07edf..4a970190d 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -68,5 +68,7 @@ "Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member", "Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}", "Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment", - "NOT_ZONE_WITH_THIS_PARAMETERS": "NOT_ZONE_WITH_THIS_PARAMETERS" + "NOT_ZONE_WITH_THIS_PARAMETERS": "NOT_ZONE_WITH_THIS_PARAMETERS", + "Created absence": "The worker {{author}} has added an absence of type '{{absenceType}}' to {{employee}} for day {{dated}}.", + "Deleted absence": "The worker {{author}} has deleted an absence of type '{{absenceType}}' to {{employee}} for day {{dated}}." } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index ea5a09952..1f536c3ce 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -143,5 +143,8 @@ "Role name must be written in camelCase": "Role name must be written in camelCase", "can't be set": "can't be set", "Email already exists": "Email already exists", - "User already exists": "User already exists" + "User already exists": "User already exists", + "Absence change notification on the labour calendar": "Notificacion de cambio de ausencia en el calendario laboral", + "Created absence": "El empleado {{author}} ha añadido una ausencia de tipo '{{absenceType}}' a {{employee}} para el día {{dated}}.", + "Deleted absence": "El empleado {{author}} ha eliminado una ausencia de tipo '{{absenceType}}' a {{employee}} del día {{dated}}." } \ No newline at end of file diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index 636e32ffa..36bc60dfa 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -186,10 +186,16 @@ module.exports = Self => { let payMethodWithIban = 4; // Validate socialName format - const socialNameChanged = orgData && changes - && orgData.socialName != changes.socialName; + const hasChanges = orgData && changes; + const socialName = changes.socialName || orgData.socialName; + const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked); - if (socialNameChanged && !isAlpha(changes.socialName)) + const socialNameChanged = hasChanges + && orgData.socialName != socialName; + const dataCheckedChanged = hasChanges + && orgData.isTaxDataChecked != isTaxDataChecked; + + if ((socialNameChanged || dataCheckedChanged) && !isAlpha(socialName)) throw new UserError('The socialName has an invalid format'); if (changes.salesPerson === null) { diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index 3163e697d..cc0ee13a6 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -31,6 +31,7 @@ module.exports = Self => { Self.createAbsence = async(ctx, id, absenceTypeId, dated) => { const models = Self.app.models; + const $t = ctx.req.__; // $translate const userId = ctx.req.accessToken.userId; const isSubordinate = await models.Worker.isSubordinate(ctx, id); const isTeamBoss = await models.Account.hasRole(userId, 'teamBoss'); @@ -39,6 +40,7 @@ module.exports = Self => { throw new UserError(`You don't have enough privileges`); const labour = await models.WorkerLabour.findOne({ + include: {relation: 'department'}, where: { and: [ {workerFk: id}, @@ -49,10 +51,42 @@ module.exports = Self => { } }); - return models.Calendar.create({ + const absence = await models.Calendar.create({ businessFk: labour.businessFk, dayOffTypeFk: absenceTypeId, dated: dated }); + + const department = labour.department(); + if (department && department.notificationEmail) { + const absenceType = await models.AbsenceType.findById(absenceTypeId); + const account = await models.Account.findById(userId); + const subordinated = await models.Account.findById(id); + const origin = ctx.req.headers.origin; + const body = $t('Created absence', { + author: account.nickname, + employee: subordinated.nickname, + absenceType: absenceType.name, + dated: formatDate(dated), + workerUrl: `${origin}/#!/worker/${id}/calendar` + }); + await models.Mail.create({ + subject: $t('Absence change notification on the labour calendar'), + body: body, + sender: department.notificationEmail + }); + } + + return absence; }; + + function formatDate(date) { + let day = date.getDate(); + if (day < 10) day = `0${day}`; + let month = date.getMonth(); + if (month < 10) month = `0${month}`; + let year = date.getFullYear(); + + return `${day}-${month}-${year}`; + } }; diff --git a/modules/worker/back/methods/worker/deleteAbsence.js b/modules/worker/back/methods/worker/deleteAbsence.js index 0fe8f7dc8..c7e24fa58 100644 --- a/modules/worker/back/methods/worker/deleteAbsence.js +++ b/modules/worker/back/methods/worker/deleteAbsence.js @@ -23,6 +23,7 @@ module.exports = Self => { Self.deleteAbsence = async(ctx, id, absenceId) => { const models = Self.app.models; + const $t = ctx.req.__; // $translate const userId = ctx.req.accessToken.userId; const isSubordinate = await models.Worker.isSubordinate(ctx, id); const isTeamBoss = await models.Account.hasRole(userId, 'teamBoss'); @@ -30,8 +31,46 @@ module.exports = Self => { if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss)) throw new UserError(`You don't have enough privileges`); - const absence = await models.Calendar.findById(absenceId); + const absence = await models.Calendar.findById(absenceId, { + include: { + relation: 'labour', + scope: { + include: {relation: 'department'} + } + } + }); + const result = await absence.destroy(); + const labour = absence.labour(); + const department = labour && labour.department(); + if (department && department.notificationEmail) { + const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk); + const account = await models.Account.findById(userId); + const subordinated = await models.Account.findById(labour.workerFk); + const origin = ctx.req.headers.origin; + const body = $t('Deleted absence', { + author: account.nickname, + employee: subordinated.nickname, + absenceType: absenceType.name, + dated: formatDate(absence.dated), + workerUrl: `${origin}/#!/worker/${id}/calendar` + }); + await models.Mail.create({ + subject: $t('Absence change notification on the labour calendar'), + body: body, + sender: department.notificationEmail + }); + } - return absence.destroy(); + return result; }; + + function formatDate(date) { + let day = date.getDate(); + if (day < 10) day = `0${day}`; + let month = date.getMonth(); + if (month < 10) month = `0${month}`; + let year = date.getFullYear(); + + return `${day}-${month}-${year}`; + } }; diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js index df48cf80b..324dab99d 100644 --- a/modules/worker/back/methods/worker/specs/createAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js @@ -1,7 +1,8 @@ const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); describe('Worker createAbsence()', () => { - const workerId = 106; + const workerId = 18; let createdAbsence; afterAll(async() => { @@ -10,7 +11,7 @@ describe('Worker createAbsence()', () => { }); it('should return an error for a user without enough privileges', async() => { - const ctx = {req: {accessToken: {userId: 106}}}; + const ctx = {req: {accessToken: {userId: 18}}}; const absenceTypeId = 1; const dated = new Date(); @@ -25,12 +26,23 @@ describe('Worker createAbsence()', () => { }); it('should create a new absence', async() => { - const ctx = {req: {accessToken: {userId: 37}}}; + const activeCtx = { + accessToken: {userId: 19}, + headers: {origin: 'http://localhost'} + }; + const ctx = {req: activeCtx}; + ctx.req.__ = value => { + return value; + }; + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + const absenceTypeId = 1; const dated = new Date(); createdAbsence = await app.models.Worker.createAbsence(ctx, workerId, absenceTypeId, dated); - const expectedBusinessId = 106; + const expectedBusinessId = 18; const expectedAbsenceTypeId = 1; expect(createdAbsence.businessFk).toEqual(expectedBusinessId); diff --git a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js index 140edada8..2f72a8435 100644 --- a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js @@ -1,12 +1,25 @@ const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); describe('Worker deleteAbsence()', () => { - const workerId = 106; + const workerId = 18; let createdAbsence; + const activeCtx = { + accessToken: {userId: 19}, + headers: {origin: 'http://localhost'} + }; + const ctx = {req: activeCtx}; + ctx.req.__ = value => { + return value; + }; it('should return an error for a user without enough privileges', async() => { - const ctx = {req: {accessToken: {userId: 106}}}; - const businessId = 106; + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + + activeCtx.accessToken.userId = 106; + const businessId = 18; createdAbsence = await app.models.Calendar.create({ businessFk: businessId, dayOffTypeFk: 1, @@ -14,7 +27,7 @@ describe('Worker deleteAbsence()', () => { }); let error; - await app.models.Worker.deleteAbsence(ctx, workerId, createdAbsence.id).catch(e => { + await app.models.Worker.deleteAbsence(ctx, 18, createdAbsence.id).catch(e => { error = e; }).finally(() => { expect(error.message).toEqual(`You don't have enough privileges`); @@ -24,8 +37,12 @@ describe('Worker deleteAbsence()', () => { }); it('should create a new absence', async() => { - const ctx = {req: {accessToken: {userId: 37}}}; - const businessId = 106; + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + + activeCtx.accessToken.userId = 19; + const businessId = 18; expect(createdAbsence.businessFk).toEqual(businessId); diff --git a/modules/worker/back/models/calendar.json b/modules/worker/back/models/calendar.json index 417442571..199d81e6c 100644 --- a/modules/worker/back/models/calendar.json +++ b/modules/worker/back/models/calendar.json @@ -1,6 +1,10 @@ { "name": "Calendar", - "base": "VnModel", + "base": "Loggable", + "log": { + "model": "WorkerLog", + "relation": "labour" + }, "options": { "mysql": { "table": "calendar" @@ -23,6 +27,11 @@ "type": "belongsTo", "model": "AbsenceType", "foreignKey": "dayOffTypeFk" + }, + "labour": { + "type": "belongsTo", + "model": "WorkerLabour", + "foreignKey": "businessFk" } } } diff --git a/modules/worker/back/models/department.json b/modules/worker/back/models/department.json index d8ec7313a..31ebbb09a 100644 --- a/modules/worker/back/models/department.json +++ b/modules/worker/back/models/department.json @@ -28,6 +28,9 @@ }, "chatName": { "type": "String" + }, + "notificationEmail": { + "type": "String" } } } diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json index 7825e9735..9ab2f597e 100644 --- a/modules/worker/front/routes.json +++ b/modules/worker/front/routes.json @@ -63,7 +63,7 @@ "state": "worker.card.workerLog", "component": "vn-worker-log", "description": "Log", - "acl": ["hr"] + "acl": ["salesAssistant"] }, { "url": "/pbx", "state": "worker.card.pbx",