fixes #5287 Añadir campo Locker (Taquilla) en Worker/Basic Data y crear nueva sección Worker/PDA #1347
|
@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- (Client -> Descriptor) Nuevo icono $ con barrotes para los clientes con impago
|
- (Client -> Descriptor) Nuevo icono $ con barrotes para los clientes con impago
|
||||||
|
- (Trabajador -> Datos Básicos) Añadido nuevo campo Taquilla
|
||||||
|
- (Trabajador -> PDA) Nueva sección
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
-
|
-
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
ALTER TABLE `vn`.`worker` ADD locker INT UNSIGNED NULL UNIQUE;
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('DeviceProduction', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProductionModels', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProductionState', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProductionUser', '*', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('DeviceProduction', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('DeviceProductionModels', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('DeviceProductionState', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('DeviceProductionUser', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('Worker', 'deallocatePDA', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('Worker', 'allocatePDA', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||||
|
('Worker', 'deallocatePDA', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('Worker', 'allocatePDA', '*', 'ALLOW', 'ROLE', 'productionAssi');
|
|
@ -2787,3 +2787,30 @@ INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, NULL, 1);
|
(1, NULL, 1);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProductionModels` (`code`)
|
||||||
|
VALUES
|
||||||
|
('BLACKVIEW'),
|
||||||
|
('DODGEE'),
|
||||||
|
('ZEBRA');
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProductionState` (`code`, `description`)
|
||||||
|
VALUES
|
||||||
|
('active', 'activo'),
|
||||||
|
('idle', 'inactivo'),
|
||||||
|
('lost', 'perdida'),
|
||||||
|
('repair', 'reparación'),
|
||||||
|
('retired', 'retirada');
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProduction` (`imei`, `modelFk`, `macWifi`, `serialNumber`, `android_id`, `purchased`, `stateFk`, `isInScalefusion`, `description`)
|
||||||
|
VALUES
|
||||||
|
('ime1', 'BLACKVIEW', 'macWifi1', 'serialNumber1', 'android_id1', util.VN_NOW(), 'active', 0, NULL),
|
||||||
|
('ime2', 'DODGEE', 'macWifi2', 'serialNumber2', 'android_id2', util.VN_NOW(), 'idle', 0, NULL),
|
||||||
|
('ime3', 'ZEBRA', 'macWifi3', 'serialNumber3', 'android_id3', util.VN_NOW(), 'active', 0, NULL),
|
||||||
|
('ime4', 'BLACKVIEW', 'macWifi4', 'serialNumber4', 'android_id4', util.VN_NOW(), 'idle', 0, NULL);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`deviceProductionUser` (`deviceProductionFk`, `userFk`, `created`)
|
||||||
|
VALUES
|
||||||
|
(1, 1, util.VN_NOW()),
|
||||||
|
(3, 3, util.VN_NOW());
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -973,6 +973,7 @@ export default {
|
||||||
id: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(3) > section > span',
|
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',
|
email: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(4) > section > span',
|
||||||
department: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(5) > section > span',
|
department: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(5) > section > span',
|
||||||
|
locker: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(10) > section > span',
|
||||||
userId: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(2) > section > span',
|
userId: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(2) > section > span',
|
||||||
userName: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(3) > section > span',
|
userName: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(3) > section > span',
|
||||||
role: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(4) > section > span',
|
role: 'vn-worker-summary vn-one:nth-child(2) > vn-label-value:nth-child(4) > section > span',
|
||||||
|
@ -983,6 +984,7 @@ export default {
|
||||||
name: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.firstName"]',
|
name: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.firstName"]',
|
||||||
surname: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.lastName"]',
|
surname: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.lastName"]',
|
||||||
phone: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.phone"]',
|
phone: 'vn-worker-basic-data vn-textfield[ng-model="$ctrl.worker.phone"]',
|
||||||
|
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]'
|
||||||
},
|
},
|
||||||
workerPbx: {
|
workerPbx: {
|
||||||
|
@ -1040,6 +1042,12 @@ export default {
|
||||||
switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
|
switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
|
||||||
createButton: 'vn-worker-create vn-submit[label="Create"]',
|
createButton: 'vn-worker-create vn-submit[label="Create"]',
|
||||||
},
|
},
|
||||||
|
workerPda: {
|
||||||
|
currentPDA: 'vn-worker-pda vn-textfield[ng-model="$ctrl.currentPDA.description"]',
|
||||||
|
newPDA: 'vn-worker-pda vn-autocomplete[ng-model="$ctrl.newPDA"]',
|
||||||
|
delete: 'vn-worker-pda vn-icon-button[icon=delete]',
|
||||||
|
submit: 'vn-worker-pda vn-submit[label="Assign"]',
|
||||||
|
},
|
||||||
invoiceOutIndex: {
|
invoiceOutIndex: {
|
||||||
topbarSearch: 'vn-searchbar',
|
topbarSearch: 'vn-searchbar',
|
||||||
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
|
|
|
@ -29,5 +29,6 @@ describe('Worker summary path', () => {
|
||||||
expect(await page.getProperty(selectors.workerSummary.userName, 'innerText')).toEqual('agency');
|
expect(await page.getProperty(selectors.workerSummary.userName, 'innerText')).toEqual('agency');
|
||||||
expect(await page.getProperty(selectors.workerSummary.role, 'innerText')).toEqual('agency');
|
expect(await page.getProperty(selectors.workerSummary.role, 'innerText')).toEqual('agency');
|
||||||
expect(await page.getProperty(selectors.workerSummary.extension, 'innerText')).toEqual('1101');
|
expect(await page.getProperty(selectors.workerSummary.extension, 'innerText')).toEqual('1101');
|
||||||
|
expect(await page.getProperty(selectors.workerSummary.locker, 'innerText')).toEqual('-');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,7 @@ describe('Worker basic data path', () => {
|
||||||
await page.overwrite(selectors.workerBasicData.name, 'David C.');
|
await page.overwrite(selectors.workerBasicData.name, 'David C.');
|
||||||
await page.overwrite(selectors.workerBasicData.surname, 'H.');
|
await page.overwrite(selectors.workerBasicData.surname, 'H.');
|
||||||
await page.overwrite(selectors.workerBasicData.phone, '444332211');
|
await page.overwrite(selectors.workerBasicData.phone, '444332211');
|
||||||
|
await page.overwrite(selectors.workerBasicData.locker, '1');
|
||||||
await page.click(selectors.workerBasicData.saveButton);
|
await page.click(selectors.workerBasicData.saveButton);
|
||||||
|
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
@ -36,5 +37,6 @@ describe('Worker basic data path', () => {
|
||||||
expect(await page.waitToGetProperty(selectors.workerBasicData.name, 'value')).toEqual('David C.');
|
expect(await page.waitToGetProperty(selectors.workerBasicData.name, 'value')).toEqual('David C.');
|
||||||
expect(await page.waitToGetProperty(selectors.workerBasicData.surname, 'value')).toEqual('H.');
|
expect(await page.waitToGetProperty(selectors.workerBasicData.surname, 'value')).toEqual('H.');
|
||||||
expect(await page.waitToGetProperty(selectors.workerBasicData.phone, 'value')).toEqual('444332211');
|
expect(await page.waitToGetProperty(selectors.workerBasicData.phone, 'value')).toEqual('444332211');
|
||||||
|
expect(await page.waitToGetProperty(selectors.workerBasicData.locker, 'value')).toEqual('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
|
describe('Worker pda path', () => {
|
||||||
|
let browser;
|
||||||
|
let page;
|
||||||
|
beforeAll(async() => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
page = browser.page;
|
||||||
|
await page.loginAndModule('hr', 'worker');
|
||||||
|
await page.accessToSearchResult('employeeNick');
|
||||||
|
await page.accessToSection('worker.card.pda');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if worker has already a PDA allocated', async() => {
|
||||||
|
expect(await page.waitToGetProperty(selectors.workerPda.currentPDA, 'value')).toContain('serialNumber1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deallocate the PDA', async() => {
|
||||||
|
await page.waitToClick(selectors.workerPda.delete);
|
||||||
|
let message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('PDA deallocated');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allocate a new PDA', async() => {
|
||||||
|
await page.autocompleteSearch(selectors.workerPda.newPDA, 'serialNumber2');
|
||||||
|
await page.waitToClick(selectors.workerPda.submit);
|
||||||
|
let message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('PDA allocated');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if a new PDA has been allocated', async() => {
|
||||||
|
expect(await page.waitToGetProperty(selectors.workerPda.currentPDA, 'value')).toContain('serialNumber2');
|
||||||
|
});
|
||||||
|
});
|
|
@ -23,6 +23,7 @@ There is a new version, click here to reload: Hay una nueva versión, pulse aqu
|
||||||
|
|
||||||
Back: Volver
|
Back: Volver
|
||||||
Save: Guardar
|
Save: Guardar
|
||||||
|
Assign: Asignar
|
||||||
Create: Crear
|
Create: Crear
|
||||||
Send: Enviar
|
Send: Enviar
|
||||||
Delete: Eliminar
|
Delete: Eliminar
|
||||||
|
|
|
@ -263,6 +263,7 @@
|
||||||
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
||||||
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
||||||
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
|
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
|
||||||
"There is no assigned email for this client": "No hay correo asignado para este cliente"
|
"There is no assigned email for this client": "No hay correo asignado para este cliente",
|
||||||
|
"This locker has already been assigned": "Esta taquilla ya ha sido asignada"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('allocatePDA', {
|
||||||
|
description: 'Deallocate the PDA of the worker',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The worker id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}, {
|
||||||
|
arg: 'pda',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The pda id'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/allocatePDA`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.allocatePDA = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const args = ctx.args;
|
||||||
|
let tx;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pda = await models.DeviceProduction.findById(args.pda, myOptions);
|
||||||
|
if (pda.stateFk != 'idle') throw new UserError(`The PDA state is not idle`);
|
||||||
|
await pda.updateAttributes({stateFk: 'active'}, myOptions);
|
||||||
|
await models.DeviceProductionUser.create({
|
||||||
|
deviceProductionFk: args.pda,
|
||||||
|
userFk: args.id,
|
||||||
|
created: new Date()
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return {
|
||||||
|
deviceProductionFk: pda.id,
|
||||||
|
deviceProduction: {
|
||||||
|
modelFk: pda.modelFk,
|
||||||
|
serialNumber: pda.serialNumber
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,53 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('deallocatePDA', {
|
||||||
|
description: 'Deallocate the worker\'s PDA',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The worker id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}, {
|
||||||
|
arg: 'pda',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The pda id'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/deallocatePDA`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deallocatePDA = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const args = ctx.args;
|
||||||
|
let tx;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pda = await models.DeviceProduction.findById(args.pda, myOptions);
|
||||||
|
await pda.updateAttributes({stateFk: 'idle'}, myOptions);
|
||||||
|
await models.DeviceProductionUser.destroyAll({userFk: args.id, deviceProductionFk: args.pda}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return pda;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Worker allocatePDA()', () => {
|
||||||
|
it('should allocate a new worker\'s PDA', async() => {
|
||||||
|
const tx = await models.Worker.beginTransaction({});
|
||||||
|
try {
|
||||||
|
const workerWithoutPDA = 1101;
|
||||||
|
const PDAWithIdleState = 4;
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {args: {id: workerWithoutPDA, pda: PDAWithIdleState}};
|
||||||
|
|
||||||
|
await models.Worker.allocatePDA(ctx, options);
|
||||||
|
const deviceProduction = await models.DeviceProduction.findById(PDAWithIdleState, null, options);
|
||||||
|
const deviceProductionUser = await models.DeviceProductionUser
|
||||||
|
.findById(PDAWithIdleState, null, options);
|
||||||
|
|
||||||
|
expect(deviceProduction.stateFk).toEqual('active');
|
||||||
|
expect(deviceProductionUser).not.toBeNull();
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error trying to allocate a non idle PDA', async() => {
|
||||||
|
const tx = await models.Worker.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const workerWithoutPDA = 1101;
|
||||||
|
const PDAWithActiveState = 1;
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {args: {id: workerWithoutPDA, pda: PDAWithActiveState}};
|
||||||
|
|
||||||
|
await models.Worker.allocatePDA(ctx, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Worker deallocatePDA()', () => {
|
||||||
|
it('should deallocate a worker\'s PDA', async() => {
|
||||||
|
const tx = await models.Worker.beginTransaction({});
|
||||||
|
try {
|
||||||
|
const workerWithPDA = 1;
|
||||||
|
const PDAWithActiveState = 1;
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {args: {id: workerWithPDA, pda: PDAWithActiveState}};
|
||||||
|
|
||||||
|
await models.Worker.deallocatePDA(ctx, options);
|
||||||
|
const deviceProduction = await models.DeviceProduction.findById(PDAWithActiveState, null, options);
|
||||||
|
const deviceProductionUser = await models.DeviceProductionUser
|
||||||
|
.findById(PDAWithActiveState, null, options);
|
||||||
|
|
||||||
|
expect(deviceProduction.stateFk).toEqual('idle');
|
||||||
|
expect(deviceProductionUser).toBe(null);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -20,6 +20,18 @@
|
||||||
"Device": {
|
"Device": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"DeviceProduction": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"DeviceProductionModels": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"DeviceProductionState": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"DeviceProductionUser": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"EducationLevel": {
|
"EducationLevel": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionModels",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionModels"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"id": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionState",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionState"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionUser",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionUser"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"deviceProductionFk": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"deviceProduction": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "DeviceProduction",
|
||||||
|
"foreignKey": "deviceProductionFk"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "User",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProduction",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProduction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"imei": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"modelFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"macWifi": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"serialNumber": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"android_id": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"purchased": {
|
||||||
|
"type" : "date"
|
||||||
|
},
|
||||||
|
"stateFk": {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"isInScaleFusion": {
|
||||||
|
"type" : "boolean"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"model": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "DeviceProductionModels",
|
||||||
|
"foreignKey": "modelFk"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "DeviceProductionState",
|
||||||
|
"foreignKey": "stateFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,4 +14,10 @@ module.exports = Self => {
|
||||||
require('../methods/worker/holidays')(Self);
|
require('../methods/worker/holidays')(Self);
|
||||||
require('../methods/worker/activeContract')(Self);
|
require('../methods/worker/activeContract')(Self);
|
||||||
require('../methods/worker/new')(Self);
|
require('../methods/worker/new')(Self);
|
||||||
|
require('../methods/worker/deallocatePDA')(Self);
|
||||||
|
require('../methods/worker/allocatePDA')(Self);
|
||||||
|
|
||||||
|
Self.validatesUniquenessOf('locker', {
|
||||||
|
message: 'This locker has already been assigned'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,6 +55,9 @@
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"type" : "string"
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"locker": {
|
||||||
|
"type" : "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Name"
|
label="Name"
|
||||||
ng-model="$ctrl.worker.firstName"
|
ng-model="$ctrl.worker.firstName"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
@ -24,14 +24,14 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Business phone"
|
label="Business phone"
|
||||||
ng-model="$ctrl.worker.phone"
|
ng-model="$ctrl.worker.phone"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Mobile extension"
|
label="Mobile extension"
|
||||||
ng-model="$ctrl.worker.mobileExtension"
|
ng-model="$ctrl.worker.mobileExtension"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
@ -73,11 +73,16 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="SSN"
|
label="SSN"
|
||||||
ng-model="$ctrl.worker.SSN"
|
ng-model="$ctrl.worker.SSN"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
<vn-input-number
|
||||||
|
min="0"
|
||||||
|
label="Locker"
|
||||||
|
ng-model="$ctrl.worker.locker">
|
||||||
|
</vn-input-number>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -5,4 +5,5 @@ SSN: NSS
|
||||||
Married: Casado/a
|
Married: Casado/a
|
||||||
Single: Soltero/a
|
Single: Soltero/a
|
||||||
Business phone: Teléfono de empresa
|
Business phone: Teléfono de empresa
|
||||||
Mobile extension: Extensión móvil
|
Mobile extension: Extensión móvil
|
||||||
|
Locker: Taquilla
|
||||||
|
|
|
@ -10,6 +10,7 @@ import './descriptor-popover';
|
||||||
import './search-panel';
|
import './search-panel';
|
||||||
import './basic-data';
|
import './basic-data';
|
||||||
import './pbx';
|
import './pbx';
|
||||||
|
import './pda';
|
||||||
import './department';
|
import './department';
|
||||||
import './calendar';
|
import './calendar';
|
||||||
import './time-control';
|
import './time-control';
|
||||||
|
|
|
@ -23,4 +23,11 @@ worker: trabajador
|
||||||
Go to the worker: Ir al trabajador
|
Go to the worker: Ir al trabajador
|
||||||
Click to exclude the user from getting disabled: Marcar para no deshabilitar
|
Click to exclude the user from getting disabled: Marcar para no deshabilitar
|
||||||
Click to allow the user to be disabled: Marcar para deshabilitar
|
Click to allow the user to be disabled: Marcar para deshabilitar
|
||||||
This user can't be disabled: Fijado para no deshabilitar
|
This user can't be disabled: Fijado para no deshabilitar
|
||||||
|
Model: Modelo
|
||||||
|
Serial Number: Número de serie
|
||||||
|
Current PDA: PDA Actual
|
||||||
|
Deallocate PDA: Desasignar PDA
|
||||||
|
PDA deallocated: PDA desasignada
|
||||||
|
PDA allocated: PDA asignada
|
||||||
|
New PDA: Nueva PDA
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<div class="vn-w-md" ng-show="$ctrl.currentPDA">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
label="Current PDA"
|
||||||
|
ng-model="$ctrl.currentPDA.description"
|
||||||
|
disabled="true">
|
||||||
|
<append>
|
||||||
|
<vn-icon-button
|
||||||
|
icon="delete"
|
||||||
|
vn-tooltip="Deallocate PDA"
|
||||||
|
ng-click="$ctrl.deallocatePDA()"
|
||||||
|
vn-acl="hr, productionAssi">
|
||||||
|
</vn-icon-button>
|
||||||
|
</append>
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-card>
|
||||||
|
</div>
|
||||||
|
<form name="form" ng-show="!$ctrl.currentPDA" ng-submit="$ctrl.allocatePDA()" class="vn-w-md">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-acl="hr, productionAssi"
|
||||||
|
ng-model="$ctrl.newPDA"
|
||||||
|
url="DeviceProductions"
|
||||||
|
fields="['id', 'modelFk', 'serialNumber']"
|
||||||
|
where="{'stateFk': 'idle'}"
|
||||||
|
label="New PDA"
|
||||||
|
order="id"
|
||||||
|
value-field="id"
|
||||||
|
show-field="serialNumber">
|
||||||
|
<tpl-item>
|
||||||
|
<span>ID: {{id}}</span>
|
||||||
|
<span class="separator"></span>
|
||||||
|
<span>{{'Model' | translate}}: {{modelFk}}</span>
|
||||||
|
<span class="separator"></span>
|
||||||
|
<span>{{'Serial Number' | translate}}: {{serialNumber}}</span>
|
||||||
|
</tpl-item>
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-card>
|
||||||
|
<vn-button-bar>
|
||||||
|
<vn-submit
|
||||||
|
disabled="!$ctrl.newPDA"
|
||||||
|
label="Assign">
|
||||||
|
</vn-submit>
|
||||||
|
</vn-button-bar>
|
||||||
|
</form>
|
|
@ -0,0 +1,53 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
const filter = {
|
||||||
|
where: {userFk: this.$params.id},
|
||||||
|
include: {relation: 'deviceProduction'}
|
||||||
|
};
|
||||||
|
this.$http.get('DeviceProductionUsers', {filter}).
|
||||||
|
then(res => {
|
||||||
|
if (res.data && res.data.length > 0)
|
||||||
|
this.setCurrentPDA(res.data[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deallocatePDA() {
|
||||||
|
this.$http.post(`Workers/${this.$params.id}/deallocatePDA`, {pda: this.currentPDA.deviceProductionFk})
|
||||||
|
.then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('PDA deallocated'));
|
||||||
|
delete this.currentPDA;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatePDA() {
|
||||||
|
this.$http.post(`Workers/${this.$params.id}/allocatePDA`, {pda: this.newPDA})
|
||||||
|
.then(res => {
|
||||||
|
if (res.data)
|
||||||
|
this.setCurrentPDA(res.data);
|
||||||
|
|
||||||
|
this.vnApp.showSuccess(this.$t('PDA allocated'));
|
||||||
|
delete this.newPDA;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentPDA(data) {
|
||||||
|
this.currentPDA = data;
|
||||||
|
this.currentPDA.description = [];
|
||||||
|
this.currentPDA.description.push(`ID: ${this.currentPDA.deviceProductionFk}`);
|
||||||
|
this.currentPDA.description.push(`${this.$t('Model')}: ${this.currentPDA.deviceProduction.modelFk}`);
|
||||||
|
this.currentPDA.description.push(`${this.$t('Serial Number')}: ${this.currentPDA.deviceProduction.serialNumber}`);
|
||||||
|
this.currentPDA.description = this.currentPDA.description.join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnWorkerPda', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
});
|
|
@ -0,0 +1,72 @@
|
||||||
|
import './index';
|
||||||
|
|
||||||
|
describe('Worker', () => {
|
||||||
|
describe('Component vnWorkerPda', () => {
|
||||||
|
let $httpBackend;
|
||||||
|
let $scope;
|
||||||
|
let $element;
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
beforeEach(ngModule('worker'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
$scope = $rootScope.$new();
|
||||||
|
$element = angular.element('<vn-worker-pda></vn-worker-pda>');
|
||||||
|
controller = $componentController('vnWorkerPda', {$element, $scope});
|
||||||
|
$httpBackend.expectGET(`DeviceProductionUsers`).respond();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('deallocatePDA()', () => {
|
||||||
|
it('should make an HTTP Post query to deallocatePDA', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
controller.currentPDA = {deviceProductionFk: 1};
|
||||||
|
controller.$params.id = 1;
|
||||||
|
|
||||||
|
$httpBackend
|
||||||
|
.expectPOST(`Workers/${controller.$params.id}/deallocatePDA`,
|
||||||
|
{pda: controller.currentPDA.deviceProductionFk})
|
||||||
|
.respond();
|
||||||
|
controller.deallocatePDA();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
expect(controller.currentPDA).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('allocatePDA()', () => {
|
||||||
|
it('should make an HTTP Post query to allocatePDA', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
controller.newPDA = 4;
|
||||||
|
controller.$params.id = 1;
|
||||||
|
|
||||||
|
$httpBackend
|
||||||
|
.expectPOST(`Workers/${controller.$params.id}/allocatePDA`,
|
||||||
|
{pda: controller.newPDA})
|
||||||
|
.respond();
|
||||||
|
controller.allocatePDA();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
expect(controller.newPDA).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCurrentPDA()', () => {
|
||||||
|
it('should set CurrentPDA', () => {
|
||||||
|
const data = {
|
||||||
|
deviceProductionFk: 1,
|
||||||
|
deviceProduction: {
|
||||||
|
modelFk: 1,
|
||||||
|
serialNumber: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controller.setCurrentPDA(data);
|
||||||
|
|
||||||
|
expect(controller.currentPDA).toBeDefined();
|
||||||
|
expect(controller.currentPDA.description).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
span.separator{
|
||||||
|
border-left: 1px solid black;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,10 @@
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{"state": "worker.card.basicData", "icon": "settings"},
|
{"state": "worker.card.basicData", "icon": "settings"},
|
||||||
{"state": "worker.card.pbx", "icon": "icon-pbx"},
|
|
||||||
{"state": "worker.card.calendar", "icon": "icon-calendar"},
|
|
||||||
{"state": "worker.card.timeControl", "icon": "access_time"},
|
{"state": "worker.card.timeControl", "icon": "access_time"},
|
||||||
|
{"state": "worker.card.calendar", "icon": "icon-calendar"},
|
||||||
|
{"state": "worker.card.pda", "icon": "phone_android"},
|
||||||
|
{"state": "worker.card.pbx", "icon": "icon-pbx"},
|
||||||
{"state": "worker.card.dms.index", "icon": "cloud_upload"},
|
{"state": "worker.card.dms.index", "icon": "cloud_upload"},
|
||||||
{
|
{
|
||||||
"icon": "icon-wiki",
|
"icon": "icon-wiki",
|
||||||
|
@ -141,6 +142,13 @@
|
||||||
"component": "vn-worker-create",
|
"component": "vn-worker-create",
|
||||||
"description": "New worker",
|
"description": "New worker",
|
||||||
"acl": ["hr"]
|
"acl": ["hr"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/pda",
|
||||||
|
"state": "worker.card.pda",
|
||||||
|
"component": "vn-worker-pda",
|
||||||
|
"description": "PDA",
|
||||||
|
"acl": ["hr", "productionAssi"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,23 +11,23 @@
|
||||||
<vn-horizontal class="vn-pa-md">
|
<vn-horizontal class="vn-pa-md">
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<h4 ng-show="$ctrl.isHr">
|
<h4 ng-show="$ctrl.isHr">
|
||||||
<a
|
<a
|
||||||
ui-sref="worker.card.basicData({id:$ctrl.worker.id})">
|
ui-sref="worker.card.basicData({id:$ctrl.worker.id})">
|
||||||
<span translate vn-tooltip="Go to">Basic data</span>
|
<span translate vn-tooltip="Go to">Basic data</span>
|
||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
<h4
|
<h4
|
||||||
translates
|
translates
|
||||||
ng-show="!$ctrl.isHr">
|
ng-show="!$ctrl.isHr">
|
||||||
Basic data
|
Basic data
|
||||||
</h4>
|
</h4>
|
||||||
<vn-label-value label="Id"
|
<vn-label-value label="Id"
|
||||||
value="{{worker.id}}">
|
value="{{worker.id}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Email" no-ellipsize
|
<vn-label-value label="Email" no-ellipsize
|
||||||
value="{{worker.user.emailUser.email}}">
|
value="{{worker.user.emailUser.email}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Department"
|
<vn-label-value label="Department"
|
||||||
value="{{worker.department.department.name}}">
|
value="{{worker.department.department.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value
|
<vn-label-value
|
||||||
|
@ -38,33 +38,36 @@
|
||||||
{{::worker.boss.nickname}}
|
{{::worker.boss.nickname}}
|
||||||
</span>
|
</span>
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Mobile extension"
|
<vn-label-value label="Mobile extension"
|
||||||
value="{{worker.mobileExtension}}">
|
value="{{worker.mobileExtension}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Business phone"
|
<vn-label-value label="Business phone"
|
||||||
value="{{worker.phone}}">
|
value="{{worker.phone}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Personal phone"
|
<vn-label-value label="Personal phone"
|
||||||
value="{{worker.client.phone}}">
|
value="{{worker.client.phone}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Locker"
|
||||||
|
value="{{worker.locker}}">
|
||||||
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<h4 translate>User data</h4>
|
<h4 translate>User data</h4>
|
||||||
<vn-label-value label="User id"
|
<vn-label-value label="User id"
|
||||||
value="{{worker.userFk}}">
|
value="{{worker.userFk}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="User"
|
<vn-label-value label="User"
|
||||||
value="{{worker.user.name}}">
|
value="{{worker.user.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Role"
|
<vn-label-value label="Role"
|
||||||
value="{{worker.user.role.name}}">
|
value="{{worker.user.role.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Extension"
|
<vn-label-value label="Extension"
|
||||||
value="{{worker.sip.extension}}">
|
value="{{worker.sip.extension}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-worker-descriptor-popover
|
<vn-worker-descriptor-popover
|
||||||
vn-id="workerDescriptor">
|
vn-id="workerDescriptor">
|
||||||
</vn-worker-descriptor-popover>
|
</vn-worker-descriptor-popover>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
Business phone: Teléfono de empresa
|
Business phone: Teléfono de empresa
|
||||||
Personal phone: Teléfono personal
|
Personal phone: Teléfono personal
|
||||||
Mobile extension: Extensión móvil
|
Mobile extension: Extensión móvil
|
||||||
|
Locker: Taquilla
|
||||||
|
|
Loading…
Reference in New Issue