#6273 register freelance #2403

Merged
jorgep merged 7 commits from 6273-registerFreelance into dev 2024-06-10 13:34:52 +00:00
4 changed files with 286 additions and 371 deletions

View File

@ -364,5 +364,6 @@
"This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario", "This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario",
"You can only have one PDA": "Solo puedes tener un PDA", "You can only have one PDA": "Solo puedes tener un PDA",
"It has been invoiced but the PDF could not be generated": "Se ha facturado pero no se ha podido generar el PDF", "It has been invoiced but the PDF could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
"It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono" "It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono",
"Payment method is required": "El método de pago es obligatorio"
} }

View File

@ -5,108 +5,80 @@ module.exports = Self => {
Self.remoteMethodCtx('new', { Self.remoteMethodCtx('new', {
description: 'Creates a new worker and returns the id', description: 'Creates a new worker and returns the id',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [ accepts: [{
{
arg: 'fi', arg: 'fi',
type: 'string', type: 'string',
description: `The worker fi`, description: `The worker fi`,
required: true, }, {
},
{
arg: 'name', arg: 'name',
type: 'string', type: 'string',
description: `The user name`, description: `The user name`,
required: true, }, {
},
{
arg: 'firstName', arg: 'firstName',
type: 'string', type: 'string',
description: `The worker firstname`, description: `The worker firstname`,
required: true, }, {
},
{
arg: 'lastNames', arg: 'lastNames',
type: 'string', type: 'string',
description: `The worker lastnames`, description: `The worker lastnames`,
required: true, }, {
},
{
arg: 'email', arg: 'email',
type: 'string', type: 'string',
description: `The worker email`, description: `The worker email`,
required: true, required: true,
}, }, {
{
arg: 'street', arg: 'street',
type: 'string', type: 'string',
description: `The worker address`, description: `The worker address`,
required: true, }, {
},
{
arg: 'city', arg: 'city',
type: 'string', type: 'string',
description: `The worker city`, description: `The worker city`,
required: true, }, {
},
{
arg: 'provinceFk', arg: 'provinceFk',
type: 'number', type: 'number',
description: `The worker province`, description: `The worker province`,
required: true, }, {
},
{
arg: 'companyFk', arg: 'companyFk',
type: 'number', type: 'number',
description: `The worker company`, description: `The worker company`,
required: true, }, {
},
{
arg: 'postcode', arg: 'postcode',
type: 'string', type: 'string',
description: `The worker postcode`, description: `The worker postcode`,
required: true, }, {
},
{
arg: 'phone', arg: 'phone',
type: 'string', type: 'string',
description: `The worker phone`, description: `The worker phone`,
required: true, }, {
},
{
arg: 'code', arg: 'code',
type: 'string', type: 'string',
description: `The worker code`, description: `The worker code`,
required: true, }, {
},
{
arg: 'bossFk', arg: 'bossFk',
type: 'number', type: 'number',
description: `The worker boss`, description: `The worker boss`,
required: true, required: true,
}, }, {
{
arg: 'birth', arg: 'birth',
type: 'date', type: 'date',
description: `The worker birth`, description: `The worker birth`,
required: true, }, {
},
{
arg: 'payMethodFk', arg: 'payMethodFk',
type: 'number', type: 'number',
description: `The client payMethod`, description: `The client payMethod`,
required: true, }, {
},
{
arg: 'iban', arg: 'iban',
type: 'string', type: 'string',
description: `The client iban`, description: `The client iban`,
}, }, {
{
arg: 'bankEntityFk', arg: 'bankEntityFk',
type: 'number', type: 'number',
description: `The client bank entity`, description: `The client bank entity`,
} }, {
], arg: 'isFreelance',
type: 'boolean',
}],
returns: { returns: {
type: 'number', type: 'number',
root: true, root: true,
@ -117,11 +89,30 @@ module.exports = Self => {
}, },
}); });
Self.new = async(ctx, options) => { Self.new = async(
ctx,
fi,
name,
firstName,
lastNames,
email,
street,
city,
provinceFk,
companyFk,
postcode,
phone,
code,
bossFk,
birth,
payMethodFk,
iban,
bankEntityFk,
isFreelance,
options
) => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId}; const myOptions = {userId: ctx.req.accessToken.userId};
const args = ctx.args;
let tx; let tx;
if (typeof options == 'object') Object.assign(myOptions, options); if (typeof options == 'object') Object.assign(myOptions, options);
@ -132,132 +123,105 @@ module.exports = Self => {
} }
let client; let client;
let user;
try { try {
client = await models.Client.findOne( client = await models.Client.findOne({where: {fi}}, myOptions);
{ const nickname = firstName.concat(' ', lastNames);
where: {fi: args.fi}, const {roleFk, businessTypeFk} = await models.WorkerConfig.findOne({fields: ['roleFk', 'businessTypeFk']});
},
myOptions
);
if (!client) { if (!isFreelance && !payMethodFk) throw new UserError('Payment method is required');
const nickname = args.firstName.concat(' ', args.lastNames);
const workerConfig = await models.WorkerConfig.findOne({fields: ['roleFk', 'businessTypeFk']});
const [randomPassword] = await models.Worker.rawSql(
'SELECT account.passwordGenerate() as password;'
);
const user = await models.VnUser.create( if (isFreelance || !client) {
{ const [{password}] = await models.Worker.rawSql('SELECT account.passwordGenerate() as password;');
name: args.name, const freelancer = isFreelance && await models.VnRole.findOne({fields: ['id'], where: {name: 'freelancer'}});
user = await models.VnUser.create({
name,
nickname, nickname,
password: randomPassword.password, password,
email: args.email, email,
roleFk: workerConfig.roleFk, roleFk: freelancer ? freelancer.id : roleFk,
}, }, myOptions);
myOptions
);
await models.Account.create( await models.Account.create({
{ id: user.id
id: user.id, }, myOptions);
}, } else if (client) user = await models.VnUser.findById(client.id, null, myOptions);
myOptions
);
const payMethod = await models.PayMethod.findById(args.payMethodFk, {fields: ['isIbanRequiredForClients']}); if (!client && !isFreelance) {
if (payMethod.isIbanRequiredForClients && !args.iban) const payMethod = await models.PayMethod.findById(payMethodFk, {fields: ['isIbanRequiredForClients']});
throw new UserError(`That payment method requires an IBAN`); if (payMethod.isIbanRequiredForClients && !iban) throw new UserError('That payment method requires an IBAN');
await models.Worker.rawSql( await models.Worker.rawSql('CALL vn.client_create(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
'CALL vn.client_create(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[ [
args.firstName, firstName,
args.lastNames, lastNames,
args.fi, fi,
args.street, street,
args.postcode, postcode,
args.city, city,
args.provinceFk, provinceFk,
args.companyFk, companyFk,
args.phone, phone,
args.email, email,
user.id, user.id,
], ],
myOptions myOptions);
);
const address = await models.Address.create( const address = await models.Address.create({
{
clientFk: user.id, clientFk: user.id,
street: args.street, street: street,
city: args.city, city: city,
provinceFk: args.provinceFk, provinceFk: provinceFk,
postalCode: args.postcode, postalCode: postcode,
mobile: args.phone, mobile: phone,
nickname: nickname, nickname: nickname,
isDefaultAddress: true, isDefaultAddress: true,
}, }, myOptions);
myOptions
);
client = await models.Client.findById( client = await models.Client.findById(user.id, {
user.id, fields: ['id', 'name', 'socialName', 'street', 'city', 'iban', 'bankEntityFk', 'defaultAddressFk', 'businessTypeFk', 'fi']
{fields: ['id', 'name', 'socialName', 'street', 'city', 'iban', 'bankEntityFk', 'defaultAddressFk', 'businessTypeFk', 'fi']}, }, myOptions);
myOptions
);
await client.updateAttributes( await client.updateAttributes({
{ payMethod: payMethodFk,
payMethod: args.payMethod, iban,
iban: args.iban, bankEntityFk,
bankEntityFk: args.bankEntityFk,
defaultAddressFk: address.id, defaultAddressFk: address.id,
businessTypeFk: workerConfig.businessTypeFk, businessTypeFk,
}, }, myOptions);
myOptions
);
} }
const user = await models.VnUser.findById(client.id, null, myOptions); await user.updateAttribute('email', email, myOptions);
await user.updateAttribute('email', args.email, myOptions);
await models.Worker.create({ await models.Worker.create({
id: client.id, id: user.id,
code: args.code, firstName,
firstName: args.firstName, lastName: lastNames,
lastName: args.lastNames, code,
bossFk: args.bossFk, bossFk,
fi: args.fi, fi,
birth: args.birth, birth,
}, myOptions); }, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
} catch (error) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
const code = error.code; const code = e.code;
const message = error.sqlMessage; const message = e.sqlMessage;
if (error.message && error.message.includes(`Email already exists`)) if (e.message && e.message.includes(`Email already exists`)) throw new UserError(`This personal mail already exists`);
throw new UserError(`This personal mail already exists`);
if (code === 'ER_DUP_ENTRY' && message.includes(`CodigoTrabajador_UNIQUE`)) if (code === 'ER_DUP_ENTRY' && message.includes(`CodigoTrabajador_UNIQUE`)) throw new UserError(`This worker code already exists`);
throw new UserError(`This worker code already exists`);
if (code === 'ER_DUP_ENTRY' && message.includes(`PRIMARY`)) if (code === 'ER_DUP_ENTRY' && message.includes(`PRIMARY`)) throw new UserError(`This worker already exists`);
throw new UserError(`This worker already exists`);
throw error; throw e;
} }
await models.VnUser.resetPassword({ await models.VnUser.resetPassword({email, emailTemplate: 'worker-welcome', id: user.id});
email: args.email,
emailTemplate: 'worker-welcome',
id: client.id
});
return {id: client.id}; return {id: user.id};
}; };
}; };

View File

@ -1,189 +1,114 @@
const models = require('vn-loopback/server/server').models; const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('Worker new', () => { describe('Worker new', () => {
beforeAll(async() => { const developerId = 9;
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
const employeeId = 1; const employeeId = 1;
const defaultWorker = { const bruceWayneId = 1101;
fi: '78457139E', const accessToken = {accessToken: {userId: developerId}};
name: 'defaulterworker', const ctx = {req: accessToken};
firstName: 'DEFAULT', let tx;
lastNames: 'WORKER', let opts;
email: 'defaultWorker@mydomain.com',
street: 'S/ DEFAULTWORKERSTREET',
city: 'defaultWorkerCity',
provinceFk: 1,
companyFk: 442,
postcode: '46680',
phone: '123456789',
code: 'DWW',
bossFk: 9,
birth: '2022-12-11T23:00:00.000Z',
payMethodFk: 1,
roleFk: 1
};
const req = {accessToken: {userId: 9}}; beforeAll(async() => {
const activeCtx = {accessToken, http: {req: {headers: {origin: 'http://localhost'}}}};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: activeCtx});
});
it('should return error if personal mail already exists', async() => { describe('should return error', () => {
beforeEach(async() => {
tx = await models.Worker.beginTransaction({});
opts = {transaction: tx};
});
afterEach(async() => await tx.rollback());
it('if personal mail already exists', async() => {
const user = await models.VnUser.findById(employeeId, {fields: ['email']}); const user = await models.VnUser.findById(employeeId, {fields: ['email']});
const tx = await models.Worker.beginTransaction({});
let error;
try { try {
const options = {transaction: tx}; await createWorker(ctx, opts, {email: user.email});
const ctx = {
args: Object.assign({}, defaultWorker, {email: user.email}),
req
};
await models.Worker.new(ctx, options);
await tx.rollback();
} catch (e) { } catch (e) {
error = e; expect(e.message).toEqual('This personal mail already exists');
await tx.rollback();
} }
expect(error.message).toEqual('This personal mail already exists');
}); });
it('should return error if worker code already exists', async() => { it('if worker code already exists', async() => {
const worker = await models.Worker.findById(employeeId, {fields: ['code']}); const worker = await models.Worker.findById(employeeId, {fields: ['code']});
const tx = await models.Worker.beginTransaction({});
let error;
try { try {
const options = {transaction: tx}; await createWorker(ctx, opts, {code: worker.code});
const ctx = {
args: Object.assign({}, defaultWorker, {code: worker.code}),
req
};
await models.Worker.new(ctx, options);
await tx.rollback();
} catch (e) { } catch (e) {
error = e; expect(e.message).toEqual('This worker code already exists');
await tx.rollback();
} }
expect(error.message).toEqual('This worker code already exists');
}); });
it('should return error if worker already exists', async() => { it('if worker already exists', async() => {
const worker = await models.Client.findById(employeeId, {fields: ['fi']}); const worker = await models.Client.findById(employeeId, {fields: ['fi']});
const tx = await models.Worker.beginTransaction({});
let error;
try { try {
const options = {transaction: tx}; await createWorker(ctx, opts, {fi: worker.fi});
const ctx = {
args: Object.assign({}, defaultWorker, {fi: worker.fi}),
req
};
await models.Worker.new(ctx, options);
await tx.rollback();
} catch (e) { } catch (e) {
error = e; expect(e.message).toEqual('This worker already exists');
await tx.rollback();
} }
expect(error.message).toEqual('This worker already exists');
}); });
it('should return error if payMethod require iban', async() => { it('if payMethod require iban', async() => {
const payMethodIbanRequired = await models.PayMethod.findOne({ const payMethodIbanRequired = await models.PayMethod.findOne({
where: { fields: ['id'], where: {isIbanRequiredForClients: true}
isIbanRequiredForClients: true
},
fields: ['id']
}); });
const tx = await models.Worker.beginTransaction({});
let error;
try { try {
const options = {transaction: tx}; await createWorker(ctx, opts, {payMethodFk: payMethodIbanRequired.id});
const ctx = {
args: Object.assign({}, defaultWorker, {payMethodFk: payMethodIbanRequired.id}),
req
};
await models.Worker.new(ctx, options);
await tx.rollback();
} catch (e) { } catch (e) {
error = e; expect(e.message).toEqual('That payment method requires an IBAN');
await tx.rollback();
} }
});
expect(error.message).toEqual('That payment method requires an IBAN');
}); });
it('should create a new worker', async() => { it('should create a new worker', async() => {
let newWorker; let newWorker;
try { try {
newWorker = await models.Worker.new({args: defaultWorker, req}); newWorker = await createWorker(ctx);
expect(newWorker.id).toBeDefined();
} finally { } finally {
await removeWorker(newWorker.id); await removeWorker(newWorker.id);
} }
expect(newWorker.id).toBeDefined();
}); });
it('should create a new client', async() => { it('should create a new client', async() => {
let newWorker; let newWorker;
let client;
try { try {
newWorker = await models.Worker.new({args: defaultWorker, req}); newWorker = await createWorker(ctx);
client = await models.Client.findById(newWorker.id); let client = await models.Client.findById(newWorker.id);
expect(client).toBeDefined();
} finally { } finally {
await removeWorker(newWorker.id); await removeWorker(newWorker.id);
} }
expect(client).toBeDefined();
}); });
it('should create a new worker in client', async() => { it('should create a new worker in client', async() => {
const bruceWayneId = 1101;
const client = await models.Client.findById(bruceWayneId, {fields: ['fi', 'email']}); const client = await models.Client.findById(bruceWayneId, {fields: ['fi', 'email']});
const newWorkerData = {
args: Object.assign(
{},
defaultWorker,
{
fi: client.fi,
email: client.email
}),
req
};
let newWorker; let newWorker;
try { try {
newWorker = await models.Worker.new(newWorkerData); newWorker = await createWorker(ctx, undefined, {fi: client.fi, email: client.email});
expect(newWorker.id).toEqual(bruceWayneId);
} finally { } finally {
await models.Worker.destroyById(newWorker.id); await models.Worker.destroyById(newWorker.id);
} }
});
expect(newWorker.id).toEqual(bruceWayneId); it('should create a new external worker', async() => {
let newWorker;
try {
newWorker = await createWorker(ctx, undefined, {isFreelance: true});
const client = await models.Client.findById(newWorker.id);
expect(newWorker.id).toBeDefined();
expect(client).toBeNull();
} finally {
await removeWorker(newWorker.id);
}
}); });
}); });
@ -194,3 +119,28 @@ async function removeWorker(id) {
await models.Client.destroyById(id); await models.Client.destroyById(id);
await models.VnUser.destroyById(id); await models.VnUser.destroyById(id);
} }
async function createWorker(ctx, opts = undefined, params = {}) {
return models.Worker.new(
ctx,
params.fi ?? '78457139E',
params.name ?? 'defaulterworker',
params.firstName ?? 'DEFAULT',
params.lastNames ?? 'WORKER',
params.email ?? 'defaultWorker@mydomain.com',
params.street ?? 'S/ DEFAULTWORKERSTREET',
params.city ?? 'defaultWorkerCity',
params.provinceFk ?? 1,
params.companyFk ?? 442,
params.postcode ?? '46680',
params.phone ?? '123456789',
params.code ?? 'DWW',
params.bossFk ?? 9,
params.birth ?? '2022-12-11T23:00:00.000Z',
params.payMethodFk ?? 1,
undefined,
undefined,
params.isFreelance ?? false,
opts
);
}

View File

@ -25,43 +25,44 @@
"required": true "required": true
}, },
"phone": { "phone": {
"type" : "string" "type": "string"
}, },
"bossFk": { "bossFk": {
"type" : "number" "type": "number"
}, },
"maritalStatus": { "maritalStatus": {
"type" : "string" "type": "string"
}, },
"originCountryFk": { "originCountryFk": {
"type" : "number" "type": "number"
}, },
"educationLevelFk": { "educationLevelFk": {
"type" : "number" "type": "number"
}, },
"SSN": { "SSN": {
"type" : "string" "type": "string"
}, },
"mobileExtension": { "mobileExtension": {
"type" : "number" "type": "number"
}, },
"code": { "code": {
"type" : "string" "type": "string",
"required": true
}, },
"fi": { "fi": {
"type" : "string" "type": "string"
}, },
"birth": { "birth": {
"type" : "date" "type": "date"
}, },
"isF11Allowed": { "isF11Allowed": {
"type" : "boolean" "type": "boolean"
}, },
"sex": { "sex": {
"type" : "string" "type": "string"
}, },
"isFreelance": { "isFreelance": {
"type" : "boolean" "type": "boolean"
}, },
"fiDueDate": { "fiDueDate": {
"type": "date" "type": "date"
@ -78,7 +79,6 @@
"isSsDiscounted": { "isSsDiscounted": {
"type": "boolean" "type": "boolean"
} }
}, },
"relations": { "relations": {
"user": { "user": {
@ -117,7 +117,7 @@
"foreignKey": "workerFk" "foreignKey": "workerFk"
} }
}, },
"acls":[ "acls": [
{ {
"property": "__get__locker", "property": "__get__locker",
"accessType": "READ", "accessType": "READ",