Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules/
dist/
build/
coverage/
uploads/
6 changes: 5 additions & 1 deletion src/middlewares/auth.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ export const authenticate = (req, res, next) => {
throw e;
}
}
req.user = { id: decoded.userId, phone: decoded.phone };
req.user = {
id: decoded.userId || decoded.id,
phone: decoded.phone,
email: decoded.email,
};
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
Expand Down
53 changes: 45 additions & 8 deletions src/services/claim.service.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ClaimModel } from '../models/claim.model.js';
import { UserModel } from '../models/user.model.js';
import { employerService } from './employer.service.js';
import { billService } from './bill.service.js';

Expand Down Expand Up @@ -150,24 +151,60 @@ export const claimService = {
},

/**
* Get all claims formatted for employer dashboard
* Get all claims formatted for employer dashboard, enriched with user profiles.
*/
getAllClaims: async () => {
const claims = await ClaimModel.find().sort({ submittedAt: -1 }).lean();
getAllClaims: async (filter = {}) => {
const claims = await ClaimModel.find(filter).sort({ submittedAt: -1 }).lean();

// Fetch user profiles for all submitters
const userIds = [...new Set(claims.map((c) => c.userId))];
const users = await UserModel.find({
$or: [{ _id: { $in: userIds.filter((id) => id && id.length === 24) } }, { email: { $in: userIds } }],
}).lean();

const userMap = {};
for (const u of users) {
userMap[u._id.toString()] = u;
if (u.email) {
userMap[u.email] = u;
}
}

return claims.map((claim) => {
const user = userMap[claim.userId] || {};
const empName = user.name || user.fullName || claim.employeeName || (user.email ? user.email.split('@')[0] : 'Employee');
const initials = empName
.split(' ')
.map((n) => n[0])
.join('')
.toUpperCase()
.slice(0, 2);

// Normalize status
let normStatus = claim.status || 'Pending';
if (normStatus === 'Submitted') {
normStatus = 'Pending';
}
if (normStatus === 'Reimbursed') {
normStatus = 'Paid';
}

return {
id: claim.claimId || claim._id.toString(),
claimId: claim.claimId || claim._id.toString(),
employeeName: claim.employeeName || 'Employee',
employeeEmail: claim.userId ? `${claim.userId}@company.com` : 'employee@company.com',
department: 'Operations',
employeeAvatar: (claim.title || 'EM').slice(0, 2).toUpperCase(),
employeeId: claim.userId,
employeeName: empName,
employeeEmail: user.email || (claim.userId ? `${claim.userId}@company.com` : 'employee@company.com'),
employeePhone: user.phone || '',
employeeAvatar: initials || (claim.title || 'EM').slice(0, 2).toUpperCase(),
department: user.department || 'Operations',
title: claim.title,
amount: claim.amount,
category: claim.category || 'General',
project: claim.project || 'Operations',
costCenter: claim.costCenter || 'CC-100',
status: claim.status || 'Submitted',
status: normStatus,
rawStatus: claim.status || 'Submitted',
submissionDate: claim.submittedAt
? new Date(claim.submittedAt).toISOString().split('T')[0]
: new Date().toISOString().split('T')[0],
Expand Down
84 changes: 66 additions & 18 deletions src/services/consent.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,35 @@ export const consentService = {
* Get current consents for a user (creates default record if none exists).
*/
getUserConsents: async (userId) => {
let record = await ConsentModel.findOne({ userId }).lean();
if (!record) {
const created = await ConsentModel.create({
try {
let record = await ConsentModel.findOne({ userId }).lean();
if (!record) {
try {
const created = await ConsentModel.create({
userId,
consents: { ...DEFAULT_CONSENTS },
history: [],
updatedAt: new Date(),
});
record = created.toObject();
} catch {
record = {
userId,
consents: { ...DEFAULT_CONSENTS },
history: [],
updatedAt: new Date(),
};
}
}
return record;
} catch {
return {
userId,
consents: { ...DEFAULT_CONSENTS },
history: [],
updatedAt: new Date(),
});
record = created.toObject();
};
}
return record;
},

/**
Expand All @@ -44,16 +62,24 @@ export const consentService = {
}
}

const updatedRecord = await ConsentModel.findOneAndUpdate(
{ userId },
{
$set: { consents: updatedConsents, updatedAt: timestamp },
$push: { history: { $each: historyEntries } },
},
{ returnDocument: 'after', upsert: true }
);

return updatedRecord;
try {
const updatedRecord = await ConsentModel.findOneAndUpdate(
{ userId },
{
$set: { consents: updatedConsents, updatedAt: timestamp },
$push: { history: { $each: historyEntries } },
},
{ returnDocument: 'after', upsert: true }
);
return updatedRecord;
} catch {
return {
userId,
consents: updatedConsents,
history: historyEntries,
updatedAt: timestamp,
};
}
},

/**
Expand All @@ -63,10 +89,13 @@ export const consentService = {
const keyMap = {
upi: 'upiConsent',
upiConsent: 'upiConsent',
UPI_DATA: 'upiConsent',
billStorage: 'billStorageConsent',
billStorageConsent: 'billStorageConsent',
BILL_STORAGE: 'billStorageConsent',
aiUsage: 'aiUsageConsent',
aiUsageConsent: 'aiUsageConsent',
AI_USAGE: 'aiUsageConsent',
};

const targetKey = keyMap[consentType];
Expand All @@ -86,8 +115,27 @@ export const consentService = {
* Check if a specific consent is currently active for a user.
*/
hasConsent: async (userId, consentType) => {
const record = await consentService.getUserConsents(userId);
return Boolean(record.consents[consentType]);
try {
const record = await consentService.getUserConsents(userId);
const keyMap = {
upi: 'upiConsent',
upiConsent: 'upiConsent',
UPI_DATA: 'upiConsent',
billStorage: 'billStorageConsent',
billStorageConsent: 'billStorageConsent',
BILL_STORAGE: 'billStorageConsent',
aiUsage: 'aiUsageConsent',
aiUsageConsent: 'aiUsageConsent',
AI_USAGE: 'aiUsageConsent',
};
const key = keyMap[consentType] || consentType;
if (record && record.consents && record.consents[key] !== undefined) {
return Boolean(record.consents[key]);
}
return false;
} catch {
return false;
}
},

/**
Expand Down
Loading