agmission/Development/server/model/user.js

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);