diff --git a/.gitignore b/.gitignore index 39e2855..f21d838 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules/ dist/ build/ coverage/ +uploads/ diff --git a/src/middlewares/auth.middleware.js b/src/middlewares/auth.middleware.js index 5a2844f..7544805 100644 --- a/src/middlewares/auth.middleware.js +++ b/src/middlewares/auth.middleware.js @@ -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') { diff --git a/src/services/claim.service.js b/src/services/claim.service.js index c68e7b3..9af2fdd 100644 --- a/src/services/claim.service.js +++ b/src/services/claim.service.js @@ -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'; @@ -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], diff --git a/src/services/consent.service.js b/src/services/consent.service.js index 8fae3af..e755d02 100644 --- a/src/services/consent.service.js +++ b/src/services/consent.service.js @@ -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; }, /** @@ -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, + }; + } }, /** @@ -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]; @@ -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; + } }, /**