150 lines
4.6 KiB
JavaScript
150 lines
4.6 KiB
JavaScript
'use strict';
|
|
|
|
const
|
|
mongoose = require('mongoose'),
|
|
Schema = mongoose.Schema,
|
|
uniqid = require('uniqid'),
|
|
mongoUtil = require('../helpers/mongo'),
|
|
Setting = require('./setting'),
|
|
RptVar = require('./rpt_var'),
|
|
Country = require('./country'),
|
|
{ Errors } = require('../helpers/constants'),
|
|
{ AddressSchema } = require('./common'),
|
|
{ DEFAULT_LANG } = require('../helpers/constants'),
|
|
{ AppInputError } = require('../helpers/app_error'),
|
|
{ ensureSingleBillingAddress } = require('../helpers/user_helper');
|
|
|
|
const schema = new Schema({
|
|
/*
|
|
The username is for uniquely identify a user and also used for sending mails to.
|
|
For example: send user the password-reset email, sending reports, etc.
|
|
*/
|
|
username: {
|
|
type: String, trim: true,
|
|
index: {
|
|
unique: true,
|
|
partialFilterExpression: { username: { $type: 'string' } }
|
|
},
|
|
set: v => (v === '' ? null : v)
|
|
},
|
|
password: {
|
|
type: String, trim: true, required: false,
|
|
set: v => (v === '' ? null : v)
|
|
},
|
|
|
|
name: { type: String, required: false },
|
|
// Legacy quick address. TODO: migrate to new address schema
|
|
address: { type: String, required: false },
|
|
phone: { type: String, required: false },
|
|
email: { type: String, required: false, unique: false },
|
|
active: { type: Boolean, default: true },
|
|
/* The preffered language from the available ones within the FE (English(en) - default, Spanish(es), Portuguese(pt)) */
|
|
lang: { type: String, default: DEFAULT_LANG },
|
|
|
|
parent: { type: Schema.Types.ObjectId, ref: 'User', required: false },
|
|
|
|
// Reference to the Partner collection (optional) - used for customers with partner integrations
|
|
partner: { type: Schema.Types.ObjectId, ref: 'User', required: false },
|
|
|
|
loggedInAt: { type: Date },
|
|
|
|
markedDelete: { type: Boolean, default: false },
|
|
|
|
// Users addresses. Only one of them can be marked as billing address.
|
|
addresses: { type: [AddressSchema], default: [] },
|
|
|
|
// The migrated date for applicator and users paid before the migration to SM
|
|
migratedDate: { type: Date, required: false, default: null },
|
|
|
|
needReview: { type: Boolean, required: false }
|
|
|
|
}, { timestamps: true, discriminatorKey: 'kind', toJSON: { virtuals: true }, toObject: { virtuals: true }, strictQuery: false });
|
|
|
|
schema.virtual('Country', {
|
|
ref: 'Country',
|
|
localField: 'country',
|
|
foreignField: 'code',
|
|
justOne: true,
|
|
});
|
|
|
|
class UserBaseSchema {
|
|
|
|
static toUser(srcOjb) {
|
|
if (!srcOjb) AppInputError.throw();
|
|
|
|
if (srcOjb['Country'] && typeof srcOjb['Country'] === 'object' && srcOjb['Country'].code) {
|
|
srcOjb['country'] = srcOjb['Country'].code;
|
|
}
|
|
return srcOjb;
|
|
}
|
|
|
|
async markAsDeleted(session) {
|
|
session && (session.startTransaction(mongoUtil.getTranOps()));
|
|
try {
|
|
// De-activate the username so same username can be re-used
|
|
this.username && (this.username = this.username + '#' + uniqid());
|
|
this.active && (this.active = false);
|
|
|
|
this.markedDelete = true;
|
|
|
|
await this.save({ session });
|
|
} catch (error) {
|
|
session && (session.abortTransaction());
|
|
throw error;
|
|
}
|
|
session && await mongoUtil.commitWithRetry(session);
|
|
}
|
|
|
|
async deleteMarked(session) {
|
|
session && (session.startTransaction(mongoUtil.getTranOps()));
|
|
try {
|
|
await this.remove({ session });
|
|
} catch (error) {
|
|
session && (session.abortTransaction());
|
|
throw error;
|
|
}
|
|
session && await mongoUtil.commitWithRetry(session);
|
|
}
|
|
}
|
|
schema.loadClass(UserBaseSchema);
|
|
|
|
async function clearRelatedData(doc) {
|
|
if (!doc) return;
|
|
await Setting.deleteMany({ userId: doc._id }).session(doc.$session());
|
|
await RptVar.deleteMany({ userId: doc._id }).session(doc.$session());
|
|
}
|
|
|
|
async function updateFields(doc) {
|
|
if (!doc) return;
|
|
// Convert ref objects to its code/id for storage
|
|
if (doc['Country'] && typeof doc['Country'] === 'object' && doc['Country'].code) {
|
|
doc['country'] = doc['Country'].code;
|
|
}
|
|
|
|
// Ensure only one billing address exists using reusable utility function
|
|
if (doc.addresses && Array.isArray(doc.addresses)) {
|
|
doc.addresses = ensureSingleBillingAddress(doc.addresses);
|
|
}
|
|
}
|
|
/**
|
|
* Handle pre-update hooks middleware before updating one user.
|
|
*/
|
|
schema.pre(['updateOne', 'findOneAndUpdate'], async function () {
|
|
await updateFields(this?._update);
|
|
});
|
|
|
|
schema.pre(['save'], async function () {
|
|
await updateFields(this);
|
|
});
|
|
|
|
schema.post('remove', async function () {
|
|
await clearRelatedData(this);
|
|
});
|
|
schema.post('findOneAndRemove', async function (doc) {
|
|
await clearRelatedData(doc);
|
|
});
|
|
schema.post('findOneAndDelete', async function (doc) {
|
|
await clearRelatedData(doc);
|
|
});
|
|
|
|
module.exports = mongoose.model('User', schema); |