307 lines
9.6 KiB
JavaScript
307 lines
9.6 KiB
JavaScript
import logger from "../config/logger.js";
|
|
import SalesforceService from "./salesforce.service.js";
|
|
import { VAModel, ProjectModel, LoggedModel, WorkTrackModel } from "../models/vaModel.js";
|
|
import bcrypt from "bcrypt";
|
|
|
|
class VaClientService {
|
|
constructor(vaClientRepository) {
|
|
this.clientRepository = vaClientRepository;
|
|
this.salesforceService = new SalesforceService();
|
|
}
|
|
|
|
async registerClient(data) {
|
|
try {
|
|
// Check if user already exists
|
|
const existingUser = await this.clientRepository.findByEmail(data.userDetails.email);
|
|
if (existingUser) {
|
|
throw new Error("User already exists with this email");
|
|
}
|
|
|
|
// Sanitize and prepare data
|
|
const clientData = await this.sanitizeClientData(data);
|
|
const response = await this.clientRepository.create(clientData);
|
|
|
|
if(response) {
|
|
await this.salesforceService.registerClientInSalesforce(data, response._id);
|
|
}
|
|
// Send data to Salesforce first
|
|
|
|
return response;
|
|
} catch (error) {
|
|
logger.error("Client Service - registerClient error: " + error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getClientByEmail(email) {
|
|
try {
|
|
const response = await this.clientRepository.findByEmail(email);
|
|
return response;
|
|
} catch (error) {
|
|
logger.error("Client Service - getClientByEmail error: " + error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getClientById(id) {
|
|
try {
|
|
const response = await this.clientRepository.findById(id);
|
|
if (!response) throw new Error("Client not found");
|
|
return response;
|
|
} catch (error) {
|
|
logger.error("Client Service - getClientById error: " + error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async updateClient(id, data) {
|
|
try {
|
|
// Check if client exists
|
|
const existingClient = await this.clientRepository.findById(id);
|
|
if (!existingClient) {
|
|
throw new Error("Client not found");
|
|
}
|
|
|
|
const { userDetails } = data;
|
|
|
|
if (!userDetails) {
|
|
throw new Error("No user details provided for update");
|
|
}
|
|
|
|
// Prepare update data
|
|
const updateData = {
|
|
userDetails: {}
|
|
};
|
|
|
|
// Define allowed fields for update (excluding password)
|
|
const allowedFields = [
|
|
"name", "firstName", "lastName", "address", "city", "state", "zip",
|
|
"billingName", "billingEmail", "billingAddress", "billingCity",
|
|
"billingState", "billingZip", "position", "signatoryName",
|
|
"signatoryEmail", "paymentMethod", "bankName", "accountNumber",
|
|
"routingNumber", "accountType", "accountHolderName", "cardNumber",
|
|
"expiryDate", "securityCode", "cardZip"
|
|
];
|
|
|
|
let hasUpdates = false;
|
|
const updatedFields = [];
|
|
|
|
// Process allowed fields
|
|
allowedFields.forEach(field => {
|
|
if (userDetails[field] !== undefined && userDetails[field] !== null && userDetails[field] !== '') {
|
|
updateData.userDetails[field] = userDetails[field];
|
|
hasUpdates = true;
|
|
updatedFields.push(field);
|
|
}
|
|
});
|
|
|
|
// Handle email update separately (needs uniqueness check)
|
|
if (userDetails.email && userDetails.email !== existingClient.userDetails.email) {
|
|
// Check if new email already exists
|
|
const emailExists = await this.clientRepository.findByEmail(userDetails.email);
|
|
if (emailExists && emailExists._id.toString() !== id) {
|
|
throw new Error("Email already exists");
|
|
}
|
|
updateData.userDetails.email = userDetails.email.toLowerCase();
|
|
hasUpdates = true;
|
|
updatedFields.push('email');
|
|
}
|
|
|
|
// Check if any fields were actually updated
|
|
if (!hasUpdates) {
|
|
throw new Error("No valid fields to update");
|
|
}
|
|
|
|
// Update the client
|
|
const updatedClient = await this.clientRepository.update(id, updateData);
|
|
|
|
// Notify Salesforce about the update
|
|
try {
|
|
await this.salesforceService.updateClientInSalesforce(id, data);
|
|
logger.info(`Salesforce notified about client update: ${id}`);
|
|
} catch (sfError) {
|
|
logger.warn(`Salesforce notification failed for client update: ${sfError.message}`);
|
|
// Don't throw error - client update should proceed even if Salesforce fails
|
|
}
|
|
|
|
logger.info(`Client updated successfully: ${id}, Fields: ${updatedFields.join(', ')}`);
|
|
|
|
return {
|
|
...updatedClient.toObject(),
|
|
updateSummary: {
|
|
updatedFields
|
|
}
|
|
};
|
|
|
|
} catch (error) {
|
|
logger.error("Client Service - updateClient error: " + error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async deleteClient(clientId) {
|
|
try {
|
|
// Check if client exists
|
|
const existingClient = await this.clientRepository.findById(clientId);
|
|
if (!existingClient) {
|
|
throw new Error("Client not found");
|
|
}
|
|
|
|
const clientName = existingClient.userDetails?.name;
|
|
const vaIds = existingClient.vaIds || [];
|
|
const projectIds = existingClient.projectIds || [];
|
|
|
|
logger.info(`Deleting client: ${clientName} (${clientId})`);
|
|
|
|
const deletionSummary = {};
|
|
|
|
// 1. Remove client from all VAs
|
|
if (vaIds.length > 0) {
|
|
const vaResult = await VAModel.updateMany(
|
|
{ _id: { $in: vaIds } },
|
|
{ $pull: { clientIds: clientId } }
|
|
);
|
|
deletionSummary.vasUpdated = vaResult.modifiedCount;
|
|
}
|
|
|
|
// 2. Delete all projects and associated data
|
|
if (projectIds.length > 0) {
|
|
// Remove project references from VAs
|
|
await VAModel.updateMany(
|
|
{ projectIds: { $in: projectIds } },
|
|
{ $pull: { projectIds: { $in: projectIds } } }
|
|
);
|
|
|
|
// Delete work tracks and logs
|
|
await WorkTrackModel.deleteMany({ projectId: { $in: projectIds } });
|
|
await LoggedModel.deleteMany({ projectId: { $in: projectIds } });
|
|
|
|
// Delete projects
|
|
const projectResult = await ProjectModel.deleteMany({ _id: { $in: projectIds } });
|
|
deletionSummary.projectsDeleted = projectResult.deletedCount;
|
|
}
|
|
|
|
// 3. Delete the client
|
|
const deletedClient = await this.clientRepository.delete(clientId);
|
|
|
|
|
|
logger.info(`✅ Successfully deleted client: ${clientName} (${clientId})`);
|
|
|
|
return {
|
|
...deletedClient.toObject(),
|
|
deletionSummary
|
|
};
|
|
|
|
} catch (error) {
|
|
logger.error("Client Service - deleteClient error: " + error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getClientProfileByEmail(email) {
|
|
try {
|
|
const response = await this.clientRepository.findProfileByEmail(email);
|
|
return response;
|
|
} catch (error) {
|
|
logger.error("Client Service - getClientProfileByEmail error: " + error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async updateClientPassword(id, passwordData) {
|
|
try {
|
|
// Check if client exists
|
|
const existingClient = await this.clientRepository.findById(id);
|
|
if (!existingClient) {
|
|
throw new Error("Client not found");
|
|
}
|
|
|
|
const { currentPassword, newPassword, confirmPassword } = passwordData;
|
|
|
|
// Validate required fields
|
|
if (!currentPassword || !newPassword || !confirmPassword) {
|
|
throw new Error("All password fields are required");
|
|
}
|
|
|
|
// Validate password match
|
|
if (newPassword !== confirmPassword) {
|
|
throw new Error("New passwords do not match");
|
|
}
|
|
|
|
// Verify current password
|
|
const isMatch = await bcrypt.compare(currentPassword, existingClient.userDetails.password);
|
|
if (!isMatch) {
|
|
throw new Error("Current password is incorrect");
|
|
}
|
|
|
|
// Hash new password
|
|
const saltRounds = 10;
|
|
const hashedPassword = await bcrypt.hash(newPassword, saltRounds);
|
|
|
|
// Prepare update data
|
|
const updateData = {
|
|
userDetails: {
|
|
password: hashedPassword
|
|
}
|
|
};
|
|
|
|
// Update the client password
|
|
const updatedClient = await this.clientRepository.update(id, updateData);
|
|
|
|
logger.info(`Client password updated successfully: ${id}`);
|
|
|
|
return {
|
|
success: true,
|
|
message: "Password updated successfully",
|
|
clientId: id
|
|
};
|
|
|
|
} catch (error) {
|
|
logger.error("Client Service - updateClientPassword error: " + error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Helper method to sanitize data
|
|
async sanitizeClientData(payload) {
|
|
const { userDetails } = payload;
|
|
|
|
const saltRounds = 10;
|
|
const hashedPassword = await bcrypt.hash(userDetails.password, saltRounds);
|
|
|
|
return {
|
|
userDetails: {
|
|
firstName: userDetails.firstName,
|
|
lastName: userDetails.lastName,
|
|
name: userDetails.name,
|
|
email: userDetails.email.toLowerCase(),
|
|
password: hashedPassword,
|
|
address: userDetails.address || null,
|
|
city: userDetails.city || null,
|
|
state: userDetails.state || null,
|
|
zip: userDetails.zip || null,
|
|
billingName: userDetails.billingName || null,
|
|
billingAddress: userDetails.billingAddress || null,
|
|
billingCity: userDetails.billingCity || null,
|
|
billingState: userDetails.billingState || null,
|
|
billingZip: userDetails.billingZip || null,
|
|
billingEmail: userDetails.billingEmail || null,
|
|
position: userDetails.position || null,
|
|
signatoryName: userDetails.signatoryName || null,
|
|
signatoryEmail: userDetails.signatoryEmail || null,
|
|
paymentMethod: null,
|
|
bankName: null,
|
|
accountNumber: null,
|
|
routingNumber: null,
|
|
accountType: null,
|
|
accountHolderName: null,
|
|
cardNumber: null,
|
|
expiryDate: null,
|
|
securityCode: null,
|
|
cardZip: null,
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
export default VaClientService; |