diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98815430e..41374b4df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,20 +5,31 @@ 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).
-## [2302.01] - 2023-01-12
+## [2304.01] - 2023-02-09
### Added
-- [General](Inicio) Permite recuperar la contraseña
-- [Ticket](Opciones) Subir albarán a Docuware
-- [Ticket](Opciones) Enviar correo con PDF de Docuware
-- [Artículo](Datos Básicos) Añadido campo Unidades/Caja
+- (Trabajadores -> Nuevo trabajador) Nueva sección
### Changed
-- [Reclamaciones](Descriptor) Cambiado el campo Agencia por Zona
-- [Tickets](Líneas preparadas) Actualizada sección para que sea más visual
+-
### Fixed
-- [General] Al utilizar el traductor de Google se descuadraban los iconos
+-
+
+## [2302.01] - 2023-01-26
+
+### Added
+- (General -> Inicio) Permite recuperar la contraseña
+- (Tickets -> Opciones) Subir albarán a Docuware
+- (Tickets -> Opciones) Enviar correo con PDF de Docuware
+- (Artículos -> Datos Básicos) Añadido campo Unidades/Caja
+
+### Changed
+- (Reclamaciones -> Descriptor) Cambiado el campo Agencia por Zona
+- (Tickets -> Líneas preparadas) Actualizada sección para que sea más visual
+
+### Fixed
+- (General) Al utilizar el traductor de Google se descuadraban los iconos
### Removed
-- [Tickets](Control clientes) Eliminada sección
+- (Tickets -> Control clientes) Eliminada sección
diff --git a/back/methods/osticket/closeTicket.js b/back/methods/osticket/closeTicket.js
index 178b09601..32b369c8d 100644
--- a/back/methods/osticket/closeTicket.js
+++ b/back/methods/osticket/closeTicket.js
@@ -36,19 +36,26 @@ module.exports = Self => {
JOIN osticket.ost_ticket_status ots ON ots.id = ot.status_id
JOIN osticket.ost_thread ot2 ON ot2.object_id = ot.ticket_id AND ot2.object_type = 'T'
JOIN (
- SELECT ote.thread_id, MAX(ote.created) created, MAX(ote.updated) updated
- FROM osticket.ost_thread_entry ote
- WHERE ote.staff_id AND ote.type = 'R'
- GROUP BY ote.thread_id
+ SELECT sub2.thread_id, sub2.type, sub2.updated, sub2.created
+ FROM (
+ SELECT ote.thread_id, ote.created, ote.updated, ote.type
+ FROM osticket.ost_thread_entry ote
+ WHERE ote.staff_id
+ ORDER BY ote.id DESC
+ LIMIT 10000000000000000000) sub2
+ GROUP BY sub2.thread_id
) sub ON sub.thread_id = ot2.id
WHERE ot.isanswered
- AND ots.state = ?
- AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ? DAY)`;
+ AND ots.id IN (?)
+ AND sub.type = 'R'
+ AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ? DAY);`;
const ticketsId = [];
+ const statusIdToClose = config.oldStatus.split(',');
+
con.connect(err => {
if (err) throw err;
- con.query(sql, [config.oldStatus, config.day],
+ con.query(sql, [statusIdToClose, config.day],
(err, results) => {
if (err) throw err;
for (const result of results)
diff --git a/db/changes/225203/00-mdbApp.sql b/db/changes/225203/00-mdbApp.sql
new file mode 100644
index 000000000..32a21eb6b
--- /dev/null
+++ b/db/changes/225203/00-mdbApp.sql
@@ -0,0 +1,5 @@
+UPDATE `vn`.`osTicketConfig`
+SET oldStatus='1,6'
+WHERE id=0;
+
+
diff --git a/db/changes/230201/00-validPriorities_ItemConfig.sql b/db/changes/230201/00-validPriorities_ItemConfig.sql
new file mode 100644
index 000000000..a793997d0
--- /dev/null
+++ b/db/changes/230201/00-validPriorities_ItemConfig.sql
@@ -0,0 +1,9 @@
+ALTER TABLE `vn`.`itemConfig` ADD defaultTag INT DEFAULT 56 NOT NULL;
+ALTER TABLE `vn`.`itemConfig` ADD CONSTRAINT itemConfig_FK FOREIGN KEY (defaultTag) REFERENCES vn.tag(id);
+ALTER TABLE `vn`.`itemConfig` ADD validPriorities varchar(50) DEFAULT '[1,2,3]' NOT NULL;
+ALTER TABLE `vn`.`itemConfig` ADD defaultPriority INT DEFAULT 2 NOT NULL;
+ALTER TABLE `vn`.`item` MODIFY COLUMN relevancy tinyint(1) DEFAULT 0 NOT NULL COMMENT 'La web ordena de forma descendiente por este campo para mostrar los artículos';
+
+INSERT INTO `salix`.`ACL`
+(model, property, accessType, permission, principalType, principalId)
+VALUES('ItemConfig', '*', 'READ', 'ALLOW', 'ROLE', 'buyer');
diff --git a/db/changes/230201/00-workerTimeControlConfig.sql b/db/changes/230201/00-workerTimeControlConfig.sql
new file mode 100644
index 000000000..c04acd936
--- /dev/null
+++ b/db/changes/230201/00-workerTimeControlConfig.sql
@@ -0,0 +1,6 @@
+ALTER TABLE `vn`.`workerTimeControlConfig` ADD teleworkingStart INT NULL COMMENT 'Hora comienzo jornada de los teletrabajdores expresada en segundos';
+ALTER TABLE `vn`.`workerTimeControlConfig` ADD teleworkingStartBreakTime INT NULL COMMENT 'Hora comienzo descanso de los teletrabjadores expresada en segundos';
+
+UPDATE `vn`.`workerTimeControlConfig`
+ SET `teleworkingStart`=28800, `teleworkingStartBreakTime`=32400
+WHERE `id`=1;
diff --git a/db/changes/230401/00-createWorker.sql b/db/changes/230401/00-createWorker.sql
new file mode 100644
index 000000000..7ca2c41ee
--- /dev/null
+++ b/db/changes/230401/00-createWorker.sql
@@ -0,0 +1,24 @@
+UPDATE `salix`.`ACL`
+SET accessType='READ'
+WHERE model='Worker'
+ AND property='*'
+ AND accessType='*'
+ AND permission='ALLOW'
+ AND principalType='ROLE'
+ AND principalId='employee';
+
+
+INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
+ VALUES
+ ('Worker', 'updateAttributes', 'WRITE', 'ALLOW', 'ROLE', 'hr'),
+ ('Worker', 'createAbsence', '*', 'ALLOW', 'ROLE', 'employee'),
+ ('Worker', 'updateAbsence', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('Worker', 'deleteAbsence', '*', 'ALLOW', 'ROLE', 'employee'),
+ ('Worker', 'new', 'WRITE', 'ALLOW', 'ROLE', 'hr'),
+ ('Role', '*', 'READ', 'ALLOW', 'ROLE', 'hr');
+
+ALTER TABLE `vn`.`workerConfig` ADD roleFk int(10) unsigned NOT NULL COMMENT 'Rol por defecto al dar de alta un trabajador nuevo';
+UPDATE `vn`.`workerConfig`
+ SET roleFk = 1
+ WHERE id = 1;
+
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index f5b884aac..218fcb9ca 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -2629,7 +2629,7 @@ INSERT INTO `vn`.`machineWorker` (`workerFk`, `machineFk`, `inTimed`, `outTimed`
INSERT INTO `vn`.`zoneExclusion` (`id`, `zoneFk`, `dated`, `created`, `userFk`)
VALUES
- (1, 1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=7, 7, 14) - DAYOFWEEK(util.VN_CURDATE())) DAY), util.VN_CURDATE(), 100),
+ (1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL (IF(DAYOFWEEK(util.VN_CURDATE())<=7, 7, 14) - DAYOFWEEK(util.VN_CURDATE())) DAY), util.VN_CURDATE(), 100),
(2, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL (IF(DAYOFWEEK(util.VN_CURDATE())<=8, 8, 15) - DAYOFWEEK(util.VN_CURDATE())) DAY), util.VN_CURDATE(), 100);
INSERT INTO `vn`.`zoneExclusionGeo` (`zoneExclusionFk`, `geoFk`)
@@ -2667,9 +2667,9 @@ INSERT INTO `vn`.`sectorCollectionSaleGroup` (`sectorCollectionFk`, `saleGroupFk
VALUES
(1, 1);
-INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`)
+INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`, `teleworkingStart`, `teleworkingStartBreakTime`)
VALUES
- (1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13);
+ (1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13, 28800, 32400);
INSERT INTO `vn`.`host` (`id`, `code`, `description`, `warehouseFk`, `bankFk`)
VALUES
@@ -2719,6 +2719,10 @@ INSERT INTO `vn`.`collection` (`id`, `created`, `workerFk`, `stateFk`, `itemPack
VALUES
(3, util.VN_NOW(), 1107, 5, NULL, 0, 0, 1, NULL, NULL);
+INSERT INTO `vn`.`itemConfig` (`id`, `isItemTagTriggerDisabled`, `monthToDeactivate`, `wasteRecipients`, `validPriorities`, `defaultPriority`, `defaultTag`)
+ VALUES
+ (0, 0, 24, '', '[1,2,3]', 2, 56);
+
INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `created`, `level`, `wagon`, `smartTagFk`, `usedShelves`, `itemCount`, `liters`)
VALUES
(9, 3, util.VN_NOW(), NULL, 0, NULL, NULL, NULL, NULL);
@@ -2741,16 +2745,20 @@ INSERT INTO `vn`.`ticketLog` (`originFk`, userFk, `action`, changedModel, oldIns
INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`)
VALUES
- (0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', 'open', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
+ (0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', '1,6', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
INSERT INTO `vn`.`mdbApp` (`app`, `baselineBranchFk`, `userFk`, `locked`)
VALUES
('foo', 'master', NULL, NULL),
('bar', 'test', 9, util.VN_NOW());
-INSERT INTO `vn`.`ticketLog` (`id`, `originFk`, `userFk`, `action`, `changedModel`, `oldInstance`, `newInstance`, `changedModelId`)
- VALUES
- (1, 1, 9, 'insert', 'Ticket', '{}', '{"clientFk":1, "nickname": "Bat cave"}', 1);
+INSERT INTO `vn`.`profileType` (`id`, `name`)
+ VALUES
+ (1, 'working');
+
+INSERT INTO `vn`.`newWorkerConfig` (`id`, `street`, `provinceFk`, `companyFk`, `profileTypeFk`, `roleFk`)
+ VALUES
+ (1, 'S/ ', 1, 442, 1, 1);
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
VALUES
@@ -2761,3 +2769,7 @@ INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
VALUES
(1, 1);
+INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`)
+ VALUES
+ (1, NULL, 1);
+
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index c0f10a506..a1412f431 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -58,6 +58,7 @@ export default {
deleteAccount: '.vn-menu [name="deleteUser"]',
setPassword: '.vn-menu [name="setPassword"]',
activateAccount: '.vn-menu [name="enableAccount"]',
+ disableAccount: '.vn-menu [name="disableAccount"]',
activateUser: '.vn-menu [name="activateUser"]',
deactivateUser: '.vn-menu [name="deactivateUser"]',
newPassword: 'vn-textfield[ng-model="$ctrl.newPassword"]',
@@ -428,6 +429,7 @@ export default {
},
itemCreateView: {
temporalName: 'vn-item-create vn-textfield[ng-model="$ctrl.item.provisionalName"]',
+ priority: 'vn-autocomplete[ng-model="$ctrl.item.priority"]',
type: 'vn-autocomplete[ng-model="$ctrl.item.typeFk"]',
intrastat: 'vn-autocomplete[ng-model="$ctrl.item.intrastatFk"]',
origin: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
@@ -521,7 +523,7 @@ export default {
},
itemLog: {
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
- fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(3) td.after',
+ fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(2) td.after',
},
ticketSummary: {
header: 'vn-ticket-summary > vn-card > h5',
@@ -968,6 +970,7 @@ export default {
confirmButton: '.vn-confirm.shown button[response="accept"]'
},
workerSummary: {
+ summaryIcon: 'vn-worker-descriptor a[title="Go to module summary"]',
header: 'vn-worker-summary h5',
id: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(3) > section > span',
email: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(4) > section > span',
@@ -1020,6 +1023,25 @@ export default {
furlough: 'vn-worker-calendar > vn-side-menu [name="absenceTypes"] > vn-chip:nth-child(4)',
halfFurlough: 'vn-worker-calendar > vn-side-menu [name="absenceTypes"] > vn-chip:nth-child(5)',
},
+ workerCreate: {
+ newWorkerButton: 'vn-worker-index a[ui-sref="worker.create"]',
+ firstname: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.firstName"]',
+ lastname: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.lastNames"]',
+ birth: 'vn-worker-create vn-date-picker[ng-model="$ctrl.worker.birth"]',
+ fi: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.fi"]',
+ code: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.code"]',
+ phone: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.phone"]',
+ city: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.city"]',
+ postcode: 'vn-worker-create vn-datalist[ng-model="$ctrl.worker.postcode"]',
+ street: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.street"]',
+ user: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.name"]',
+ email: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.email"]',
+ boss: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bossFk"]',
+ role: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.roleFk"]',
+ iban: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.iban"]',
+ switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
+ createButton: 'vn-worker-create vn-submit[label="Create"]',
+ },
invoiceOutIndex: {
topbarSearch: 'vn-searchbar',
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
diff --git a/e2e/paths/03-worker/06_create.spec.js b/e2e/paths/03-worker/06_create.spec.js
new file mode 100644
index 000000000..5f6f5cf9f
--- /dev/null
+++ b/e2e/paths/03-worker/06_create.spec.js
@@ -0,0 +1,74 @@
+import selectors from '../../helpers/selectors.js';
+import getBrowser from '../../helpers/puppeteer';
+
+describe('Worker create path', () => {
+ let browser;
+ let page;
+ let newWorker;
+ beforeAll(async() => {
+ browser = await getBrowser();
+ page = browser.page;
+ await page.loginAndModule('hr', 'worker');
+ await page.waitToClick(selectors.workerCreate.newWorkerButton);
+ await page.waitForState('worker.create');
+ });
+
+ afterAll(async() => {
+ await browser.close();
+ });
+
+ it('should insert default data', async() => {
+ await page.write(selectors.workerCreate.firstname, 'Victor');
+ await page.write(selectors.workerCreate.lastname, 'Von Doom');
+ await page.write(selectors.workerCreate.fi, '78457139E');
+ await page.write(selectors.workerCreate.phone, '12356789');
+ await page.write(selectors.workerCreate.postcode, '46680');
+ await page.write(selectors.workerCreate.street, 'S/ Doomstadt');
+ await page.write(selectors.workerCreate.email, 'doctorDoom@marvel.com');
+ await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332');
+ await page.autocompleteSearch(selectors.workerCreate.switft, 'BBKKESMMMMM');
+
+ // should check for autocompleted worker code and worker user name
+ const workerCode = await page
+ .waitToGetProperty(selectors.workerCreate.code, 'value');
+
+ newWorker = await page
+ .waitToGetProperty(selectors.workerCreate.user, 'value');
+
+ expect(workerCode).toEqual('VVD');
+ expect(newWorker).toContain('victorvd');
+
+ // should fail if necessary data is void
+ await page.waitToClick(selectors.workerCreate.createButton);
+ let message = await page.waitForSnackbar();
+
+ expect(message.text).toContain('is a required argument');
+
+ // should create a new worker and go to worker basic data'
+ await page.pickDate(selectors.workerCreate.birth, new Date(1962, 8, 5));
+ await page.autocompleteSearch(selectors.workerCreate.boss, 'deliveryBoss');
+ await page.waitToClick(selectors.workerCreate.createButton);
+ message = await page.waitForSnackbar();
+ await page.waitForState('worker.card.basicData');
+
+ expect(message.text).toContain('Data saved!');
+
+ // 'rollback'
+ await page.loginAndModule('sysadmin', 'account');
+ await page.accessToSearchResult(newWorker);
+
+ await page.waitToClick(selectors.accountDescriptor.menuButton);
+ await page.waitToClick(selectors.accountDescriptor.deactivateUser);
+ await page.waitToClick(selectors.accountDescriptor.acceptButton);
+ message = await page.waitForSnackbar();
+
+ expect(message.text).toContain('User deactivated!');
+
+ await page.waitToClick(selectors.accountDescriptor.menuButton);
+ await page.waitToClick(selectors.accountDescriptor.disableAccount);
+ await page.waitToClick(selectors.accountDescriptor.acceptButton);
+ message = await page.waitForSnackbar();
+
+ expect(message.text).toContain('Account disabled!');
+ });
+});
diff --git a/e2e/paths/04-item/07_create.spec.js b/e2e/paths/04-item/07_create.spec.js
index 0820f2db7..33324cdba 100644
--- a/e2e/paths/04-item/07_create.spec.js
+++ b/e2e/paths/04-item/07_create.spec.js
@@ -36,11 +36,20 @@ describe('Item Create', () => {
await page.waitForState('item.create');
});
- it('should create the Infinity Gauntlet item', async() => {
+ it('should throw an error when insert an invalid priority', async() => {
await page.write(selectors.itemCreateView.temporalName, 'Infinity Gauntlet');
await page.autocompleteSearch(selectors.itemCreateView.type, 'Crisantemo');
await page.autocompleteSearch(selectors.itemCreateView.intrastat, 'Coral y materiales similares');
await page.autocompleteSearch(selectors.itemCreateView.origin, 'Holand');
+ await page.clearInput(selectors.itemCreateView.priority);
+ await page.waitToClick(selectors.itemCreateView.createButton);
+ const message = await page.waitForSnackbar();
+
+ expect(message.text).toContain('Valid priorities');
+ });
+
+ it('should create the Infinity Gauntlet item', async() => {
+ await page.autocompleteSearch(selectors.itemCreateView.priority, '2');
await page.waitToClick(selectors.itemCreateView.createButton);
const message = await page.waitForSnackbar();
diff --git a/front/salix/components/reset-password/locale/es.yml b/front/salix/components/reset-password/locale/es.yml
index 0771d5dc3..30893a152 100644
--- a/front/salix/components/reset-password/locale/es.yml
+++ b/front/salix/components/reset-password/locale/es.yml
@@ -1,6 +1,6 @@
Reset password: Restrablecer contraseña
New password: Nueva contraseña
-Repeat password: Repetir contraseñaç
+Repeat password: Repetir contraseña
Password changed!: ¡Contraseña cambiada!
Password requirements: >
La contraseña debe tener al menos {{ length }} caracteres de longitud,
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index f6f305dc3..d65054f37 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -252,9 +252,12 @@
"Receipt's bank was not found": "No se encontró el banco del recibo",
"This receipt was not compensated": "Este recibo no ha sido compensado",
"Client's email was not found": "No se encontró el email del cliente",
+ "This worker code already exists": "Este codigo de trabajador ya existe",
+ "This personal mail already exists": "Este correo personal ya existe",
+ "This worker already exists": "Este trabajador ya existe",
"App name does not exist": "El nombre de aplicación no es válido",
"Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload file": "Error al subir archivo",
"The DOCUWARE PDF document does not exists": "The DOCUWARE PDF document does not exists"
-}
\ No newline at end of file
+}
diff --git a/modules/invoiceIn/front/descriptor/index.html b/modules/invoiceIn/front/descriptor/index.html
index 40f7dec18..223914c9c 100644
--- a/modules/invoiceIn/front/descriptor/index.html
+++ b/modules/invoiceIn/front/descriptor/index.html
@@ -36,13 +36,13 @@
ng-if="$ctrl.isAgricultural()"
ng-click="$ctrl.showPdfInvoice()"
translate>
- Show agricultural invoice as PDF
+ Show agricultural receipt as PDF
- Send agricultural invoice as PDF
+ Send agricultural receipt as PDF
diff --git a/modules/invoiceIn/front/locale/es.yml b/modules/invoiceIn/front/locale/es.yml
index d8400dd67..35b43f9f6 100644
--- a/modules/invoiceIn/front/locale/es.yml
+++ b/modules/invoiceIn/front/locale/es.yml
@@ -19,6 +19,6 @@ To book: Contabilizar
Total amount: Total importe
Total net: Total neto
Total stems: Total tallos
-Show agricultural invoice as PDF: Ver factura agrícola como PDF
-Send agricultural invoice as PDF: Enviar factura agrícola como PDF
-New InvoiceIn: Nueva Factura
\ No newline at end of file
+Show agricultural receipt as PDF: Ver recibo agrícola como PDF
+Send agricultural receipt as PDF: Enviar recibo agrícola como PDF
+New InvoiceIn: Nueva Factura
diff --git a/modules/item/back/methods/item/new.js b/modules/item/back/methods/item/new.js
index fae37836f..0057cb50f 100644
--- a/modules/item/back/methods/item/new.js
+++ b/modules/item/back/methods/item/new.js
@@ -37,7 +37,8 @@ module.exports = Self => {
'typeFk',
'intrastatFk',
'originFk',
- 'relevancy'
+ 'priority',
+ 'tag'
];
for (const key in params) {
@@ -46,10 +47,14 @@ module.exports = Self => {
}
try {
+ const itemConfig = await models.ItemConfig.findOne({fields: ['validPriorities']}, myOptions);
+ if (!itemConfig.validPriorities.includes(params.priority))
+ throw new UserError(`Valid priorities: ${[...itemConfig.validPriorities]}`);
+
const provisionalName = params.provisionalName;
delete params.provisionalName;
- const itemType = await models.ItemType.findById(params.typeFk, myOptions);
+ const itemType = await models.ItemType.findById(params.typeFk, {fields: ['isLaid']}, myOptions);
params.isLaid = itemType.isLaid;
@@ -63,13 +68,14 @@ module.exports = Self => {
await Self.rawSql(query, null, myOptions);
- let nameTag = await models.Tag.findOne({where: {name: 'Nombre temporal'}});
+ const nameTag = await models.Tag.findById(params.tag, {fields: ['id']}, myOptions);
let newTags = [];
- newTags.push({itemFk: item.id, tagFk: nameTag.id, value: provisionalName, priority: '2'});
+ newTags.push({itemFk: item.id, tagFk: nameTag.id, value: provisionalName, priority: item.priority});
typeTags.forEach(typeTag => {
- newTags.push({itemFk: item.id, tagFk: typeTag.tagFk, value: '', priority: typeTag.priority});
+ if (nameTag.id != typeTag.tagFk)
+ newTags.push({itemFk: item.id, tagFk: typeTag.tagFk, value: '', priority: typeTag.priority});
});
await models.ItemTag.create(newTags, myOptions);
diff --git a/modules/item/back/methods/item/specs/new.spec.js b/modules/item/back/methods/item/specs/new.spec.js
index 7364faa7d..e34ab2cf5 100644
--- a/modules/item/back/methods/item/specs/new.spec.js
+++ b/modules/item/back/methods/item/specs/new.spec.js
@@ -11,7 +11,8 @@ describe('item new()', () => {
originFk: 1,
provisionalName: 'planta',
typeFk: 2,
- relevancy: 0
+ priority: 2,
+ tag: 1
};
let item = await models.Item.new(itemParams, options);
@@ -20,7 +21,7 @@ describe('item new()', () => {
item.isLaid = itemType.isLaid;
- const temporalNameTag = await models.Tag.findOne({where: {name: 'Nombre temporal'}}, options);
+ const temporalNameTag = await models.Tag.findById(itemParams.tag, {fields: ['id']}, options);
const temporalName = await models.ItemTag.findOne({
where: {
@@ -31,7 +32,7 @@ describe('item new()', () => {
item = await models.Item.findById(item.id, null, options);
- itemType = await models.ItemType.findById(item.typeFk, options);
+ itemType = await models.ItemType.findById(item.typeFk, {fields: ['isLaid']}, options);
item.isLaid = itemType.isLaid;
diff --git a/modules/item/back/models/item-config.json b/modules/item/back/models/item-config.json
index 364879986..36d25e0bb 100644
--- a/modules/item/back/models/item-config.json
+++ b/modules/item/back/models/item-config.json
@@ -16,6 +16,22 @@
"wasteRecipients": {
"type": "string",
"description": "Buyers waste report recipients"
+ },
+ "validPriorities": {
+ "type": "array"
+ },
+ "defaultPriority": {
+ "type": "int"
+ },
+ "defaultTag": {
+ "type": "int"
+ }
+ },
+ "relations": {
+ "tag": {
+ "type": "belongsTo",
+ "model": "Tag",
+ "foreignKey": "defaultTag"
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/item/front/create/index.html b/modules/item/front/create/index.html
index 6deaab1cd..15e212250 100644
--- a/modules/item/front/create/index.html
+++ b/modules/item/front/create/index.html
@@ -16,10 +16,24 @@
+
+
+
+
{
+ if (res.data) {
+ const dataRow = res.data[0];
+ dataRow.validPriorities.forEach(priority => {
+ this.validPriorities.push({priority});
+ });
+ this.item = {
+ priority: dataRow.defaultPriority,
+ tag: dataRow.defaultTag
+ };
+ }
+ });
}
onSubmit() {
diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js
index b38405c1d..78c212e9b 100644
--- a/modules/worker/back/methods/worker-time-control/sendMail.js
+++ b/modules/worker/back/methods/worker-time-control/sendMail.js
@@ -32,94 +32,87 @@ module.exports = Self => {
const models = Self.app.models;
const conn = Self.dataSource.connector;
const args = ctx.args;
- const $t = ctx.req.__; // $translate
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
- if (!myOptions.transaction) {
- tx = await Self.beginTransaction({});
- myOptions.transaction = tx;
- }
-
const stmts = [];
let stmt;
- try {
- if (!args.week || !args.year) {
- const from = new Date();
- const to = new Date();
+ if (!args.week || !args.year) {
+ const from = new Date();
+ const to = new Date();
- const time = await models.Time.findOne({
- where: {
- dated: {between: [from.setDate(from.getDate() - 10), to.setDate(to.getDate() - 4)]}
- },
- order: 'week ASC'
- }, myOptions);
+ const time = await models.Time.findOne({
+ where: {
+ dated: {between: [from.setDate(from.getDate() - 10), to.setDate(to.getDate() - 4)]}
+ },
+ order: 'week ASC'
+ }, myOptions);
- args.week = time.week;
- args.year = time.year;
- }
+ args.week = time.week;
+ args.year = time.year;
+ }
- const started = getStartDateOfWeekNumber(args.week, args.year);
- started.setHours(0, 0, 0, 0);
+ const started = getStartDateOfWeekNumber(args.week, args.year);
+ started.setHours(0, 0, 0, 0);
- const ended = new Date(started);
- ended.setDate(started.getDate() + 6);
- ended.setHours(23, 59, 59, 999);
+ const ended = new Date(started);
+ ended.setDate(started.getDate() + 6);
+ ended.setHours(23, 59, 59, 999);
- stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate');
- stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate');
+ stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate');
+ stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate');
- if (args.workerId) {
- await models.WorkerTimeControl.destroyAll({
- userFk: args.workerId,
- timed: {between: [started, ended]},
- isSendMail: true
- }, myOptions);
+ if (args.workerId) {
+ await models.WorkerTimeControl.destroyAll({
+ userFk: args.workerId,
+ timed: {between: [started, ended]},
+ isSendMail: true
+ }, myOptions);
- const where = {
- workerFk: args.workerId,
- year: args.year,
- week: args.week
- };
- await models.WorkerTimeControlMail.updateAll(where, {
- updated: new Date(), state: 'SENDED'
- }, myOptions);
+ const where = {
+ workerFk: args.workerId,
+ year: args.year,
+ week: args.week
+ };
+ await models.WorkerTimeControlMail.updateAll(where, {
+ updated: new Date(), state: 'SENDED'
+ }, myOptions);
- stmt = new ParameterizedSQL(
- `CALL vn.timeControl_calculateByUser(?, ?, ?)
+ stmt = new ParameterizedSQL(
+ `CALL vn.timeControl_calculateByUser(?, ?, ?)
`, [args.workerId, started, ended]);
- stmts.push(stmt);
+ stmts.push(stmt);
- stmt = new ParameterizedSQL(
- `CALL vn.timeBusiness_calculateByUser(?, ?, ?)
+ stmt = new ParameterizedSQL(
+ `CALL vn.timeBusiness_calculateByUser(?, ?, ?)
`, [args.workerId, started, ended]);
- stmts.push(stmt);
- } else {
- await models.WorkerTimeControl.destroyAll({
- timed: {between: [started, ended]},
- isSendMail: true
- }, myOptions);
+ stmts.push(stmt);
+ } else {
+ await models.WorkerTimeControl.destroyAll({
+ timed: {between: [started, ended]},
+ isSendMail: true
+ }, myOptions);
- const where = {
- year: args.year,
- week: args.week
- };
- await models.WorkerTimeControlMail.updateAll(where, {
- updated: new Date(), state: 'SENDED'
- }, myOptions);
+ const where = {
+ year: args.year,
+ week: args.week
+ };
+ await models.WorkerTimeControlMail.updateAll(where, {
+ updated: new Date(), state: 'SENDED'
+ }, myOptions);
- stmt = new ParameterizedSQL(`CALL vn.timeControl_calculateAll(?, ?)`, [started, ended]);
- stmts.push(stmt);
+ stmt = new ParameterizedSQL(`CALL vn.timeControl_calculateAll(?, ?)`, [started, ended]);
+ stmts.push(stmt);
- stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [started, ended]);
- stmts.push(stmt);
- }
+ stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [started, ended]);
+ stmts.push(stmt);
+ }
- stmt = new ParameterizedSQL(`
+ stmt = new ParameterizedSQL(`
SELECT CONCAT(u.name, '@verdnatura.es') receiver,
u.id workerFk,
tb.dated,
@@ -154,25 +147,33 @@ module.exports = Self => {
AND w.businessFk
ORDER BY u.id, tb.dated
`, [args.workerId]);
- const index = stmts.push(stmt) - 1;
+ const index = stmts.push(stmt) - 1;
- const sql = ParameterizedSQL.join(stmts, ';');
- const days = await conn.executeStmt(sql, myOptions);
+ stmts.push('DROP TEMPORARY TABLE tmp.timeControlCalculate');
+ stmts.push('DROP TEMPORARY TABLE tmp.timeBusinessCalculate');
- let previousWorkerFk = days[index][0].workerFk;
- let previousReceiver = days[index][0].receiver;
+ const sql = ParameterizedSQL.join(stmts, ';');
+ const days = await conn.executeStmt(sql, myOptions);
- const workerTimeControlConfig = await models.WorkerTimeControlConfig.findOne(null, myOptions);
+ let previousWorkerFk = days[index][0].workerFk;
+ let previousReceiver = days[index][0].receiver;
- for (let day of days[index]) {
+ const workerTimeControlConfig = await models.WorkerTimeControlConfig.findOne(null, myOptions);
+
+ for (let day of days[index]) {
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+ try {
workerFk = day.workerFk;
if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null
- && (day.permissionRate ? day.permissionRate : true)) {
+ && (day.permissionRate == null ? true : day.permissionRate)) {
if (day.timeTable == null) {
const timed = new Date(day.dated);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
- timed: timed.setHours(8),
+ timed: timed.setHours(workerTimeControlConfig.teleworkingStart / 3600),
manual: true,
direction: 'in',
isSendMail: true
@@ -181,7 +182,7 @@ module.exports = Self => {
if (day.timeWorkDecimal >= workerTimeControlConfig.timeToBreakTime / 3600) {
await models.WorkerTimeControl.create({
userFk: day.workerFk,
- timed: timed.setHours(9),
+ timed: timed.setHours(workerTimeControlConfig.teleworkingStartBreakTime / 3600),
manual: true,
direction: 'middle',
isSendMail: true
@@ -189,7 +190,10 @@ module.exports = Self => {
await models.WorkerTimeControl.create({
userFk: day.workerFk,
- timed: timed.setHours(9, 20),
+ timed: timed.setHours(
+ workerTimeControlConfig.teleworkingStartBreakTime / 3600,
+ workerTimeControlConfig.breakTime / 60
+ ),
manual: true,
direction: 'middle',
isSendMail: true
@@ -199,7 +203,11 @@ module.exports = Self => {
const [hoursWork, minutesWork, secondsWork] = getTime(day.timeWorkSexagesimal);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
- timed: timed.setHours(8 + hoursWork, minutesWork, secondsWork),
+ timed: timed.setHours(
+ workerTimeControlConfig.teleworkingStart / 3600 + hoursWork,
+ minutesWork,
+ secondsWork
+ ),
manual: true,
direction: 'out',
isSendMail: true
@@ -307,7 +315,7 @@ module.exports = Self => {
}, myOptions);
if (firstWorkerTimeControl)
- firstWorkerTimeControl.updateAttribute('direction', 'in', myOptions);
+ await firstWorkerTimeControl.updateAttribute('direction', 'in', myOptions);
const lastWorkerTimeControl = await models.WorkerTimeControl.findOne({
where: {
@@ -318,7 +326,7 @@ module.exports = Self => {
}, myOptions);
if (lastWorkerTimeControl)
- lastWorkerTimeControl.updateAttribute('direction', 'out', myOptions);
+ await lastWorkerTimeControl.updateAttribute('direction', 'out', myOptions);
}
}
@@ -339,15 +347,18 @@ module.exports = Self => {
previousWorkerFk = day.workerFk;
previousReceiver = day.receiver;
}
+
+ if (tx) {
+ await tx.commit();
+ delete myOptions.transaction;
+ }
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
}
-
- if (tx) await tx.commit();
-
- return true;
- } catch (e) {
- if (tx) await tx.rollback();
- throw e;
}
+
+ return true;
};
function getStartDateOfWeekNumber(week, year) {
diff --git a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js
index 5b2436be9..24bfd6904 100644
--- a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js
+++ b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js
@@ -10,7 +10,6 @@ describe('workerTimeControl sendMail()', () => {
const ctx = {req: activeCtx, args: {}};
it('should fill time control of a worker without records in Journey and with rest', async() => {
- pending('https://redmine.verdnatura.es/issues/4903');
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
@@ -35,7 +34,6 @@ describe('workerTimeControl sendMail()', () => {
});
it('should fill time control of a worker without records in Journey and without rest', async() => {
- pending('https://redmine.verdnatura.es/issues/4903');
const workdayOf20Hours = 3;
const tx = await models.WorkerTimeControl.beginTransaction({});
@@ -63,7 +61,6 @@ describe('workerTimeControl sendMail()', () => {
});
it('should fill time control of a worker with records in Journey and with rest', async() => {
- pending('https://redmine.verdnatura.es/issues/4903');
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
@@ -95,7 +92,6 @@ describe('workerTimeControl sendMail()', () => {
});
it('should fill time control of a worker with records in Journey and without rest', async() => {
- pending('https://redmine.verdnatura.es/issues/4903');
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
diff --git a/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js
index 0cf614e57..6feadb936 100644
--- a/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js
+++ b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js
@@ -2,7 +2,7 @@ const {Email} = require('vn-print');
module.exports = Self => {
Self.remoteMethodCtx('weeklyHourRecordEmail', {
- description: 'Sends the buyer waste email',
+ description: 'Sends the weekly hour record',
accessType: 'WRITE',
accepts: [
{
diff --git a/modules/worker/back/methods/worker/new.js b/modules/worker/back/methods/worker/new.js
new file mode 100644
index 000000000..a7bb883cd
--- /dev/null
+++ b/modules/worker/back/methods/worker/new.js
@@ -0,0 +1,256 @@
+/* eslint max-len: ["error", { "code": 130 }]*/
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('new', {
+ description: 'Creates a new worker and returns the id',
+ accessType: 'WRITE',
+ accepts: [
+ {
+ arg: 'fi',
+ type: 'string',
+ description: `The worker fi`,
+ required: true,
+ },
+ {
+ arg: 'name',
+ type: 'string',
+ description: `The user name`,
+ required: true,
+ },
+ {
+ arg: 'firstName',
+ type: 'string',
+ description: `The worker firstname`,
+ required: true,
+ },
+ {
+ arg: 'lastNames',
+ type: 'string',
+ description: `The worker lastnames`,
+ required: true,
+ },
+ {
+ arg: 'email',
+ type: 'string',
+ description: `The worker email`,
+ required: true,
+ },
+ {
+ arg: 'street',
+ type: 'string',
+ description: `The worker address`,
+ required: true,
+ },
+ {
+ arg: 'city',
+ type: 'string',
+ description: `The worker city`,
+ required: true,
+ },
+ {
+ arg: 'provinceFk',
+ type: 'number',
+ description: `The worker province`,
+ required: true,
+ },
+ {
+ arg: 'iban',
+ type: 'string',
+ description: `The worker iban`,
+ required: true,
+ },
+ {
+ arg: 'bankEntityFk',
+ type: 'number',
+ description: `The worker bank entity`,
+ required: true,
+ },
+ {
+ arg: 'companyFk',
+ type: 'number',
+ description: `The worker company`,
+ required: true,
+ },
+ {
+ arg: 'postcode',
+ type: 'string',
+ description: `The worker postcode`,
+ required: true,
+ },
+ {
+ arg: 'phone',
+ type: 'string',
+ description: `The worker phone`,
+ required: true,
+ },
+ {
+ arg: 'code',
+ type: 'string',
+ description: `The worker code`,
+ required: true,
+ },
+ {
+ arg: 'bossFk',
+ type: 'number',
+ description: `The worker boss`,
+ required: true,
+ },
+ {
+ arg: 'birth',
+ type: 'date',
+ description: `The worker birth`,
+ required: true,
+ }
+ ],
+ returns: {
+ type: 'number',
+ root: true,
+ },
+ http: {
+ path: `/new`,
+ verb: 'POST',
+ },
+ });
+
+ Self.new = async(ctx, options) => {
+ const models = Self.app.models;
+ const myOptions = {};
+ const args = ctx.args;
+
+ let tx;
+
+ if (typeof options == 'object') Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ let client;
+
+ try {
+ client = await models.Client.findOne(
+ {
+ where: {fi: args.fi},
+ },
+ myOptions
+ );
+
+ if (!client) {
+ const nickname = args.firstName.concat(' ', args.lastNames);
+ const workerConfig = await models.WorkerConfig.findOne({fields: ['roleFk']});
+ const [randomPassword] = await models.Worker.rawSql(
+ 'SELECT account.passwordGenerate() as password;'
+ );
+
+ const user = await models.Account.create(
+ {
+ name: args.name,
+ nickname,
+ password: randomPassword.password,
+ email: args.email,
+ roleFk: workerConfig.roleFk,
+ },
+ myOptions
+ );
+
+ await models.UserAccount.create(
+ {
+ id: user.id,
+ },
+ myOptions
+ );
+
+ await models.Worker.rawSql(
+ 'CALL vn.clientCreate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
+ [
+ args.firstName,
+ args.lastNames,
+ args.fi,
+ args.street,
+ args.postalCode,
+ args.city,
+ args.provinceFk,
+ args.companyFk,
+ args.phone,
+ args.email,
+ user.id,
+ ],
+ myOptions
+ );
+
+ const address = await models.Address.create(
+ {
+ clientFk: user.id,
+ street: args.street,
+ city: args.city,
+ provinceFk: args.provinceFk,
+ postalCode: args.postalCode,
+ mobile: args.phone,
+ nickname: nickname,
+ isDefaultAddress: true,
+ },
+ myOptions
+ );
+
+ client = await models.Client.findById(
+ user.id,
+ {fields: ['id', 'name', 'socialName', 'street', 'city', 'iban', 'bankEntityFk', 'defaultAddressFk']},
+ myOptions
+ );
+
+ await client.updateAttributes(
+ {
+ iban: args.iban,
+ bankEntityFk: args.bankEntityFk,
+ defaultAddressFk: address.id,
+ },
+ myOptions
+ );
+ }
+
+ const user = await models.Account.findById(client.id, null, myOptions);
+ await user.updateAttribute('email', args.email, myOptions);
+
+ await models.Worker.rawSql(
+ 'CALL vn.workerCreate(?, ?, ?, ?, ?, ?, ?)',
+ [
+ args.firstName,
+ args.lastNames,
+ args.code,
+ args.bossFk,
+ client.id,
+ args.fi,
+ args.birth,
+ ],
+ myOptions
+ );
+
+ if (tx) await tx.commit();
+ } catch (error) {
+ if (tx) await tx.rollback();
+ const code = error.code;
+ const message = error.sqlMessage;
+
+ if (code === 'ER_DUP_ENTRY' && message.includes(`for key 'mail'`))
+ throw new UserError(`This personal mail already exists`);
+
+ if (code === 'ER_DUP_ENTRY' && message.includes(`CodigoTrabajador_UNIQUE`))
+ throw new UserError(`This worker code already exists`);
+
+ if (code === 'ER_DUP_ENTRY' && message.includes(`PRIMARY`))
+ throw new UserError(`This worker already exists`);
+
+ throw error;
+ }
+
+ await models.user.resetPassword({
+ email: args.email,
+ emailTemplate: 'worker-welcome',
+ id: client.id
+ });
+
+ return {id: client.id};
+ };
+};
diff --git a/modules/worker/back/methods/worker/specs/new.spec.js b/modules/worker/back/methods/worker/specs/new.spec.js
new file mode 100644
index 000000000..f695ab80e
--- /dev/null
+++ b/modules/worker/back/methods/worker/specs/new.spec.js
@@ -0,0 +1,139 @@
+const models = require('vn-loopback/server/server').models;
+const LoopBackContext = require('loopback-context');
+
+describe('Worker new', () => {
+ beforeAll(async() => {
+ const activeCtx = {
+ accessToken: {userId: 9},
+ http: {
+ req: {
+ headers: {origin: 'http://localhost'}
+ }
+ }
+ };
+
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: activeCtx
+ });
+ });
+
+ const employeeId = 1;
+ const defaultWorker = {
+ fi: '78457139E',
+ name: 'defaultWorker',
+ firstName: 'default',
+ lastNames: 'worker',
+ email: 'defaultWorker@mydomain.com',
+ street: 'S/ defaultWorkerStreet',
+ city: 'defaultWorkerCity',
+ provinceFk: 1,
+ iban: 'ES8304879798578129532677',
+ bankEntityFk: 128,
+ companyFk: 442,
+ postcode: '46680',
+ phone: '123456789',
+ code: 'DWW',
+ bossFk: 9,
+ birth: '2022-12-11T23:00:00.000Z'
+ };
+
+ it('should return error if personal mail already exists', async() => {
+ const user = await models.Account.findById(employeeId, {fields: ['email']});
+
+ const tx = await models.Worker.beginTransaction({});
+
+ let error;
+ try {
+ const options = {transaction: tx};
+ const ctx = {
+ args: Object.assign({}, defaultWorker, {email: user.email})
+ };
+
+ await models.Worker.new(ctx, options);
+
+ await tx.rollback();
+ } catch (e) {
+ error = e;
+ await tx.rollback();
+ }
+
+ expect(error.message).toEqual('This personal mail already exists');
+ });
+
+ it('should return error if worker code already exists', async() => {
+ const worker = await models.Worker.findById(employeeId, {fields: ['code']});
+
+ const tx = await models.Worker.beginTransaction({});
+
+ let error;
+ try {
+ const options = {transaction: tx};
+ const ctx = {
+ args: Object.assign({}, defaultWorker, {code: worker.code})
+ };
+
+ await models.Worker.new(ctx, options);
+
+ await tx.rollback();
+ } catch (e) {
+ error = e;
+ await tx.rollback();
+ }
+
+ expect(error.message).toEqual('This worker code already exists');
+ });
+
+ it('should return error if worker already exists', async() => {
+ const worker = await models.Client.findById(employeeId, {fields: ['fi']});
+
+ const tx = await models.Worker.beginTransaction({});
+
+ let error;
+ try {
+ const options = {transaction: tx};
+ const ctx = {
+ args: Object.assign({}, defaultWorker, {fi: worker.fi})
+ };
+ await models.Worker.new(ctx, options);
+
+ await tx.rollback();
+ } catch (e) {
+ error = e;
+ await tx.rollback();
+ }
+
+ expect(error.message).toEqual('This worker already exists');
+ });
+
+ it('should create a new worker', async() => {
+ const newWorker = await models.Worker.new({args: defaultWorker});
+
+ await models.Worker.destroyById(newWorker.id);
+ await models.Address.destroyAll({clientFk: newWorker.id});
+ await models.Mandate.destroyAll({clientFk: newWorker.id});
+ await models.Client.destroyById(newWorker.id);
+ await models.Account.destroyById(newWorker.id);
+
+ expect(newWorker.id).toBeDefined();
+ });
+
+ it('should create a new worker in client', async() => {
+ const bruceWayneId = 1101;
+ const client = await models.Client.findById(bruceWayneId, {fields: ['fi', 'email']});
+
+ const newWorkerData = {
+ args: Object.assign(
+ {},
+ defaultWorker,
+ {
+ fi: client.fi,
+ email: client.email
+ })
+ };
+ const newWorker = await models.Worker.new(newWorkerData);
+
+ await models.Worker.destroyById(newWorker.id);
+
+ expect(newWorker.id).toEqual(bruceWayneId);
+ });
+});
diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json
index 3f3416504..7e03c8a23 100644
--- a/modules/worker/back/model-config.json
+++ b/modules/worker/back/model-config.json
@@ -1,7 +1,7 @@
{
"AbsenceType": {
"dataSource": "vn"
- },
+ },
"Calendar": {
"dataSource": "vn"
},
@@ -16,13 +16,19 @@
},
"Department": {
"dataSource": "vn"
- },
+ },
+ "Device": {
+ "dataSource": "vn"
+ },
"EducationLevel": {
"dataSource": "vn"
},
"Journey": {
"dataSource": "vn"
},
+ "ProfileType":{
+ "dataSource": "vn"
+ },
"Time": {
"dataSource": "vn"
},
@@ -32,39 +38,42 @@
"WorkCenterHoliday": {
"dataSource": "vn"
},
- "WorkerDms": {
+ "Worker": {
"dataSource": "vn"
},
- "Worker": {
+ "WorkerConfig": {
+ "dataSource": "vn"
+ },
+ "WorkerDepartment": {
+ "dataSource": "vn"
+ },
+ "WorkerDisableExcluded": {
+ "dataSource": "vn"
+ },
+ "WorkerDms": {
"dataSource": "vn"
},
"WorkerLabour": {
"dataSource": "vn"
},
+ "WorkerLog": {
+ "dataSource": "vn"
+ },
"WorkerMana": {
"dataSource": "vn"
},
+ "WorkerMedia": {
+ "dataSource": "vn"
+ },
"WorkerTeam": {
"dataSource": "vn"
},
"WorkerTeamCollegues": {
"dataSource": "vn"
},
- "WorkerMedia": {
- "dataSource": "vn"
- },
- "WorkerDepartment": {
- "dataSource": "vn"
- },
"WorkerTimeControl": {
"dataSource": "vn"
},
- "Device": {
- "dataSource": "vn"
- },
- "WorkerLog": {
- "dataSource": "vn"
- },
"WorkerTimeControlConfig": {
"dataSource": "vn"
},
@@ -73,9 +82,6 @@
},
"WorkerTimeControlMail": {
"dataSource": "vn"
- },
- "WorkerDisableExcluded": {
- "dataSource": "vn"
}
}
diff --git a/modules/worker/back/models/profile-type.json b/modules/worker/back/models/profile-type.json
new file mode 100644
index 000000000..d1d750de8
--- /dev/null
+++ b/modules/worker/back/models/profile-type.json
@@ -0,0 +1,19 @@
+{
+ "name": "ProfileType",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "profileType"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "number",
+ "id": true,
+ "description": "Identifier"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+}
diff --git a/modules/worker/back/models/worker-config.json b/modules/worker/back/models/worker-config.json
new file mode 100644
index 000000000..05cdfef42
--- /dev/null
+++ b/modules/worker/back/models/worker-config.json
@@ -0,0 +1,27 @@
+{
+ "name": "WorkerConfig",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "workerConfig"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "number",
+ "id": true,
+ "description": "Identifier"
+ },
+ "roleFk": {
+ "type": "number"
+ }
+ },
+ "acls": [
+ {
+ "accessType": "READ",
+ "principalType": "ROLE",
+ "principalId": "$everyone",
+ "permission": "ALLOW"
+ }
+ ]
+}
diff --git a/modules/worker/back/models/worker-time-control-config.json b/modules/worker/back/models/worker-time-control-config.json
index 4c12ce5d7..b96e2ae3b 100644
--- a/modules/worker/back/models/worker-time-control-config.json
+++ b/modules/worker/back/models/worker-time-control-config.json
@@ -11,8 +11,17 @@
"id": true,
"type": "number"
},
+ "breakTime": {
+ "type": "number"
+ },
"timeToBreakTime": {
"type": "number"
+ },
+ "teleworkingStart": {
+ "type": "number"
+ },
+ "teleworkingStartBreakTime": {
+ "type": "number"
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js
index ec6c4af28..e66259cd0 100644
--- a/modules/worker/back/models/worker.js
+++ b/modules/worker/back/models/worker.js
@@ -13,4 +13,5 @@ module.exports = Self => {
require('../methods/worker/contracts')(Self);
require('../methods/worker/holidays')(Self);
require('../methods/worker/activeContract')(Self);
+ require('../methods/worker/new')(Self);
};
diff --git a/modules/worker/back/models/worker.json b/modules/worker/back/models/worker.json
index 3d41707ce..e3a941dd3 100644
--- a/modules/worker/back/models/worker.json
+++ b/modules/worker/back/models/worker.json
@@ -52,6 +52,9 @@
},
"mobileExtension": {
"type" : "number"
+ },
+ "code": {
+ "type" : "string"
}
},
"relations": {
@@ -91,4 +94,4 @@
"foreignKey": "sectorFk"
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/worker/front/create/index.html b/modules/worker/front/create/index.html
new file mode 100644
index 000000000..5f5ab9d07
--- /dev/null
+++ b/modules/worker/front/create/index.html
@@ -0,0 +1,194 @@
+
+
+
+
+
+
diff --git a/modules/worker/front/create/index.js b/modules/worker/front/create/index.js
new file mode 100644
index 000000000..7e837fe02
--- /dev/null
+++ b/modules/worker/front/create/index.js
@@ -0,0 +1,127 @@
+import ngModule from '../module';
+import Section from 'salix/components/section';
+
+export default class Controller extends Section {
+ constructor($element, $) {
+ super($element, $);
+ this.worker = {companyFk: this.vnConfig.user.companyFk};
+ }
+
+ onSubmit() {
+ return this.$.watcher.submit().then(json => {
+ this.$state.go('worker.card.basicData', {id: json.data.id});
+ });
+ }
+
+ autofillBic() {
+ if (!this.worker || !this.worker.iban) return;
+
+ let bankEntityId = parseInt(this.worker.iban.substr(4, 4));
+ let filter = {where: {id: bankEntityId}};
+
+ if (this.ibanCountry != 'ES') return;
+
+ this.$http.get(`BankEntities`, {filter}).then(response => {
+ const hasData = response.data && response.data[0];
+
+ if (hasData)
+ this.worker.bankEntityFk = response.data[0].id;
+ else if (!hasData)
+ this.worker.bankEntityFk = null;
+ });
+ }
+
+ generateCodeUser() {
+ if (!this.worker.firstName || !this.worker.lastNames) return;
+
+ const totalName = this.worker.firstName.concat(' ' + this.worker.lastNames).toLowerCase();
+ const totalNameArray = totalName.split(' ');
+ let newCode = '';
+
+ for (let part of totalNameArray)
+ newCode += part.charAt(0);
+
+ this.worker.code = newCode.toUpperCase().slice(0, 3);
+ this.worker.name = totalNameArray[0] + newCode.slice(1);
+
+ if (!this.worker.companyFk)
+ this.worker.companyFk = this.vnConfig.user.companyFk;
+ }
+
+ get province() {
+ return this._province;
+ }
+
+ // Province auto complete
+ set province(selection) {
+ this._province = selection;
+
+ if (!selection) return;
+
+ const country = selection.country;
+
+ if (!this.worker.countryFk)
+ this.worker.countryFk = country.id;
+ }
+
+ get town() {
+ return this._town;
+ }
+
+ // Town auto complete
+ set town(selection) {
+ this._town = selection;
+
+ if (!selection) return;
+
+ const province = selection.province;
+ const country = province.country;
+ const postcodes = selection.postcodes;
+
+ if (!this.worker.provinceFk)
+ this.worker.provinceFk = province.id;
+
+ if (!this.worker.countryFk)
+ this.worker.countryFk = country.id;
+
+ if (postcodes.length === 1)
+ this.worker.postcode = postcodes[0].code;
+ }
+
+ get postcode() {
+ return this._postcode;
+ }
+
+ // Postcode auto complete
+ set postcode(selection) {
+ this._postcode = selection;
+
+ if (!selection) return;
+
+ const town = selection.town;
+ const province = town.province;
+ const country = province.country;
+
+ if (!this.worker.city)
+ this.worker.city = town.name;
+
+ if (!this.worker.provinceFk)
+ this.worker.provinceFk = province.id;
+
+ if (!this.worker.countryFk)
+ this.worker.countryFk = country.id;
+ }
+
+ onResponse(response) {
+ this.worker.postcode = response.code;
+ this.worker.city = response.city;
+ this.worker.provinceFk = response.provinceFk;
+ }
+}
+
+Controller.$inject = ['$element', '$scope'];
+
+ngModule.vnComponent('vnWorkerCreate', {
+ template: require('./index.html'),
+ controller: Controller
+});
diff --git a/modules/worker/front/create/index.spec.js b/modules/worker/front/create/index.spec.js
new file mode 100644
index 000000000..c2e9acce0
--- /dev/null
+++ b/modules/worker/front/create/index.spec.js
@@ -0,0 +1,133 @@
+import './index';
+
+describe('Worker', () => {
+ describe('Component vnWorkerCreate', () => {
+ let $scope;
+ let $state;
+ let controller;
+
+ beforeEach(ngModule('worker'));
+
+ beforeEach(inject(($componentController, $rootScope, _$state_) => {
+ $scope = $rootScope.$new();
+ $state = _$state_;
+ $scope.watcher = {
+ submit: () => {
+ return {
+ then: callback => {
+ callback({data: {id: '1234'}});
+ }
+ };
+ }
+ };
+ const $element = angular.element('');
+ controller = $componentController('vnWorkerCreate', {$element, $scope});
+ controller.worker = {};
+ controller.vnConfig = {user: {companyFk: 1}};
+ }));
+
+ describe('onSubmit()', () => {
+ it(`should call submit() on the watcher then expect a callback`, () => {
+ jest.spyOn($state, 'go');
+ controller.onSubmit();
+
+ expect(controller.$state.go).toHaveBeenCalledWith('worker.card.basicData', {id: '1234'});
+ });
+ });
+
+ describe('province() setter', () => {
+ it(`should set countryFk property`, () => {
+ controller.worker.countryFk = null;
+ controller.province = {
+ id: 1,
+ name: 'New york',
+ country: {
+ id: 2,
+ name: 'USA'
+ }
+ };
+
+ expect(controller.worker.countryFk).toEqual(2);
+ });
+ });
+
+ describe('town() setter', () => {
+ it(`should set provinceFk property`, () => {
+ controller.town = {
+ provinceFk: 1,
+ code: 46001,
+ province: {
+ id: 1,
+ name: 'New york',
+ country: {
+ id: 2,
+ name: 'USA'
+ }
+ },
+ postcodes: []
+ };
+
+ expect(controller.worker.provinceFk).toEqual(1);
+ });
+
+ it(`should set provinceFk property and fill the postalCode if there's just one`, () => {
+ controller.town = {
+ provinceFk: 1,
+ code: 46001,
+ province: {
+ id: 1,
+ name: 'New york',
+ country: {
+ id: 2,
+ name: 'USA'
+ }
+ },
+ postcodes: [{code: '46001'}]
+ };
+
+ expect(controller.worker.provinceFk).toEqual(1);
+ expect(controller.worker.postcode).toEqual('46001');
+ });
+ });
+
+ describe('postcode() setter', () => {
+ it(`should set the town, provinceFk and contryFk properties`, () => {
+ controller.postcode = {
+ townFk: 1,
+ code: 46001,
+ town: {
+ id: 1,
+ name: 'New York',
+ province: {
+ id: 1,
+ name: 'New york',
+ country: {
+ id: 2,
+ name: 'USA'
+ }
+ }
+ }
+ };
+
+ expect(controller.worker.city).toEqual('New York');
+ expect(controller.worker.provinceFk).toEqual(1);
+ expect(controller.worker.countryFk).toEqual(2);
+ });
+ });
+
+ describe('generateCodeUser()', () => {
+ it(`should generate worker code, name and company `, () => {
+ controller.worker = {
+ firstName: 'default',
+ lastNames: 'generate worker'
+ };
+
+ controller.generateCodeUser();
+
+ expect(controller.worker.code).toEqual('DGW');
+ expect(controller.worker.name).toEqual('defaultgw');
+ expect(controller.worker.companyFk).toEqual(controller.vnConfig.user.companyFk);
+ });
+ });
+ });
+});
diff --git a/modules/worker/front/create/locale/es.yml b/modules/worker/front/create/locale/es.yml
new file mode 100644
index 000000000..8c79d770c
--- /dev/null
+++ b/modules/worker/front/create/locale/es.yml
@@ -0,0 +1,12 @@
+Firstname: Nombre
+Lastname: Apellidos
+Fi: DNI/NIF/NIE
+Birth: Fecha de nacimiento
+Code: Código de trabajador
+Province: Provincia
+City: Población
+ProfileType: Tipo de perfil
+Street: Dirección
+Postcode: Código postal
+Web user: Usuario Web
+Access permission: Permiso de acceso
diff --git a/modules/worker/front/index.js b/modules/worker/front/index.js
index f703e7c21..97126407c 100644
--- a/modules/worker/front/index.js
+++ b/modules/worker/front/index.js
@@ -4,6 +4,7 @@ import './main';
import './index/';
import './summary';
import './card';
+import './create';
import './descriptor';
import './descriptor-popover';
import './search-panel';
diff --git a/modules/worker/front/index/index.html b/modules/worker/front/index/index.html
index 98df416b4..7044ca551 100644
--- a/modules/worker/front/index/index.html
+++ b/modules/worker/front/index/index.html
@@ -6,23 +6,23 @@
class="vn-w-sm">
+
+
+
-
\ No newline at end of file
+
diff --git a/modules/worker/front/index/locale/es.yml b/modules/worker/front/index/locale/es.yml
new file mode 100644
index 000000000..df6383273
--- /dev/null
+++ b/modules/worker/front/index/locale/es.yml
@@ -0,0 +1 @@
+New worker: Nuevo trabajador
diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json
index ca33eaa76..dad55512b 100644
--- a/modules/worker/front/routes.json
+++ b/modules/worker/front/routes.json
@@ -16,7 +16,7 @@
{"state": "worker.card.timeControl", "icon": "access_time"},
{"state": "worker.card.dms.index", "icon": "cloud_upload"},
{
- "icon": "icon-wiki",
+ "icon": "icon-wiki",
"external":true,
"url": "http://wiki.verdnatura.es",
"description": "Wikipedia"
@@ -134,6 +134,13 @@
"worker": "$ctrl.worker"
},
"acl": ["hr"]
+ },
+ {
+ "url": "/create",
+ "state": "worker.create",
+ "component": "vn-worker-create",
+ "description": "New worker",
+ "acl": ["hr"]
}
]
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index 9633751a0..2e0fb9ae8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salix-back",
- "version": "9.0.0",
+ "version": "230401",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",
diff --git a/print/templates/email/email-verify/email-verify.js b/print/templates/email/email-verify/email-verify.js
index 7f0b80a13..4f2b29266 100755
--- a/print/templates/email/email-verify/email-verify.js
+++ b/print/templates/email/email-verify/email-verify.js
@@ -10,7 +10,7 @@ module.exports = {
},
props: {
url: {
- type: [String],
+ type: String,
required: true
}
}
diff --git a/print/templates/email/invoiceIn/locale/en.yml b/print/templates/email/invoiceIn/locale/en.yml
index 47ebc3966..e238ecf61 100644
--- a/print/templates/email/invoiceIn/locale/en.yml
+++ b/print/templates/email/invoiceIn/locale/en.yml
@@ -1,5 +1,5 @@
-subject: Your agricultural invoice
-title: Your agricultural invoice
+subject: Your agricultural receipt
+title: Your agricultural receipt
dear: Dear supplier
description: Attached you can find agricultural receipt generated from your last deliveries. Please return a signed and stamped copy to our administration department.
conclusion: Thanks for your attention!
diff --git a/print/templates/email/invoiceIn/locale/es.yml b/print/templates/email/invoiceIn/locale/es.yml
index 2698763cf..456122c75 100644
--- a/print/templates/email/invoiceIn/locale/es.yml
+++ b/print/templates/email/invoiceIn/locale/es.yml
@@ -1,5 +1,5 @@
-subject: Tu factura agrícola
-title: Tu factura agrícola
+subject: Tu recibo agrícola
+title: Tu recibo agrícola
dear: Estimado proveedor
description: Adjunto puede encontrar recibo agrícola generado de sus últimas entregas. Por favor, devuelva una copia firmada y sellada a nuestro de departamento de administración.
conclusion: ¡Gracias por tu atención!
diff --git a/print/templates/email/invoiceIn/locale/fr.yml b/print/templates/email/invoiceIn/locale/fr.yml
index 1c38f3c25..dd35631e5 100644
--- a/print/templates/email/invoiceIn/locale/fr.yml
+++ b/print/templates/email/invoiceIn/locale/fr.yml
@@ -1,5 +1,5 @@
-subject: Votre facture agricole
-title: Votre facture agricole
+subject: Votre reçu agricole
+title: Votre reçu agricole
dear: Cher Fournisseur
description: Vous trouverez en pièce jointe le reçu agricole généré à partir de vos dernières livraisons. Veuillez retourner une copie signée et tamponnée à notre service administratif.
conclusion: Merci pour votre attention!
diff --git a/print/templates/email/invoiceIn/locale/pt.yml b/print/templates/email/invoiceIn/locale/pt.yml
index a43e3a79d..5dffc7acf 100644
--- a/print/templates/email/invoiceIn/locale/pt.yml
+++ b/print/templates/email/invoiceIn/locale/pt.yml
@@ -1,5 +1,5 @@
-subject: A sua fatura agrícola
-title: A sua fatura agrícola
+subject: A sua recibo agrícola
+title: A sua recibo agrícola
dear: Caro Fornecedor
description: Em anexo encontra-se o recibo agrícola gerado a partir das suas últimas entregas. Por favor, devolva uma cópia assinada e carimbada ao nosso departamento de administração.
conclusion: Obrigado pela atenção.
diff --git a/print/templates/email/printer-setup/locale/es.yml b/print/templates/email/printer-setup/locale/es.yml
index 77a3a7299..7361e5ed3 100644
--- a/print/templates/email/printer-setup/locale/es.yml
+++ b/print/templates/email/printer-setup/locale/es.yml
@@ -8,8 +8,8 @@ description:
https://www.youtube.com/watch?v=qhb0kgQF3o8. También
necesitarás el QLabel, el programa para imprimir las cintas.
- downloadFrom: Puedes descargarlo desde este enlace https://godex.s3-accelerate.amazonaws.com/gGnOPoojkP6vC1lgmrbEqQ.file?v01
+ downloadFrom: Puedes descargarlo desde este enlace https://cdn.verdnatura.es/public/QLabel_IV_V1.37_Install_en.exe
downloadDriver: En este enlace puedes descargar el driver de la impresora https://es.seagullscientific.com/support/downloads/drivers/godex/download/
sections:
diff --git a/print/templates/email/recover-password/recover-password.js b/print/templates/email/recover-password/recover-password.js
index b589411a9..d8448f370 100755
--- a/print/templates/email/recover-password/recover-password.js
+++ b/print/templates/email/recover-password/recover-password.js
@@ -10,7 +10,7 @@ module.exports = {
},
props: {
url: {
- type: [String],
+ type: String,
required: true
}
}
diff --git a/print/templates/email/worker-welcome/assets/css/import.js b/print/templates/email/worker-welcome/assets/css/import.js
new file mode 100644
index 000000000..4b4bb7086
--- /dev/null
+++ b/print/templates/email/worker-welcome/assets/css/import.js
@@ -0,0 +1,11 @@
+const Stylesheet = require(`vn-print/core/stylesheet`);
+
+const path = require('path');
+const vnPrintPath = path.resolve('print');
+
+module.exports = new Stylesheet([
+ `${vnPrintPath}/common/css/spacing.css`,
+ `${vnPrintPath}/common/css/misc.css`,
+ `${vnPrintPath}/common/css/layout.css`,
+ `${vnPrintPath}/common/css/email.css`])
+ .mergeStyles();
diff --git a/print/templates/email/worker-welcome/locale/es.yml b/print/templates/email/worker-welcome/locale/es.yml
new file mode 100644
index 000000000..d53a4e1f0
--- /dev/null
+++ b/print/templates/email/worker-welcome/locale/es.yml
@@ -0,0 +1,8 @@
+subject: Bienvenido a Verdnatura
+title: "¡Te damos la bienvenida!"
+dearWorker: Estimado trabajador
+workerData: 'Estos son los datos de tu usuario de Verdnatura.
+ Usuario: {0}. Haz click aquí para
+ establecer tu contraseña
+ .'
diff --git a/print/templates/email/worker-welcome/sql/worker.sql b/print/templates/email/worker-welcome/sql/worker.sql
new file mode 100644
index 000000000..f75d135d9
--- /dev/null
+++ b/print/templates/email/worker-welcome/sql/worker.sql
@@ -0,0 +1,7 @@
+SELECT
+ u.id,
+ u.name,
+ e.email
+FROM account.user u
+ LEFT JOIN account.emailUser e ON e.userFk = u.id
+WHERE u.id = ?;
diff --git a/print/templates/email/worker-welcome/worker-welcome.html b/print/templates/email/worker-welcome/worker-welcome.html
new file mode 100644
index 000000000..fbb05d149
--- /dev/null
+++ b/print/templates/email/worker-welcome/worker-welcome.html
@@ -0,0 +1,9 @@
+
+
+
+
{{ $t('title', [id]) }}
+
{{ $t('dearWorker') }},
+
+
+
+
diff --git a/print/templates/email/worker-welcome/worker-welcome.js b/print/templates/email/worker-welcome/worker-welcome.js
new file mode 100755
index 000000000..043a172a1
--- /dev/null
+++ b/print/templates/email/worker-welcome/worker-welcome.js
@@ -0,0 +1,27 @@
+const Component = require(`vn-print/core/component`);
+const emailBody = new Component('email-body');
+
+module.exports = {
+ name: 'worker-welcome',
+ async serverPrefetch() {
+ this.worker = await this.fetchWorker(this.id);
+ },
+ methods: {
+ fetchWorker(id) {
+ return this.findOneFromDef('worker', [id]);
+ },
+ },
+ components: {
+ 'email-body': emailBody.build(),
+ },
+ props: {
+ id: {
+ type: Number,
+ required: true
+ },
+ url: {
+ type: String,
+ required: true
+ }
+ }
+};