diff --git a/db/changes/231401/00-workerNotes.sql b/db/changes/231401/00-workerNotes.sql
new file mode 100644
index 000000000..0d9eaae7e
--- /dev/null
+++ b/db/changes/231401/00-workerNotes.sql
@@ -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');
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 32a60a4e2..461f5b2dc 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -987,6 +987,12 @@ export default {
locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]',
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: {
extension: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"]',
saveButton: 'vn-worker-pbx button[type=submit]'
diff --git a/e2e/paths/03-worker/08_add_notes.spec.js b/e2e/paths/03-worker/08_add_notes.spec.js
new file mode 100644
index 000000000..eb2e4c041
--- /dev/null
+++ b/e2e/paths/03-worker/08_add_notes.spec.js
@@ -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');
+ });
+});
diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json
index 63fc65827..145934700 100644
--- a/modules/worker/back/model-config.json
+++ b/modules/worker/back/model-config.json
@@ -53,6 +53,9 @@
"Worker": {
"dataSource": "vn"
},
+ "WorkerObservation": {
+ "dataSource": "vn"
+ },
"WorkerConfig": {
"dataSource": "vn"
},
diff --git a/modules/worker/back/models/worker-observation.js b/modules/worker/back/models/worker-observation.js
new file mode 100644
index 000000000..cccc2cfbd
--- /dev/null
+++ b/modules/worker/back/models/worker-observation.js
@@ -0,0 +1,12 @@
+module.exports = function(Self) {
+ Self.validatesPresenceOf('text', {
+ message: 'Description 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;
+ });
+};
diff --git a/modules/worker/back/models/worker-observation.json b/modules/worker/back/models/worker-observation.json
new file mode 100644
index 000000000..90eb35837
--- /dev/null
+++ b/modules/worker/back/models/worker-observation.json
@@ -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"
+ }
+ }
+}
diff --git a/modules/worker/front/index.js b/modules/worker/front/index.js
index 657f6a8c6..8fad2c0df 100644
--- a/modules/worker/front/index.js
+++ b/modules/worker/front/index.js
@@ -18,3 +18,6 @@ import './log';
import './dms/index';
import './dms/create';
import './dms/edit';
+import './note/index';
+import './note/create';
+
diff --git a/modules/worker/front/locale/es.yml b/modules/worker/front/locale/es.yml
index b5bcfefa4..a25377122 100644
--- a/modules/worker/front/locale/es.yml
+++ b/modules/worker/front/locale/es.yml
@@ -31,3 +31,5 @@ Deallocate PDA: Desasignar PDA
PDA deallocated: PDA desasignada
PDA allocated: PDA asignada
New PDA: Nueva PDA
+Notes: Notas
+New note: Nueva nota
diff --git a/modules/worker/front/note/create/index.html b/modules/worker/front/note/create/index.html
new file mode 100644
index 000000000..d09fc2da5
--- /dev/null
+++ b/modules/worker/front/note/create/index.html
@@ -0,0 +1,30 @@
+