diff --git a/db/changes/232801/00-client_create.sql b/db/changes/232801/00-client_create.sql
new file mode 100644
index 000000000..0728ba05e
--- /dev/null
+++ b/db/changes/232801/00-client_create.sql
@@ -0,0 +1,89 @@
+DROP PROCEDURE IF EXISTS vn.clientCreate;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`client_create`(
+ vFirstname VARCHAR(50),
+ vSurnames VARCHAR(50),
+ vFi VARCHAR(9),
+ vAddress TEXT,
+ vPostcode CHAR(5),
+ vCity VARCHAR(25),
+ vProvinceFk SMALLINT(5),
+ vCompanyFk SMALLINT(5),
+ vPhone VARCHAR(11),
+ vEmail VARCHAR(255),
+ vUserFk INT
+)
+BEGIN
+/**
+ * Create new client
+ *
+ * @params vFirstname firstName
+ * @params vSurnames surnames
+ * @params vFi company code from accounting transactions
+ * @params vAddress address
+ * @params vPostcode postCode
+ * @params vCity city
+ * @params vProvinceFk province
+ * @params vCompanyFk company in which he has become a client
+ * @params vPhone telephone number
+ * @params vEmail email address
+ * @params vUserFk user id
+ */
+ DECLARE vPayMethodFk INT;
+ DECLARE vDueDay INT;
+ DECLARE vDefaultCredit DECIMAL(10, 2);
+ DECLARE vIsTaxDataChecked TINYINT(1);
+ DECLARE vHasCoreVnl BOOLEAN;
+ DECLARE vMandateTypeFk INT;
+
+ SELECT defaultPayMethodFk,
+ defaultDueDay,
+ defaultCredit,
+ defaultIsTaxDataChecked,
+ defaultHasCoreVnl,
+ defaultMandateTypeFk
+ INTO vPayMethodFk,
+ vDueDay,
+ vDefaultCredit,
+ vIsTaxDataChecked,
+ vHasCoreVnl,
+ vMandateTypeFk
+ FROM clientConfig;
+
+ INSERT INTO `client`
+ SET id = vUserFk,
+ name = CONCAT(vFirstname, ' ', vSurnames),
+ street = vAddress,
+ fi = TRIM(vFi),
+ phone = vPhone,
+ email = vEmail,
+ provinceFk = vProvinceFk,
+ city = vCity,
+ postcode = vPostcode,
+ socialName = CONCAT(vSurnames, ' ', vFirstname),
+ payMethodFk = vPayMethodFk,
+ dueDay = vDueDay,
+ credit = vDefaultCredit,
+ isTaxDataChecked = vIsTaxDataChecked,
+ hasCoreVnl = vHasCoreVnl,
+ isEqualizated = FALSE
+ ON duplicate KEY UPDATE
+ payMethodFk = vPayMethodFk,
+ dueDay = vDueDay,
+ credit = vDefaultCredit,
+ isTaxDataChecked = vIsTaxDataChecked,
+ hasCoreVnl = vHasCoreVnl,
+ isActive = TRUE;
+
+ INSERT INTO mandate (clientFk, companyFk, mandateTypeFk)
+ SELECT vUserFk, vCompanyFk, vMandateTypeFk
+ WHERE NOT EXISTS (
+ SELECT id
+ FROM mandate
+ WHERE clientFk = vUserFk
+ AND companyFk = vCompanyFk
+ AND mandateTypeFk = vMandateTypeFk
+ );
+END$$
+DELIMITER ;
diff --git a/db/changes/232801/00-client_create2.sql b/db/changes/232801/00-client_create2.sql
new file mode 100644
index 000000000..8ba4e78e5
--- /dev/null
+++ b/db/changes/232801/00-client_create2.sql
@@ -0,0 +1,17 @@
+ALTER TABLE vn.clientConfig ADD defaultPayMethodFk tinyint(3) unsigned NULL;
+ALTER TABLE vn.clientConfig ADD defaultDueDay int unsigned NULL;
+ALTER TABLE vn.clientConfig ADD defaultCredit decimal(10, 2) NULL;
+ALTER TABLE vn.clientConfig ADD defaultIsTaxDataChecked tinyint(1) NULL;
+ALTER TABLE vn.clientConfig ADD defaultHasCoreVnl boolean NULL;
+ALTER TABLE vn.clientConfig ADD defaultMandateTypeFk smallint(5) NULL;
+ALTER TABLE vn.clientConfig ADD CONSTRAINT clientNewConfigPayMethod_FK FOREIGN KEY (dafaultPayMethodFk) REFERENCES vn.payMethod(id);
+ALTER TABLE vn.clientConfig ADD CONSTRAINT clientNewConfigMandateType_FK FOREIGN KEY (defaultMandateTypeFk) REFERENCES vn.mandateType(id);
+
+UPDATE vn.clientConfig
+ SET defaultPayMethodFk = 4,
+ defaultDueDay = 5,
+ defaultCredit = 300.0,
+ defaultIsTaxDataChecked = 1,
+ defaultHasCoreVnl = 1,
+ defaultMandateTypeFk = 2
+ WHERE id = 1;
\ No newline at end of file
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 46bccf9dc..14c9fba5d 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -385,9 +385,10 @@ INSERT INTO `vn`.`clientManaCache`(`clientFk`, `mana`, `dated`)
(1103, 0, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(1104, -30, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH));
-INSERT INTO `vn`.`clientConfig`(`riskTolerance`, `maxCreditRows`)
+INSERT INTO `vn`.`clientConfig`(`id`, `riskTolerance`, `maxCreditRows`, `maxPriceIncreasingRatio`, `riskScope`, `defaultPayMethodFk`, `defaultDueDay`, `defaultCredit`, `defaultIsTaxDataChecked`, `defaultHasCoreVnl`, `defaultMandateTypeFk`)
VALUES
- (200, 10);
+ (1, 200, 10, 0.25, 2, 4, 5, 300.00, 1, 1, 2);
+
INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `phone`, `mobile`, `isActive`, `clientFk`, `agencyModeFk`, `longitude`, `latitude`, `isEqualizated`, `isDefaultAddress`)
VALUES
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index d4a8a316f..b10813488 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -894,6 +894,18 @@ export default {
extension: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(5) > section > span',
},
+ department: {
+ firstDepartment: 'vn-worker-department-index vn-card > vn-treeview vn-treeview-childs vn-treeview-childs vn-treeview-childs a'
+ },
+ departmentSummary: {
+ header: 'vn-worker-department-summary h5',
+ name: 'vn-worker-department-summary vn-horizontal > vn-one > vn-vertical > vn-label-value:nth-child(1) > section > span',
+ code: 'vn-worker-department-summary vn-horizontal > vn-one > vn-vertical > vn-label-value:nth-child(2) > section > span',
+ chat: 'vn-worker-department-summary vn-horizontal > vn-one > vn-vertical > vn-label-value:nth-child(3) > section > span',
+ bossDepartment: 'vn-worker-department-summary vn-horizontal > vn-one > vn-vertical > vn-label-value:nth-child(4) > section > span',
+ email: 'vn-worker-department-summary vn-horizontal > vn-one > vn-vertical > vn-label-value:nth-child(5) > section > span',
+ clientFk: 'vn-worker-department-summary vn-horizontal > vn-one > vn-vertical > vn-label-value:nth-child(6) > section > span',
+ },
workerBasicData: {
name: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.firstName"]',
surname: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.lastName"]',
@@ -901,6 +913,13 @@ export default {
locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]',
saveButton: 'vn-worker-basic-data button[type=submit]'
},
+ departmentBasicData: {
+ Name: 'vn-worker-department-basic-data vn-textfield[ng-model="$ctrl.department.name"]',
+ Code: 'vn-worker-department-basic-data vn-textfield[ng-model="$ctrl.department.code"]',
+ Chat: 'vn-worker-department-basic-data vn-textfield[ng-model="$ctrl.department.chat"]',
+ Email: 'vn-worker-department-basic-data vn-textfield[ng-model="$ctrl.department.notificationEmail"]',
+ saveButton: 'vn-worker-department-basic-data button[type=submit]'
+ },
workerNotes: {
addNoteFloatButton: 'vn-worker-note vn-icon[icon="add"]',
note: 'vn-note-worker-create vn-textarea[ng-model="$ctrl.note.text"]',
diff --git a/e2e/paths/03-worker/01-department/01_summary.spec.js b/e2e/paths/03-worker/01-department/01_summary.spec.js
new file mode 100644
index 000000000..e4bf8fc2d
--- /dev/null
+++ b/e2e/paths/03-worker/01-department/01_summary.spec.js
@@ -0,0 +1,29 @@
+import selectors from '../../../helpers/selectors.js';
+import getBrowser from '../../../helpers/puppeteer';
+
+describe('department summary path', () => {
+ let browser;
+ let page;
+ beforeAll(async() => {
+ browser = await getBrowser();
+ page = browser.page;
+ await page.loginAndModule('hr', 'worker');
+ await page.accessToSection('worker.department');
+ await page.doSearch('INFORMATICA');
+ await page.click(selectors.department.firstDepartment);
+ });
+
+ afterAll(async() => {
+ await browser.close();
+ });
+
+ it('should reach the employee summary section and check all properties', async() => {
+ expect(await page.waitToGetProperty(selectors.departmentSummary.header, 'innerText')).toEqual('INFORMATICA');
+ expect(await page.getProperty(selectors.departmentSummary.name, 'innerText')).toEqual('INFORMATICA');
+ expect(await page.getProperty(selectors.departmentSummary.code, 'innerText')).toEqual('it');
+ expect(await page.getProperty(selectors.departmentSummary.chat, 'innerText')).toEqual('informatica-cau');
+ expect(await page.getProperty(selectors.departmentSummary.bossDepartment, 'innerText')).toEqual('');
+ expect(await page.getProperty(selectors.departmentSummary.email, 'innerText')).toEqual('-');
+ expect(await page.getProperty(selectors.departmentSummary.clientFk, 'innerText')).toEqual('-');
+ });
+});
diff --git a/e2e/paths/03-worker/01-department/02-basicData.spec.js b/e2e/paths/03-worker/01-department/02-basicData.spec.js
new file mode 100644
index 000000000..219d1426c
--- /dev/null
+++ b/e2e/paths/03-worker/01-department/02-basicData.spec.js
@@ -0,0 +1,43 @@
+import getBrowser from '../../../helpers/puppeteer';
+import selectors from '../../../helpers/selectors.js';
+
+const $ = {
+ form: 'vn-worker-department-basic-data form',
+};
+
+describe('department summary path', () => {
+ let browser;
+ let page;
+ beforeAll(async() => {
+ browser = await getBrowser();
+ page = browser.page;
+ await page.loginAndModule('hr', 'worker');
+ await page.accessToSection('worker.department');
+ await page.doSearch('INFORMATICA');
+ await page.click(selectors.department.firstDepartment);
+ });
+
+ beforeEach(async() => {
+ await page.accessToSection('worker.department.card.basicData');
+ });
+
+ afterAll(async() => {
+ await browser.close();
+ });
+
+ it(`should edit the department basic data and confirm the department data was edited`, async() => {
+ const values = {
+ Name: 'Informatica',
+ Code: 'IT',
+ Chat: 'informatica-cau',
+ Email: 'it@verdnatura.es',
+ };
+
+ await page.fillForm($.form, values);
+ const formValues = await page.fetchForm($.form, Object.keys(values));
+ const message = await page.sendForm($.form, values);
+
+ expect(message.isSuccess).toBeTrue();
+ expect(formValues).toEqual(values);
+ });
+});
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index d6eec4f78..d33b3b3c3 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -147,14 +147,12 @@
"Receipt's bank was not found": "Receipt's bank was not found",
"This receipt was not compensated": "This receipt was not compensated",
"Client's email was not found": "Client's email was not found",
- "Tickets with associated refunds": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº {{id}}",
+ "Tickets with associated refunds": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº %d",
"It is not possible to modify tracked sales": "It is not possible to modify tracked sales",
"It is not possible to modify sales that their articles are from Floramondo": "It is not possible to modify sales that their articles are from Floramondo",
"It is not possible to modify cloned sales": "It is not possible to modify cloned sales",
- "Valid priorities: 1,2,3": "Valid priorities: 1,2,3",
"Warehouse inventory not set": "Almacén inventario no está establecido",
"Component cost not set": "Componente coste no está estabecido",
- "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2",
"Description cannot be blank": "Description cannot be blank",
"company": "Company",
"country": "Country",
@@ -178,5 +176,6 @@
"Failed to upload delivery note": "Error to upload delivery note {{id}}",
"Mail not sent": "There has been an error sending the invoice to the client [{{clientId}}]({{{clientUrl}}}), please check the email address",
"The renew period has not been exceeded": "The renew period has not been exceeded",
- "You can not use the same password": "You can not use the same password"
+ "You can not use the same password": "You can not use the same password",
+ "Valid priorities": "Valid priorities: %d"
}
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index 578a54c06..106df379e 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -268,7 +268,7 @@
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
"Warehouse inventory not set": "El almacén inventario no está establecido",
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
- "Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº {{id}}",
+ "Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº %d",
"Not exist this branch": "La rama no existe",
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado",
"Collection does not exist": "La colección no existe",
@@ -301,6 +301,7 @@
"Fecha fuera de rango": "Fecha fuera de rango",
"Error while generating PDF": "Error al generar PDF",
"Error when sending mail to client": "Error al enviar el correo al cliente",
- "Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
- "The renew period has not been exceeded": "El periodo de renovación no ha sido superado"
+ "Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
+ "The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
+ "Valid priorities": "Prioridades válidas: %d"
}
diff --git a/modules/item/back/methods/item/new.js b/modules/item/back/methods/item/new.js
index c82e2a6b1..b4b081cd7 100644
--- a/modules/item/back/methods/item/new.js
+++ b/modules/item/back/methods/item/new.js
@@ -1,4 +1,4 @@
-let UserError = require('vn-loopback/util/user-error');
+const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('new', {
@@ -49,7 +49,7 @@ 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]}`);
+ throw new UserError('Valid priorities', 'VALID_PRIORITIES', [...itemConfig.validPriorities]);
const provisionalName = params.provisionalName;
delete params.provisionalName;
diff --git a/modules/ticket/back/methods/ticket/setDeleted.js b/modules/ticket/back/methods/ticket/setDeleted.js
index 4d7fe73d4..878cce056 100644
--- a/modules/ticket/back/methods/ticket/setDeleted.js
+++ b/modules/ticket/back/methods/ticket/setDeleted.js
@@ -50,7 +50,7 @@ module.exports = Self => {
fields: ['id']}
, myOptions);
if (ticketRefunds.length > 0)
- throw new UserError($t('Tickets with associated refunds', {id: ticketRefunds[0].id}));
+ throw new UserError('Tickets with associated refunds', 'TICKET_REFUND', ticketRefunds[0].id);
// Check if has sales with shelving
const canDeleteTicketWithPartPrepared =
diff --git a/modules/worker/back/methods/department/getLeaves.js b/modules/worker/back/methods/department/getLeaves.js
index b1ce2fbe8..38ff2ef3e 100644
--- a/modules/worker/back/methods/department/getLeaves.js
+++ b/modules/worker/back/methods/department/getLeaves.js
@@ -33,7 +33,6 @@ module.exports = Self => {
map.set(node.parentFk, []);
map.get(node.parentFk).push(node);
}
-
function setLeaves(nodes) {
if (!nodes) return;
for (let node of nodes) {
@@ -43,6 +42,7 @@ module.exports = Self => {
}
let leaves = map.get(parentId);
+
setLeaves(leaves);
return leaves || [];
diff --git a/modules/worker/back/methods/department/specs/getLeaves.spec.js b/modules/worker/back/methods/department/specs/getLeaves.spec.js
new file mode 100644
index 000000000..fb7c84ff4
--- /dev/null
+++ b/modules/worker/back/methods/department/specs/getLeaves.spec.js
@@ -0,0 +1,10 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('department getLeaves()', () => {
+ const ctx = {req: {accessToken: {userId: 9}}};
+ it('should return the department and the childs containing the search value', async() => {
+ let result = await models.Department.getLeaves(ctx, null, 'INFORMATICA');
+
+ expect(result.length).toEqual(1);
+ });
+});
diff --git a/modules/worker/back/methods/worker/new.js b/modules/worker/back/methods/worker/new.js
index 27acc98ab..199a3be62 100644
--- a/modules/worker/back/methods/worker/new.js
+++ b/modules/worker/back/methods/worker/new.js
@@ -171,7 +171,7 @@ module.exports = Self => {
throw new UserError(`That payment method requires an IBAN`);
await models.Worker.rawSql(
- 'CALL vn.clientCreate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
+ 'CALL vn.client_create(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[
args.firstName,
args.lastNames,
diff --git a/modules/worker/back/models/department.json b/modules/worker/back/models/department.json
index c3f627e93..edeba74f7 100644
--- a/modules/worker/back/models/department.json
+++ b/modules/worker/back/models/department.json
@@ -8,8 +8,9 @@
},
"properties": {
"id": {
+ "type": "number",
"id": true,
- "type": "number"
+ "description": "Identifier"
},
"code": {
"type": "string"
@@ -37,6 +38,30 @@
},
"hasToMistake": {
"type": "number"
+ },
+ "isTeleworking": {
+ "type": "boolean"
+ },
+ "hasToRefill": {
+ "type": "boolean"
+ },
+ "hasToSendMail": {
+ "type": "boolean"
+ },
+ "isProduction": {
+ "type": "boolean"
+ }
+ },
+ "relations": {
+ "client": {
+ "type": "belongsTo",
+ "model": "Client",
+ "foreignKey": "clientFk"
+ },
+ "worker": {
+ "type": "belongsTo",
+ "model": "Worker",
+ "foreignKey": "workerFk"
}
}
}
diff --git a/modules/worker/back/models/worker.json b/modules/worker/back/models/worker.json
index 88200a4a9..978d613e9 100644
--- a/modules/worker/back/models/worker.json
+++ b/modules/worker/back/models/worker.json
@@ -60,7 +60,7 @@
"user": {
"type": "belongsTo",
"model": "VnUser",
- "foreignKey": "userFk"
+ "foreignKey": "id"
},
"boss": {
"type": "belongsTo",
diff --git a/modules/worker/front/department/basic-data/index.html b/modules/worker/front/department/basic-data/index.html
new file mode 100644
index 000000000..2d1e315d4
--- /dev/null
+++ b/modules/worker/front/department/basic-data/index.html
@@ -0,0 +1,102 @@
+