Team Audit Backend
1. Overview
The Team Audit feature in the ScheduleLite module provides functionalities for managing team audits, including check-ins, section progress tracking, and report generation. This document outlines the key components, their responsibilities, and the flow of data within the system.
2. Libraries Used
@nimbly-technologies/nimbly-backend-utils: ProvidesValidatorandmiddlewaresfor validation and middleware handling.express: Used for defining routes via theRouterinterface.../controllers/scheduleLite.controller: ImportsScheduleLiteControllerto map routes to controller methods../validator/scheduleLite.validator: Provides validation logic for specific routes.
3. Route Definitions
This file defines routes for the “Team Audit” feature, mapping HTTP endpoints to methods in the ScheduleLiteController. It also integrates middleware and validation logic to ensure proper request handling.
File Path: src/routes/scheduleLite.routes.ts
3.1 GET /sections-progress/:reportID
- Purpose: Fetches progress of all sections in a team audit.
- Controller Method:
getSectionProgress - Middleware:
middlewares.expressMiddlewareHandler
3.2 POST /team-audit-checkin
- Purpose: Handles check-in for team audits.
- Controller Method:
teamAuditCheckIn - Middleware:
middlewares.expressMiddlewareHandler
3.3 PATCH /reset-section-progress
- Purpose: Resets progress for a specific section.
- Controller Method:
resetSectionProgress - Middleware:
middlewares.expressMiddlewareHandler
3.4 POST /section-checkin
- Purpose: Handles section-specific check-ins.
- Controller Method:
sectionCheckIn - Middleware:
middlewares.expressMiddlewareHandler
3.5 POST /review-section
- Purpose: Reviews and updates the status of a section.
- Controller Method:
reviewSection - Middleware:
middlewares.expressMiddlewareHandler
3.6 GET /audit-members/:reportID
- Purpose: Retrieves team audit members.
- Controller Method:
getTeamAuditMember - Middleware:
middlewares.expressMiddlewareHandler
The routes in this file are integral to the “Team Audit” feature, providing endpoints for fetching section progress, handling check-ins, resetting progress, reviewing sections, and retrieving audit members. Each route is mapped to a corresponding method in the ScheduleLiteController, ensuring a clear separation of concerns and maintainable code structure.
4. Team Audit Implementation
4.1 Section Progress
The /sections-progress/:reportID route fetches the progress of all sections in a team audit. It maps to the getSectionProgress method in the ScheduleLiteController.
4.1.1 File Paths
| Layer | File Path |
|---|---|
| Route Definition | src/routes/scheduleLite.routes.ts |
| Controller Method | src/controllers/scheduleLite.controller.ts |
| Use Case Implementation | src/domains/scheduleLite/usecase/scheduleLite.usecase.ts |
4.1.2 Implementation
Route Definition
.get('/sections-progress/:reportID', middlewares.expressMiddlewareHandler(this.controller.getSectionProgress))- Purpose: Defines a GET route that accepts a
reportIDas a parameter. - Middleware: Uses
expressMiddlewareHandlerto handle the controller logic.
Controller Method
public async getSectionProgress(
{ context, payload }: FunctionParam<TeamAuditParams, Context<UserAuth>>
) {
const query: TeamAuditParams = {
reportID: payload.params.reportID,
};
try {
const res: TeamAuditSectionsResponse = await this.usecase.getSectionProgress(context, query);
return { sections: res, message: 'SUCCESS', full: true };
} catch (error) {
return this.handleError(error);
}
}- Purpose: Extracts the
reportIDfrom the request payload and calls the use case method to fetch section progress. - Error Handling: Uses
handleErrorto manage exceptions.
Use Case Method
public async getSectionProgress(
ctx: Context<UserAuth>,
query: TeamAuditParams
): Promise<TeamAuditSectionsResponse> {
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode('not-found', 'Report not found');
}
// Process sections and return progress
const sections = report.sections.map((section, index) => ({
secIdx: index,
qnrID: section.questionnaireID,
sectionName: section.sectionName,
isRequired: section.isRequired,
doneBy: section.doneBy,
approvalStatus: section.approvalStatus,
}));
return { sections };
}Use Case Logic Breakdown
Fetching Report:
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode("not-found", "Report not found");
}- Checks if the report exists. Throws an error if not found.
Mapping Sections:
const sections = report.sections.map((section, index) => ({
secIdx: index,
qnrID: section.questionnaireID,
sectionName: section.sectionName,
isRequired: section.isRequired,
doneBy: section.doneBy,
approvalStatus: section.approvalStatus,
}));- Iterates over the sections in the report and extracts relevant details.
4.1.3 Data Flow
- Route:
/sections-progress/:reportIDis invoked. - Controller:
getSectionProgressextractsreportIDand calls the use case. - Use Case:
getSectionProgressfetches the report and processes its sections. - Response: Returns a structured response with section progress details.
4.2 Team Audit Check In
The /team-audit-checkin route handles the check-in process for team audits. It maps to the teamAuditCheckIn method in the ScheduleLiteController.
4.2.1 File Paths
| Layer | File Path |
|---|---|
| Route Definition | src/routes/scheduleLite.routes.ts |
| Controller Method | src/controllers/scheduleLite.controller.ts |
| Use Case Implementation | src/domains/scheduleLite/usecase/scheduleLite.usecase.ts |
4.2.2 Implementation
Route Definition
.post('/team-audit-checkin', middlewares.expressMiddlewareHandler(this.controller.teamAuditCheckIn))- Purpose: Defines a POST route for team audit check-ins.
- Middleware: Uses
expressMiddlewareHandlerto handle the controller logic.
Controller Method
public async teamAuditCheckIn(
{ context, payload }: FunctionParam<TeamAuditCheckInPayload, Context<UserAuth>>
) {
const query: TeamAuditCheckInPayload = {
scheduleID: payload.data.scheduleID,
date: payload.data.date,
type: payload.data.type,
coordinates: payload.data.coordinates,
deviceTimeIn: payload.data.deviceTimeIn,
metadata: payload.data.metadata,
};
try {
const res: TeamAuditCheckInResponse = await this.usecase.teamAuditCheckIn(context, query);
return this.handleSuccess(res);
} catch (error) {
return this.handleError(error);
}
}- Purpose: Extracts the check-in details from the request payload and calls the use case method to process the check-in.
- Error Handling: Uses
handleErrorto manage exceptions.
Use Case Method
public async teamAuditCheckIn(
ctx: Context<UserAuth>,
payload: TeamAuditCheckInPayload
): Promise<TeamAuditCheckInResponse> {
const site = await this.siteRepo.findByID(payload.scheduleID);
if (!site) {
throw new ErrorCode('not-found', 'Site not found');
}
// Validate user location
this.checkUserWithinRadius(payload.coordinates, ctx.user.organization, site);
// Process check-in
const report = await this.processExistingReport(ctx, payload);
return {
siteID: site.siteID,
siteName: site.name,
sections: report.sections,
scheduleID: payload.scheduleID,
reportID: report.reportID,
leader: ctx.user.userID,
};
}- Validates the user’s location and processes the check-in for the specified schedule.
- Fetches the site details using
siteRepo.findByID. - Validates the user’s location using
checkUserWithinRadius. - Processes the check-in using
processExistingReport.
Use Case Logic Breakdown
Fetching Site Details: Ensures the site exists for the given schedule ID.
const site = await this.siteRepo.findByID(payload.scheduleID);
if (!site) {
throw new ErrorCode("not-found", "Site not found");
}Validating User Location:
this.checkUserWithinRadius(payload.coordinates, ctx.user.organization, site);- Checks if the user’s coordinates are within the allowed radius of the site.
checkUserWithinRadius
This function validates whether a user is within the allowed radius for a site.
private checkUserWithinRadius(
coordinates: Coordinates,
organization: any,
site: any
): void {
if (!coordinates) {
throw new ErrorCode(errors.INVALID, 'Coordinates are required');
}
if (
typeof coordinates.latitude !== 'number' ||
typeof coordinates.longitude !== 'number'
) {
throw new ErrorCode(
errors.INVALID,
'Invalid coordinate values - latitude and longitude must be numbers'
);
}
const userWithinTheRadius = isUserWithinTheSiteRadius(coordinates, organization, site);
if (!userWithinTheRadius) {
throw new ErrorCode(errors.INVALID, 'User outside the check-in radius');
}
}-
Input Validation: Ensures
coordinatesare provided and contain validlatitudeandlongitudevalues. Throws an error if invalid. -
Radius Check: Calls
isUserWithinTheSiteRadiuswith the providedcoordinates,organization, andsite. -
Validation: If the user is outside the allowed radius, it throws an error indicating the user is outside the check-in radius.
Processing Check-In:
const report = await this.processExistingReport(ctx, payload);- Processes the check-in and generates a report.
processExistingReport
This function processes an existing report and returns a structured response containing details about the site, sections, and summary.
private async processExistingReport(
ctx: Context<UserAuth>,
report: any
): Promise<TeamAuditCheckInResponse> {
if (!report.siteID || !report.reportID) {
throw new ErrorCode('invalid', 'Invalid report data');
}
try {
const [site, reportSummary] = await Promise.all([
this.siteRepo.findByID(report.siteID, ctx.user.organizationID),
this.reportSummaryRepo.findByID(report.reportID),
]);
if (!site) {
log.warn('Site not found for report', {
reportID: report.reportID,
siteID: report.siteID,
});
}
const sections =
report.sections?.map((section, index) => ({
secIdx: index,
qnrID: section.questionnaireID,
sectionName: site?.children?.[section.siteChild]?.name || '',
isRequired: site?.children?.[section.siteChild]?.isRequired || false,
doneBy: section.doneBy || '',
})) || [];
return {
siteID: report.siteID,
siteName: site?.name || '',
sections,
scheduleID: report.scheduleID || '',
reportID: report.reportID,
summary: reportSummary || '',
leader: site?.assignedAuditor || '',
};
} catch (error) {
log.error('Error while processing existing report', {
reportID: report.reportID,
error,
});
throw new ErrorCode('internal', 'Error processing existing report');
}
}-
Input Validation: Ensures the
reportobject contains validsiteIDandreportID. If not, it throws an error. -
Data Fetching: Fetches the site details using
siteRepo.findByIDand the report summary usingreportSummaryRepo.findByID. -
Error Handling: Logs a warning if the site is not found for the given report.
-
Section Mapping: Maps the sections of the report to include details like section index, questionnaire ID, section name, and whether the section is required.
-
Response Construction:
- Constructs and returns a response object containing:
siteIDandsiteName- Mapped
sections scheduleIDandreportID- Report summary and leader details.
-
Error Logging: Catches and logs any errors during processing and throws an internal error.
4.2.3 Data Flow
- Route:
/team-audit-checkinis invoked. - Controller:
teamAuditCheckInextracts check-in details and calls the use case. - Use Case:
teamAuditCheckInvalidates the user’s location and processes the check-in. - Response: Returns a structured response with check-in details.
4.3 Reset Section Progress
The /reset-section-progress route resets the progress of a specific section in a team audit. It maps to the resetSectionProgress method in the ScheduleLiteController.
4.3.1 File Paths
| Component | File Path |
|---|---|
| Route Definition | src/routes/scheduleLite.routes.ts |
| Controller Method | src/controllers/scheduleLite.controller.ts |
| Use Case Implementation | src/domains/scheduleLite/usecase/scheduleLite.usecase.ts |
4.3.2 Implementation
Route Definition
.patch('/reset-section-progress', middlewares.expressMiddlewareHandler(this.controller.resetSectionProgress))- Purpose: Defines a PATCH route for resetting section progress.
- Middleware: Uses
expressMiddlewareHandlerto handle the controller logic.
Controller Method
public async resetSectionProgress({
context,
payload,
}: FunctionParam<TeamAuditParams, Context<UserAuth>>) {
const query: TeamAuditParams = {
reportID: payload.data.reportID,
sectionIdx: payload.data.sectionIdx,
};
try {
await this.usecase.resetSectionProgress(context, query);
return this.handleSuccess({ message: 'SUCCESS' });
} catch (error) {
return this.handleError(error);
}
}- Purpose: Extracts the
reportIDandsectionIdxfrom the request payload and calls the use case method to reset section progress. - Error Handling: Uses
handleErrorto manage exceptions.
Use Case Method
public async resetSectionProgress(
ctx: Context<UserAuth>,
query: TeamAuditParams
): Promise<void> {
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode('not-found', 'Report not found');
}
const section = report.sections?.[query.sectionIdx];
if (!section) {
throw new ErrorCode('not-found', 'Section not found');
}
// Reset section progress
section.questions.forEach((question) => {
question.isAnswered = false;
});
await this.reportRepo.update(report.reportID, {
sections: report.sections,
});
}- Fetches the report and resets the progress of the specified section.
- Retrieves the report using
reportRepo.findByID. - Validates the existence of the section.
- Resets the progress of all questions in the section.
Use Case Logic Breakdown
- Fetching Report: Ensures the report exists for the given
reportID.
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode("not-found", "Report not found");
}- Validating Section: Checks if the specified section exists in the report.
const section = report.sections?.[query.sectionIdx];
if (!section) {
throw new ErrorCode("not-found", "Section not found");
}- Resetting Progress: Iterates over the questions in the section and marks them as unanswered.
section.questions.forEach((question) => {
question.isAnswered = false;
});
await this.reportRepo.update(report.reportID, { sections: report.sections });4.3.3 Data Flow
- Route:
/reset-section-progressis invoked. - Controller:
resetSectionProgressextractsreportIDandsectionIdxand calls the use case. - Use Case:
resetSectionProgressfetches the report, validates the section, and resets its progress. - Response: Returns a success message upon completion.
4.4 Section Check-In
The /section-checkin route handles section-specific check-ins for team audits. It maps to the sectionCheckIn method in the ScheduleLiteController.
4.4.1 File Paths
| Layer | File Path |
|---|---|
| Route Definition | src/routes/scheduleLite.routes.ts |
| Controller Method | src/controllers/scheduleLite.controller.ts |
| Use Case Implementation | src/domains/scheduleLite/usecase/scheduleLite.usecase.ts |
4.4.2 Implementation
Route Definition
.post('/section-checkin', middlewares.expressMiddlewareHandler(this.controller.sectionCheckIn))- Purpose: Defines a POST route for section-specific check-ins.
- Middleware: Uses
expressMiddlewareHandlerto handle the controller logic.
Controller Method
public async sectionCheckIn({
context,
payload,
}: FunctionParam<TeamAuditParams, Context<UserAuth>>) {
const query: TeamAuditParams = {
reportID: payload.data.reportID,
sectionIdx: payload.data.sectionIdx,
};
try {
const res: TeamAuditSectionsResponse = await this.usecase.sectionCheckIn(context, query);
return this.handleSuccess(res);
} catch (error) {
return this.handleError(error);
}
}- Purpose: Extracts the
reportIDandsectionIdxfrom the request payload and calls the use case method to handle the section check-in. - Error Handling: Uses
handleErrorto manage exceptions.
Use Case Method
public async sectionCheckIn(
ctx: Context<UserAuth>,
query: TeamAuditParams
): Promise<TeamAuditSectionsResponse> {
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode('not-found', 'Report not found');
}
const section = report.sections?.[query.sectionIdx];
if (!section) {
throw new ErrorCode('not-found', 'Section not found');
}
// Process section check-in
section.checkInAt = new Date();
await this.reportRepo.update(report.reportID, { sections: report.sections });
return {
userID: ctx.user.userID,
displayName: ctx.user.displayName,
email: ctx.user.email,
photoUrl: ctx.user.photoUrl,
percentage: 100, // Example value
totalAnsweredQuestions: section.questions.filter((q) => q.isAnswered).length,
checkInAt: section.checkInAt,
checkOutAt: section.checkOutAt,
sectionName: section.sectionName,
required: section.isRequired,
sectionID: section.sectionID,
sectionIdx: query.sectionIdx,
};
}- Fetches the report and processes the check-in for the specified section.
- Retrieves the report using
reportRepo.findByID. - Validates the existence of the section.
- Updates the
checkInAttimestamp for the section.
Use Case Logic Breakdown
- Fetching Report: Ensures the report exists for the given
reportID.
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode("not-found", "Report not found");
}- Validating Section: Checks if the specified section exists in the report.
const section = report.sections?.[query.sectionIdx];
if (!section) {
throw new ErrorCode("not-found", "Section not found");
}- Processing Check-In: Updates the
checkInAttimestamp and saves the changes to the database.
section.checkInAt = new Date();
await this.reportRepo.update(report.reportID, { sections: report.sections });- Returning Response: Constructs a detailed response with section and user details.
return {
userID: ctx.user.userID,
displayName: ctx.user.displayName,
email: ctx.user.email,
photoUrl: ctx.user.photoUrl,
percentage: 100, // Example value
totalAnsweredQuestions: section.questions.filter((q) => q.isAnswered).length,
checkInAt: section.checkInAt,
checkOutAt: section.checkOutAt,
sectionName: section.sectionName,
required: section.isRequired,
sectionID: section.sectionID,
sectionIdx: query.sectionIdx,
};4.4.3 Data Flow
- Route:
/section-checkinis invoked. - Controller:
sectionCheckInextractsreportIDandsectionIdxand calls the use case. - Use Case:
sectionCheckInfetches the report, validates the section, and processes the check-in. - Response: Returns a structured response with section and user details.
4.5 Review Section
The /review-section route handles the review and status update of a specific section in a team audit. It maps to the reviewSection method in the ScheduleLiteController.
4.5.1 File Paths
| Layer | File Path |
|---|---|
| Route Definition | src/routes/scheduleLite.routes.ts |
| Controller Method | src/controllers/scheduleLite.controller.ts |
| Use Case Implementation | src/domains/scheduleLite/usecase/scheduleLite.usecase.ts |
4.5.2 Implementation
Route Definition
.post('/review-section', middlewares.expressMiddlewareHandler(this.controller.reviewSection))- Purpose: Defines a POST route for reviewing a section.
- Middleware: Uses
expressMiddlewareHandlerto handle the controller logic.
Controller Method
public async reviewSection({
context,
payload,
}: FunctionParam<TeamAuditParams, Context<UserAuth>>) {
const query: TeamAuditParams = {
reportID: payload.data.reportID,
sectionIdx: payload.data.sectionIdx,
approvalStatus: payload.data.approvalStatus,
};
try {
await this.usecase.reviewSection(context, query);
return this.handleSuccess({ message: 'SUCCESS' });
} catch (error) {
return this.handleError(error);
}
}- Purpose: Extracts the
reportID,sectionIdx, andapprovalStatusfrom the request payload and calls the use case method to review the section. - Error Handling: Uses
handleErrorto manage exceptions.
Use Case Method
public async reviewSection(
ctx: Context<UserAuth>,
query: TeamAuditParams
): Promise<void> {
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode('not-found', 'Report not found');
}
const section = report.sections?.[query.sectionIdx];
if (!section) {
throw new ErrorCode('not-found', 'Section not found');
}
// Update approval status
section.approvalStatus = query.approvalStatus;
await this.reportRepo.update(report.reportID, {
sections: report.sections,
});
}- Fetches the report and updates the approval status of the specified section.
- Retrieves the report using
reportRepo.findByID. - Validates the existence of the section.
- Updates the
approvalStatusof the section.
Use Case Logic Breakdown
- Fetching Report: Ensures the report exists for the given
reportID.
const report = await this.reportRepo.findByID(query.reportID);
if (!report) {
throw new ErrorCode("not-found", "Report not found");
}- Validating Section: Checks if the specified section exists in the report.
const section = report.sections?.[query.sectionIdx];
if (!section) {
throw new ErrorCode("not-found", "Section not found");
}- Updating Approval Status: Updates the
approvalStatusof the section and saves the changes to the database.
section.approvalStatus = query.approvalStatus;
await this.reportRepo.update(report.reportID, { sections: report.sections });4.5.3 Data Flow
- Route:
/review-sectionis invoked. - Controller:
reviewSectionextractsreportID,sectionIdx, andapprovalStatusand calls the use case. - Use Case:
reviewSectionfetches the report, validates the section, and updates its approval status. - Response: Returns a success message upon completion.
4.6 Team Audit Report Generation
The reportQueueHandler function in src/func/report/reportQueueHandler.ts is responsible for handling the backend logic for generating reports in team audits. This section covers the report generation process with a focus on team audits.
4.6.1 File Paths
| Layer | File Path |
|---|---|
| Handler Function | src/func/report/reportQueueHandler.ts |
| Report Generation Utility | src/util/reportGeneration.ts |
| Gallery Creation API | src/func/report/reportQueueHandler.ts |
| Issue Creation Helper | src/func/report/helper/sendReportIssue.ts |
| Notification Utility | src/util/notification.ts |
| MongoDB Initialization | src/func/report/helper/initMongo.ts |
| Object Detection Helper | src/func/report/helper/objectDetectionVertexAiProcessor.ts |
4.6.2 Implementation
Initialization
await initMongoose(functions.config().mongodb.url);
const siteRepo: ISiteRepository = new SiteMongo(connection.useDb("nimbly"));
const reportRepositoryMongo = new ReportRepository(
connection.useDb("nimbly"),
tracer
);- Purpose: Establishes connections to the database and initializes repositories for accessing
Site,Report, and other entities.
Identifying Team Audit Reports
if (report.sections !== null && !report.questionnaireIndexID) {
console.log(`[DEBUG] Processing Team Audit report: ${reportKey}`);
}- Purpose: Ensures the function processes only “Team Audit” reports.
Distinguishing Between Single and Team Audits
if (report.sections && !report.questionnaireIndexID) {
// Team Audit logic
console.log(`[DEBUG] Generating report for Team Audit with sections`);
} else {
// Single Audit logic
console.log(`[DEBUG] Generating report for Single Audit`);
}- Purpose: Applies the appropriate logic for generating reports based on the audit type.
Generating the Report
const generatedReport = await generateReport(
report,
{ key: organizationKey, value: organization },
{ key: siteKey, value: site },
reportKey,
report.sections ? null : questionnaire, // Null for Team Audit
report.sections ? null : questionnaireIndex, // Null for Team Audit
auditorInCharge,
organizationSKUs,
flag,
scoreV1,
score.total,
score.raw,
false,
departmentID,
branding,
answerColumnFlag,
isAutoFailEnabled,
scoreDetails,
totalFailedQuestions,
reportFailed,
useImageDetectionConfigFlag
);- Purpose: Creates a detailed report document with all required information.
Gallery Creation
const payload: CreateAttachmentGalleryFromReport = {
reportID: `${siteKey}_${reportKey}`,
siteID: siteKey,
deptID: departmentID,
auditorID: report.auditor,
submittedDate: new Date(report.datetimeSubmitted),
attachmentsMetadata: report.attachmentsMetadata,
questionnaireID: report?.questionnaire || "Team Audit",
sections: report.sections as any,
questions: report.questions,
};
await api.post("/gallery/gallery/report-attachments", payload, options);
console.log(`[DEBUG] Attachment creation completed`);Issue Creation
const payload = {
reportID: `${siteKey}_${reportKey}`,
siteKey,
departmentID: departmentID || "",
report,
};
await postReportIssueWithRetry(internalAPI, payload, options, report);
console.log(`[DEBUG] Report issue posted successfully`);Sending Notifications
if (userEmailTargets.length > 0 && emailNotificationsEnabled) {
await sendEmail(emailTriesRemaining);
}
if (recipientsPushNotificationUids.length > 0 && pushNotificationEnabled) {
for (const auditor of recipientsPushNotificationUids) {
await sendNotification(
auditor,
site.name,
"Your report was just generated. Check your email!"
);
}
}Error Handling
const handleError = async (error: Error, updates: Partial<Classes.Report>) => {
if (retryCount === 0) {
throw error;
}
console.error(error.message);
return await reportQueueHandler(snapshot, context, retryCount - 1);
};Updating the Report Status
report.isGenerated = true;
report.downloadURL = url;
await reportRef.update({ downloadURL: url });
await reportRepositoryMongo.updateByQuery(
{ organizationID: organizationKey, reportID: siteKey + "_" + reportKey },
{ $set: { downloadURL: url } }
);4.6.3 Data Flow
-
Input:
- A Firebase snapshot containing the report data is received.
- The report is identified as a “Team Audit” if it has sections and no
questionnaireIndexID.
-
Validation: The report data is validated to ensure it is complete and has the required fields.
-
Data Fetching: Related data such as site details, organization details, and department information is retrieved.
-
Processing:
- The function determines whether to process a single audit or a team audit.
- For team audits, the report is processed with its sections, and grades are computed.
-
Report Generation: A detailed report document is generated using the
generateReportutility. -
Gallery Creation: An attachment gallery is created from the report data and stored via an API.
-
Issue Creation: Issues related to the report are posted for tracking and further action.
-
Notifications: Email, push, and WhatsApp notifications are sent to relevant users.
-
Output:
- The report status is updated, and a download URL is provided.
- The report is marked as complete, and any remaining tasks are queued.
5. Summary
The Team Audit feature in the ScheduleLite module is a robust implementation that ensures seamless check-in and reporting functionalities. Key highlights include:
-
Route Definitions:
- Clearly defined routes in
scheduleLite.routes.tsensure modularity and ease of access to various functionalities like check-ins, section progress, and reviews.
- Clearly defined routes in
-
Controller Logic:
- The
ScheduleLiteControlleracts as a bridge between the routes and the use cases, handling request validation and response formatting.
- The
-
Use Case Implementations:
- Core business logic resides in
scheduleLite.usecase.ts, where functions likeprocessExistingReportandcheckUserWithinRadiusensure data integrity and user validation.
- Core business logic resides in
-
Error Handling:
- Comprehensive error handling mechanisms are in place to manage invalid inputs, missing data, and system errors.
-
Scalability:
- The modular design of the codebase allows for easy extension of functionalities, making it future-proof for additional features.
This analysis highlights the importance of maintaining a clean architecture and modular design for complex features like Team Audit. The integration of validation, error handling, and business logic ensures a seamless user experience and maintainable codebase.
6. Related Documentation
- [Schedule|[Schedules]]
- [Features/[Notifications/Notifications-Overview|Notifications]]
- Issue Tracker
- Gallery
- Team Audit
- Mobile App Development Guide
- API Documentation
- Schedule Data Model
- Authentication|Authentication]]