diff --git a/db/changes/10503-november/00-aclProfileType.sql b/db/changes/10503-november/00-aclProfileType.sql new file mode 100644 index 000000000..638b3d580 --- /dev/null +++ b/db/changes/10503-november/00-aclProfileType.sql @@ -0,0 +1,18 @@ + +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('ProfileType', '*', '*', 'ALLOW', 'ROLE', 'employee'); + +CREATE TABLE `vn`.`newWorkerConfig` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `street` VARCHAR(25) NULL, + `provinceFk` smallint(6) unsigned NULL, + `companyFk` smallint(5) unsigned NULL, + `profileTypeFk` INT(11) NULL, + `roleFk` int(10) unsigned NULL, + PRIMARY KEY (`id`), + CONSTRAINT `newWorkerConfig_province_fk` FOREIGN KEY (`provinceFk`) REFERENCES `vn`.`province` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `newWorkerConfig_company_fk` FOREIGN KEY (`companyFk`) REFERENCES `vn`.`company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `newWorkerConfig_profileType_fk` FOREIGN KEY (`profileTypeFk`) REFERENCES `vn`.`profileType` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `newWorkerConfig_role_fk` FOREIGN KEY (`roleFk`) REFERENCES `account`.`role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +); diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 6624e99f4..a6fb9cbbb 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2719,3 +2719,11 @@ UPDATE `account`.`user` 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'); + +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); diff --git a/modules/worker/back/methods/worker/new.js b/modules/worker/back/methods/worker/new.js index 481011563..bdab1335a 100644 --- a/modules/worker/back/methods/worker/new.js +++ b/modules/worker/back/methods/worker/new.js @@ -8,7 +8,7 @@ module.exports = Self => { accepts: [ { arg: 'fi', - type: 'number', + type: 'string', description: `The fi of worker`, required: true }, @@ -30,12 +30,6 @@ module.exports = Self => { description: `REPLACE!`, required: true }, - { - arg: 'password', - type: 'string', - description: `REPLACE!`, - required: true - }, { arg: 'email', type: 'string', @@ -43,49 +37,76 @@ module.exports = Self => { required: true }, { - arg: 'role', + arg: 'roleFk', type: 'number', - description: `REPLACE!` + description: `REPLACE!`, + required: true }, { arg: 'street', type: 'string', - description: `REPLACE!` + description: `REPLACE!`, + required: true }, { - arg: 'string', - type: 'number', - description: `REPLACE!` + arg: 'city', + type: 'string', + description: `REPLACE!`, + required: true }, { arg: 'provinceFk', type: 'number', - description: `REPLACE!` + description: `REPLACE!`, + required: true }, { - arg: 'postalCode', + arg: 'iban', + type: 'string', + description: `REPLACE!`, + required: true + }, + { + arg: 'bankEntityFk', type: 'number', - description: `REPLACE!` + description: `REPLACE!`, + required: true + }, + { + arg: 'companyFk', + type: 'number', + description: `REPLACE!`, + required: true + }, + { + arg: 'postcode', + type: 'string', + description: `REPLACE!`, + required: true }, { arg: 'phone', - type: 'number', - description: `REPLACE!` + type: 'string', + description: `REPLACE!`, + required: true }, { - arg: 'workerCode', + arg: 'code', type: 'string', - description: `REPLACE!` + description: `REPLACE!`, + required: true }, { arg: 'bossFk', type: 'number', - description: `REPLACE!` + description: `REPLACE!`, + required: true }, { arg: 'birth', type: 'date', - description: `REPLACE!` + description: `REPLACE!`, + required: true } ], returns: { @@ -104,7 +125,7 @@ module.exports = Self => { const args = ctx.args; let tx; - + console.log(args); if (typeof options == 'object') Object.assign(myOptions, options); @@ -116,15 +137,16 @@ module.exports = Self => { let client; try { client = await models.Client.findOne({ - where: {fi: fi}, + where: {fi: args.fi}, }, myOptions); if (!client) { const nickname = args.firstName.concat(' ', args.lastNames); + const randomPassword = await models.Worker.rawSql('SELECT account.passwordGenerate();'); const user = await models.Account.create({ name: args.name, nickname, - password: md5(args.password), + password: md5(randomPassword), email: args.email, role: args.role }, myOptions); @@ -138,11 +160,11 @@ module.exports = Self => { args.firstName, args.lastNames, args.fi, - args.address, + args.street, args.postalCode, - args.town, - args.province, - args.company, + args.city, + args.provinceFk, + args.companyFk, args.phone, args.email, user.id @@ -151,8 +173,8 @@ module.exports = Self => { const address = await models.Address.create({ clientFk: user.id, - address: args.street, - town: args.town, + street: args.street, + city: args.city, provinceFk: args.provinceFk, postalCode: args.postalCode, mobile: args.phone, @@ -160,17 +182,22 @@ module.exports = Self => { isDefaultAddress: true }, myOptions); - client = await models.Sale.findById(user.id, null, myOptions); - await client.updateAttribute('defaultAddressFk ', address.id); - } + client = await models.Client.findById(user.id, null, myOptions); + console.log(address.id); + await client.updateAttributes({ + iban: args.iban, + bankEntityFk: args.bankEntityFk, + defaultAddressFk: address.id + }, myOptions); + } await models.Worker.rawSql('CALL vn.workerCreate(?, ?, ?, ?, ?, ?, ?)', [ args.firstName, args.lastNames, - args.workerCode, + args.code, args.bossFk, - user.id, + client.id, args.fi, args.birth ] @@ -184,6 +211,9 @@ module.exports = Self => { // TODO: create this email, use client-welcome as template. And view CALL mail_insert in redmine for the body // TODO: or use same funcionality back/methods/account/recover-password.js + + // TODO: call worerWelcomeEmail, and this is who create the url for change password + const email = new Email('worker-welcome', { recipient: args.email, lang: ctx.req.getLocale() diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json index 3f3416504..de8aadd41 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,22 @@ }, "Department": { "dataSource": "vn" - }, + }, + "Device": { + "dataSource": "vn" + }, "EducationLevel": { "dataSource": "vn" }, "Journey": { "dataSource": "vn" }, + "NewWorkerConfig":{ + "dataSource": "vn" + }, + "ProfileType":{ + "dataSource": "vn" + }, "Time": { "dataSource": "vn" }, @@ -55,11 +64,8 @@ }, "WorkerDepartment": { "dataSource": "vn" - }, - "WorkerTimeControl": { - "dataSource": "vn" }, - "Device": { + "WorkerTimeControl": { "dataSource": "vn" }, "WorkerLog": { diff --git a/modules/worker/back/models/new-worker-config.json b/modules/worker/back/models/new-worker-config.json new file mode 100644 index 000000000..2102d236c --- /dev/null +++ b/modules/worker/back/models/new-worker-config.json @@ -0,0 +1,39 @@ +{ + "name": "NewWorkerConfig", + "base": "VnModel", + "options": { + "mysql": { + "table": "newWorkerConfig" + } + }, + "properties": { + "id": { + "type": "number", + "id": true, + "description": "Identifier" + }, + "street": { + "type": "string" + }, + "provinceFk": { + "type": "number" + }, + "companyFk": { + "type": "number" + }, + "profileTypeFk": { + "type": "number" + }, + "roleFk": { + "type": "number" + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ] +} 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.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..58b4da060 --- /dev/null +++ b/modules/worker/front/create/index.html @@ -0,0 +1,208 @@ + + +
+ + + + + + + + + + + + + + + + + + + + {{name}} ({{country.country}}) + + + + {{name}}, {{province.name}} + ({{province.country.country}}) + + + + + + + {{code}} - {{town.name}} ({{town.province.name}}, + {{town.province.country.country}}) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{bic}} {{name}} + + + + + + + + + + + + + +
+ + + diff --git a/modules/worker/front/create/index.js b/modules/worker/front/create/index.js new file mode 100644 index 000000000..a068b2006 --- /dev/null +++ b/modules/worker/front/create/index.js @@ -0,0 +1,98 @@ +import ngModule from '../module'; +import Section from 'salix/components/section'; + +export default class Controller extends Section { + constructor($element, $) { + super($element, $); + this.$http.get('NewWorkerConfigs/findOne').then(res => { + return this.worker = Object.assign({}, res.data); + }); + } + + onSubmit() { + return this.$.watcher.submit().then(json => { + this.$state.go('client.card.basicData', {id: json.data.id}); + this.$http.get(`Clients/${this.client.id}/checkDuplicatedData`); + }); + } + + 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; + }); + } + + get province() { + return this._province; + } + + // Province auto complete + set province(selection) { + this._province = selection; + } + + get town() { + return this._town; + } + + // Town auto complete + set town(selection) { + this._town = selection; + + if (!selection) return; + + const province = selection.province; + const postcodes = selection.postcodes; + + if (!this.client.provinceFk) + this.client.provinceFk = province.id; + + if (postcodes.length === 1) + this.client.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; + + if (!this.client.city) + this.client.city = town.name; + + if (!this.client.provinceFk) + this.client.provinceFk = province.id; + } + + onResponse(response) { + this.client.postcode = response.code; + this.client.city = response.city; + this.client.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..24fc80d21 --- /dev/null +++ b/modules/worker/front/create/index.spec.js @@ -0,0 +1,122 @@ +import './index'; + +describe('Client', () => { + describe('Component vnClientCreate', () => { + let $scope; + let $state; + let controller; + + beforeEach(ngModule('client')); + + 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('vnClientCreate', {$element, $scope}); + })); + + it('should define and set scope, state and client properties', () => { + expect(controller.$).toBe($scope); + expect(controller.$state).toBe($state); + expect(controller.client.active).toBe(true); + }); + + describe('onSubmit()', () => { + it(`should call submit() on the watcher then expect a callback`, () => { + jest.spyOn($state, 'go'); + controller.onSubmit(); + + expect(controller.$state.go).toHaveBeenCalledWith('client.card.basicData', {id: '1234'}); + }); + }); + + describe('province() setter', () => { + it(`should set countryFk property`, () => { + controller.client.countryFk = null; + controller.province = { + id: 1, + name: 'New york', + country: { + id: 2, + name: 'USA' + } + }; + + expect(controller.client.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.client.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.client.provinceFk).toEqual(1); + expect(controller.client.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.client.city).toEqual('New York'); + expect(controller.client.provinceFk).toEqual(1); + expect(controller.client.countryFk).toEqual(2); + }); + }); + }); +}); diff --git a/modules/worker/front/create/locale/es.yml b/modules/worker/front/create/locale/es.yml new file mode 100644 index 000000000..afe3feaa6 --- /dev/null +++ b/modules/worker/front/create/locale/es.yml @@ -0,0 +1,12 @@ +Name: Nombre +Tax number: NIF/CIF +Business name: Razón social +Web user: Usuario Web +Email: E-mail +Create and edit: Crear y editar +You can save multiple emails: >- + Puede guardar varios correos electrónicos encadenándolos mediante comas + sin espacios, ejemplo: user@dominio.com, user2@dominio.com siendo el primer + correo electrónico el principal +The type of business must be filled in basic data: El tipo de negocio debe estar rellenado en datos básicos +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..b48a84854 100644 --- a/modules/worker/front/index/index.html +++ b/modules/worker/front/index/index.html @@ -42,6 +42,12 @@ + + + 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/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/client-welcome.html b/print/templates/email/worker-welcome/client-welcome.html new file mode 100644 index 000000000..6aaee6120 --- /dev/null +++ b/print/templates/email/worker-welcome/client-welcome.html @@ -0,0 +1,10 @@ + +
+
+

{{ $t('title', [id]) }}

+

{{ $t('description.dearWorker') }},

+

+

{{ $t('workerData', this.user.name, this.url) }}

+
+
+
diff --git a/print/templates/email/worker-welcome/client-welcome.js b/print/templates/email/worker-welcome/client-welcome.js new file mode 100755 index 000000000..9fbcf86a6 --- /dev/null +++ b/print/templates/email/worker-welcome/client-welcome.js @@ -0,0 +1,23 @@ +const Component = require(`vn-print/core/component`); +const emailBody = new Component('email-body'); + +module.exports = { + name: 'client-welcome', + async serverPrefetch() { + this.client = await this.fetchClient(this.id); + }, + methods: { + fetchClient(id) { + return this.findOneFromDef('client', [id]); + }, + }, + components: { + 'email-body': emailBody.build(), + }, + props: { + id: { + type: Number, + required: true + } + } +}; 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..769df453e --- /dev/null +++ b/print/templates/email/worker-welcome/locale/es.yml @@ -0,0 +1,7 @@ +subject: Bienvenido a Verdnatura +title: "¡Te damos la bienvenida!" +dearWorker: Estimado trabajador +workerData: 'Bienvenido a Verdnatura SL, este es tu usuario: {0}. Pero primero debes + cambiar tu contraseña. + ' diff --git a/print/templates/email/worker-welcome/sql/client.sql b/print/templates/email/worker-welcome/sql/client.sql new file mode 100644 index 000000000..49e1d4bf6 --- /dev/null +++ b/print/templates/email/worker-welcome/sql/client.sql @@ -0,0 +1,11 @@ +SELECT + c.id, + u.name AS userName, + CONCAT(w.lastName, ' ', w.firstName) salesPersonName, + w.phone AS salesPersonPhone, + CONCAT(wu.name, '@verdnatura.es') AS salesPersonEmail +FROM client c + JOIN account.user u ON u.id = c.id + LEFT JOIN worker w ON w.id = c.salesPersonFk + LEFT JOIN account.user wu ON wu.id = w.userFk +WHERE c.id = ? \ No newline at end of file