refs #5250 campo de notes #1407

Merged
joan merged 12 commits from 5250-observacionesWorker into dev 2023-04-11 07:07:56 +00:00
16 changed files with 274 additions and 0 deletions

View File

@ -0,0 +1,14 @@
CREATE TABLE `vn`.`workerObservation` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`workerFk` int(10) unsigned DEFAULT NULL,
`userFk` int(10) unsigned DEFAULT NULL,
`text` text COLLATE utf8mb3_unicode_ci NOT NULL,
`created` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
CONSTRAINT `workerFk_workerObservation_FK` FOREIGN KEY (`workerFk`) REFERENCES `vn`.`worker` (`id`) ON UPDATE CASCADE,
CONSTRAINT `userFk_workerObservation_FK` FOREIGN KEY (`userFk`) REFERENCES `account`.`user`(`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Todas las observaciones referentes a un trabajador';
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('WorkerObservation', '*', '*', 'ALLOW', 'ROLE', 'hr');

View File

@ -987,6 +987,12 @@ export default {
locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]', locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]',
saveButton: 'vn-worker-basic-data button[type=submit]' saveButton: 'vn-worker-basic-data button[type=submit]'
}, },
workerNotes: {
addNoteFloatButton: 'vn-float-button',
note: 'vn-textarea[ng-model="$ctrl.note.text"]',
saveButton: 'button[type=submit]',
firstNoteText: 'vn-worker-note .text'
},
workerPbx: { workerPbx: {
extension: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"]', extension: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"]',
saveButton: 'vn-worker-pbx button[type=submit]' saveButton: 'vn-worker-pbx button[type=submit]'

View File

@ -0,0 +1,42 @@
import selectors from '../../helpers/selectors';
import getBrowser from '../../helpers/puppeteer';
describe('Worker Add notes path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'worker');
await page.accessToSearchResult('Bruce Banner');
await page.accessToSection('worker.card.note.index');
});
afterAll(async() => {
await browser.close();
});
it(`should reach the notes index`, async() => {
await page.waitForState('worker.card.note.index');
});
it(`should click on the add note button`, async() => {
await page.waitToClick(selectors.workerNotes.addNoteFloatButton);
await page.waitForState('worker.card.note.create');
});
it(`should create a note`, async() => {
await page.waitForSelector(selectors.workerNotes.note);
await page.type(`${selectors.workerNotes.note} textarea`, 'Meeting with Black Widow 21st 9am');
await page.waitToClick(selectors.workerNotes.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the note was created', async() => {
const result = await page.waitToGetProperty(selectors.workerNotes.firstNoteText, 'innerText');
expect(result).toEqual('Meeting with Black Widow 21st 9am');
});
});

View File

@ -53,6 +53,9 @@
"Worker": { "Worker": {
"dataSource": "vn" "dataSource": "vn"
}, },
"WorkerObservation": {
"dataSource": "vn"
},
"WorkerConfig": { "WorkerConfig": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,12 @@
module.exports = function(Self) {
Self.validatesPresenceOf('text', {
Outdated
Review

Aci pots gastar Self.validatesPresenceOf

Aci pots gastar Self.validatesPresenceOf

Actualitzat en la nova versió

Actualitzat en la nova versió
Outdated
Review

La funció isEnabled no es necesaria si gastes validatesPresenceOf(): Exemple

Self.validatesPresenceOf('name', {
        message: 'Name cannot be blank'
});
La funció isEnabled no es necesaria si gastes validatesPresenceOf(): Exemple ``` Self.validatesPresenceOf('name', { message: 'Name cannot be blank' }); ```

Solucionat en la nova versió

Solucionat en la nova versió
message: 'Description cannot be blank'
Outdated
Review

La funció isEnabled no es necesaria si gastes validatesPresenceOf(): Exemple

Self.validatesPresenceOf('name', {
        message: 'Name cannot be blank'
});
La funció isEnabled no es necesaria si gastes validatesPresenceOf(): Exemple ``` Self.validatesPresenceOf('name', { message: 'Name cannot be blank' }); ```
});
Self.observe('before save', async function(ctx) {
ctx.instance.created = new Date();
let token = ctx.options.accessToken;
let userId = token && token.userId;
ctx.instance.userFk = userId;
});
};

View File

@ -0,0 +1,39 @@
{
"name": "WorkerObservation",
"base": "VnModel",
"options": {
"mysql": {
"table": "workerObservation"
}
},
"properties": {
"id": {
"id": true,
"type": "number"
},
"workerFk": {
"type": "number"
},
"userFk": {
"type": "number"
},
"text": {
"type": "string"
},
"created": {
"type": "date"
}
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
},
"user":{
"type": "belongsTo",
"model": "Account",
"foreignKey": "userFk"
}
}
}

View File

@ -18,3 +18,6 @@ import './log';
import './dms/index'; import './dms/index';
import './dms/create'; import './dms/create';
import './dms/edit'; import './dms/edit';
import './note/index';
import './note/create';

View File

@ -31,3 +31,5 @@ Deallocate PDA: Desasignar PDA
PDA deallocated: PDA desasignada PDA deallocated: PDA desasignada
PDA allocated: PDA asignada PDA allocated: PDA asignada
New PDA: Nueva PDA New PDA: Nueva PDA
Notes: Notas
New note: Nueva nota

View File

@ -0,0 +1,30 @@
<vn-watcher
vn-id="watcher"
url="WorkerObservations"
id-field="id"
data="$ctrl.note"
insert-mode="true"
form="form">
</vn-watcher>
<form name="form" ng-submit="watcher.submitGo('worker.card.note.index')" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textarea
vn-one
label="Note"
ng-model="$ctrl.note.text"
vn-focus>
</vn-textarea>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
ng-if="watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
ng-click="$ctrl.cancel()"
label="Cancel">
</vn-button>
</vn-button-bar>
</form>

View File

@ -0,0 +1,21 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.note = {
workerFk: parseInt(this.$params.id),
text: null
};
}
cancel() {
this.$state.go('worker.card.note.index', {id: this.$params.id});
}
}
ngModule.vnComponent('vnNoteWorkerCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,22 @@
import './index';
describe('Worker', () => {
describe('Component vnNoteWorkerCreate', () => {
let $state;
let controller;
beforeEach(ngModule('worker'));
beforeEach(inject(($componentController, _$state_) => {
$state = _$state_;
$state.params.id = '1234';
const $element = angular.element('<vn-note-create></vn-note-create>');
controller = $componentController('vnNoteWorkerCreate', {$element, $state});
}));
it('should define workerFk using $state.params.id', () => {
expect(controller.note.workerFk).toBe(1234);
expect(controller.note.worker).toBe(undefined);
});
});
});

View File

@ -0,0 +1,2 @@
New note: Nueva nota
Note: Nota

View File

@ -0,0 +1,32 @@
<vn-crud-model
vn-id="model"
url="WorkerObservations"
filter="$ctrl.filter"
link="{workerFk: $ctrl.$params.id}"
include="{relation: 'user'}"
data="notes"
auto-load="true">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-md">
<vn-card class="vn-pa-md">
<div
ng-repeat="note in notes"
class="note vn-pa-sm border-solid border-radius vn-mb-md">
<vn-horizontal class="vn-mb-sm" style="color: #666">
<vn-one>{{::note.user.nickname}}</vn-one>
<vn-auto>{{::note.created | date:'dd/MM/yyyy HH:mm'}}</vn-auto>
</vn-horizontal>
<vn-horizontal class="text">
{{::note.text}}
</vn-horizontal>
</div>
</vn-card>
</vn-data-viewer>
<a vn-tooltip="New note"
ui-sref="worker.card.note.create({id: $ctrl.$params.id})"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,22 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
order: 'created DESC',
};
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnWorkerNote', {
template: require('./index.html'),
controller: Controller,
bindings: {
worker: '<'
}
});

View File

@ -0,0 +1,5 @@
vn-worker-note {
.note:last-child {
margin-bottom: 0;
}
}

View File

@ -11,6 +11,7 @@
], ],
"card": [ "card": [
{"state": "worker.card.basicData", "icon": "settings"}, {"state": "worker.card.basicData", "icon": "settings"},
{"state": "worker.card.note.index", "icon": "insert_drive_file"},
{"state": "worker.card.timeControl", "icon": "access_time"}, {"state": "worker.card.timeControl", "icon": "access_time"},
{"state": "worker.card.calendar", "icon": "icon-calendar"}, {"state": "worker.card.calendar", "icon": "icon-calendar"},
{"state": "worker.card.pda", "icon": "phone_android"}, {"state": "worker.card.pda", "icon": "phone_android"},
@ -72,6 +73,24 @@
"component": "vn-worker-log", "component": "vn-worker-log",
"description": "Log", "description": "Log",
"acl": ["salesAssistant"] "acl": ["salesAssistant"]
}, {
"url": "/note",
Review

Veig un ACL per a HHRR, pero pareix que esta secció no te restriccions de accés. Es correcte?

Veig un ACL per a HHRR, pero pareix que esta secció no te restriccions de accés. Es correcte?
Review

Com se demana que siga com en el de clients, el accés es per a tots

Com se demana que siga com en el de clients, el accés es per a tots
"state": "worker.card.note",
"component": "ui-view",
"abstract": true
}, {
"url": "/index",
"state": "worker.card.note.index",
"component": "vn-worker-note",
"description": "Notes",
"params": {
"worker": "$ctrl.worker"
}
}, {
"url": "/create",
"state": "worker.card.note.create",
"component": "vn-note-worker-create",
"description": "New note"
}, { }, {
"url": "/pbx", "url": "/pbx",
"state": "worker.card.pbx", "state": "worker.card.pbx",