2020-11-02 13:30:33 +00:00
|
|
|
const UserError = require('vn-loopback/util/user-error');
|
|
|
|
const getFinalState = require('vn-loopback/util/hook').getFinalState;
|
|
|
|
const isMultiple = require('vn-loopback/util/hook').isMultiple;
|
|
|
|
const validateTin = require('vn-loopback/util/validateTin');
|
|
|
|
const validateIban = require('vn-loopback/util/validateIban');
|
2019-11-06 07:20:45 +00:00
|
|
|
const LoopBackContext = require('loopback-context');
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2018-05-04 09:46:03 +00:00
|
|
|
module.exports = Self => {
|
2018-01-29 11:37:54 +00:00
|
|
|
// Methods
|
2022-09-26 06:07:45 +00:00
|
|
|
require('./client-methods')(Self);
|
2018-01-29 11:37:54 +00:00
|
|
|
|
|
|
|
// Validations
|
|
|
|
|
2019-02-28 07:55:34 +00:00
|
|
|
Self.validatesPresenceOf('street', {
|
|
|
|
message: 'Street cannot be empty'
|
|
|
|
});
|
2023-06-28 12:10:21 +00:00
|
|
|
|
2019-02-28 07:55:34 +00:00
|
|
|
Self.validatesPresenceOf('city', {
|
|
|
|
message: 'City cannot be empty'
|
|
|
|
});
|
|
|
|
|
2018-01-29 11:37:54 +00:00
|
|
|
Self.validatesUniquenessOf('fi', {
|
2018-04-05 11:08:10 +00:00
|
|
|
message: 'TIN must be unique'
|
2018-01-29 11:37:54 +00:00
|
|
|
});
|
2018-10-23 12:56:38 +00:00
|
|
|
|
2018-01-29 11:37:54 +00:00
|
|
|
Self.validatesFormatOf('email', {
|
2018-04-05 11:20:27 +00:00
|
|
|
message: 'Invalid email',
|
2018-01-29 11:37:54 +00:00
|
|
|
allowNull: true,
|
|
|
|
allowBlank: true,
|
2022-12-02 08:18:52 +00:00
|
|
|
with: /^[\W]*([\w+\-.%]+@[\w\-.]+\.[A-Za-z]{1,61}[\W]*,{1}[\W]*)*([\w+\-.%]+@[\w\-.]+\.[A-Za-z]{1,61})[\W]*$/
|
2018-01-29 11:37:54 +00:00
|
|
|
});
|
2018-10-23 12:56:38 +00:00
|
|
|
|
2018-01-29 11:37:54 +00:00
|
|
|
Self.validatesLengthOf('postcode', {
|
|
|
|
allowNull: true,
|
|
|
|
allowBlank: true,
|
|
|
|
min: 3, max: 10
|
|
|
|
});
|
|
|
|
|
2023-07-14 11:35:13 +00:00
|
|
|
Self.validatesFormatOf('street', {
|
|
|
|
message: 'Street should be uppercase',
|
|
|
|
allowNull: false,
|
|
|
|
allowBlank: false,
|
|
|
|
with: /^[^a-z]*$/
|
|
|
|
});
|
|
|
|
|
|
|
|
Self.validatesFormatOf('socialName', {
|
|
|
|
message: 'Social name should be uppercase',
|
|
|
|
allowNull: false,
|
|
|
|
allowBlank: false,
|
|
|
|
with: /^[^a-z]*$/
|
|
|
|
});
|
|
|
|
|
2022-04-25 06:09:58 +00:00
|
|
|
Self.validateAsync('socialName', socialNameIsUnique, {
|
|
|
|
message: 'The company name must be unique'
|
|
|
|
});
|
|
|
|
|
|
|
|
async function socialNameIsUnique(err, done) {
|
|
|
|
const filter = {
|
|
|
|
where: {
|
|
|
|
and: [
|
|
|
|
{socialName: this.socialName},
|
2022-04-25 08:30:44 +00:00
|
|
|
{isActive: true},
|
2022-04-25 06:09:58 +00:00
|
|
|
{id: {neq: this.id}}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const client = await Self.app.models.Client.findOne(filter);
|
|
|
|
if (client)
|
|
|
|
err();
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
|
2018-07-27 13:13:01 +00:00
|
|
|
Self.validateAsync('iban', ibanNeedsValidation, {
|
|
|
|
message: 'The IBAN does not have the correct format'
|
2018-01-29 11:37:54 +00:00
|
|
|
});
|
2020-11-02 13:30:33 +00:00
|
|
|
|
2018-07-27 13:13:01 +00:00
|
|
|
async function ibanNeedsValidation(err, done) {
|
2022-04-25 06:09:58 +00:00
|
|
|
const filter = {
|
2018-07-27 13:13:01 +00:00
|
|
|
fields: ['code'],
|
|
|
|
where: {id: this.countryFk}
|
|
|
|
};
|
2022-04-25 06:09:58 +00:00
|
|
|
const country = await Self.app.models.Country.findOne(filter);
|
|
|
|
const code = country ? country.code.toLowerCase() : null;
|
2018-11-23 07:21:21 +00:00
|
|
|
if (code != 'es')
|
2018-07-27 13:13:01 +00:00
|
|
|
return done();
|
|
|
|
|
|
|
|
if (!validateIban(this.iban))
|
|
|
|
err();
|
|
|
|
done();
|
|
|
|
}
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2018-03-21 11:57:23 +00:00
|
|
|
Self.validateAsync('fi', tinIsValid, {
|
2018-04-05 11:08:10 +00:00
|
|
|
message: 'Invalid TIN'
|
2018-01-29 11:37:54 +00:00
|
|
|
});
|
2019-03-13 07:35:25 +00:00
|
|
|
|
2018-03-21 11:57:23 +00:00
|
|
|
async function tinIsValid(err, done) {
|
2018-05-22 10:09:53 +00:00
|
|
|
if (!this.isTaxDataChecked)
|
|
|
|
return done();
|
|
|
|
|
2022-04-25 06:09:58 +00:00
|
|
|
const filter = {
|
2018-03-12 13:13:36 +00:00
|
|
|
fields: ['code'],
|
|
|
|
where: {id: this.countryFk}
|
|
|
|
};
|
2022-04-25 06:09:58 +00:00
|
|
|
const country = await Self.app.models.Country.findOne(filter);
|
|
|
|
const code = country ? country.code.toLowerCase() : null;
|
2023-07-11 07:19:15 +00:00
|
|
|
const countryCode = this.fi?.toLowerCase().substring(0, 2);
|
2018-03-12 13:13:36 +00:00
|
|
|
|
2023-04-28 12:23:44 +00:00
|
|
|
if (!this.fi || !validateTin(this.fi, code) || (this.isVies && countryCode == code))
|
2018-03-12 13:13:36 +00:00
|
|
|
err();
|
|
|
|
done();
|
|
|
|
}
|
2018-01-29 11:37:54 +00:00
|
|
|
|
|
|
|
Self.validate('payMethod', hasSalesMan, {
|
2018-04-05 11:20:27 +00:00
|
|
|
message: 'Cannot change the payment method if no salesperson'
|
2018-01-29 11:37:54 +00:00
|
|
|
});
|
2018-10-16 11:06:58 +00:00
|
|
|
|
2018-01-29 11:37:54 +00:00
|
|
|
function hasSalesMan(err) {
|
2020-09-15 17:43:07 +00:00
|
|
|
if (this.payMethod && !this.salesPersonUser)
|
2018-01-29 11:37:54 +00:00
|
|
|
err();
|
|
|
|
}
|
|
|
|
|
2018-11-20 10:02:57 +00:00
|
|
|
Self.validate('isEqualizated', cannotHaveET, {
|
|
|
|
message: 'Cannot check Equalization Tax in this NIF/CIF'
|
|
|
|
});
|
|
|
|
|
|
|
|
function cannotHaveET(err) {
|
2019-09-19 06:58:42 +00:00
|
|
|
if (!this.fi) return;
|
|
|
|
|
2022-04-25 06:09:58 +00:00
|
|
|
const tin = this.fi.toUpperCase();
|
|
|
|
const cannotHaveET = /^[A-B]/.test(tin);
|
2018-11-20 10:02:57 +00:00
|
|
|
|
|
|
|
if (cannotHaveET && this.isEqualizated)
|
|
|
|
err();
|
|
|
|
}
|
|
|
|
|
2018-11-07 08:17:07 +00:00
|
|
|
Self.validateAsync('payMethodFk', hasIban, {
|
|
|
|
message: 'That payment method requires an IBAN'
|
|
|
|
});
|
|
|
|
|
|
|
|
function hasIban(err, done) {
|
|
|
|
Self.app.models.PayMethod.findById(this.payMethodFk, (_, instance) => {
|
2021-11-22 12:44:22 +00:00
|
|
|
const isMissingIban = instance && instance.isIbanRequiredForClients && !this.iban;
|
|
|
|
if (isMissingIban)
|
2018-11-07 08:17:07 +00:00
|
|
|
err();
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Self.validateAsync('bankEntityFk', hasBic, {
|
|
|
|
message: 'That payment method requires a BIC'
|
|
|
|
});
|
|
|
|
|
|
|
|
function hasBic(err, done) {
|
2018-11-22 14:17:35 +00:00
|
|
|
if (this.iban && !this.bankEntityFk)
|
|
|
|
err();
|
|
|
|
done();
|
2018-11-07 08:17:07 +00:00
|
|
|
}
|
|
|
|
|
2023-01-30 07:30:39 +00:00
|
|
|
Self.validateAsync('isToBeMailed', isToBeMailed, {
|
|
|
|
message: 'There is no assigned email for this client'
|
|
|
|
});
|
|
|
|
|
|
|
|
function isToBeMailed(err, done) {
|
|
|
|
if (this.isToBeMailed == true && !this.email)
|
|
|
|
err();
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
|
2019-02-28 07:55:34 +00:00
|
|
|
Self.validateAsync('defaultAddressFk', isActive,
|
|
|
|
{message: 'Unable to default a disabled consignee'}
|
|
|
|
);
|
|
|
|
|
|
|
|
async function isActive(err, done) {
|
|
|
|
if (!this.defaultAddressFk)
|
|
|
|
return done();
|
|
|
|
|
|
|
|
const address = await Self.app.models.Address.findById(this.defaultAddressFk);
|
|
|
|
if (address && !address.isActive) err();
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
|
2019-09-18 08:35:46 +00:00
|
|
|
Self.validateAsync('postCode', hasValidPostcode, {
|
2020-01-29 13:13:25 +00:00
|
|
|
message: `The postcode doesn't exist. Please enter a correct one`
|
2019-09-18 08:35:46 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
async function hasValidPostcode(err, done) {
|
|
|
|
if (!this.postcode)
|
|
|
|
return done();
|
|
|
|
|
|
|
|
const models = Self.app.models;
|
|
|
|
const postcode = await models.Postcode.findById(this.postcode);
|
|
|
|
|
|
|
|
if (!postcode) err();
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
|
2019-09-23 07:27:58 +00:00
|
|
|
function isAlpha(value) {
|
2019-10-01 13:12:02 +00:00
|
|
|
const regexp = new RegExp(/^[ñça-zA-Z0-9\s]*$/i);
|
2019-09-23 07:27:58 +00:00
|
|
|
|
|
|
|
return regexp.test(value);
|
|
|
|
}
|
|
|
|
|
2022-01-05 13:29:03 +00:00
|
|
|
Self.observe('before save', async ctx => {
|
|
|
|
const changes = ctx.data || ctx.instance;
|
|
|
|
const orgData = ctx.currentInstance;
|
|
|
|
|
2022-01-05 14:41:26 +00:00
|
|
|
const businessTypeFk = changes && changes.businessTypeFk || orgData && orgData.businessTypeFk;
|
|
|
|
const isTaxDataChecked = changes && changes.isTaxDataChecked || orgData && orgData.isTaxDataChecked;
|
|
|
|
|
|
|
|
let invalidBusinessType = false;
|
2022-01-05 14:17:21 +00:00
|
|
|
if (!ctx.isNewInstance) {
|
2023-01-31 13:57:24 +00:00
|
|
|
const isWorker = await Self.app.models.Account.findById(orgData.id);
|
2022-01-05 14:17:21 +00:00
|
|
|
const changedFields = Object.keys(changes);
|
|
|
|
const hasChangedOtherFields = changedFields.some(key => key !== 'businessTypeFk');
|
2022-01-05 13:29:03 +00:00
|
|
|
|
2022-01-05 14:17:21 +00:00
|
|
|
if (!businessTypeFk && !isTaxDataChecked && !isWorker && !hasChangedOtherFields)
|
2022-01-05 14:41:26 +00:00
|
|
|
invalidBusinessType = true;
|
2022-01-05 14:17:21 +00:00
|
|
|
}
|
2022-01-05 14:41:26 +00:00
|
|
|
|
|
|
|
if (ctx.isNewInstance) {
|
|
|
|
if (!businessTypeFk && !isTaxDataChecked)
|
|
|
|
invalidBusinessType = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (invalidBusinessType)
|
|
|
|
throw new UserError(`The type of business must be filled in basic data`);
|
2022-01-05 13:29:03 +00:00
|
|
|
});
|
|
|
|
|
2022-04-12 05:56:03 +00:00
|
|
|
Self.observe('before save', async ctx => {
|
|
|
|
const changes = ctx.data || ctx.instance;
|
|
|
|
const orgData = ctx.currentInstance;
|
|
|
|
const models = Self.app.models;
|
|
|
|
|
|
|
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
2023-04-24 09:07:04 +00:00
|
|
|
const accessToken = {req: loopBackContext.active.accessToken};
|
2022-04-12 05:56:03 +00:00
|
|
|
|
2023-04-24 09:07:04 +00:00
|
|
|
const editVerifiedDataWithoutTaxDataChecked = models.ACL.checkAccessAcl(accessToken, 'Client', 'editVerifiedDataWithoutTaxDataCheck', 'WRITE');
|
2022-04-12 05:56:03 +00:00
|
|
|
const hasChanges = orgData && changes;
|
|
|
|
|
|
|
|
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
|
|
|
const isTaxDataCheckedChanged = hasChanges && orgData.isTaxDataChecked != isTaxDataChecked;
|
|
|
|
|
|
|
|
const sageTaxType = hasChanges && (changes.sageTaxTypeFk || orgData.sageTaxTypeFk);
|
|
|
|
const sageTaxTypeChanged = hasChanges && orgData.sageTaxTypeFk != sageTaxType;
|
|
|
|
|
|
|
|
const sageTransactionType = hasChanges && (changes.sageTransactionTypeFk || orgData.sageTransactionTypeFk);
|
|
|
|
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
|
|
|
|
|
2023-04-24 09:07:04 +00:00
|
|
|
const cantEditVerifiedData = isTaxDataCheckedChanged && !editVerifiedDataWithoutTaxDataChecked;
|
|
|
|
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !editVerifiedDataWithoutTaxDataChecked;
|
2022-04-12 05:56:03 +00:00
|
|
|
|
2022-05-10 06:48:51 +00:00
|
|
|
if (cantEditVerifiedData || cantChangeSageData)
|
2022-04-12 05:56:03 +00:00
|
|
|
throw new UserError(`You don't have enough privileges`);
|
|
|
|
});
|
|
|
|
|
2018-01-29 11:37:54 +00:00
|
|
|
Self.observe('before save', async function(ctx) {
|
2021-09-07 14:07:46 +00:00
|
|
|
const changes = ctx.data || ctx.instance;
|
|
|
|
const orgData = ctx.currentInstance;
|
|
|
|
const finalState = getFinalState(ctx);
|
|
|
|
const payMethodWithIban = 4;
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2019-09-23 07:27:58 +00:00
|
|
|
// Validate socialName format
|
2020-09-30 07:31:01 +00:00
|
|
|
const hasChanges = orgData && changes;
|
2020-10-28 13:12:57 +00:00
|
|
|
const socialName = changes && changes.socialName || orgData && orgData.socialName;
|
2020-09-30 09:19:52 +00:00
|
|
|
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
2019-09-23 07:27:58 +00:00
|
|
|
|
2020-09-30 07:31:01 +00:00
|
|
|
const socialNameChanged = hasChanges
|
|
|
|
&& orgData.socialName != socialName;
|
2022-04-12 05:56:03 +00:00
|
|
|
const isTaxDataCheckedChanged = hasChanges
|
2020-09-30 07:31:01 +00:00
|
|
|
&& orgData.isTaxDataChecked != isTaxDataChecked;
|
|
|
|
|
2022-04-12 05:56:03 +00:00
|
|
|
if ((socialNameChanged || isTaxDataCheckedChanged) && !isAlpha(socialName))
|
2022-01-05 15:23:58 +00:00
|
|
|
throw new UserError(`The social name has an invalid format`);
|
2019-09-23 07:27:58 +00:00
|
|
|
|
2018-01-29 11:37:54 +00:00
|
|
|
if (changes.salesPerson === null) {
|
|
|
|
changes.credit = 0;
|
|
|
|
changes.discount = 0;
|
|
|
|
changes.payMethodFk = 5; // Credit card
|
|
|
|
}
|
|
|
|
|
2021-09-07 14:07:46 +00:00
|
|
|
const payMethodFk = changes.payMethodFk || (orgData && orgData.payMethodFk);
|
|
|
|
const dueDay = changes.dueDay || (orgData && orgData.dueDay);
|
2018-07-10 12:13:47 +00:00
|
|
|
|
|
|
|
if (payMethodFk == payMethodWithIban && dueDay == 0)
|
2018-01-29 11:37:54 +00:00
|
|
|
changes.dueDay = 5;
|
|
|
|
|
|
|
|
if (isMultiple(ctx)) return;
|
|
|
|
|
2021-09-07 14:07:46 +00:00
|
|
|
if (!ctx.isNewInstance) {
|
|
|
|
const isTaxDataCheckedChanged = !orgData.isTaxDataChecked && changes.isTaxDataChecked;
|
|
|
|
if (isTaxDataCheckedChanged && !orgData.businessTypeFk)
|
|
|
|
throw new UserError(`Can't verify data unless the client has a business type`);
|
|
|
|
}
|
2021-09-09 12:43:18 +00:00
|
|
|
// Credit changes
|
|
|
|
if (changes.credit !== undefined)
|
|
|
|
await Self.changeCredit(ctx, finalState, changes);
|
2023-01-26 14:49:30 +00:00
|
|
|
|
2023-04-11 12:57:56 +00:00
|
|
|
// Credit management changes
|
2023-06-07 13:06:37 +00:00
|
|
|
if (changes?.rating || changes?.recommendedCredit)
|
2023-04-11 12:57:56 +00:00
|
|
|
await Self.changeCreditManagement(ctx, finalState, changes);
|
|
|
|
|
2023-01-26 14:49:30 +00:00
|
|
|
const oldInstance = {};
|
|
|
|
if (!ctx.isNewInstance) {
|
|
|
|
const newProps = Object.keys(changes);
|
|
|
|
Object.keys(orgData.__data).forEach(prop => {
|
|
|
|
if (newProps.includes(prop))
|
|
|
|
oldInstance[prop] = orgData[prop];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.hookState.oldInstance = oldInstance;
|
|
|
|
ctx.hookState.newInstance = changes;
|
2020-06-10 10:09:27 +00:00
|
|
|
});
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2019-02-28 13:30:29 +00:00
|
|
|
Self.observe('after save', async ctx => {
|
|
|
|
if (ctx.isNewInstance) return;
|
|
|
|
|
|
|
|
const hookState = ctx.hookState;
|
|
|
|
const newInstance = hookState.newInstance;
|
|
|
|
const oldInstance = hookState.oldInstance;
|
|
|
|
const instance = ctx.instance;
|
2020-01-23 12:31:07 +00:00
|
|
|
const models = Self.app.models;
|
2019-02-28 13:30:29 +00:00
|
|
|
|
|
|
|
const payMethodChanged = oldInstance.payMethodFk != newInstance.payMethodFk;
|
|
|
|
const ibanChanged = oldInstance.iban != newInstance.iban;
|
|
|
|
const dueDayChanged = oldInstance.dueDay != newInstance.dueDay;
|
|
|
|
|
|
|
|
if (payMethodChanged || ibanChanged || dueDayChanged) {
|
2020-01-23 12:31:07 +00:00
|
|
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
|
|
const httpCtx = {req: loopBackContext.active};
|
|
|
|
const httpRequest = httpCtx.req.http.req;
|
|
|
|
const $t = httpRequest.__;
|
2020-03-05 12:56:45 +00:00
|
|
|
const headers = httpRequest.headers;
|
|
|
|
const origin = headers.origin;
|
2020-01-23 12:31:07 +00:00
|
|
|
|
|
|
|
const salesPersonId = instance.salesPersonFk;
|
|
|
|
|
|
|
|
if (salesPersonId) {
|
2021-04-07 11:09:53 +00:00
|
|
|
// Send email to client
|
|
|
|
if (instance.email) {
|
2022-09-30 11:54:20 +00:00
|
|
|
const {Email} = require('vn-print');
|
2021-04-07 11:09:53 +00:00
|
|
|
const worker = await models.EmailUser.findById(salesPersonId);
|
|
|
|
const params = {
|
2022-09-30 11:54:20 +00:00
|
|
|
id: instance.id,
|
2021-04-07 11:09:53 +00:00
|
|
|
recipientId: instance.id,
|
|
|
|
recipient: instance.email,
|
|
|
|
replyTo: worker.email
|
|
|
|
};
|
2022-09-30 11:54:20 +00:00
|
|
|
const email = new Email('payment-update', params);
|
|
|
|
await email.send();
|
2021-04-07 11:09:53 +00:00
|
|
|
}
|
|
|
|
|
2020-01-23 12:31:07 +00:00
|
|
|
const fullUrl = `${origin}/#!/client/${instance.id}/billing-data`;
|
2021-04-07 11:09:53 +00:00
|
|
|
const message = $t('Changed client paymethod', {
|
2020-01-23 12:31:07 +00:00
|
|
|
clientId: instance.id,
|
|
|
|
clientName: instance.name,
|
|
|
|
url: fullUrl
|
2019-02-28 13:30:29 +00:00
|
|
|
});
|
2020-01-23 12:31:07 +00:00
|
|
|
await models.Chat.sendCheckingPresence(httpCtx, salesPersonId, message);
|
2019-02-28 13:30:29 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-23 15:19:45 +00:00
|
|
|
|
|
|
|
const workerIdBefore = oldInstance.salesPersonFk;
|
|
|
|
const workerIdAfter = newInstance.salesPersonFk;
|
|
|
|
const assignmentChanged = workerIdBefore != workerIdAfter;
|
|
|
|
if (assignmentChanged)
|
2021-03-24 11:45:26 +00:00
|
|
|
await Self.notifyAssignment(instance, workerIdBefore, workerIdAfter);
|
2019-02-28 13:30:29 +00:00
|
|
|
});
|
|
|
|
|
2021-03-23 15:19:45 +00:00
|
|
|
// Send notification on client worker assignment
|
2021-03-24 11:45:26 +00:00
|
|
|
Self.notifyAssignment = async function notifyAssignment(client, previousWorkerId, currentWorkerId) {
|
2021-03-23 15:19:45 +00:00
|
|
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
|
|
const httpCtx = {req: loopBackContext.active};
|
|
|
|
const httpRequest = httpCtx.req.http.req;
|
|
|
|
const $t = httpRequest.__;
|
|
|
|
const headers = httpRequest.headers;
|
|
|
|
const origin = headers.origin;
|
|
|
|
const models = Self.app.models;
|
|
|
|
|
2021-03-24 11:45:26 +00:00
|
|
|
let previousWorker = {name: $t('None')};
|
|
|
|
let currentWorker = {name: $t('None')};
|
2021-03-23 15:19:45 +00:00
|
|
|
if (previousWorkerId) {
|
|
|
|
const worker = await models.Worker.findById(previousWorkerId, {
|
|
|
|
include: {relation: 'user'}
|
|
|
|
});
|
2021-03-24 11:45:26 +00:00
|
|
|
previousWorker.user = worker && worker.user().name;
|
|
|
|
previousWorker.name = worker && worker.user().nickname;
|
2021-03-23 15:19:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (currentWorkerId) {
|
|
|
|
const worker = await models.Worker.findById(currentWorkerId, {
|
|
|
|
include: {relation: 'user'}
|
|
|
|
});
|
2021-03-24 11:45:26 +00:00
|
|
|
currentWorker.user = worker && worker.user().name;
|
|
|
|
currentWorker.name = worker && worker.user().nickname;
|
2021-03-23 15:19:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const fullUrl = `${origin}/#!/client/${client.id}/basic-data`;
|
|
|
|
const message = $t('Client assignment has changed', {
|
|
|
|
clientId: client.id,
|
|
|
|
clientName: client.name,
|
|
|
|
url: fullUrl,
|
2021-03-24 11:45:26 +00:00
|
|
|
previousWorkerName: previousWorker.name,
|
|
|
|
currentWorkerName: currentWorker.name
|
2021-03-23 15:19:45 +00:00
|
|
|
});
|
|
|
|
|
2021-03-24 11:45:26 +00:00
|
|
|
if (previousWorkerId)
|
|
|
|
await models.Chat.send(httpCtx, `@${previousWorker.user}`, message);
|
2021-03-23 15:19:45 +00:00
|
|
|
|
2021-03-24 11:45:26 +00:00
|
|
|
if (currentWorkerId)
|
|
|
|
await models.Chat.send(httpCtx, `@${currentWorker.user}`, message);
|
|
|
|
};
|
2021-03-23 15:19:45 +00:00
|
|
|
|
2021-09-09 12:43:18 +00:00
|
|
|
// Credit change validations
|
|
|
|
Self.changeCredit = async function changeCredit(ctx, finalState, changes) {
|
|
|
|
const models = Self.app.models;
|
|
|
|
const userId = ctx.options.accessToken.userId;
|
2023-06-28 12:10:21 +00:00
|
|
|
const accessToken = {req: {accessToken: ctx.options.accessToken}};
|
2021-09-09 12:43:18 +00:00
|
|
|
|
2023-04-24 09:07:04 +00:00
|
|
|
const canEditCredit = await models.ACL.checkAccessAcl(accessToken, 'Client', 'editCredit', 'WRITE');
|
|
|
|
if (!canEditCredit) {
|
2021-09-09 12:43:18 +00:00
|
|
|
const lastCredit = await models.ClientCredit.findOne({
|
2023-06-28 12:10:21 +00:00
|
|
|
field: ['workerFk', 'amount'],
|
2021-09-09 12:43:18 +00:00
|
|
|
where: {
|
|
|
|
clientFk: finalState.id
|
|
|
|
},
|
|
|
|
order: 'id DESC'
|
|
|
|
}, ctx.options);
|
|
|
|
|
2023-06-30 06:26:51 +00:00
|
|
|
if (lastCredit && lastCredit.amount == 0) {
|
|
|
|
const zeroCreditEditor =
|
|
|
|
await models.ACL.checkAccessAcl(accessToken, 'Client', 'zeroCreditEditor', 'WRITE');
|
2023-06-28 12:10:21 +00:00
|
|
|
const lastCreditIsNotEditable =
|
|
|
|
await models.ACL.checkAccessAcl(
|
|
|
|
{req: {accessToken: {userId: lastCredit.workerFk}}},
|
|
|
|
'Client',
|
2023-06-30 06:26:51 +00:00
|
|
|
'zeroCreditEditor',
|
2023-06-28 12:10:21 +00:00
|
|
|
'WRITE'
|
|
|
|
);
|
|
|
|
|
2023-06-30 06:26:51 +00:00
|
|
|
if (lastCreditIsNotEditable && !zeroCreditEditor)
|
2023-06-28 12:10:21 +00:00
|
|
|
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
|
|
|
|
}
|
2021-09-09 12:43:18 +00:00
|
|
|
|
|
|
|
const creditLimits = await models.ClientCreditLimit.find({
|
|
|
|
fields: ['roleFk'],
|
|
|
|
where: {
|
|
|
|
maxAmount: {gte: changes.credit}
|
|
|
|
}
|
|
|
|
}, ctx.options);
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2021-09-09 12:43:18 +00:00
|
|
|
const requiredRoles = [];
|
|
|
|
for (limit of creditLimits)
|
|
|
|
requiredRoles.push(limit.roleFk);
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2021-09-09 12:43:18 +00:00
|
|
|
const userRequiredRoles = await models.RoleMapping.count({
|
|
|
|
roleId: {inq: requiredRoles},
|
|
|
|
principalType: 'USER',
|
|
|
|
principalId: userId
|
|
|
|
}, ctx.options);
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2021-09-09 12:43:18 +00:00
|
|
|
if (userRequiredRoles <= 0)
|
|
|
|
throw new UserError(`You don't have enough privileges to set this credit amount`);
|
|
|
|
}
|
2018-01-29 11:37:54 +00:00
|
|
|
|
2022-01-05 13:29:03 +00:00
|
|
|
await models.ClientCredit.create({
|
|
|
|
amount: changes.credit,
|
|
|
|
clientFk: finalState.id,
|
|
|
|
workerFk: userId
|
|
|
|
}, ctx.options);
|
2021-09-09 12:43:18 +00:00
|
|
|
};
|
2021-03-23 15:19:45 +00:00
|
|
|
|
2023-04-11 12:57:56 +00:00
|
|
|
Self.changeCreditManagement = async function changeCreditManagement(ctx, finalState, changes) {
|
|
|
|
const models = Self.app.models;
|
2023-04-14 11:05:57 +00:00
|
|
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
|
|
const userId = loopBackContext.active.accessToken.userId;
|
2023-04-11 12:57:56 +00:00
|
|
|
|
|
|
|
await models.ClientInforma.create({
|
|
|
|
clientFk: finalState.id,
|
|
|
|
rating: changes.rating,
|
|
|
|
recommendedCredit: changes.recommendedCredit,
|
|
|
|
workerFk: userId
|
|
|
|
}, ctx.options);
|
|
|
|
};
|
|
|
|
|
2021-03-23 15:19:45 +00:00
|
|
|
const app = require('vn-loopback/server/server');
|
|
|
|
app.on('started', function() {
|
2023-01-23 14:24:00 +00:00
|
|
|
const VnUser = app.models.VnUser;
|
2021-03-23 15:19:45 +00:00
|
|
|
|
2023-01-23 14:24:00 +00:00
|
|
|
VnUser.observe('before save', async ctx => {
|
2021-03-23 15:19:45 +00:00
|
|
|
if (ctx.isNewInstance) return;
|
2022-10-26 06:27:28 +00:00
|
|
|
if (ctx.currentInstance)
|
|
|
|
ctx.hookState.oldInstance = JSON.parse(JSON.stringify(ctx.currentInstance));
|
2021-03-23 15:19:45 +00:00
|
|
|
});
|
|
|
|
|
2023-01-23 14:24:00 +00:00
|
|
|
VnUser.observe('after save', async ctx => {
|
2022-05-17 13:37:52 +00:00
|
|
|
const changes = ctx.data || ctx.instance;
|
2021-03-23 15:19:45 +00:00
|
|
|
if (!ctx.isNewInstance && changes) {
|
2022-05-17 13:37:52 +00:00
|
|
|
const oldData = ctx.hookState.oldInstance;
|
2022-10-26 06:27:28 +00:00
|
|
|
let hasChanges;
|
|
|
|
|
|
|
|
if (oldData)
|
|
|
|
hasChanges = oldData.name != changes.name || oldData.active != changes.active;
|
|
|
|
|
2021-03-23 15:19:45 +00:00
|
|
|
if (!hasChanges) return;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2018-01-29 11:37:54 +00:00
|
|
|
};
|